fish.payara.service.example.ExampleService Maven / Gradle / Ivy
/*
* 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;
}
}