Java源码示例:org.springframework.messaging.simp.annotation.support.SimpAnnotationMethodMessageHandler

示例1
@Bean
public SimpAnnotationMethodMessageHandler simpAnnotationMethodMessageHandler() {
	SimpAnnotationMethodMessageHandler handler = createAnnotationMethodMessageHandler();
	handler.setDestinationPrefixes(getBrokerRegistry().getApplicationDestinationPrefixes());
	handler.setMessageConverter(brokerMessageConverter());
	handler.setValidator(simpValidator());

	List<HandlerMethodArgumentResolver> argumentResolvers = new ArrayList<>();
	addArgumentResolvers(argumentResolvers);
	handler.setCustomArgumentResolvers(argumentResolvers);

	List<HandlerMethodReturnValueHandler> returnValueHandlers = new ArrayList<>();
	addReturnValueHandlers(returnValueHandlers);
	handler.setCustomReturnValueHandlers(returnValueHandlers);

	PathMatcher pathMatcher = getBrokerRegistry().getPathMatcher();
	if (pathMatcher != null) {
		handler.setPathMatcher(pathMatcher);
	}
	return handler;
}
 
示例2
@Test
public void clientOutboundChannelUsedByAnnotatedMethod() {
	ApplicationContext context = loadConfig(SimpleBrokerConfig.class);

	TestChannel channel = context.getBean("clientOutboundChannel", TestChannel.class);
	SimpAnnotationMethodMessageHandler messageHandler =
			context.getBean(SimpAnnotationMethodMessageHandler.class);

	StompHeaderAccessor headers = StompHeaderAccessor.create(StompCommand.SUBSCRIBE);
	headers.setSessionId("sess1");
	headers.setSessionAttributes(new ConcurrentHashMap<>());
	headers.setSubscriptionId("subs1");
	headers.setDestination("/foo");
	Message<?> message = MessageBuilder.withPayload(new byte[0]).setHeaders(headers).build();

	messageHandler.handleMessage(message);

	message = channel.messages.get(0);
	headers = StompHeaderAccessor.wrap(message);

	assertEquals(SimpMessageType.MESSAGE, headers.getMessageType());
	assertEquals("/foo", headers.getDestination());
	assertEquals("bar", new String((byte[]) message.getPayload()));
}
 
示例3
@Test
public void brokerChannelUsedByAnnotatedMethod() {
	ApplicationContext context = loadConfig(SimpleBrokerConfig.class);

	TestChannel channel = context.getBean("brokerChannel", TestChannel.class);
	SimpAnnotationMethodMessageHandler messageHandler =
			context.getBean(SimpAnnotationMethodMessageHandler.class);

	StompHeaderAccessor headers = StompHeaderAccessor.create(StompCommand.SEND);
	headers.setSessionId("sess1");
	headers.setSessionAttributes(new ConcurrentHashMap<>());
	headers.setDestination("/foo");
	Message<?> message = MessageBuilder.createMessage(new byte[0], headers.getMessageHeaders());

	messageHandler.handleMessage(message);

	message = channel.messages.get(0);
	headers = StompHeaderAccessor.wrap(message);

	assertEquals(SimpMessageType.MESSAGE, headers.getMessageType());
	assertEquals("/bar", headers.getDestination());
	assertEquals("bar", new String((byte[]) message.getPayload()));
}
 
示例4
@Test
public void customPathMatcher() {
	ApplicationContext context = loadConfig(CustomConfig.class);

	SimpleBrokerMessageHandler broker = context.getBean(SimpleBrokerMessageHandler.class);
	DefaultSubscriptionRegistry registry = (DefaultSubscriptionRegistry) broker.getSubscriptionRegistry();
	assertEquals("a.a", registry.getPathMatcher().combine("a", "a"));

	PathMatcher pathMatcher =
			context.getBean(SimpAnnotationMethodMessageHandler.class).getPathMatcher();

	assertEquals("a.a", pathMatcher.combine("a", "a"));

	DefaultUserDestinationResolver resolver = context.getBean(DefaultUserDestinationResolver.class);
	assertNotNull(resolver);
	assertEquals(false, resolver.isRemoveLeadingSlash());
}
 
示例5
@Test
public void customArgumentAndReturnValueTypes() {
	loadBeanDefinitions("websocket-config-broker-custom-argument-and-return-value-types.xml");

	SimpAnnotationMethodMessageHandler handler = this.appContext.getBean(SimpAnnotationMethodMessageHandler.class);

	List<HandlerMethodArgumentResolver> customResolvers = handler.getCustomArgumentResolvers();
	assertEquals(2, customResolvers.size());
	assertTrue(handler.getArgumentResolvers().contains(customResolvers.get(0)));
	assertTrue(handler.getArgumentResolvers().contains(customResolvers.get(1)));

	List<HandlerMethodReturnValueHandler> customHandlers = handler.getCustomReturnValueHandlers();
	assertEquals(2, customHandlers.size());
	assertTrue(handler.getReturnValueHandlers().contains(customHandlers.get(0)));
	assertTrue(handler.getReturnValueHandlers().contains(customHandlers.get(1)));
}
 
示例6
@Bean
public SimpAnnotationMethodMessageHandler simpAnnotationMethodMessageHandler() {
	SimpAnnotationMethodMessageHandler handler = createAnnotationMethodMessageHandler();
	handler.setDestinationPrefixes(getBrokerRegistry().getApplicationDestinationPrefixes());
	handler.setMessageConverter(brokerMessageConverter());
	handler.setValidator(simpValidator());

	List<HandlerMethodArgumentResolver> argumentResolvers = new ArrayList<>();
	addArgumentResolvers(argumentResolvers);
	handler.setCustomArgumentResolvers(argumentResolvers);

	List<HandlerMethodReturnValueHandler> returnValueHandlers = new ArrayList<>();
	addReturnValueHandlers(returnValueHandlers);
	handler.setCustomReturnValueHandlers(returnValueHandlers);

	PathMatcher pathMatcher = getBrokerRegistry().getPathMatcher();
	if (pathMatcher != null) {
		handler.setPathMatcher(pathMatcher);
	}
	return handler;
}
 
示例7
@Test
public void clientOutboundChannelUsedByAnnotatedMethod() {
	ApplicationContext context = loadConfig(SimpleBrokerConfig.class);

	TestChannel channel = context.getBean("clientOutboundChannel", TestChannel.class);
	SimpAnnotationMethodMessageHandler messageHandler =
			context.getBean(SimpAnnotationMethodMessageHandler.class);

	StompHeaderAccessor headers = StompHeaderAccessor.create(StompCommand.SUBSCRIBE);
	headers.setSessionId("sess1");
	headers.setSessionAttributes(new ConcurrentHashMap<>());
	headers.setSubscriptionId("subs1");
	headers.setDestination("/foo");
	Message<?> message = MessageBuilder.withPayload(new byte[0]).setHeaders(headers).build();

	messageHandler.handleMessage(message);

	message = channel.messages.get(0);
	headers = StompHeaderAccessor.wrap(message);

	assertEquals(SimpMessageType.MESSAGE, headers.getMessageType());
	assertEquals("/foo", headers.getDestination());
	assertEquals("bar", new String((byte[]) message.getPayload()));
}
 
示例8
@Test
public void brokerChannelUsedByAnnotatedMethod() {
	ApplicationContext context = loadConfig(SimpleBrokerConfig.class);

	TestChannel channel = context.getBean("brokerChannel", TestChannel.class);
	SimpAnnotationMethodMessageHandler messageHandler =
			context.getBean(SimpAnnotationMethodMessageHandler.class);

	StompHeaderAccessor headers = StompHeaderAccessor.create(StompCommand.SEND);
	headers.setSessionId("sess1");
	headers.setSessionAttributes(new ConcurrentHashMap<>());
	headers.setDestination("/foo");
	Message<?> message = MessageBuilder.createMessage(new byte[0], headers.getMessageHeaders());

	messageHandler.handleMessage(message);

	message = channel.messages.get(0);
	headers = StompHeaderAccessor.wrap(message);

	assertEquals(SimpMessageType.MESSAGE, headers.getMessageType());
	assertEquals("/bar", headers.getDestination());
	assertEquals("bar", new String((byte[]) message.getPayload()));
}
 
示例9
@Test
public void customPathMatcher() {
	ApplicationContext context = loadConfig(CustomConfig.class);

	SimpleBrokerMessageHandler broker = context.getBean(SimpleBrokerMessageHandler.class);
	DefaultSubscriptionRegistry registry = (DefaultSubscriptionRegistry) broker.getSubscriptionRegistry();
	assertEquals("a.a", registry.getPathMatcher().combine("a", "a"));

	PathMatcher pathMatcher =
			context.getBean(SimpAnnotationMethodMessageHandler.class).getPathMatcher();

	assertEquals("a.a", pathMatcher.combine("a", "a"));

	DefaultUserDestinationResolver resolver = context.getBean(DefaultUserDestinationResolver.class);
	assertNotNull(resolver);
	assertEquals(false, resolver.isRemoveLeadingSlash());
}
 
示例10
@Test
public void customArgumentAndReturnValueTypes() {
	loadBeanDefinitions("websocket-config-broker-custom-argument-and-return-value-types.xml");

	SimpAnnotationMethodMessageHandler handler = this.appContext.getBean(SimpAnnotationMethodMessageHandler.class);

	List<HandlerMethodArgumentResolver> customResolvers = handler.getCustomArgumentResolvers();
	assertEquals(2, customResolvers.size());
	assertTrue(handler.getArgumentResolvers().contains(customResolvers.get(0)));
	assertTrue(handler.getArgumentResolvers().contains(customResolvers.get(1)));

	List<HandlerMethodReturnValueHandler> customHandlers = handler.getCustomReturnValueHandlers();
	assertEquals(2, customHandlers.size());
	assertTrue(handler.getReturnValueHandlers().contains(customHandlers.get(0)));
	assertTrue(handler.getReturnValueHandlers().contains(customHandlers.get(1)));
}
 
示例11
@Bean
public SimpAnnotationMethodMessageHandler simpAnnotationMethodMessageHandler() {
	SimpAnnotationMethodMessageHandler handler = createAnnotationMethodMessageHandler();
	handler.setDestinationPrefixes(getBrokerRegistry().getApplicationDestinationPrefixes());
	handler.setMessageConverter(brokerMessageConverter());
	handler.setValidator(simpValidator());

	List<HandlerMethodArgumentResolver> argumentResolvers = new ArrayList<HandlerMethodArgumentResolver>();
	addArgumentResolvers(argumentResolvers);
	handler.setCustomArgumentResolvers(argumentResolvers);

	List<HandlerMethodReturnValueHandler> returnValueHandlers = new ArrayList<HandlerMethodReturnValueHandler>();
	addReturnValueHandlers(returnValueHandlers);
	handler.setCustomReturnValueHandlers(returnValueHandlers);

	PathMatcher pathMatcher = this.getBrokerRegistry().getPathMatcher();
	if (pathMatcher != null) {
		handler.setPathMatcher(pathMatcher);
	}
	return handler;
}
 
示例12
@Test
public void clientOutboundChannelUsedByAnnotatedMethod() {
	TestChannel channel = this.simpleBrokerContext.getBean("clientOutboundChannel", TestChannel.class);
	SimpAnnotationMethodMessageHandler messageHandler = this.simpleBrokerContext.getBean(SimpAnnotationMethodMessageHandler.class);

	StompHeaderAccessor headers = StompHeaderAccessor.create(StompCommand.SUBSCRIBE);
	headers.setSessionId("sess1");
	headers.setSessionAttributes(new ConcurrentHashMap<>());
	headers.setSubscriptionId("subs1");
	headers.setDestination("/foo");
	Message<?> message = MessageBuilder.withPayload(new byte[0]).setHeaders(headers).build();

	messageHandler.handleMessage(message);

	message = channel.messages.get(0);
	headers = StompHeaderAccessor.wrap(message);

	assertEquals(SimpMessageType.MESSAGE, headers.getMessageType());
	assertEquals("/foo", headers.getDestination());
	assertEquals("bar", new String((byte[]) message.getPayload()));
}
 
示例13
@Test
public void brokerChannelUsedByAnnotatedMethod() {
	TestChannel channel = this.simpleBrokerContext.getBean("brokerChannel", TestChannel.class);
	SimpAnnotationMethodMessageHandler messageHandler =
			this.simpleBrokerContext.getBean(SimpAnnotationMethodMessageHandler.class);

	StompHeaderAccessor headers = StompHeaderAccessor.create(StompCommand.SEND);
	headers.setSessionId("sess1");
	headers.setSessionAttributes(new ConcurrentHashMap<>());
	headers.setDestination("/foo");
	Message<?> message = MessageBuilder.createMessage(new byte[0], headers.getMessageHeaders());

	messageHandler.handleMessage(message);

	message = channel.messages.get(0);
	headers = StompHeaderAccessor.wrap(message);

	assertEquals(SimpMessageType.MESSAGE, headers.getMessageType());
	assertEquals("/bar", headers.getDestination());
	assertEquals("bar", new String((byte[]) message.getPayload()));
}
 
示例14
@Test
public void customChannels() {
	loadBeanDefinitions("websocket-config-broker-customchannels.xml");

	List<Class<? extends MessageHandler>> subscriberTypes =
			Arrays.<Class<? extends MessageHandler>>asList(SimpAnnotationMethodMessageHandler.class,
					UserDestinationMessageHandler.class, SimpleBrokerMessageHandler.class);

	testChannel("clientInboundChannel", subscriberTypes, 3);
	testExecutor("clientInboundChannel", 100, 200, 600);

	subscriberTypes = Collections.singletonList(SubProtocolWebSocketHandler.class);

	testChannel("clientOutboundChannel", subscriberTypes, 3);
	testExecutor("clientOutboundChannel", 101, 201, 601);

	subscriberTypes = Arrays.<Class<? extends MessageHandler>>asList(SimpleBrokerMessageHandler.class,
			UserDestinationMessageHandler.class);

	testChannel("brokerChannel", subscriberTypes, 1);
	testExecutor("brokerChannel", 102, 202, 602);
}
 
示例15
@Test
public void customArgumentAndReturnValueTypes() {
	loadBeanDefinitions("websocket-config-broker-custom-argument-and-return-value-types.xml");

	SimpAnnotationMethodMessageHandler handler = this.appContext.getBean(SimpAnnotationMethodMessageHandler.class);

	List<HandlerMethodArgumentResolver> customResolvers = handler.getCustomArgumentResolvers();
	assertEquals(2, customResolvers.size());
	assertTrue(handler.getArgumentResolvers().contains(customResolvers.get(0)));
	assertTrue(handler.getArgumentResolvers().contains(customResolvers.get(1)));

	List<HandlerMethodReturnValueHandler> customHandlers = handler.getCustomReturnValueHandlers();
	assertEquals(2, customHandlers.size());
	assertTrue(handler.getReturnValueHandlers().contains(customHandlers.get(0)));
	assertTrue(handler.getReturnValueHandlers().contains(customHandlers.get(1)));
}
 
示例16
@Test
public void clientInboundChannel() {
	ApplicationContext context = loadConfig(SimpleBrokerConfig.class);

	TestChannel channel = context.getBean("clientInboundChannel", TestChannel.class);
	Set<MessageHandler> handlers = channel.getSubscribers();

	assertEquals(3, handlers.size());
	assertTrue(handlers.contains(context.getBean(SimpAnnotationMethodMessageHandler.class)));
	assertTrue(handlers.contains(context.getBean(UserDestinationMessageHandler.class)));
	assertTrue(handlers.contains(context.getBean(SimpleBrokerMessageHandler.class)));
}
 
示例17
@Test
public void clientInboundChannelWithBrokerRelay() {
	ApplicationContext context = loadConfig(BrokerRelayConfig.class);

	TestChannel channel = context.getBean("clientInboundChannel", TestChannel.class);
	Set<MessageHandler> handlers = channel.getSubscribers();

	assertEquals(3, handlers.size());
	assertTrue(handlers.contains(context.getBean(SimpAnnotationMethodMessageHandler.class)));
	assertTrue(handlers.contains(context.getBean(UserDestinationMessageHandler.class)));
	assertTrue(handlers.contains(context.getBean(StompBrokerRelayMessageHandler.class)));
}
 
示例18
@Test
public void customArgumentAndReturnValueTypes() {
	ApplicationContext context = loadConfig(CustomConfig.class);

	SimpAnnotationMethodMessageHandler handler =
			context.getBean(SimpAnnotationMethodMessageHandler.class);

	List<HandlerMethodArgumentResolver> customResolvers = handler.getCustomArgumentResolvers();
	assertEquals(1, customResolvers.size());
	assertTrue(handler.getArgumentResolvers().contains(customResolvers.get(0)));

	List<HandlerMethodReturnValueHandler> customHandlers = handler.getCustomReturnValueHandlers();
	assertEquals(1, customHandlers.size());
	assertTrue(handler.getReturnValueHandlers().contains(customHandlers.get(0)));
}
 
示例19
@Test
public void simpValidatorInjected() {
	ApplicationContext context = loadConfig(SimpleBrokerConfig.class);

	SimpAnnotationMethodMessageHandler messageHandler =
			context.getBean(SimpAnnotationMethodMessageHandler.class);

	assertThat(messageHandler.getValidator(), Matchers.notNullValue(Validator.class));
}
 
示例20
@Test
public void customChannels() {
	loadBeanDefinitions("websocket-config-broker-customchannels.xml");

	SimpAnnotationMethodMessageHandler annotationMethodMessageHandler =
			this.appContext.getBean(SimpAnnotationMethodMessageHandler.class);

	Validator validator = annotationMethodMessageHandler.getValidator();
	assertNotNull(validator);
	assertSame(this.appContext.getBean("myValidator"), validator);
	assertThat(validator, Matchers.instanceOf(TestValidator.class));

	List<Class<? extends MessageHandler>> subscriberTypes = Arrays.asList(SimpAnnotationMethodMessageHandler.class,
			UserDestinationMessageHandler.class, SimpleBrokerMessageHandler.class);

	testChannel("clientInboundChannel", subscriberTypes, 3);
	testExecutor("clientInboundChannel", 100, 200, 600);

	subscriberTypes = Collections.singletonList(SubProtocolWebSocketHandler.class);

	testChannel("clientOutboundChannel", subscriberTypes, 3);
	testExecutor("clientOutboundChannel", 101, 201, 601);

	subscriberTypes = Arrays.asList(SimpleBrokerMessageHandler.class, UserDestinationMessageHandler.class);

	testChannel("brokerChannel", subscriberTypes, 1);
	testExecutor("brokerChannel", 102, 202, 602);
}
 
示例21
@Test
public void clientInboundChannel() {
	ApplicationContext context = loadConfig(SimpleBrokerConfig.class);

	TestChannel channel = context.getBean("clientInboundChannel", TestChannel.class);
	Set<MessageHandler> handlers = channel.getSubscribers();

	assertEquals(3, handlers.size());
	assertTrue(handlers.contains(context.getBean(SimpAnnotationMethodMessageHandler.class)));
	assertTrue(handlers.contains(context.getBean(UserDestinationMessageHandler.class)));
	assertTrue(handlers.contains(context.getBean(SimpleBrokerMessageHandler.class)));
}
 
示例22
@Test
public void clientInboundChannelWithBrokerRelay() {
	ApplicationContext context = loadConfig(BrokerRelayConfig.class);

	TestChannel channel = context.getBean("clientInboundChannel", TestChannel.class);
	Set<MessageHandler> handlers = channel.getSubscribers();

	assertEquals(3, handlers.size());
	assertTrue(handlers.contains(context.getBean(SimpAnnotationMethodMessageHandler.class)));
	assertTrue(handlers.contains(context.getBean(UserDestinationMessageHandler.class)));
	assertTrue(handlers.contains(context.getBean(StompBrokerRelayMessageHandler.class)));
}
 
示例23
@Test
public void customArgumentAndReturnValueTypes() {
	ApplicationContext context = loadConfig(CustomConfig.class);

	SimpAnnotationMethodMessageHandler handler =
			context.getBean(SimpAnnotationMethodMessageHandler.class);

	List<HandlerMethodArgumentResolver> customResolvers = handler.getCustomArgumentResolvers();
	assertEquals(1, customResolvers.size());
	assertTrue(handler.getArgumentResolvers().contains(customResolvers.get(0)));

	List<HandlerMethodReturnValueHandler> customHandlers = handler.getCustomReturnValueHandlers();
	assertEquals(1, customHandlers.size());
	assertTrue(handler.getReturnValueHandlers().contains(customHandlers.get(0)));
}
 
示例24
@Test
public void simpValidatorInjected() {
	ApplicationContext context = loadConfig(SimpleBrokerConfig.class);

	SimpAnnotationMethodMessageHandler messageHandler =
			context.getBean(SimpAnnotationMethodMessageHandler.class);

	assertThat(messageHandler.getValidator(), Matchers.notNullValue(Validator.class));
}
 
示例25
@Test
public void customChannels() {
	loadBeanDefinitions("websocket-config-broker-customchannels.xml");

	SimpAnnotationMethodMessageHandler annotationMethodMessageHandler =
			this.appContext.getBean(SimpAnnotationMethodMessageHandler.class);

	Validator validator = annotationMethodMessageHandler.getValidator();
	assertNotNull(validator);
	assertSame(this.appContext.getBean("myValidator"), validator);
	assertThat(validator, Matchers.instanceOf(TestValidator.class));

	List<Class<? extends MessageHandler>> subscriberTypes = Arrays.asList(SimpAnnotationMethodMessageHandler.class,
			UserDestinationMessageHandler.class, SimpleBrokerMessageHandler.class);

	testChannel("clientInboundChannel", subscriberTypes, 3);
	testExecutor("clientInboundChannel", 100, 200, 600);

	subscriberTypes = Collections.singletonList(SubProtocolWebSocketHandler.class);

	testChannel("clientOutboundChannel", subscriberTypes, 3);
	testExecutor("clientOutboundChannel", 101, 201, 601);

	subscriberTypes = Arrays.asList(SimpleBrokerMessageHandler.class, UserDestinationMessageHandler.class);

	testChannel("brokerChannel", subscriberTypes, 1);
	testExecutor("brokerChannel", 102, 202, 602);
}
 
示例26
@Test
public void clientInboundChannel() {
	TestChannel channel = this.simpleBrokerContext.getBean("clientInboundChannel", TestChannel.class);
	Set<MessageHandler> handlers = channel.getSubscribers();

	assertEquals(3, handlers.size());
	assertTrue(handlers.contains(simpleBrokerContext.getBean(SimpAnnotationMethodMessageHandler.class)));
	assertTrue(handlers.contains(simpleBrokerContext.getBean(UserDestinationMessageHandler.class)));
	assertTrue(handlers.contains(simpleBrokerContext.getBean(SimpleBrokerMessageHandler.class)));
}
 
示例27
@Test
public void clientInboundChannelWithBrokerRelay() {
	TestChannel channel = this.brokerRelayContext.getBean("clientInboundChannel", TestChannel.class);
	Set<MessageHandler> handlers = channel.getSubscribers();

	assertEquals(3, handlers.size());
	assertTrue(handlers.contains(brokerRelayContext.getBean(SimpAnnotationMethodMessageHandler.class)));
	assertTrue(handlers.contains(brokerRelayContext.getBean(UserDestinationMessageHandler.class)));
	assertTrue(handlers.contains(brokerRelayContext.getBean(StompBrokerRelayMessageHandler.class)));
}
 
示例28
@Test
public void customArgumentAndReturnValueTypes() throws Exception {
	SimpAnnotationMethodMessageHandler handler = this.customContext.getBean(SimpAnnotationMethodMessageHandler.class);

	List<HandlerMethodArgumentResolver> customResolvers = handler.getCustomArgumentResolvers();
	assertEquals(1, customResolvers.size());
	assertTrue(handler.getArgumentResolvers().contains(customResolvers.get(0)));

	List<HandlerMethodReturnValueHandler> customHandlers = handler.getCustomReturnValueHandlers();
	assertEquals(1, customHandlers.size());
	assertTrue(handler.getReturnValueHandlers().contains(customHandlers.get(0)));
}
 
示例29
@Test
public void simpValidatorInjected() {
	SimpAnnotationMethodMessageHandler messageHandler =
			this.simpleBrokerContext.getBean(SimpAnnotationMethodMessageHandler.class);

	assertThat(messageHandler.getValidator(), Matchers.notNullValue(Validator.class));
}
 
示例30
@Test
public void customPathMatcher() {
	SimpleBrokerMessageHandler broker = this.customContext.getBean(SimpleBrokerMessageHandler.class);
	DefaultSubscriptionRegistry registry = (DefaultSubscriptionRegistry) broker.getSubscriptionRegistry();
	assertEquals("a.a", registry.getPathMatcher().combine("a", "a"));

	SimpAnnotationMethodMessageHandler handler = this.customContext.getBean(SimpAnnotationMethodMessageHandler.class);
	assertEquals("a.a", handler.getPathMatcher().combine("a", "a"));
}