All Downloads are FREE. Search and download functionalities are using the official Maven repository.

fish.payara.service.example.ExampleService Maven / Gradle / Ivy

There is a newer version: 7.2024.1.Alpha1
Show newest version
/*
 * Copyright (c) 2016-2021 Payara Foundation. All rights reserved.
 *
 * The contents of this file are subject to the terms of the Common Development
 * and Distribution License("CDDL") (collectively, the "License").  You
 * may not use this file except in compliance with the License.  You can
 * obtain a copy of the License at
 * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
 * or packager/legal/LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 *
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at packager/legal/LICENSE.txt.
  */
package fish.payara.service.example;

import com.sun.enterprise.config.serverbeans.Config;
import fish.payara.nucleus.cluster.PayaraCluster;
import fish.payara.nucleus.eventbus.ClusterMessage;
import fish.payara.nucleus.eventbus.EventBus;
import fish.payara.nucleus.eventbus.MessageReceiver;
import fish.payara.nucleus.store.ClusteredStore;
import fish.payara.service.example.config.ExampleServiceConfiguration;
import java.beans.PropertyChangeEvent;
import java.util.logging.Logger;
import jakarta.annotation.PostConstruct;
import jakarta.inject.Inject;
import jakarta.inject.Named;
import org.glassfish.api.StartupRunLevel;
import org.glassfish.api.admin.ServerEnvironment;
import org.glassfish.api.event.EventListener;
import org.glassfish.api.event.EventTypes;
import org.glassfish.api.event.Events;
import org.glassfish.hk2.api.ServiceLocator;
import org.glassfish.hk2.runlevel.RunLevel;
import org.glassfish.internal.api.ServerContext;
import org.glassfish.internal.data.ApplicationRegistry;
import org.glassfish.internal.deployment.Deployment;
import org.jvnet.hk2.annotations.Service;
import org.jvnet.hk2.config.ConfigBeanProxy;
import org.jvnet.hk2.config.ConfigListener;
import org.jvnet.hk2.config.Transactions;
import org.jvnet.hk2.config.UnprocessedChangeEvents;

/**
 * An example of a service which can be used as an aid to developing future services
 * The service implements Event Listener to hook into the Sever wide events system
 * The service implements MessageReceiver to hook into the cluster wide messaging system
 * The service implements ConfigListener to receive notifications if it's configuration changes
 * 
 * Note you do not need to inject all the services below they are just examples of what is available
 * 
 * Note if you need to respond to configuration changes and be dynamic then implement the ConfigListener interface
 * if you are unable to respond to config changes don't implement the interface.
 * @author steve
 */
@Service(name = "example-service") // this specifies that the classis an HK2 service
@RunLevel(StartupRunLevel.VAL)  // this specifies the servce is created at server boot time
public class ExampleService implements EventListener, MessageReceiver, ConfigListener {
    
    private static final Logger LOGGER= Logger.getLogger(ExampleService.class.getCanonicalName());
    
    // Below is a list of useful services you can inject
    
    // Provides ability to register a configuration listener
    @Inject
    Transactions transactions;
    
    // Provides access to information on the server including;
    // command line, initial context, service locator, installation
    // Classloaders, config root for the server
    @Inject
    ServerContext context;
    
    // Provide access to information on the server instance
    @Inject
    ServerEnvironment serverEnv;
    
    //Provides methods to find other HK2 services
    @Inject
    ServiceLocator habitat;
    
    //Provides access to the event manager to hook into server lifecycle events
    // or to raise various events
    @Inject
    Events events;
    
    // Gives access to deployed applications
    @Inject
    ApplicationRegistry applicationRegistry;
    
    // The following are Payara Specific useful services built on Hazelcast and
    // only work if Hazelcast is enabled
    // Gives access to Hazelcast Clustered Services like the Event Bus and Clustered Store
    @Inject
    PayaraCluster cluster;
            
    // This service is access to the Hazelcast Base clustered Event Bus which can send
    // messages to other servers in the same Hazelcast cluster
    @Inject
    EventBus eventBus;
    
    // This gives access to the Hazelcast Based cluster wide in-memory data store
    // this is a bunch of named key-value stores accessible across the Hazelcast Cluster
    @Inject
    ClusteredStore clusterStore;
    
    // This injects the configuration from the domain.xml magically
    // and for the correct server configuation
    @Inject
    @Named(ServerEnvironment.DEFAULT_INSTANCE_NAME)
    ExampleServiceConfiguration config;
    
    /**
     * This method is called after the service instance has been created
     */
    @PostConstruct
    public void bootService() {
        LOGGER.info("Example Service has booted " + config.getMessage());
        
        // this code demonstrates how you can hook into the server event system to
        // be notified of events like when the server is running
        events.register(this);
        
        // this code hooks the service into the cluster wide messaging system
        // based on Hazelcast
        eventBus.addMessageReceiver("ExampleService", this);
        eventBus.publish("ExampleService", new ClusterMessage("Hello World"));
        
        // notify that I am interested in changes to the configuration class
        transactions.addListenerForType(ExampleServiceConfiguration.class, this);
    }

    /**
     * This is the required method for the EventListener interface for server wide messages
     * handling these events means your service can take action depending on things
     * happening within the server
     * @param event
     */
    @Override
    public void event(Event event) {
        // useful message types to register for are shown below
        if (event.is(EventTypes.SERVER_READY)) {
            LOGGER.info("Server has now finished booting and is ready to process");
        } else if (event.is(EventTypes.SERVER_STARTUP)) {
            LOGGER.info("Server is preparing to start");
        } else if (event.is(EventTypes.PREPARE_SHUTDOWN)) {
            LOGGER.info("Server is preparing to shutdown");
        } else if (event.is(EventTypes.SERVER_SHUTDOWN)) {
            LOGGER.info("Server has shutdown");
        } else if (event.is(Deployment.APPLICATION_STARTED)) // there are many deployment event types
        {
            LOGGER.info("Application has been loaded");
        }
    }

    /**
     * This is the required method for the Payara Specific cluster wide event bus
     * @param message
     */
    @Override
    public void receiveMessage(ClusterMessage message) {
        LOGGER.info("Received Message " + message.getPayload());
    }
    
    /**
     * This is the required method for the ConfigListener it is called
     * if our configuration is changed.
     * 
     * Note: Look at the implementation you must check whether you are the DAS
     * and if so check that it is the DAS config that has been changed
     * @see isCurrentInstanceMatchTarget
     * @param pces
     * @return 
     */
    @Override
    public UnprocessedChangeEvents changed(PropertyChangeEvent[] pces) {
        String newMessage = null;
        for (PropertyChangeEvent pce : pces) {
            if (pce.getPropertyName().equals("message")) {
                // ok it is a property we are interested in
                // we now need to check whether it is for our config
                // The DAS will receive all events even for other configs
                if (isCurrentInstanceMatchTarget(pce)) {
                    LOGGER.info("message has been change");
                    newMessage = (String) pce.getNewValue();               
                    reconfigure();
                }
            }
        }
        return null;
    }

    public void reconfigure() {
        LOGGER.info("Got a notification that I need to update myself from my config " );
        LOGGER.info("Config now says " + config.getMessage());
    }
    
    /**
     * Example method to be used by the instance targeted asadmin command
     * to interact with the service directly
     * @return 
     */
    public void doSomethingDirectly(String message) {
        LOGGER.info("Did something directly from asadmin " + message);
    }
    
    private boolean isCurrentInstanceMatchTarget(PropertyChangeEvent pe) {
        
        // if we are an instance then the change will apply to us as it has been
        // replicated directly to us by the DAS
        if (serverEnv.isInstance()) {
            return true;
        }
        // ok now we are on the DAS
        ConfigBeanProxy proxy = (ConfigBeanProxy) pe.getSource();
        
        // find the config parent
        while (proxy != null && !(proxy instanceof Config)) {
            proxy = proxy.getParent();
        }
        
        if (proxy != null) {
            // we have found a config node at the root
            // if the root config is the das config return true
            return ((Config)proxy).isDas();
        }
        return false;
    }
    
    
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy