/*
DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
Copyright (c) 2014,2015,2016 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.nucleus.hazelcast;
import com.hazelcast.cache.impl.HazelcastServerCachingProvider;
import com.hazelcast.config.Config;
import com.hazelcast.config.ConfigLoader;
import com.hazelcast.config.GroupConfig;
import com.hazelcast.config.MulticastConfig;
import com.hazelcast.config.XmlConfigBuilder;
import com.hazelcast.core.Hazelcast;
import com.hazelcast.core.HazelcastInstance;
import fish.payara.nucleus.events.HazelcastEvents;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.PostConstruct;
import javax.cache.spi.CachingProvider;
import javax.inject.Inject;
import javax.inject.Named;
import javax.naming.InitialContext;
import javax.naming.NamingException;
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.runlevel.RunLevel;
import org.glassfish.internal.api.ServerContext;
import org.jvnet.hk2.annotations.Service;
/**
*
* @author steve
*/
@Service(name = "hazelcast-core")
@RunLevel(StartupRunLevel.VAL)
public class HazelcastCore implements EventListener {
public final static String INSTANCE_ATTRIBUTE = "GLASSFISH-INSTANCE";
private static HazelcastCore theCore;
private static MulticastConfiguration overrideConfiguration;
private HazelcastInstance theInstance;
private CachingProvider hazelcastCachingProvider;
private boolean enabled;
private String memberName;
@Inject
Events events;
@Inject
ServerContext context;
@Inject
@Named(ServerEnvironment.DEFAULT_INSTANCE_NAME)
HazelcastRuntimeConfiguration configuration;
public static HazelcastCore getCore() {
return theCore;
}
public static void setMulticastOverride(MulticastConfiguration config){
overrideConfiguration = config;
}
@PostConstruct
public void postConstruct() {
theCore = this;
events.register(this);
}
public HazelcastInstance getInstance() {
return theInstance;
}
public CachingProvider getCachingProvider() {
return hazelcastCachingProvider;
}
public boolean isEnabled() {
return enabled;
}
@Override
public void event(Event event) {
if (event.is(EventTypes.SERVER_SHUTDOWN)) {
shutdownHazelcast();
} else if (event.is(EventTypes.SERVER_READY)) {
if (enabled) {
bindToJNDI();
}
} else if (event.is(EventTypes.SERVER_STARTUP)) {
if ((Boolean.valueOf(configuration.getEnabled()))) {
enabled = true;
bootstrapHazelcast();
}
}
}
public void setEnabled(Boolean enabled) {
if (!this.enabled && !enabled) {
// do nothing
} else if (this.enabled && !enabled) {
this.enabled = false;
shutdownHazelcast();
} else if (!this.enabled && enabled) {
this.enabled = true;
bootstrapHazelcast();
bindToJNDI();
} else if (this.enabled && enabled) {
// we need to reboot
shutdownHazelcast();
bootstrapHazelcast();
bindToJNDI();
}
}
private Config buildConfiguration() {
Config config = new Config();
String hazelcastFilePath = "";
URL serverConfigURL;
try {
if (overrideConfiguration != null && overrideConfiguration.getAlternateConfigFile() != null) {
XmlConfigBuilder builder = new XmlConfigBuilder(overrideConfiguration.getAlternateConfigFile().toURL());
config = builder.build();
return config;
}
serverConfigURL = new URL(context.getServerConfigURL());
File serverConfigFile = new File(serverConfigURL.getPath());
hazelcastFilePath = serverConfigFile.getParentFile().getAbsolutePath() + File.separator + configuration.getHazelcastConfigurationFile();
File file = new File(hazelcastFilePath);
if (file.exists()) {
config = ConfigLoader.load(hazelcastFilePath);
if (config == null) {
Logger.getLogger(HazelcastCore.class.getName()).log(Level.WARNING, "Hazelcast Core could not find configuration file {0} using default configuration", hazelcastFilePath);
config = new Config();
}
} else { // there is no config override
memberName = context.getInstanceName();
MulticastConfig mcConfig = config.getNetworkConfig().getJoin().getMulticastConfig();
config.getNetworkConfig().setPortAutoIncrement(true);
mcConfig.setEnabled(true); // check Payara micro overrides
if (overrideConfiguration != null) {
mcConfig.setMulticastGroup(overrideConfiguration.getMulticastGroup());
mcConfig.setMulticastPort(overrideConfiguration.getMulticastPort());
config.getNetworkConfig().setPort(overrideConfiguration.getStartPort());
if (overrideConfiguration.getMemberName() != null) {
memberName = overrideConfiguration.getMemberName();
}
config.setLiteMember(overrideConfiguration.isLite());
config.setLicenseKey(overrideConfiguration.getLicenseKey());
// set group config
GroupConfig gc = config.getGroupConfig();
gc.setName(overrideConfiguration.getClusterGroupName());
gc.setPassword(overrideConfiguration.getClusterGroupPassword());
} else {
mcConfig.setMulticastGroup(configuration.getMulticastGroup());
mcConfig.setMulticastPort(Integer.valueOf(configuration.getMulticastPort()));
config.getNetworkConfig().setPort(Integer.valueOf(configuration.getStartPort()));
config.setLicenseKey(configuration.getLicenseKey());
config.setLiteMember(Boolean.parseBoolean(configuration.getLite()));
// set group config
GroupConfig gc = config.getGroupConfig();
gc.setName(configuration.getClusterGroupName());
gc.setPassword(configuration.getClusterGroupPassword());
}
// build the configuration
config.setProperty("hazelcast.jmx", "true");
}
} catch (MalformedURLException ex) {
Logger.getLogger(HazelcastCore.class.getName()).log(Level.WARNING, "Unable to parse server config URL", ex);
} catch (IOException ex) {
Logger.getLogger(HazelcastCore.class.getName()).log(Level.WARNING, "Hazelcast Core could not load configuration file " + hazelcastFilePath + " using default configuration", ex);
}
return config;
}
private void shutdownHazelcast() {
if (theInstance != null) {
unbindFromJNDI();
hazelcastCachingProvider.getCacheManager().close();
hazelcastCachingProvider.close();
theInstance.shutdown();
theInstance = null;
events.send(new Event(HazelcastEvents.HAZELCAST_SHUTDOWN_COMPLETE));
Logger.getLogger(HazelcastCore.class.getName()).log(Level.INFO, "Shutdown Hazelcast");
}
}
private void bootstrapHazelcast() {
Config config = buildConfiguration();
theInstance = Hazelcast.newHazelcastInstance(config);
if (memberName == null) {
memberName = context.getInstanceName();
}
theInstance.getCluster().getLocalMember().setStringAttribute(INSTANCE_ATTRIBUTE, memberName);
hazelcastCachingProvider = HazelcastServerCachingProvider.createCachingProvider(theInstance);
events.send(new Event(HazelcastEvents.HAZELCAST_BOOTSTRAP_COMPLETE));
}
private void bindToJNDI() {
try {
InitialContext ctx;
ctx = new InitialContext();
ctx.bind(configuration.getJNDIName(), theInstance);
ctx.bind(configuration.getCachingProviderJNDIName(), hazelcastCachingProvider);
ctx.bind(configuration.getCacheManagerJNDIName(), hazelcastCachingProvider.getCacheManager());
Logger.getLogger(HazelcastCore.class.getName()).log(Level.INFO, "Hazelcast Instance Bound to JNDI at {0}", configuration.getJNDIName());
Logger.getLogger(HazelcastCore.class.getName()).log(Level.INFO, "JSR107 Caching Provider Bound to JNDI at {0}", configuration.getCachingProviderJNDIName());
Logger.getLogger(HazelcastCore.class.getName()).log(Level.INFO, "JSR107 Default Cache Manager Bound to JNDI at {0}", configuration.getCacheManagerJNDIName());
} catch (NamingException ex) {
Logger.getLogger(HazelcastCore.class.getName()).log(Level.SEVERE, null, ex);
}
}
private void unbindFromJNDI() {
try {
InitialContext ctx;
ctx = new InitialContext();
ctx.unbind(configuration.getJNDIName());
ctx.unbind(configuration.getCacheManagerJNDIName());
ctx.unbind(configuration.getCachingProviderJNDIName());
Logger.getLogger(HazelcastCore.class.getName()).log(Level.INFO, "Hazelcast Instance Unbound from JNDI at {0}", configuration.getJNDIName());
Logger.getLogger(HazelcastCore.class.getName()).log(Level.INFO, "JSR107 Caching Provider Unbound from JNDI at {0}", configuration.getCachingProviderJNDIName());
Logger.getLogger(HazelcastCore.class.getName()).log(Level.INFO, "JSR107 Cache Manager Unbound from JNDI at {0}", configuration.getCacheManagerJNDIName());
} catch (NamingException ex) {
Logger.getLogger(HazelcastCore.class.getName()).log(Level.SEVERE, null, ex);
}
}
}