/*
* Copyright (c) 2008-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.cometd.annotation;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.inject.Inject;
import org.cometd.bayeux.ChannelId;
import org.cometd.bayeux.MarkedReference;
import org.cometd.bayeux.Message;
import org.cometd.bayeux.server.BayeuxServer;
import org.cometd.bayeux.server.ConfigurableServerChannel;
import org.cometd.bayeux.server.LocalSession;
import org.cometd.bayeux.server.ServerChannel;
import org.cometd.bayeux.server.ServerMessage;
import org.cometd.bayeux.server.ServerSession;
import org.cometd.server.BayeuxServerImpl;
import org.cometd.server.ServerSessionImpl;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
public class ServerAnnotationProcessorTest {
private BayeuxServerImpl bayeuxServer;
private ServerAnnotationProcessor processor;
@Before
public void init() throws Exception {
bayeuxServer = new BayeuxServerImpl();
bayeuxServer.start();
processor = new ServerAnnotationProcessor(bayeuxServer);
}
@After
public void destroy() throws Exception {
bayeuxServer.stop();
}
@Test
public void testNull() throws Exception {
boolean processed = processor.process(null);
assertFalse(processed);
}
@Test
public void testNonServiceAnnotatedClass() throws Exception {
NonServiceAnnotatedService s = new NonServiceAnnotatedService();
boolean processed = processor.process(s);
assertFalse(processed);
assertNull(s.bayeux);
}
public static class NonServiceAnnotatedService {
@Inject
private BayeuxServer bayeux;
}
@Test
public void testInjectBayeuxServerOnField() throws Exception {
InjectBayeuxServerOnFieldService s = new InjectBayeuxServerOnFieldService();
boolean processed = processor.process(s);
assertTrue(processed);
assertNotNull(s.bayeux);
}
@Service
public static class InjectBayeuxServerOnFieldService {
@Inject
private BayeuxServer bayeux;
}
@Test
public void testInjectBayeuxServerOnMethod() throws Exception {
InjectBayeuxServerOnMethodService s = new InjectBayeuxServerOnMethodService();
boolean processed = processor.process(s);
assertTrue(processed);
assertNotNull(s.bayeux);
}
@Service
public static class InjectBayeuxServerOnMethodService {
private BayeuxServer bayeux;
@Inject
private void setBayeuxServer(BayeuxServer bayeuxServer) {
this.bayeux = bayeuxServer;
}
}
@Test
public void testInjectBayeuxServerOnOverriddenMethod() throws Exception {
DerivedInjectBayeuxServerOnOverriddenMethodService s = new DerivedInjectBayeuxServerOnOverriddenMethodService();
boolean processed = processor.process(s);
assertFalse(processed);
assertNull(s.bayeux);
}
@Service
public static class InjectBayeuxServerOnOverriddenMethodService {
protected BayeuxServer bayeux;
@Inject
protected void setBayeuxServer(BayeuxServer bayeuxServer) {
this.bayeux = bayeuxServer;
}
}
public static class DerivedInjectBayeuxServerOnOverriddenMethodService extends InjectBayeuxServerOnOverriddenMethodService {
// Overrides without annotation.
@Override
protected void setBayeuxServer(BayeuxServer bayeuxServer) {
super.setBayeuxServer(bayeuxServer);
}
}
@Test
public void testInjectBayeuxServerOnOverridingMethod() throws Exception {
DerivedInjectBayeuxServerOnOverridingMethodService s = new DerivedInjectBayeuxServerOnOverridingMethodService();
boolean processed = processor.process(s);
assertTrue(processed);
assertNotNull(s.bayeux);
}
@Service
public static class InjectBayeuxServerOnOverridingMethodService {
protected BayeuxServer bayeux;
@Inject
protected void setBayeuxServer(BayeuxServer bayeuxServer) {
this.bayeux = bayeuxServer;
}
}
public static class DerivedInjectBayeuxServerOnOverridingMethodService extends InjectBayeuxServerOnOverridingMethodService {
// Overrides with annotation.
@Override
@Inject
protected void setBayeuxServer(BayeuxServer bayeuxServer) {
super.setBayeuxServer(bayeuxServer);
}
}
@Test
public void testInjectLocalSessionOnField() throws Exception {
InjectLocalSessionOnFieldService s = new InjectLocalSessionOnFieldService();
boolean processed = processor.process(s);
assertTrue(processed);
assertNotNull(s.localSession);
}
@Service
public static class InjectLocalSessionOnFieldService {
@Session
private LocalSession localSession;
}
@Test
public void testInjectLocalSessionOnMethod() throws Exception {
InjectLocalSessionOnMethodService s = new InjectLocalSessionOnMethodService();
boolean processed = processor.process(s);
assertTrue(processed);
assertNotNull(s.localSession);
}
@Service
public static class InjectLocalSessionOnMethodService {
private LocalSession localSession;
@Session
private void set(LocalSession localSession) {
this.localSession = localSession;
}
}
@Test
public void testInjectLocalSessionOnOverriddenMethod() throws Exception {
DerivedInjectLocalSessionOnOverriddenMethodService s = new DerivedInjectLocalSessionOnOverriddenMethodService();
boolean processed = processor.process(s);
assertFalse(processed);
assertNull(s.localSession);
}
@Service
public static class InjectLocalSessionOnOverriddenMethodService {
protected LocalSession localSession;
@Session
protected void set(LocalSession localSession) {
this.localSession = localSession;
}
}
public static class DerivedInjectLocalSessionOnOverriddenMethodService extends InjectLocalSessionOnOverriddenMethodService {
// Overrides without annotation.
@Override
protected void set(LocalSession localSession) {
super.set(localSession);
}
}
@Test
public void testInjectLocalSessionOnOverridingMethod() throws Exception {
DerivedInjectLocalSessionOnOverridingMethodService s = new DerivedInjectLocalSessionOnOverridingMethodService();
boolean processed = processor.process(s);
assertTrue(processed);
assertNotNull(s.localSession);
}
@Service
public static class InjectLocalSessionOnOverridingMethodService {
protected LocalSession localSession;
@Session
protected void set(LocalSession localSession) {
this.localSession = localSession;
}
}
public static class DerivedInjectLocalSessionOnOverridingMethodService extends InjectLocalSessionOnOverridingMethodService {
// Overrides with annotation.
@Override
@Session
protected void set(LocalSession localSession) {
super.set(localSession);
}
}
@Test
public void testInjectServerSessionOnField() throws Exception {
InjectServerSessionOnFieldService s = new InjectServerSessionOnFieldService();
boolean processed = processor.process(s);
assertTrue(processed);
assertNotNull(s.serverSession);
}
@Service
public static class InjectServerSessionOnFieldService {
@Session
private ServerSession serverSession;
}
@Test
public void testInjectServerSessionOnMethod() throws Exception {
InjectServerSessionOnMethodService s = new InjectServerSessionOnMethodService();
boolean processed = processor.process(s);
assertTrue(processed);
assertNotNull(s.serverSession);
}
@Service
public static class InjectServerSessionOnMethodService {
private ServerSession serverSession;
@Session
private void set(ServerSession serverSession) {
this.serverSession = serverSession;
}
}
@Test
public void testInjectLocalSessionAndServerSession() throws Exception {
InjectLocalSessionAndServerSessionService s = new InjectLocalSessionAndServerSessionService();
boolean processed = processor.process(s);
assertTrue(processed);
assertNotNull(s.localSession);
assertNotNull(s.serverSession);
assertSame(s.localSession.getServerSession(), s.serverSession);
}
@Service
public static class InjectLocalSessionAndServerSessionService {
@Session
private LocalSession localSession;
@Session
private ServerSession serverSession;
}
@Test
public void testListenUnlisten() throws Exception {
final AtomicReference<ServerSession> sessionRef = new AtomicReference<>();
final AtomicReference<ServerMessage> messageRef = new AtomicReference<>();
ListenUnlistenService s = new ListenUnlistenService(sessionRef, messageRef);
boolean processed = processor.process(s);
assertTrue(processed);
ServerChannel channel = bayeuxServer.getChannel("/foo");
assertNotNull(channel);
assertEquals(1, channel.getListeners().size());
// Fake a publish
LocalSession remote = bayeuxServer.newLocalSession("remote");
remote.handshake();
ServerMessage.Mutable message = bayeuxServer.newMessage();
message.setChannel(channel.getId());
bayeuxServer.handle((ServerSessionImpl)remote.getServerSession(), message);
assertNotNull(sessionRef.get());
assertSame(sessionRef.get(), remote.getServerSession());
assertNotNull(messageRef.get());
processed = processor.deprocessCallbacks(s);
assertTrue(processed);
// Fake another publish
sessionRef.set(null);
messageRef.set(null);
message = bayeuxServer.newMessage();
message.setChannel(channel.getId());
bayeuxServer.handle((ServerSessionImpl)remote.getServerSession(), message);
assertNull(sessionRef.get());
assertNull(messageRef.get());
// Be sure the channel is removed after few sweeps
for (int i = 0; i < 3; ++i) {
bayeuxServer.sweep();
}
assertNull(bayeuxServer.getChannel(channel.getId()));
}
@Service
public static class ListenUnlistenService {
private final AtomicReference<ServerSession> sessionRef;
private final AtomicReference<ServerMessage> messageRef;
public ListenUnlistenService(AtomicReference<ServerSession> sessionRef, AtomicReference<ServerMessage> messageRef) {
this.sessionRef = sessionRef;
this.messageRef = messageRef;
}
@Listener("/foo")
public void foo(ServerSession remote, ServerMessage.Mutable message) {
assertNotNull(remote);
assertNotNull(message);
sessionRef.set(remote);
messageRef.set(message);
}
}
@Test
public void testListenerPublishingOnOwnChannelDoesNotReceive() throws Exception {
final AtomicInteger counter = new AtomicInteger();
ListenerPublishingOnOwnChannelDoesNotReceiveService s = new ListenerPublishingOnOwnChannelDoesNotReceiveService(counter);
boolean processed = processor.process(s);
assertTrue(processed);
// Fake a publish
LocalSession remote = bayeuxServer.newLocalSession("remote");
remote.handshake();
ServerMessage.Mutable message = bayeuxServer.newMessage();
message.setChannel("/foo/bar");
bayeuxServer.handle((ServerSessionImpl)remote.getServerSession(), message);
assertEquals(1, counter.get());
}
@Service
public static class ListenerPublishingOnOwnChannelDoesNotReceiveService {
private final AtomicInteger counter;
@Inject
private BayeuxServer bayeuxServer;
@Session
private ServerSession serverSession;
public ListenerPublishingOnOwnChannelDoesNotReceiveService(AtomicInteger counter) {
this.counter = counter;
}
@Listener("/foo/*")
public void foo(ServerSession remote, ServerMessage.Mutable message) {
int count = counter.incrementAndGet();
String channelName = "/foo/own";
MarkedReference<ServerChannel> channel = bayeuxServer.createChannelIfAbsent(channelName);
// This callback should be called only once, triggered by the client's publish
// However if the Listener.receiveOwnPublishes attribute is not taken in account
// this callback is called again, and we want to test that this does not happen.
if (count == 1) {
channel.getReference().publish(serverSession, new HashMap<>());
}
}
}
@Test
public void testListenerPublishingOnOwnChannelReceives() throws Exception {
final AtomicInteger counter = new AtomicInteger();
ListenerPublishingOnOwnChannelReceivesService s = new ListenerPublishingOnOwnChannelReceivesService(counter);
boolean processed = processor.process(s);
assertTrue(processed);
// Fake a publish
LocalSession remote = bayeuxServer.newLocalSession("remote");
remote.handshake();
ServerMessage.Mutable message = bayeuxServer.newMessage();
message.setChannel("/foo/bar");
bayeuxServer.handle((ServerSessionImpl)remote.getServerSession(), message);
assertEquals(2, counter.get());
}
@Service
public static class ListenerPublishingOnOwnChannelReceivesService {
private final AtomicInteger counter;
@Inject
private BayeuxServer bayeuxServer;
@Session
private ServerSession serverSession;
public ListenerPublishingOnOwnChannelReceivesService(AtomicInteger counter) {
this.counter = counter;
}
@Listener(value = "/foo/*", receiveOwnPublishes = true)
public void foo(ServerSession remote, ServerMessage.Mutable message) {
counter.incrementAndGet();
String channelName = "/foo/own";
MarkedReference<ServerChannel> channel = bayeuxServer.createChannelIfAbsent(channelName);
if (!channelName.equals(message.getChannel())) {
channel.getReference().publish(serverSession, new HashMap<>());
}
}
}
@Test
public void testSubscribeUnsubscribe() throws Exception {
final AtomicReference<Message> messageRef = new AtomicReference<>();
SubscribeUnsubscribeService s = new SubscribeUnsubscribeService(messageRef);
boolean processed = processor.process(s);
assertTrue(processed);
// Fake a publish
LocalSession remote = bayeuxServer.newLocalSession("remote");
remote.handshake();
ServerMessage.Mutable message = bayeuxServer.newMessage();
message.setChannel("/foo/bar/baz");
message.setData(new HashMap<>());
bayeuxServer.handle((ServerSessionImpl)remote.getServerSession(), message);
assertNotNull(messageRef.get());
processed = processor.deprocessCallbacks(s);
assertTrue(processed);
// Fake another publish
messageRef.set(null);
message = bayeuxServer.newMessage();
message.setChannel("/foo/bar/baz");
message.setData(new HashMap<>());
bayeuxServer.handle((ServerSessionImpl)remote.getServerSession(), message);
assertNull(messageRef.get());
}
@Service
public static class SubscribeUnsubscribeService {
private final AtomicReference<Message> messageRef;
@Session
private LocalSession serverSession;
public SubscribeUnsubscribeService(AtomicReference<Message> messageRef) {
this.messageRef = messageRef;
}
@Subscription("/foo/**")
public void foo(Message message) {
messageRef.set(message);
}
}
@Test
public void testListenerOnOverriddenMethod() throws Exception {
final CountDownLatch messageLatch = new CountDownLatch(2);
DerivedListenerOnOverriddenMethodService ss = new DerivedListenerOnOverriddenMethodService(messageLatch);
boolean processed = processor.process(ss);
assertFalse(processed);
// Fake a publish
LocalSession remote = bayeuxServer.newLocalSession("remote");
remote.handshake();
remote.getChannel("/foo").publish(new HashSet<>());
assertFalse(messageLatch.await(1, TimeUnit.SECONDS));
assertEquals(2, messageLatch.getCount());
}
@Service
public static class ListenerOnOverriddenMethodService {
protected final CountDownLatch messageLatch;
public ListenerOnOverriddenMethodService(CountDownLatch messageLatch) {
this.messageLatch = messageLatch;
}
@Listener("/foo")
public void foo(ServerSession remote, ServerMessage.Mutable message) {
messageLatch.countDown();
}
}
public static class DerivedListenerOnOverriddenMethodService extends ListenerOnOverriddenMethodService {
public DerivedListenerOnOverriddenMethodService(CountDownLatch messageLatch) {
super(messageLatch);
}
// Overrides without annotation.
@Override
public void foo(ServerSession remote, ServerMessage.Mutable message) {
super.foo(remote, message);
messageLatch.countDown();
}
}
@Test
public void testListenerOnOverridingMethod() throws Exception {
final CountDownLatch messageLatch = new CountDownLatch(2);
DerivedListenerOnOverridingMethodService ss = new DerivedListenerOnOverridingMethodService(messageLatch);
boolean processed = processor.process(ss);
assertTrue(processed);
assertEquals(1, bayeuxServer.getChannel("/foo").getListeners().size());
// Fake a publish
LocalSession remote = bayeuxServer.newLocalSession("remote");
remote.handshake();
remote.getChannel("/foo").publish(new HashSet<>());
assertTrue(messageLatch.await(5, TimeUnit.SECONDS));
}
@Service
public static class ListenerOnOverridingMethodService {
protected final CountDownLatch messageLatch;
public ListenerOnOverridingMethodService(CountDownLatch messageLatch) {
this.messageLatch = messageLatch;
}
@Listener("/foo")
public void foo(ServerSession remote, ServerMessage.Mutable message) {
messageLatch.countDown();
}
}
public static class DerivedListenerOnOverridingMethodService extends ListenerOnOverridingMethodService {
public DerivedListenerOnOverridingMethodService(CountDownLatch messageLatch) {
super(messageLatch);
}
// Overridden with annotation.
@Override
@Listener("/foo")
public void foo(ServerSession remote, ServerMessage.Mutable message) {
super.foo(remote, message);
messageLatch.countDown();
}
}
@Test
public void testSubscriptionOnOverriddenMethod() throws Exception {
final CountDownLatch messageLatch = new CountDownLatch(2);
DerivedSubscriptionOnOverriddenMethodService ss = new DerivedSubscriptionOnOverriddenMethodService(messageLatch);
boolean processed = processor.process(ss);
assertFalse(processed);
// Fake a publish
LocalSession remote = bayeuxServer.newLocalSession("remote");
remote.handshake();
remote.getChannel("/foo").publish(new HashSet<>());
assertFalse(messageLatch.await(1, TimeUnit.SECONDS));
assertEquals(2, messageLatch.getCount());
}
@Service
public static class SubscriptionOnOverriddenMethodService {
protected final CountDownLatch messageLatch;
public SubscriptionOnOverriddenMethodService(CountDownLatch messageLatch) {
this.messageLatch = messageLatch;
}
@Subscription("/foo")
public void foo(Message message) {
messageLatch.countDown();
}
}
public static class DerivedSubscriptionOnOverriddenMethodService extends SubscriptionOnOverriddenMethodService {
public DerivedSubscriptionOnOverriddenMethodService(CountDownLatch messageLatch) {
super(messageLatch);
}
// Overrides without annotation.
@Override
public void foo(Message message) {
super.foo(message);
messageLatch.countDown();
}
}
@Test
public void testSubscriptionOnOverridingMethod() throws Exception {
final CountDownLatch messageLatch = new CountDownLatch(2);
DerivedSubscriptionOnOverridingMethodService ss = new DerivedSubscriptionOnOverridingMethodService(messageLatch);
boolean processed = processor.process(ss);
assertTrue(processed);
assertEquals(1, bayeuxServer.getChannel("/foo").getSubscribers().size());
// Fake a publish
LocalSession remote = bayeuxServer.newLocalSession("remote");
remote.handshake();
remote.getChannel("/foo").publish(new HashSet<>());
assertTrue(messageLatch.await(5, TimeUnit.SECONDS));
}
@Service
public static class SubscriptionOnOverridingMethodService {
protected final CountDownLatch messageLatch;
public SubscriptionOnOverridingMethodService(CountDownLatch messageLatch) {
this.messageLatch = messageLatch;
}
@Subscription("/foo")
public void foo(Message message) {
messageLatch.countDown();
}
}
public static class DerivedSubscriptionOnOverridingMethodService extends SubscriptionOnOverridingMethodService {
public DerivedSubscriptionOnOverridingMethodService(CountDownLatch messageLatch) {
super(messageLatch);
}
// Overridden with annotation.
@Override
@Subscription("/foo")
public void foo(Message message) {
super.foo(message);
messageLatch.countDown();
}
}
@Test
public void testRemoteCallOnOverriddenMethod() throws Exception {
final CountDownLatch messageLatch = new CountDownLatch(2);
DerivedRemoteCallOnOverriddenMethodService ss = new DerivedRemoteCallOnOverriddenMethodService(messageLatch);
boolean processed = processor.process(ss);
assertFalse(processed);
// Fake a publish
LocalSession remote = bayeuxServer.newLocalSession("remote");
remote.handshake();
remote.getChannel("/service/foo").publish(new HashSet<>());
assertFalse(messageLatch.await(1, TimeUnit.SECONDS));
assertEquals(2, messageLatch.getCount());
}
@Service
public static class RemoteCallOnOverriddenMethodService {
protected final CountDownLatch messageLatch;
public RemoteCallOnOverriddenMethodService(CountDownLatch messageLatch) {
this.messageLatch = messageLatch;
}
@RemoteCall("/foo")
public void foo(RemoteCall.Caller caller, Object data) {
caller.result(data);
messageLatch.countDown();
}
}
public static class DerivedRemoteCallOnOverriddenMethodService extends RemoteCallOnOverriddenMethodService {
public DerivedRemoteCallOnOverriddenMethodService(CountDownLatch messageLatch) {
super(messageLatch);
}
// Overrides without annotation.
@Override
public void foo(RemoteCall.Caller caller, Object data) {
super.foo(caller, data);
messageLatch.countDown();
}
}
@Test
public void testRemoteCallOnOverridingMethod() throws Exception {
final CountDownLatch messageLatch = new CountDownLatch(2);
DerivedRemoteCallOnOverridingMethodService ss = new DerivedRemoteCallOnOverridingMethodService(messageLatch);
boolean processed = processor.process(ss);
assertTrue(processed);
assertEquals(1, bayeuxServer.getChannel("/service/foo").getListeners().size());
// Fake a publish
LocalSession remote = bayeuxServer.newLocalSession("remote");
remote.handshake();
remote.getChannel("/service/foo").publish(new HashSet<>());
assertTrue(messageLatch.await(5, TimeUnit.SECONDS));
}
@Service
public static class RemoteCallOnOverridingMethodService {
protected final CountDownLatch messageLatch;
public RemoteCallOnOverridingMethodService(CountDownLatch messageLatch) {
this.messageLatch = messageLatch;
}
@RemoteCall("/foo")
public void foo(RemoteCall.Caller caller, Object data) {
caller.result(data);
messageLatch.countDown();
}
}
public static class DerivedRemoteCallOnOverridingMethodService extends RemoteCallOnOverridingMethodService {
public DerivedRemoteCallOnOverridingMethodService(CountDownLatch messageLatch) {
super(messageLatch);
}
// Overridden with annotation.
@Override
@RemoteCall("/foo")
public void foo(RemoteCall.Caller caller, Object data) {
super.foo(caller, data);
messageLatch.countDown();
}
}
@Test
public void testListenerMethodWithCovariantParameters() throws Exception {
final CountDownLatch messageLatch = new CountDownLatch(1);
ListenerMethodWithCovariantParametersService s = new ListenerMethodWithCovariantParametersService(messageLatch);
boolean processed = processor.process(s);
assertTrue(processed);
// Fake a publish
LocalSession remote = bayeuxServer.newLocalSession("remote");
remote.handshake();
ServerMessage.Mutable message = bayeuxServer.newMessage();
message.setChannel("/foo");
message.setData(new HashMap());
bayeuxServer.handle((ServerSessionImpl)remote.getServerSession(), message);
assertTrue(messageLatch.await(5, TimeUnit.SECONDS));
}
@Service
public static class ListenerMethodWithCovariantParametersService {
private final CountDownLatch messageLatch;
public ListenerMethodWithCovariantParametersService(CountDownLatch messageLatch) {
this.messageLatch = messageLatch;
}
@Listener("/foo")
public void foo(org.cometd.bayeux.Session remote, Message message) {
messageLatch.countDown();
}
}
@Test
public void testListenerMethodReturningNewBooleanFalse() throws Exception {
final CountDownLatch messageLatch = new CountDownLatch(1);
ListenerMethodReturningNewBooleanFalseService s = new ListenerMethodReturningNewBooleanFalseService(messageLatch);
boolean processed = processor.process(s);
assertTrue(processed);
// Fake a publish
LocalSession remote = bayeuxServer.newLocalSession("remote");
remote.handshake();
ServerMessage.Mutable message = bayeuxServer.newMessage();
message.setChannel("/foo");
message.setData(new HashMap());
bayeuxServer.handle((ServerSessionImpl)remote.getServerSession(), message);
assertFalse(messageLatch.await(1, TimeUnit.SECONDS));
}
@Service
public static class ListenerMethodReturningNewBooleanFalseService {
private final CountDownLatch messageLatch;
public ListenerMethodReturningNewBooleanFalseService(CountDownLatch messageLatch) {
this.messageLatch = messageLatch;
}
@Listener("/foo")
public Object foo(ServerSession remote, ServerMessage.Mutable message) {
// Do not unbox it, we are testing exactly this case.
return new Boolean(false);
}
@Subscription("/foo")
public void foo(Message message) {
messageLatch.countDown();
}
}
@Test
public void testLifecycleMethodsWithWrongReturnType() throws Exception {
LifecycleMethodsWithWrongReturnTypeService s = new LifecycleMethodsWithWrongReturnTypeService();
try {
processor.processPostConstruct(s);
fail();
} catch (RuntimeException x) {
}
try {
processor.processPreDestroy(s);
fail();
} catch (RuntimeException x) {
}
}
@Service
public static class LifecycleMethodsWithWrongReturnTypeService {
@PostConstruct
public Object init() {
return null;
}
@PreDestroy
public Object destroy() {
return null;
}
}
@Test
public void testLifecycleMethodsWithWrongParameters() throws Exception {
LifecycleMethodsWithWrongParametersService s = new LifecycleMethodsWithWrongParametersService();
try {
processor.processPostConstruct(s);
fail();
} catch (RuntimeException x) {
}
try {
processor.processPreDestroy(s);
fail();
} catch (RuntimeException x) {
}
}
@Service
public static class LifecycleMethodsWithWrongParametersService {
@PostConstruct
public void init(Object param) {
}
@PreDestroy
public void destroy(Object param) {
}
}
@Test
public void testLifecycleMethodsWithStaticModifier() throws Exception {
S s = new S();
try {
processor.processPostConstruct(s);
fail();
} catch (RuntimeException x) {
}
try {
processor.processPreDestroy(s);
fail();
} catch (RuntimeException x) {
}
}
@Test
public void testMultipleLifecycleMethodsInSameClass() throws Exception {
MultipleLifecycleMethodsInSameClassService s = new MultipleLifecycleMethodsInSameClassService();
try {
processor.processPostConstruct(s);
fail();
} catch (RuntimeException x) {
}
try {
processor.processPreDestroy(s);
fail();
} catch (RuntimeException x) {
}
}
@Service
public static class MultipleLifecycleMethodsInSameClassService {
@PostConstruct
public void init1() {
}
@PostConstruct
public void init2() {
}
@PreDestroy
public void destroy1() {
}
@PreDestroy
public void destroy2() {
}
}
@Test
public void testPostConstructPreDestroy() throws Exception {
final CountDownLatch initLatch = new CountDownLatch(1);
final CountDownLatch destroyLatch = new CountDownLatch(1);
PostConstructPreDestroyService s = new PostConstructPreDestroyService(initLatch, destroyLatch);
processor.process(s);
assertTrue(initLatch.await(5, TimeUnit.SECONDS));
processor.deprocess(s);
assertTrue(destroyLatch.await(5, TimeUnit.SECONDS));
}
@Service
public static class PostConstructPreDestroyService {
private final CountDownLatch initLatch;
private final CountDownLatch destroyLatch;
public PostConstructPreDestroyService(CountDownLatch initLatch, CountDownLatch destroyLatch) {
this.initLatch = initLatch;
this.destroyLatch = destroyLatch;
}
@PostConstruct
public void init() {
initLatch.countDown();
}
@PreDestroy
public void destroy() {
destroyLatch.countDown();
}
}
@Test
public void testPostConstructInSuperClass() throws Exception {
final CountDownLatch initLatch = new CountDownLatch(1);
final CountDownLatch destroyLatch = new CountDownLatch(1);
DerivedPostConstructInSuperClassService ss = new DerivedPostConstructInSuperClassService(initLatch, destroyLatch);
processor.process(ss);
assertTrue(initLatch.await(5, TimeUnit.SECONDS));
processor.deprocess(ss);
assertTrue(destroyLatch.await(5, TimeUnit.SECONDS));
}
@Service
public static class PostConstructInSuperClassService {
private final CountDownLatch initLatch;
public PostConstructInSuperClassService(CountDownLatch initLatch) {
this.initLatch = initLatch;
}
@PostConstruct
protected void init() {
initLatch.countDown();
}
}
public class DerivedPostConstructInSuperClassService extends PostConstructInSuperClassService {
private final CountDownLatch destroyLatch;
public DerivedPostConstructInSuperClassService(CountDownLatch initLatch, CountDownLatch destroyLatch) {
super(initLatch);
this.destroyLatch = destroyLatch;
}
@PreDestroy
private void destroy() {
destroyLatch.countDown();
}
}
@Test
public void testPostConstructPreDestroyOnOverriddenMethod() throws Exception {
final CountDownLatch initLatch = new CountDownLatch(2);
final CountDownLatch destroyLatch = new CountDownLatch(2);
DerivedPostConstructPreDestroyOnOverriddenMethodService ss = new DerivedPostConstructPreDestroyOnOverriddenMethodService(initLatch, destroyLatch);
processor.process(ss);
assertFalse(initLatch.await(1, TimeUnit.SECONDS));
processor.deprocess(ss);
assertFalse(destroyLatch.await(1, TimeUnit.SECONDS));
}
@Service
public static class PostConstructPreDestroyOnOverriddenMethodService {
protected final CountDownLatch initLatch;
protected final CountDownLatch destroyLatch;
public PostConstructPreDestroyOnOverriddenMethodService(CountDownLatch initLatch, CountDownLatch destroyLatch) {
this.initLatch = initLatch;
this.destroyLatch = destroyLatch;
}
@PostConstruct
public void init() {
assertEquals(1, initLatch.getCount());
initLatch.countDown();
}
@PreDestroy
public void destroy() {
assertEquals(1, destroyLatch.getCount());
destroyLatch.countDown();
}
}
public static class DerivedPostConstructPreDestroyOnOverriddenMethodService extends PostConstructPreDestroyOnOverriddenMethodService {
public DerivedPostConstructPreDestroyOnOverriddenMethodService(CountDownLatch initLatch, CountDownLatch destroyLatch) {
super(initLatch, destroyLatch);
}
// Overridden without annotation.
@Override
public void init() {
assertEquals(2, initLatch.getCount());
initLatch.countDown();
super.init();
}
// Overridden without annotation.
@Override
public void destroy() {
assertEquals(2, destroyLatch.getCount());
destroyLatch.countDown();
super.init();
}
}
@Test
public void testPostConstructPreDestroyOnOverridingMethod() throws Exception {
final CountDownLatch initLatch = new CountDownLatch(2);
final CountDownLatch destroyLatch = new CountDownLatch(2);
DerivedPostConstructPreDestroyOnOverridingMethodService ss = new DerivedPostConstructPreDestroyOnOverridingMethodService(initLatch, destroyLatch);
processor.process(ss);
assertTrue(initLatch.await(5, TimeUnit.SECONDS));
processor.deprocess(ss);
assertTrue(initLatch.await(5, TimeUnit.SECONDS));
}
@Service
public static class PostConstructPreDestroyOnOverridingMethodService {
protected final CountDownLatch initLatch;
protected final CountDownLatch destroyLatch;
public PostConstructPreDestroyOnOverridingMethodService(CountDownLatch initLatch, CountDownLatch destroyLatch) {
this.initLatch = initLatch;
this.destroyLatch = destroyLatch;
}
@PostConstruct
public void init() {
assertEquals(1, initLatch.getCount());
initLatch.countDown();
}
@PreDestroy
public void destroy() {
assertEquals(1, destroyLatch.getCount());
destroyLatch.countDown();
}
}
public static class DerivedPostConstructPreDestroyOnOverridingMethodService extends PostConstructPreDestroyOnOverridingMethodService {
public DerivedPostConstructPreDestroyOnOverridingMethodService(CountDownLatch initLatch, CountDownLatch destroyLatch) {
super(initLatch, destroyLatch);
}
// Overriding with annotation.
@Override
@PostConstruct
public void init() {
assertEquals(2, initLatch.getCount());
initLatch.countDown();
super.init();
}
@Override
@PreDestroy
public void destroy() {
assertEquals(2, destroyLatch.getCount());
destroyLatch.countDown();
super.destroy();
}
}
@Test
public void testMultipleLifecycleMethodsInDifferentClasses() throws Exception {
final CountDownLatch initLatch = new CountDownLatch(2);
final CountDownLatch destroyLatch = new CountDownLatch(2);
DerivedMultipleLifecycleMethodsInDifferentClassesService ss = new DerivedMultipleLifecycleMethodsInDifferentClassesService(initLatch, destroyLatch);
processor.process(ss);
assertTrue(initLatch.await(5, TimeUnit.SECONDS));
processor.deprocess(ss);
assertTrue(destroyLatch.await(5, TimeUnit.SECONDS));
}
@Service
public static class MultipleLifecycleMethodsInDifferentClassesService {
protected final CountDownLatch initLatch;
protected final CountDownLatch destroyLatch;
public MultipleLifecycleMethodsInDifferentClassesService(CountDownLatch initLatch, CountDownLatch destroyLatch) {
this.initLatch = initLatch;
this.destroyLatch = destroyLatch;
}
@PostConstruct
public void init1() {
assertEquals(1, initLatch.getCount());
initLatch.countDown();
}
@PreDestroy
public void destroy1() {
assertEquals(1, destroyLatch.getCount());
destroyLatch.countDown();
}
}
public class DerivedMultipleLifecycleMethodsInDifferentClassesService extends MultipleLifecycleMethodsInDifferentClassesService {
public DerivedMultipleLifecycleMethodsInDifferentClassesService(CountDownLatch initLatch, CountDownLatch destroyLatch) {
super(initLatch, destroyLatch);
}
@PostConstruct
public void init2() {
assertEquals(2, initLatch.getCount());
initLatch.countDown();
}
@PreDestroy
public void destroy2() {
assertEquals(2, destroyLatch.getCount());
destroyLatch.countDown();
}
}
@Test
public void testConfigureErrorIfExists() throws Exception {
final Set<String> configured = new HashSet<>();
ConfigureErrorIfExistsService s = new ConfigureErrorIfExistsService(configured);
boolean processed = processor.process(s);
assertTrue(processed);
assertTrue(configured.contains("/foo/bar"));
assertTrue(configured.contains("/blah"));
assertTrue(configured.contains("/halb"));
ConfigureErrorIfExistsService s2 = new ConfigureErrorIfExistsService(configured);
try {
processor.process(s2);
fail();
} catch (IllegalStateException expected) {
}
}
@Service
public static class ConfigureErrorIfExistsService {
private final Set<String> configured;
public ConfigureErrorIfExistsService(Set<String> configured) {
this.configured = configured;
}
@Configure(value = "/foo/bar")
private void configureFooBar(ConfigurableServerChannel channel) {
configured.add(channel.getId());
}
@Configure(value = {"/blah", "/halb"})
private void configureBlah(ConfigurableServerChannel channel) {
configured.add(channel.getId());
}
}
@Test
public void testConfigureNoErrorIfExists() throws Exception {
final List<String> configured = new ArrayList<>();
ConfigureNoErrorIfExistsService s1 = new ConfigureNoErrorIfExistsService(configured);
boolean processed = processor.process(s1);
assertTrue(processed);
ConfigureNoErrorIfExistsService s2 = new ConfigureNoErrorIfExistsService(configured);
processed = processor.process(s2);
assertTrue(processed);
assertEquals(1, configured.size());
assertEquals("/foo", configured.get(0));
}
@Service
public static class ConfigureNoErrorIfExistsService {
private final List<String> configured;
public ConfigureNoErrorIfExistsService(List<String> configured) {
this.configured = configured;
}
@Configure(value = "/foo", errorIfExists = false)
private void configureFooBar(ConfigurableServerChannel channel) {
configured.add(channel.getId());
}
}
@Test
public void testConfigureConfigureIfExists() throws Exception {
final List<String> configured = new ArrayList<>();
ConfigureConfigureIfExistsService s1 = new ConfigureConfigureIfExistsService(configured);
boolean processed = processor.process(s1);
assertTrue(processed);
ConfigureConfigureIfExistsService s2 = new ConfigureConfigureIfExistsService(configured);
processed = processor.process(s2);
assertTrue(processed);
assertEquals(2, configured.size());
assertEquals("/foo", configured.get(0));
assertEquals("/foo", configured.get(1));
}
@Service
public static class ConfigureConfigureIfExistsService {
private final List<String> configured;
public ConfigureConfigureIfExistsService(List<String> configured) {
this.configured = configured;
}
@Configure(value = "/foo", configureIfExists = true)
private void configureFooBar(ConfigurableServerChannel channel) {
configured.add(channel.getId());
}
}
@Test
public void testConfigureOnOverriddenMethod() throws Exception {
CountDownLatch latch = new CountDownLatch(2);
DerivedConfigureOnOverriddenMethodService s = new DerivedConfigureOnOverriddenMethodService(latch);
boolean processed = processor.process(s);
assertFalse(processed);
assertFalse(latch.await(1, TimeUnit.SECONDS));
}
@Service
public static class ConfigureOnOverriddenMethodService {
protected final CountDownLatch latch;
public ConfigureOnOverriddenMethodService(CountDownLatch latch) {
this.latch = latch;
}
@Configure(value = "/foo")
protected void configureFoo(ConfigurableServerChannel channel) {
assertEquals(1, latch.getCount());
latch.countDown();
}
}
public static class DerivedConfigureOnOverriddenMethodService extends ConfigureOnOverriddenMethodService {
public DerivedConfigureOnOverriddenMethodService(CountDownLatch latch) {
super(latch);
}
// Overriding without annotation.
@Override
public void configureFoo(ConfigurableServerChannel channel) {
assertEquals(2, latch.getCount());
latch.countDown();
super.configureFoo(channel);
}
}
@Test
public void testConfigureOnOverridingMethod() throws Exception {
CountDownLatch latch = new CountDownLatch(2);
DerivedConfigureOnOverridingMethodService s = new DerivedConfigureOnOverridingMethodService(latch);
boolean processed = processor.process(s);
assertTrue(processed);
assertTrue(latch.await(5, TimeUnit.SECONDS));
}
@Service
public static class ConfigureOnOverridingMethodService {
protected final CountDownLatch latch;
public ConfigureOnOverridingMethodService(CountDownLatch latch) {
this.latch = latch;
}
@Configure(value = "/foo")
protected void configureFoo(ConfigurableServerChannel channel) {
assertEquals(1, latch.getCount());
latch.countDown();
}
}
public static class DerivedConfigureOnOverridingMethodService extends ConfigureOnOverridingMethodService {
public DerivedConfigureOnOverridingMethodService(CountDownLatch latch) {
super(latch);
}
// Overriding with annotation.
@Override
@Configure(value = "/foo")
public void configureFoo(ConfigurableServerChannel channel) {
assertEquals(2, latch.getCount());
latch.countDown();
super.configureFoo(channel);
}
}
@Test
public void testInjectables() throws Exception {
Injectables i = new DerivedInjectables();
InjectablesService s = new InjectablesService();
processor = new ServerAnnotationProcessor(bayeuxServer, i);
boolean processed = processor.process(s);
assertTrue(processed);
assertSame(i, s.i);
}
class Injectables {
}
class DerivedInjectables extends Injectables {
}
@Service
public static class InjectablesService {
@Inject
private Injectables i;
}
@Service
private static class S {
@PostConstruct
public static void init() {
}
@PreDestroy
public static void destroy() {
}
}
@Test
public void testListenerWithParameters() throws Exception {
String value = "test";
CountDownLatch latch = new CountDownLatch(1);
Object service = new ListenerWithParametersService(latch, value);
boolean processed = processor.process(service);
assertTrue(processed);
String parentChannel = new ChannelId(ListenerWithParametersService.CHANNEL).getParent();
// Fake a publish
LocalSession remote = bayeuxServer.newLocalSession("remote");
remote.handshake();
ServerMessage.Mutable message = bayeuxServer.newMessage();
message.setChannel(parentChannel + "/" + value);
message.setData(new HashMap());
bayeuxServer.handle((ServerSessionImpl)remote.getServerSession(), message);
assertTrue(latch.await(5, TimeUnit.SECONDS));
}
@Service
public class ListenerWithParametersService {
private static final String CHANNEL = "/foo/{var}";
private final CountDownLatch latch;
private final String value;
public ListenerWithParametersService(CountDownLatch latch, String value) {
this.latch = latch;
this.value = value;
}
@Listener(CHANNEL)
public void service(ServerSession session, ServerMessage message, @Param("var") String var) {
assertNotNull(session);
assertNotNull(message);
assertEquals(value, var);
latch.countDown();
}
}
@Test(expected = IllegalArgumentException.class)
public void testListenerWithParametersNoParamAnnotation() throws Exception {
Object service = new ListenerWithoutParamAnnotationService();
processor.process(service);
}
@Service
public static class ListenerWithoutParamAnnotationService {
@Listener("/foo/{var}")
public void service(ServerSession session, ServerMessage message, String var) {
}
}
@Test
public void testListenerWithParametersNotBinding() throws Exception {
CountDownLatch latch = new CountDownLatch(1);
Object service = new ListenerWithParametersNotBindingService(latch);
boolean processed = processor.process(service);
assertTrue(processed);
String parentChannel = new ChannelId(ListenerWithParametersNotBindingService.CHANNEL).getParent();
String grandParentChannel = new ChannelId(parentChannel).getParent();
// Fake a publish
LocalSession remote = bayeuxServer.newLocalSession("remote");
remote.handshake();
ServerMessage.Mutable message = bayeuxServer.newMessage();
// Wrong channel (does not bind to the template), the message must not be delivered.
message.setChannel(grandParentChannel + "/test");
message.setData(new HashMap());
bayeuxServer.handle((ServerSessionImpl)remote.getServerSession(), message);
assertFalse(latch.await(1, TimeUnit.SECONDS));
}
@Service
public static class ListenerWithParametersNotBindingService {
public static final String CHANNEL = "/a/{b}/c";
private final CountDownLatch latch;
public ListenerWithParametersNotBindingService(CountDownLatch latch) {
this.latch = latch;
}
@Listener(CHANNEL)
public void service(ServerSession session, ServerMessage message, @Param("b") String b) {
latch.countDown();
}
}
@Test(expected = IllegalArgumentException.class)
public void testListenerWithParameterWithWrongType() throws Exception {
Object service = new ListenerWithParametersWithWrongTypeService();
processor.process(service);
}
@Service
public static class ListenerWithParametersWithWrongTypeService {
public static final String CHANNEL = "/foo/{var}";
@Listener(CHANNEL)
public void service(ServerSession session, ServerMessage message, @Param("b") Integer b) {
}
}
@Test(expected = IllegalArgumentException.class)
public void testListenerWithParameterWithoutArgument() throws Exception {
Object service = new ListenerWithParameterWithoutArgumentService();
processor.process(service);
}
@Service
public static class ListenerWithParameterWithoutArgumentService {
public static final String CHANNEL = "/foo/{var}";
@Listener(CHANNEL)
public void service(ServerSession session, ServerMessage message) {
}
}
@Test(expected = IllegalArgumentException.class)
public void testListenerWithParameterWrongVariableName() throws Exception {
Object service = new ListenerWithParameterWrongVariableNameService();
processor.process(service);
}
@Service
public static class ListenerWithParameterWrongVariableNameService {
public static final String CHANNEL = "/foo/{var}";
@Listener(CHANNEL)
public void service(ServerSession session, ServerMessage message, @Param("wrong") String var) {
}
}
@Test(expected = IllegalArgumentException.class)
public void testListenerWithBadChannelTest() throws Exception {
Object service = new BadChannelTestService();
processor.process(service);
}
@Service
public static class BadChannelTestService {
@Listener("/foo/{var}/*")
public void bad(ServerSession session, ServerMessage message, @Param("var") String var) {
}
}
@Test
public void testSubscriptionWithParameters() throws Exception {
String value = "test";
CountDownLatch latch = new CountDownLatch(1);
Object service = new SubscriberWithParametersService(latch, value);
boolean processed = processor.process(service);
assertTrue(processed);
String parentChannel = new ChannelId(SubscriberWithParametersService.CHANNEL).getParent();
// Fake a publish
LocalSession remote = bayeuxServer.newLocalSession("remote");
remote.handshake();
ServerMessage.Mutable message = bayeuxServer.newMessage();
message.setChannel(parentChannel + "/" + value);
message.setData(new HashMap());
bayeuxServer.handle((ServerSessionImpl)remote.getServerSession(), message);
assertTrue(latch.await(5, TimeUnit.SECONDS));
}
@Service
public static class SubscriberWithParametersService {
public static final String CHANNEL = "/foo/{var}";
private final CountDownLatch latch;
private final String value;
public SubscriberWithParametersService(CountDownLatch latch, String value) {
this.latch = latch;
this.value = value;
}
@Subscription(CHANNEL)
public void service(Message message, @Param("var") String var) {
assertEquals(value, var);
latch.countDown();
}
}
@Test(expected = IllegalArgumentException.class)
public void testSubscriptionWithParametersInWrongOrder() throws Exception {
Object service = new SubscriptionWithParametersInWrongOrderService();
processor.process(service);
}
@Service
public static class SubscriptionWithParametersInWrongOrderService {
@Subscription("/a/{b}/{c}")
public void service(Message message, @Param("c") String c, @Param("b") String b) {
}
}
}