Package org.cometd.annotation

Source Code of org.cometd.annotation.ServerAnnotationProcessorTest

/*
* Copyright (c) 2008-2014 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 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 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);
        assertTrue(processed);

        // 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 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);
        }

        @Override
        public void foo(ServerSession remote, ServerMessage.Mutable message)
        {
            super.foo(remote, message);
            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 testInitDestroy() throws Exception
    {
        final CountDownLatch initLatch = new CountDownLatch(1);
        final CountDownLatch destroyLatch = new CountDownLatch(1);

        InitDestroyService s = new InitDestroyService(initLatch, destroyLatch);

        processor.process(s);
        assertTrue(initLatch.await(5, TimeUnit.SECONDS));

        processor.deprocess(s);
        assertTrue(destroyLatch.await(5, TimeUnit.SECONDS));
    }

    @Service
    public static class InitDestroyService
    {
        private final CountDownLatch initLatch;
        private final CountDownLatch destroyLatch;

        public InitDestroyService(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 testInitInSuperClass() throws Exception
    {
        final CountDownLatch initLatch = new CountDownLatch(1);
        final CountDownLatch destroyLatch = new CountDownLatch(1);

        DerivedInitInSuperClassService ss = new DerivedInitInSuperClassService(initLatch, destroyLatch);

        processor.process(ss);
        assertTrue(initLatch.await(5, TimeUnit.SECONDS));

        processor.deprocess(ss);
        assertTrue(destroyLatch.await(5, TimeUnit.SECONDS));
    }

    @Service
    public static class InitInSuperClassService
    {
        private final CountDownLatch initLatch;

        public InitInSuperClassService(CountDownLatch initLatch)
        {
            this.initLatch = initLatch;
        }

        @PostConstruct
        protected void init()
        {
            initLatch.countDown();
        }
    }

    public class DerivedInitInSuperClassService extends InitInSuperClassService
    {
        private final CountDownLatch destroyLatch;

        public DerivedInitInSuperClassService(CountDownLatch initLatch, CountDownLatch destroyLatch)
        {
            super(initLatch);
            this.destroyLatch = destroyLatch;
        }

        @PreDestroy
        private void destroy()
        {
            destroyLatch.countDown();
        }
    }

    @Test
    public void testInitOverridden() throws Exception
    {
        final CountDownLatch initLatch = new CountDownLatch(2);
        final CountDownLatch destroyLatch = new CountDownLatch(1);

        DerivedInitOverriddenService ss = new DerivedInitOverriddenService(initLatch, destroyLatch);

        processor.process(ss);
        assertTrue(initLatch.await(5, TimeUnit.SECONDS));

        processor.deprocess(ss);
        assertTrue(destroyLatch.await(5, TimeUnit.SECONDS));
    }

    @Service
    public static class InitOverriddenService
    {
        protected final CountDownLatch initLatch;

        public InitOverriddenService(CountDownLatch initLatch)
        {
            this.initLatch = initLatch;
        }

        @PostConstruct
        public void init()
        {
            assertEquals(1, initLatch.getCount());
            initLatch.countDown();
        }
    }

    public static class DerivedInitOverriddenService extends InitOverriddenService
    {
        private final CountDownLatch destroyLatch;

        public DerivedInitOverriddenService(CountDownLatch initLatch, CountDownLatch destroyLatch)
        {
            super(initLatch);
            this.destroyLatch = destroyLatch;
        }

        @Override
        public void init()
        {
            assertEquals(2, initLatch.getCount());
            initLatch.countDown();
            super.init();
        }

        @PreDestroy
        void destroy()
        {
            destroyLatch.countDown();
        }
    }

    @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(2, 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(1, initLatch.getCount());
            initLatch.countDown();
        }

        @PreDestroy
        public void destroy2()
        {
            assertEquals(2, destroyLatch.getCount());
            destroyLatch.countDown();
        }
    }

    @Test
    public void testConfigureDefault() throws Exception
    {
        final Set<String> configured = new HashSet<>();

        ConfigureDefaultService s = new ConfigureDefaultService(configured);
        boolean processed = processor.process(s);
        assertTrue(processed);

        assertTrue(configured.contains("/foo/bar"));
        assertTrue(configured.contains("/blah"));
        assertTrue(configured.contains("/halb"));

        ConfigureDefaultService s2 = new ConfigureDefaultService(configured);
        try
        {
            processor.process(s2);
            fail();
        }
        catch (IllegalStateException expected)
        {
        }
    }

    @Service
    public static class ConfigureDefaultService
    {
        private final Set<String> configured;

        public ConfigureDefaultService(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 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)
        {
        }
    }
}
TOP

Related Classes of org.cometd.annotation.ServerAnnotationProcessorTest

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.