12 Replies Latest reply: Jun 26, 2012 11:09 AM by Paul Ferraro RSS

How to implement a hasingleton service in as7?

David Robison Newbie

I am migrating some of my jboss services (SARs) from as5 to as7.1. These services were previously deployed in the deploy-hasingleton directory under as5. How do I implement a hasingleton service in as7.1?

  • 1. Re: How to implement a hasingleton service in as7?
    Tomaz Cerar Master

    Hi,

     

    I don't know the specifics about ha singleton. But support for them was added in version 7.1.0.CR1

    you can read about it in jira: https://issues.jboss.org/browse/AS7-2958

     

    and take a look at arq test https://github.com/jbossas/jboss-as/blob/master/testsuite/integration/clust/src/test/java/org/jboss/as/test/clustering/unmanaged/singleton/SingletonTestCase.java

     

    hope anything of this helps.

     

    cheers,

    tomaž

  • 2. Re: How to implement a hasingleton service in as7?
    Martijn Zuidhof Newbie

    Hi,

     

    We are in the same situation.. I was wondering if you had found a solution for this?

     

    kind regards,

    Martijn

  • 3. Re: How to implement a hasingleton service in as7?
    David Robison Newbie

    Here is my solution:

     

    The MBean for the service controlling the singleton (DummySingletonSvcMBean.java)

    package com.orci.TravelTimeEngine.dummy;

     

    public interface DummySingletonSvcMBean {

     

        /**

         * stop the service.

         */

        void stop();

     

        /**

         * start the service.

         * @throws Exception error

         */

        void start() throws Exception;

     

    }

     

    The actual service controlling the singleton (DummySingletonSvc.java)

    package com.orci.TravelTimeEngine.dummy;

     

    import java.util.Collection;

    import java.util.EnumSet;

     

    import org.jboss.as.clustering.singleton.SingletonService;

    import org.jboss.as.clustering.singleton.election.NamePreference;

    import org.jboss.as.clustering.singleton.election.PreferredSingletonElectionPolicy;

    import org.jboss.as.clustering.singleton.election.SimpleSingletonElectionPolicy;

    import org.jboss.as.server.CurrentServiceContainer;

    import org.jboss.as.server.ServerEnvironment;

    import org.jboss.as.server.ServerEnvironmentService;

    import org.jboss.logging.Logger;

    import org.jboss.msc.service.AbstractServiceListener;

    import org.jboss.msc.service.ServiceController;

    import org.jboss.msc.service.ServiceController.Transition;

    import org.jboss.msc.service.ServiceListener;

     

    import com.orci.TravelTimeEngine.hibernate.utils.ThreadTransaction;

     

    public class DummySingletonSvc implementsDummySingletonSvcMBean {

        private static final Logger LOG = Logger.getLogger(DummySingletonSvc.class.getName());

     

        private DummySvc service;

        private ServiceController<String> controller;

     

        /**

         * {@inheritDoc}

         */

        @Override

        public void start() throws Exception {

            LOG.info("Starting service...");

            ThreadTransaction.init();

            String preferedNode = System.getProperty("DummySvc.PreferedNode", "tte-processor-01");

            service = new DummySvc();

            SingletonService<String> singleton = new SingletonService<String>(service, DummySvc.SERVICE_NAME);

            singleton.setElectionPolicy(new PreferredSingletonElectionPolicy(new NamePreference(preferedNode + "/" + SingletonService.DEFAULT_CONTAINER), new SimpleSingletonElectionPolicy()));

            controller = singleton.build(CurrentServiceContainer.getServiceContainer())

                    .addDependency(ServerEnvironmentService.SERVICE_NAME, ServerEnvironment.class, service.getEnvInjector())

                    .install()

                    ;

            controller.setMode(ServiceController.Mode.ACTIVE);

            wait(controller, EnumSet.of(ServiceController.State.DOWN, ServiceController.State.STARTING), ServiceController.State.UP);

            LOG.info("Start done");

        }

     

        /**

         * {@inheritDoc}

         */

        @Override

        public void stop() {

            LOG.info("Stopping service...");

            service.stop(null);

            controller.setMode(ServiceController.Mode.REMOVE);

            wait(controller, EnumSet.of(ServiceController.State.UP, ServiceController.State.STOPPING, ServiceController.State.DOWN), ServiceController.State.REMOVED);

            LOG.info("Stop done");

        }

     

        /**

         * Wait for the controller state.

         * @param controller the controller

         * @param expectedStates the expected sates

         * @param targetState the target state

         * @param <T> the type

         */

        private static <T> void wait(ServiceController<T> controller, Collection<ServiceController.State> expectedStates, ServiceController.State targetState) {

            if (controller.getState() != targetState) {

                ServiceListener<T> listener = new NotifyingServiceListener<T>();

                controller.addListener(listener);

                try {

                    synchronized (controller) {

                        while (expectedStates.contains(controller.getState())) {

                            LOG.info(String.format("Service controller state is %s, waiting for transition to %s", controller.getState(), targetState));

                            controller.wait();

                        }

                    }

                } catch (InterruptedException e) {

                    Thread.currentThread().interrupt();

                }

                controller.removeListener(listener);

                ServiceController.State state = controller.getState();

                if (state != targetState) {

                    throw new IllegalStateException(String.format("Failed to wait for state to transition to %s.  Current state is %s", targetState, state), controller.getStartException());

                }

            }

        }

     

        private static class NotifyingServiceListener<T> extends AbstractServiceListener<T> {

            @Override

            public void transition(ServiceController<? extends T> controller, Transition transition) {

                synchronized (controller) {

                    controller.notify();

                }

            }

        }

    }

     

    Here is the actual singleton service (DummySvc)

    package com.orci.TravelTimeEngine.AwamDataProvidor;

     

    import java.util.Date;

    import java.util.Timer;

    import java.util.concurrent.atomic.AtomicBoolean;

     

    import org.jboss.as.server.ServerEnvironment;

    import org.jboss.logging.Logger;

    import org.jboss.msc.inject.Injector;

    import org.jboss.msc.service.Service;

    import org.jboss.msc.service.ServiceName;

    import org.jboss.msc.service.StartContext;

    import org.jboss.msc.service.StartException;

    import org.jboss.msc.service.StopContext;

    import org.jboss.msc.value.InjectedValue;

     

    import com.orci.commons.lang.properties.PropertyFileListener;

    import com.orci.commons.lang.properties.PropertyFileWatcher;

     

    public class DummySvc implements Service<String>, PropertyFileListener  {

        private static final Logger LOG = Logger.getLogger(DummySvc.class.getName());

        public static final ServiceName SERVICE_NAME = ServiceName.JBOSS.append("orci", "DummySvc");

     

        private final InjectedValue<ServerEnvironment> env = new InjectedValue<ServerEnvironment>();

        private final AtomicBoolean started = new AtomicBoolean(false);

     

        /**

         * @return the environment

         */

        public Injector<ServerEnvironment> getEnvInjector() {

            return this.env;

        }

     

        @Override

        public String getValue() throws IllegalStateException, IllegalArgumentException {

            return null;

        }

     

        /**

         * {@inheritDoc}

         */

        @Override

        public void start(StartContext ctx) throws StartException {

            LOG.info("Starting singleton service...");

            LOG.info("Start singleton done");

        }

     

        /**

         * {@inheritDoc}

         */

        @Override

        public void stop(StopContext ctx) {

            LOG.info("Stopping singleton service...");

            LOG.info("Stop singleton done");

        }

    }

    Hope this helps

  • 4. Re: How to implement a hasingleton service in as7?
    VITHUN VENGOPALAN Newbie

    I'm facing the same problem here!, Even i taught of the solution David Robison has given, but I'm not sure whether its a good practice to  deploy/undeploy  ourselves (I dont actually know). Do we have any other alternative to deploy this kind of  application. In other words, what is the JBOSS recommended way of migrating application that used to be deployed in deploy-hasingleton directory. Application that wiill be active only on one machine at once.

  • 5. Re: How to implement a hasingleton service in as7?
    Joseph Wu Newbie

    This worked as read value from hasingleton,but how do we set value to a hasingleton? any idea?

    Thanks a lot

  • 6. Re: How to implement a hasingleton service in as7?
    Wesley Janik Newbie

    David - Thanks for the complete example on how to accomplish this.

     

    Does anyone know if there is a way to accomplish the same goal without the service MBean code (DummySingletonSvc), and dependence on the Service interface in David's example?  Maybe some kind of MBean XML configuration?  I'm still fuzzy on what can and cannot be done via MBean XML, so I can't really tell if this is an option or not.  The bottom line is I'm hoping to avoid adding JBoss-specific library dependencies in my source, and instead do everything with JBoss-specific configuration.

  • 7. Re: How to implement a hasingleton service in as7?
    Paul Ferraro Master

    In short, there's no way to do this without exposing JBoss specific dependencies - there is no standard API for clustered singletons.  You could get away with this in older JBoss releases by adhering to lifecycle method conventions (e.g. create(), start(), stop(), destroy()).  In constrast, AS7 uses org.jboss.msc.Service to define component lifecycle - thus the dependency on its interface.

     

    Also, to address some of the confusion between the deploy-hasingleton directory approach in older JBoss releases and the SingletonService approach in AS7:

    The deploy-hasingleton logic achieved the goal of having a target service started on only 1 node in a cluster at a time by only deploying the service on one node at any given time.  In constrast, using SingletonService in AS7, the target service will be installed on every node in the cluster, but is only ever started on one node at any given time.  This approach both simplifies deployment requirements and minimizes the time required to relocate the singleton master between nodes.

  • 8. Re: How to implement a hasingleton service in as7?
    Paul Ferraro Master

    Singleton services are not meant to behave like a clustered cache.  If you want the ability to make a set of data available to all nodes in your cluster, consider using an Infinispan cache.

  • 9. Re: How to implement a hasingleton service in as7?
    Wesley Janik Newbie

    Makes sense now...  Thanks for the quick response!

  • 10. Re: How to implement a hasingleton service in as7?
    Fred Vogt Newbie

    David, your example doesn't redeploy cleanly.

     

    There is two other MSC services that must be removed by your context listener: ServiceName SERVICE_NAME."service" and ServiceName SERVICE_NAME."singleton"

     

    When the application is redeployed these services are added, if they already exist you get a duplicate service exception.

  • 11. Re: How to implement a hasingleton service in as7?
    Alexander Radzishevsky Newbie

    You saved my day, Fred. Thanks a lot.

  • 12. Re: How to implement a hasingleton service in as7?
    Paul Ferraro Master

    I've opened https://issues.jboss.org/browse/AS7-5073 so that we auto-remove these child services when the parent service is removed.