13 Replies Latest reply: Jan 31, 2013 3:29 PM by Paul Ferraro RSS

Using jgroups CounterService with jboss's cluster channel

Koen Janssens Newbie

Hi,

 

Groups supports a counterservice (http://docs.jboss.org/jbossas/javadoc/7.1.2.Final/org/jgroups/blocks/atomic/CounterService.html)

To use it, i need to instantiate a CounterService object and pass it a JChannel object. 

I would like to use the same JChannel that is used by jboss internally.

 

Is that possible ?

I know the channel is exposed via JMX, but that does not give me the complete JChannel object.

 

I'm using jboss 7.1.1


Regards,
Koen

  • 1. Re: Using jgroups CounterService with jboss's cluster channel
    Paul Ferraro Master

    The best way to do this involves creating a custom MSC service in combination with a ServiceActivator.

    e.g.

    First create an MSC service that creates the counter service:

     

    public class MyCounterService implements org.jboss.msc.service.Service<CounterService> {
        private final Value<Channel> channel;
        private volatile CounterService service;
        public MyCounterService(Value<Channel> channel) {
            this.channel = channel;
        }
        @Override
        public CounterService getValue() {
            return this.service;
        }
        @Override
        public void start(StartContext context) {
            this.service = new CounterService((JChannel) this.channel.getValue());
        }
        @Override
        public void stop(StopContext context) {
            this.service = null;
        }
    }
    

     

     

    Next, create a service activator that installs your counter service injecting the desired channel:

     

    public class MyCounterServiceActivator implements org.jboss.msc.service.ServiceActivator {
        private static final ServiceName SERVICE_NAME = ...; // The service name of the counter service
        private static final String CHANNEL_NAME = ...; // The name of the channel used by the counter service
        @Override
        public void activate(ServiceActivatorContext context) {
            InjectedValue<Channel> channel = new InjectedValue<Channel>();
            context.getServiceTarget().addService(SERVICE_NAME, new MyCounterService(channel))
                .addDependency(ServiceName.JBOSS.append("jgroups", "channel", CHANNEL_NAME), Channel.class, channel)
                .setInitialMode(ServiceController.Mode.ACTIVE)
                .install()
            ;
        }
    }
    

     

     

    Finally, add a META-INF/services/org.jboss.msc.service.ServiceActivator file to your deployment that specifies the fully qualified class name of your MyCounterServiceActivator class.  When you deploy your application, the AS will locate and activate any service activators defined by your deployment.  Your service activator will then create the CounterService using a given channel.

  • 2. Re: Using jgroups CounterService with jboss's cluster channel
    Dave Rathnow Newbie

    I want to do something similar with the LockService. Will this code work for that service as well?  If so, can you tell me what the values should be for "SERVICE_NAME" and "CHANNEL_NAME"?  I'm not sure where these should come from.


    Thanks,
    Dave.

  • 3. Re: Using jgroups CounterService with jboss's cluster channel
    Paul Ferraro Master

    Yes - this code should work for LockService as well.

    The code above assumes that the channel with the given service name exists.

    If you want to create a new channel for use with these services, you'll need to install a separate channel service, and make sure the MyCounterService starts/stops the channel.

    Here's the above example modified to create a LockService, complete with functional channel/service names.

    e.g.

     

    public class MyLockService implements org.jboss.msc.service.Service<LockService> {
        private final Value<Channel> channel;
        private final String cluster;
        private volatile LockService service;
        public MyLockService(Value<Channel> channel, String cluster) {
            this.channel = channel;
            this.cluster = cluster;
        }
        @Override
        public LockService getValue() {
            return this.service;
        }
        @Override
        public void start(StartContext context) throws StartException {
            JChannel channel = (JChannel) this.channel.getValue();
            this.service = new LockService(channel);
            try {
                channel.connect(this.cluster);
            } catch (Exception e) {
                throw new StartException(e);
            }
        }
        @Override
        public void stop(StopContext context) {
            this.channel.getValue().close();
            this.service = null;
        }
    }

     

     

    public class MyLockServiceActivator implements org.jboss.msc.service.ServiceActivator {
        @Override
        public void activate(ServiceActivatorContext context) {
            String cluster = "lock"; // Any name to uniquely identify the cluster
            ServiceName channelName = ChannelService.getServiceName(cluster); // Generates the appropriate service name for a channel
            ServiceName lockName = channelName.append("service"); // Any service name to unique identify the lock service
            ServiceTarget target = context.getServiceTarget();
            // Creates the lock service
            InjectedValue<Channel> channel = new InjectedValue<Channel>();
            target.addService(counterName, new MyLockService(channel, cluster))
                .addDependency(channelName, Channel.class, channel)
                .setInitialMode(ServiceController.Mode.ACTIVE)
                .install()
            ;
            // Creates the requisite channel service used by the counter service
            InjectedValue<ChannelFactory> factory = new InjectedValue<ChannelFactory>();
            target.addService(channelName, new ChannelService(cluster, factory))
                .addDependency(ChannelFactoryService.getServiceName(null), ChannelFactory.class, factory) // Use the default-stack
                .setInitialMode(ServiceController.Mode.ON_DEMAND)
                .install()
            ;
        }
    }

     

  • 4. Re: Using jgroups CounterService with jboss's cluster channel
    Dave Rathnow Newbie

    Thanks for taking the time to answer this.  However, I have to admit, I'm unsure how to use ServiceActivator. 

     

    Could you provide a brief description or point me to some documentation where I can read about it myself.  I've been searching the JBoss web site but haven't found where it's documented.

     

    Thanks.

  • 5. Re: Using jgroups CounterService with jboss's cluster channel
    Paul Ferraro Master

    Upon deployment, the AS will load any ServiceActivator classes via ServiceLoader and call their activate(...) method.

    To do this, you need to add a META-INF/services/org.jboss.msc.service.ServiceActivator file to your application archive containing the fully qualified class name of your MyLockServiceActivator class.

  • 6. Re: Using jgroups CounterService with jboss's cluster channel
    Dave Rathnow Newbie

    That much if figured out but how do I find my service at runtime?

  • 7. Re: Using jgroups CounterService with jboss's cluster channel
    Paul Ferraro Master

    I can think of 2 ways off the top of my head...

     

    The quick and dirty way:

     

    ServiceController<?> controller = org.jboss.as.clustering.msc.ServiceContainerHelper.getService(lockName);
    LockService service = org.jboss.as.clustering.msc.ServiceContainerHelper.getValue(controller, LockService.class);

     

     

    The more elegant way:

    Bind the LockService to JNDI in your service activator.

    e.g.

     

    String jndiName = "java:jboss/my-lock-service"; // Pick any jndi name
    ContextNames.BindInfo bindInfo = ContextNames.bindInfoFor(jndiName);
    BinderService binder = new BinderService(bindInfo.getBindName());
    target.addService(bindInfo.getBinderServiceName(), binder)
            .addAliases(ContextNames.JAVA_CONTEXT_SERVICE_NAME.append(jndiName))
            .addDependency(lockName, LockService.class, new ManagedReferenceInjector<LockService>(binder.getManagedObjectInjector()))
            .addDependency(bindInfo.getParentContextServiceName(), ServiceBasedNamingStore.class, binder.getNamingStoreInjector())
            .setInitialMode(ServiceController.Mode.PASSIVE)
            .install()
    ;

     

     

    Now you can inject the LockService wherever needed:

    e.g.

    @Resource(lookup = "java:jboss/my-lock-service")
    private LockService service;

     

  • 8. Re: Using jgroups CounterService with jboss's cluster channel
    Dave Rathnow Newbie

    Well, I decided to try the elegant approach but I've hit a snag.  I put the code in place but when I attempt to start my app, I'm getting the exceptions below:

     

     

    20:04:47,409 ERROR (MSC service thread 1-7) MSC00001: Failed to start service jboss.deployment.subunit."myapp.ear"."myapp-ejb-6.0.0.jar".INSTALL: org.jboss.msc.service.StartException in service jboss.deployment.subunit."myapp.ear"."myapp-ejb-6.0.0.jar".INSTALL: Failed to process phase INSTALL of subdeployment "myapp-ejb-6.0.0.jar" of deployment "myapp.ear"

              at org.jboss.as.server.deployment.DeploymentUnitPhaseService.start(DeploymentUnitPhaseService.java:119)

              at org.jboss.msc.service.ServiceControllerImpl$StartTask.startService(ServiceControllerImpl.java:1811)

              at org.jboss.msc.service.ServiceControllerImpl$StartTask.run(ServiceControllerImpl.java:1746)

              at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1110)

              at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:603)

              at java.lang.Thread.run(Thread.java:722)

    Caused by: java.lang.NoClassDefFoundError: org/jboss/as/clustering/jgroups/subsystem/ChannelService

              at zedi.myapp.app.msc.MyLockServiceActivator.activate(MyLockServiceActivator.java:23)

              at org.jboss.as.server.deployment.service.ServiceActivatorProcessor.deploy(ServiceActivatorProcessor.java:62)

              at org.jboss.as.server.deployment.DeploymentUnitPhaseService.start(DeploymentUnitPhaseService.java:113)

              ... 5 more

    Caused by: java.lang.ClassNotFoundException: org.jboss.as.clustering.jgroups.subsystem.ChannelService from [Module "deployment.myapp.ear.myapp-ejb-6.0.0.jar:main" from Service Module Loader]

              at org.jboss.modules.ModuleClassLoader.findClass(ModuleClassLoader.java:190)

              at org.jboss.modules.ConcurrentClassLoader.performLoadClassUnchecked(ConcurrentClassLoader.java:468)

              at org.jboss.modules.ConcurrentClassLoader.performLoadClassChecked(ConcurrentClassLoader.java:456)

              at org.jboss.modules.ConcurrentClassLoader.performLoadClass(ConcurrentClassLoader.java:398)

              at org.jboss.modules.ConcurrentClassLoader.loadClass(ConcurrentClassLoader.java:120)

              ... 8 more

     

     

    For some reason, ChannelService can't be found, yet it is in one of the provided jboss jar files. My ear looks like this:

     

          myapp.ear

              |-- myapp.jar &lt;---ejb

              |-- myapp.ejb &lt;---ejb

              |-- myapp.war &lt;---war

              |- \lib     &lt; lib with jars

     

     

    Any ideas why ChannelService isn't being found?

     

    Here is what my code looks like:

    public class MyLockServiceActivator implements org.jboss.msc.service.ServiceActivator {

     

        @Override

        public void activate(ServiceActivatorContext context) {

            String cluster = "cluster";

            ServiceName channelName = ChannelService.getServiceName(cluster);

            ServiceName lockName = channelName.append("lock-service");

            ServiceTarget target = context.getServiceTarget();

     

            InjectedValue(binder.getManagedObjectInjector()))

                    .addDependency(bindInfo.getParentContextServiceName(), ServiceBasedNamingStore.class, binder.getNamingStoreInjector())

                    .setInitialMode(ServiceController.Mode.PASSIVE)

                    .install();       

        }

    }

     

     

     

    public class MyLockService implements org.jboss.msc.service.Service channel, String cluster) {

            this.channel = channel;

            this.cluster = cluster;

        }

        @Override

        public LockService getValue() {

            return this.service;

        }

        @Override

        public void start(StartContext context) throws StartException {

            JChannel channel = (JChannel) this.channel.getValue();

            this.service = new LockService(channel);

            try {

                channel.connect(this.cluster);

            } catch (Exception e) {

                throw new StartException(e);

            }

        }

     

        @Override

        public void stop(StopContext context) {

            this.channel.getValue().close();

            this.service = null;

        }

    }

     

  • 9. Re: Using jgroups CounterService with jboss's cluster channel
    Dave Rathnow Newbie

    And BTW: where is org.jboss.as.clustering.msc.ServiceContainerHelper?  It doesn't seem to exist in JBossAS 7.1.1

  • 10. Re: Using jgroups CounterService with jboss's cluster channel
    Dave Rathnow Newbie

    After plugging away at this for a couple of hours, I was able to solve the ClassNotFound issues.  I had to add the necessary incantations to my MANIFEST.MF file:

     

    Dependencies: org.jboss.as.clustering.jgroups services export, org.jboss.as.naming

     

    But now I'm stuck on this error:

     

    JBAS014775:    New missing/unsatisfied dependencies:

          service jboss.jgroups.stack (missing) dependents: [service jboss.jgroups.channel.cluster]

     

    Thoughts?

  • 11. Re: Using jgroups CounterService with jboss's cluster channel
    Dave Rathnow Newbie

    Finally!

     

    Getting this to work was an effort but, in doing so, I cleaned up a few things in my app that would've ultimiately come back to haunt me.  The issue with the "missing/unsatisfied dependencies" was simply that I was using a profile from the domain.xml file that did not have a jgroups subsystem defined.(Oops! )  Once I added that, I ended up with other errors that exposed other problems.  The worst being that I had an extra copy of the jgroups jar under my lib folder of my ear.  I thought it needed to be there because of the ClassNotFoundExceptions I was getting but it ended up causing ClassCastExceptions when it was there....and on it went...

     

    The code that Paul provided works fine but in order to get it working, I had to make sure that the appropriate dependencies were in the MANIFEST.MF files (i.e. the jar or ejb file that contains the MyLockServiceActivator class).  The ones that eventually did it for me were:

     

    Dependencies: org.jboss.as.clustering.jgroups,

                                     org.jgroups,

                                     org.jboss.as.naming

     

    As Paul pointed out, the other thing that needs to be done is to create a folder under you META-INF directory calleed "services" and create a file called "org.jboss.msc.service.ServiceActivator" that contains the fully qualified name of the ServiceActivator implemenation (in my case my MyLockServiceActivator).

     

    Thanks Paul for all your help!