13 Replies Latest reply on Jan 31, 2013 3:29 PM by pferraro

    Using jgroups CounterService with jboss's cluster channel

    janssk77

      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
          pferraro

          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:

           

          {code}

          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;

              }

          }

          {code}

           

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

           

          {code}

          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()

                  ;

              }

          }

          {code}

           

          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
            drathnow

            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
              pferraro

              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.

               

              {code}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;

                  }

              }{code}

               

              {code}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()

                      ;

                  }

              }{code}

              • 4. Re: Using jgroups CounterService with jboss's cluster channel
                drathnow

                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
                  pferraro

                  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
                    drathnow

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

                    • 7. Re: Using jgroups CounterService with jboss's cluster channel
                      pferraro

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

                       

                      The quick and dirty way:

                       

                      {code}ServiceController<?> controller = org.jboss.as.clustering.msc.ServiceContainerHelper.getService(lockName);

                      LockService service = org.jboss.as.clustering.msc.ServiceContainerHelper.getValue(controller, LockService.class);{code}

                       

                      The more elegant way:

                      Bind the LockService to JNDI in your service activator.

                      e.g.

                       

                      {code}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()

                      ;{code}

                       

                      Now you can inject the LockService wherever needed:

                      e.g.

                      {code}@Resource(lookup = "java:jboss/my-lock-service")

                      private LockService service;{code}

                      • 8. Re: Using jgroups CounterService with jboss's cluster channel
                        drathnow

                        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 [org.jboss.msc.service.fail] (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) [jboss-as-server-7.1.1.Final.jar:7.1.1.Final]

                                  at org.jboss.msc.service.ServiceControllerImpl$StartTask.startService(ServiceControllerImpl.java:1811) [jboss-msc-1.0.2.GA.jar:1.0.2.GA]

                                  at org.jboss.msc.service.ServiceControllerImpl$StartTask.run(ServiceControllerImpl.java:1746) [jboss-msc-1.0.2.GA.jar:1.0.2.GA]

                                  at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1110) [rt.jar:1.7.0_06]

                                  at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:603) [rt.jar:1.7.0_06]

                                  at java.lang.Thread.run(Thread.java:722) [rt.jar:1.7.0_06]

                        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) [jboss-as-server-7.1.1.Final.jar:7.1.1.Final]

                                  at org.jboss.as.server.deployment.DeploymentUnitPhaseService.start(DeploymentUnitPhaseService.java:113) [jboss-as-server-7.1.1.Final.jar:7.1.1.Final]

                                  ... 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) [jboss-modules.jar:1.1.1.GA]

                                  at org.jboss.modules.ConcurrentClassLoader.performLoadClassUnchecked(ConcurrentClassLoader.java:468) [jboss-modules.jar:1.1.1.GA]

                                  at org.jboss.modules.ConcurrentClassLoader.performLoadClassChecked(ConcurrentClassLoader.java:456) [jboss-modules.jar:1.1.1.GA]

                                  at org.jboss.modules.ConcurrentClassLoader.performLoadClass(ConcurrentClassLoader.java:398) [jboss-modules.jar:1.1.1.GA]

                                  at org.jboss.modules.ConcurrentClassLoader.loadClass(ConcurrentClassLoader.java:120) [jboss-modules.jar:1.1.1.GA]

                                  ... 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 <---ejb

                                  |-- myapp.ejb <---ejb

                                  |-- myapp.war <---war

                                  |--- \lib     <-- 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<Channel> channel = new InjectedValue<Channel>();

                                target.addService(lockName, new MyLockService(channel, cluster))

                                    .addDependency(channelName, Channel.class, channel)

                                    .setInitialMode(ServiceController.Mode.ACTIVE)

                                    .install();

                         

                                InjectedValue<ChannelFactory> factory = new InjectedValue<ChannelFactory>();

                                target.addService(channelName, new ChannelService(cluster, factory))

                                    .addDependency(ChannelFactoryService.getServiceName(null), ChannelFactory.class, factory)

                                    .setInitialMode(ServiceController.Mode.ON_DEMAND)

                                    .install();

                         

                                String jndiName = "java:jboss/myapp/cluster-lock-service";

                                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();       

                            }

                        }

                         

                         

                        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;

                            }

                        }

                        • 9. Re: Using jgroups CounterService with jboss's cluster channel
                          drathnow

                          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
                            drathnow

                            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
                              drathnow

                              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!

                              • 13. Re: Using jgroups CounterService with jboss's cluster channel
                                pferraro

                                Congrats!