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

fish.payara.nucleus.hazelcast.HazelcastCore Maven / Gradle / Ivy

/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) [2016-2020] Payara Foundation and/or its affiliates. All rights reserved.
 *
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or 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://github.com/payara/Payara/blob/master/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 glassfish/legal/LICENSE.txt.
 *
 * GPL Classpath Exception:
 * The Payara Foundation designates this particular file as subject to the "Classpath"
 * exception as provided by the Payara Foundation in the GPL Version 2 section of the License
 * file that accompanied this code.
 *
 * Modifications:
 * If applicable, add the following below the License Header, with the fields
 * enclosed by brackets [] replaced by your own identifying information:
 * "Portions Copyright [year] [name of copyright owner]"
 *
 * Contributor(s):
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 */
package fish.payara.nucleus.hazelcast;

import static java.lang.String.valueOf;

import com.hazelcast.cache.impl.HazelcastServerCachingProvider;
import com.hazelcast.config.Config;
import com.hazelcast.config.ExecutorConfig;
import com.hazelcast.config.GlobalSerializerConfig;
import com.hazelcast.config.MemberAddressProviderConfig;
import com.hazelcast.config.MulticastConfig;
import com.hazelcast.config.NetworkConfig;
import com.hazelcast.config.PartitionGroupConfig;
import com.hazelcast.config.ScheduledExecutorConfig;
import com.hazelcast.config.SerializationConfig;
import com.hazelcast.config.TcpIpConfig;
import com.hazelcast.core.Hazelcast;
import com.hazelcast.core.HazelcastInstance;
import com.hazelcast.internal.config.ConfigLoader;
import com.hazelcast.kubernetes.KubernetesProperties;
import com.hazelcast.map.IMap;
import com.hazelcast.nio.serialization.Serializer;
import com.hazelcast.nio.serialization.StreamSerializer;
import static com.hazelcast.spi.properties.ClusterProperty.WAIT_SECONDS_BEFORE_JOIN;
import com.sun.enterprise.util.Utility;
import fish.payara.nucleus.events.HazelcastEvents;
import org.glassfish.api.StartupRunLevel;
import org.glassfish.api.admin.ServerEnvironment;
import org.glassfish.api.admin.ServerEnvironment.Status;
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.ClassLoaderHierarchy;
import org.glassfish.internal.api.JavaEEContextUtil;
import org.glassfish.internal.api.ServerContext;
import org.glassfish.internal.deployment.Deployment;
import org.jvnet.hk2.annotations.Optional;
import org.jvnet.hk2.annotations.Service;
import org.jvnet.hk2.config.ConfigListener;
import org.jvnet.hk2.config.Transactions;
import org.jvnet.hk2.config.UnprocessedChangeEvent;
import org.jvnet.hk2.config.UnprocessedChangeEvents;

import javax.annotation.PostConstruct;
import javax.cache.spi.CachingProvider;
import javax.inject.Inject;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import java.beans.PropertyChangeEvent;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.UUID;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * The core class for using Hazelcast in Payara
 * @author Steve Millidge (Payara Foundation)
 * @since 4.1.151
 */
@Service(name = "hazelcast-core")
@RunLevel(StartupRunLevel.VAL)
public class HazelcastCore implements EventListener, ConfigListener {

    public final static String INSTANCE_ATTRIBUTE_MAP = "payara-instance-map";
    public final static String INSTANCE_ATTRIBUTE = "GLASSFISH-INSTANCE";
    public final static String INSTANCE_GROUP_ATTRIBUTE = "GLASSFISH_INSTANCE_GROUP";
    public static final String CLUSTER_EXECUTOR_SERVICE_NAME="payara-cluster-execution";
    public static final String SCHEDULED_CLUSTER_EXECUTOR_SERVICE_NAME="payara-scheduled-execution";
    private static HazelcastCore theCore;

    private HazelcastInstance theInstance;

    private CachingProvider hazelcastCachingProvider;
    private boolean enabled;
    private boolean booted=false;
    private String memberName;
    private String memberGroup;

    private boolean datagridEncryptionValue;

    @Inject
    Events events;

    @Inject
    ServerContext context;

    @Inject
    ServerEnvironment env;

    @Inject
    HazelcastRuntimeConfiguration configuration;

    @Inject
    HazelcastConfigSpecificConfiguration nodeConfig;

    @Inject
    private ClassLoaderHierarchy clh;

    @Inject @Optional
    private JavaEEContextUtil ctxUtil;

    // Provides ability to register a configuration listener
    @Inject
    Transactions transactions;

    /**
     * Returns the version of the object that has been instantiated.
     * @return null if an instance of {@link HazelcastCore} has not been created
     */
    public static HazelcastCore getCore() {
        return theCore;
    }

    @PostConstruct
    public void postConstruct() {
        theCore = this;
        events.register(this);
        enabled = Boolean.valueOf(nodeConfig.getEnabled());
        transactions.addListenerForType(HazelcastConfigSpecificConfiguration.class, this);
        transactions.addListenerForType(HazelcastRuntimeConfiguration.class, this);

        if (env.isMicro()) {
            memberName = nodeConfig.getMemberName();
            memberGroup = nodeConfig.getMemberGroup();
        } else {
            memberName = context.getInstanceName();
            memberGroup = nodeConfig.getMemberGroup();
        }

        datagridEncryptionValue = Boolean.parseBoolean(configuration.getDatagridEncryptionEnabled());
        if (datagridEncryptionValue) {
            Logger.getLogger(HazelcastCore.class.getName()).log(Level.INFO, "Data grid encryption is enabled");
        }
    }

    /**
     * Returns the Hazelcast name of the instance
     * 

* Note this is not the same as the name of the instance config or node * @return {@code Payara} by default * @since 4.1.1.171 */ public String getMemberName() { return memberName; } /** * Gets the name of the member group that this instance belongs to * @return {@code MicroShoal} by default * @since 4.1.1.171 */ public String getMemberGroup() { return memberGroup; } /** * Returns the UUID of the instance. * If Hazelcast is not enabled then a new random one will be returned. * @return a 128-bit immutable universally unique identifier * @since 4.1.1.171 */ public UUID getUUID() { bootstrapHazelcast(); if (!enabled) { return UUID.randomUUID(); } return theInstance.getCluster().getLocalMember().getUuid(); } /** * Returns true if this instance is a Hazelcast Lite instance * @return * @since 4.1.1.171 */ public boolean isLite() { bootstrapHazelcast(); if (!enabled) { return false; } return theInstance.getCluster().getLocalMember().isLiteMember(); } /** * Gets the actual Hazelcast instance. * Hazelcast will be booted by this method if * it hasn't already started. * @return */ public HazelcastInstance getInstance() { bootstrapHazelcast(); return theInstance; } /** * Gets the JCache provider used by Hazelcast * @return * @see HazelcastCachingProvider */ public CachingProvider getCachingProvider() { bootstrapHazelcast(); return hazelcastCachingProvider; } /** * * @return Whether Hazelcast is currently enabled */ public boolean isEnabled() { return enabled; } @Override public void event(Event event) { if (event.is(Deployment.ALL_APPLICATIONS_STOPPED)) { shutdownHazelcast(); } else if (event.is(Deployment.ALL_APPLICATIONS_LOADED)) { ClassLoader oldCL = Utility.getClassLoader(); try { Utility.setContextClassLoader(clh.getCommonClassLoader()); bootstrapHazelcast(); } finally { Utility.setContextClassLoader(oldCL); } } else if(event.is(EventTypes.SERVER_STARTUP) && isEnabled() && booted) { // send this event only after all Startup services have been initialized events.send(new Event(HazelcastEvents.HAZELCAST_BOOTSTRAP_COMPLETE)); } } /** * Sets whether Hazelcast should be enabled. * @param enabled If true will start Hazelcast or restart if currently running; * if false will shut down Hazelcast. */ public void setEnabled(Boolean enabled) { if (!this.enabled && !enabled) { // do nothing } else if (this.enabled && !enabled) { this.enabled = false; shutdownHazelcast(); booted = false; } else if (!this.enabled && enabled) { this.enabled = true; bootstrapHazelcast(); } else if (this.enabled && enabled) { // we need to reboot shutdownHazelcast(); this.enabled = true; booted =false; bootstrapHazelcast(); } } private Config buildConfiguration() { Config config = new Config(); String hazelcastFilePath = ""; URL serverConfigURL; try { 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(); } config.setClassLoader(clh.getCommonClassLoader()); if(ctxUtil == null) { Logger.getLogger(HazelcastCore.class.getName()).log(Level.WARNING, "Hazelcast Application Object Serialization Not Available"); } else { SerializationConfig serConfig = config.getSerializationConfig(); if (serConfig == null) { serConfig = new SerializationConfig(); setPayaraSerializerConfig(serConfig); config.setSerializationConfig(serConfig); } else { if(serConfig.getGlobalSerializerConfig() == null) { setPayaraSerializerConfig(serConfig); } else { Serializer ser = serConfig.getGlobalSerializerConfig().getImplementation(); if (ser instanceof StreamSerializer) { config.getSerializationConfig().getGlobalSerializerConfig().setImplementation( new PayaraHazelcastSerializer(ctxUtil, (StreamSerializer) ser)); } else { Logger.getLogger(HazelcastCore.class.getName()).log(Level.WARNING, "Global serializer is not StreamSerializer: {0}", ser.getClass().getName()); } } } } } else { // there is no config override config.setClassLoader(clh.getCommonClassLoader()); // The below are to test split-brain scenario, // see https://github.com/hazelcast/hazelcast/issues/17586 // and https://github.com/hazelcast/hazelcast/issues/17260 // config.setProperty(MAX_NO_HEARTBEAT_SECONDS.getName(), "5"); // config.setProperty(HEARTBEAT_INTERVAL_SECONDS.getName(), "1"); // config.setProperty(MERGE_FIRST_RUN_DELAY_SECONDS.getName(), "5"); // config.setProperty(MERGE_NEXT_RUN_DELAY_SECONDS.getName(), "5"); // can't quite set it to zero yet because of: // https://github.com/hazelcast/hazelcast/issues/17586 config.setProperty(WAIT_SECONDS_BEFORE_JOIN.getName(), "1"); if(ctxUtil != null) { SerializationConfig serializationConfig = new SerializationConfig(); setPayaraSerializerConfig(serializationConfig); config.setSerializationConfig(serializationConfig); } buildNetworkConfiguration(config); config.setLicenseKey(configuration.getLicenseKey()); config.setLiteMember(Boolean.parseBoolean(nodeConfig.getLite())); config.setClusterName(configuration.getClusterGroupName()); // build the configuration if ("true".equals(configuration.getHostAwarePartitioning())) { PartitionGroupConfig partitionGroupConfig = config.getPartitionGroupConfig(); partitionGroupConfig.setEnabled(enabled); partitionGroupConfig.setGroupType(PartitionGroupConfig.MemberGroupType.HOST_AWARE); } // build the executor config ExecutorConfig executorConfig = config.getExecutorConfig(CLUSTER_EXECUTOR_SERVICE_NAME); executorConfig.setStatisticsEnabled(true); executorConfig.setPoolSize(Integer.valueOf(nodeConfig.getExecutorPoolSize())); executorConfig.setQueueCapacity(Integer.valueOf(nodeConfig.getExecutorQueueCapacity())); ScheduledExecutorConfig scheduledExecutorConfig = config.getScheduledExecutorConfig(SCHEDULED_CLUSTER_EXECUTOR_SERVICE_NAME); scheduledExecutorConfig.setDurability(1); scheduledExecutorConfig.setCapacity(Integer.valueOf(nodeConfig.getScheduledExecutorQueueCapacity())); scheduledExecutorConfig.setPoolSize(Integer.valueOf(nodeConfig.getScheduledExecutorPoolSize())); config.setProperty("hazelcast.jmx", "true"); } if (config.getCPSubsystemConfig().getCPMemberCount() == 0) { config.getCPSubsystemConfig().setCPMemberCount(Integer.getInteger("hazelcast.cp-subsystem.cp-member-count", 0)); } } 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 setPayaraSerializerConfig(SerializationConfig serConfig) { if(serConfig == null || ctxUtil == null) { throw new IllegalStateException("either serialization config or ctxUtil is null"); } serConfig.setGlobalSerializerConfig(new GlobalSerializerConfig().setImplementation( new PayaraHazelcastSerializer(ctxUtil, null)) .setOverrideJavaSerialization(true)); } private void buildNetworkConfiguration(Config config) throws NumberFormatException { NetworkConfig nConfig = config.getNetworkConfig(); if (nodeConfig.getPublicAddress() != null && !nodeConfig.getPublicAddress().isEmpty()) { nConfig.setPublicAddress(nodeConfig.getPublicAddress()); } if (!configuration.getInterface().isEmpty()) { // add an interfaces configuration String[] interfaceNames = configuration.getInterface().split(","); for (String interfaceName : interfaceNames) { nConfig.getInterfaces().addInterface(interfaceName.trim()); } nConfig.getInterfaces().setEnabled(true); } else { MemberAddressProviderConfig memberAddressProviderConfig = nConfig.getMemberAddressProviderConfig(); memberAddressProviderConfig.setEnabled(enabled); memberAddressProviderConfig.setImplementation(new MemberAddressPicker(env, configuration, nodeConfig)); } int port = Integer.valueOf(configuration.getStartPort()); String configSpecificPort = nodeConfig.getConfigSpecificDataGridStartPort(); if (configSpecificPort != null && !configSpecificPort.isEmpty()) { // Setting it equal to zero will be the same as null or empty (to maintain backwards compatibility) int configSpecificPortInt = Integer.parseInt(configSpecificPort); if (configSpecificPortInt != 0) { port = configSpecificPortInt; } } String discoveryMode = configuration.getDiscoveryMode(); if (discoveryMode.startsWith("tcpip")) { TcpIpConfig tConfig = config.getNetworkConfig().getJoin().getTcpIpConfig(); tConfig.setEnabled(true); tConfig.addMember(configuration.getTcpipMembers()); config.getNetworkConfig().getJoin().getMulticastConfig().setEnabled(false); } else if (discoveryMode.startsWith("dns")) { config.setProperty("hazelcast.discovery.enabled", "true"); config.getNetworkConfig().getJoin().getDiscoveryConfig().setDiscoveryServiceProvider(new DnsDiscoveryServiceProvider(configuration.getDnsMembers())); config.getNetworkConfig().getJoin().getMulticastConfig().setEnabled(false); } else if (discoveryMode.startsWith("multicast")) { // build networking MulticastConfig mcConfig = config.getNetworkConfig().getJoin().getMulticastConfig(); mcConfig.setEnabled(true); mcConfig.setMulticastGroup(configuration.getMulticastGroup()); mcConfig.setMulticastPort(Integer.valueOf(configuration.getMulticastPort())); } else if (discoveryMode.startsWith("kubernetes")) { config.getNetworkConfig().getJoin().getMulticastConfig().setEnabled(false); config.getNetworkConfig().getJoin().getKubernetesConfig().setEnabled(true) .setProperty(KubernetesProperties.NAMESPACE.key(), configuration.getKubernetesNamespace()) .setProperty(KubernetesProperties.SERVICE_NAME.key(), configuration.getKubernetesServiceName()) .setProperty(KubernetesProperties.SERVICE_PORT.key(), valueOf(port)); } else { //build the domain discovery config config.setProperty("hazelcast.discovery.enabled", "true"); config.getNetworkConfig().getJoin().getDiscoveryConfig().setDiscoveryServiceProvider(DomainDiscoveryService::new); config.getNetworkConfig().getJoin().getMulticastConfig().setEnabled(false); } if (env.isDas() && !env.isMicro()) { port = Integer.valueOf(configuration.getDasPort()); } config.getNetworkConfig().setPort(port); config.getNetworkConfig().setPortAutoIncrement("true".equalsIgnoreCase(configuration.getAutoIncrementPort())); } private void shutdownHazelcast() { if (theInstance != null) { enabled = false; events.send(new Event(HazelcastEvents.HAZELCAST_SHUTDOWN_STARTED, true)); 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"); } } /** * Starts Hazelcast if not already enabled */ private synchronized void bootstrapHazelcast() { if (!booted && isEnabled()) { Config config = buildConfiguration(); Logger cpSubsystemLogger = Logger.getLogger("com.hazelcast.cp.CPSubsystem"); Level cpSubsystemLevel = cpSubsystemLogger.getLevel(); try { // remove "CP Subsystem Unsafe" warning on startup cpSubsystemLogger.setLevel(Level.SEVERE); theInstance = Hazelcast.newHazelcastInstance(config); } finally { cpSubsystemLogger.setLevel(cpSubsystemLevel); } if (env.isMicro()) { if (Boolean.valueOf(configuration.getGenerateNames()) || memberName == null) { memberName = PayaraMicroNameGenerator.generateName(); Set clusterMembers = theInstance.getCluster().getMembers(); // If the instance name was generated, we need to compile a list of all the instance names in use within // the instance group, excluding this local instance List takenNames = new ArrayList<>(); for (com.hazelcast.cluster.Member member : clusterMembers) { Map attributes = getAttributes(member.getUuid()); if (member != theInstance.getCluster().getLocalMember() && attributes.get(HazelcastCore.INSTANCE_GROUP_ATTRIBUTE) != null && attributes.get(HazelcastCore.INSTANCE_GROUP_ATTRIBUTE).equalsIgnoreCase(memberGroup)) { takenNames.add(attributes.get(HazelcastCore.INSTANCE_ATTRIBUTE)); } } // If our generated name is already in use within the instance group, either generate a new one or set the // name to this instance's UUID if there are no more unique generated options left if (takenNames.contains(memberName)) { memberName = PayaraMicroNameGenerator.generateUniqueName(takenNames, theInstance.getCluster().getLocalMember().getUuid()); setAttribute(theInstance.getCluster().getLocalMember().getUuid(), HazelcastCore.INSTANCE_ATTRIBUTE, memberName); } } } setAttribute(theInstance.getCluster().getLocalMember().getUuid(), INSTANCE_ATTRIBUTE, memberName); setAttribute(theInstance.getCluster().getLocalMember().getUuid(), INSTANCE_GROUP_ATTRIBUTE, memberGroup); hazelcastCachingProvider = new HazelcastServerCachingProvider(theInstance); bindToJNDI(); if(env.getStatus() == Status.started) { // only issue this event if the server is already running, // otherwise the SERVER_STARTUP event will issue this event as well events.send(new Event(HazelcastEvents.HAZELCAST_BOOTSTRAP_COMPLETE)); } booted = true; } } public String getAttribute(UUID memberUUID, String key) { return getAttributes(memberUUID).get(key); } public void setAttribute(UUID memberUUID, String key, String value) { IMap> instanceAttributeMap = theInstance.getMap(INSTANCE_ATTRIBUTE_MAP); instanceAttributeMap.compute(memberUUID, (uuid, map) -> { if (map == null) { map = new HashMap<>(); } map.put(HazelcastCore.INSTANCE_ATTRIBUTE, memberName); return map; }); } public Map getAttributes(UUID memberUUID) { IMap> instanceAttributeMap = theInstance.getMap(INSTANCE_ATTRIBUTE_MAP); return instanceAttributeMap.getOrDefault(memberUUID, Collections.unmodifiableMap(new TreeMap<>())); } private void bindToJNDI() { try { InitialContext ctx; ctx = new InitialContext(); ctx.bind(nodeConfig.getJNDIName(), theInstance); ctx.bind(nodeConfig.getCachingProviderJNDIName(), hazelcastCachingProvider); ctx.bind(nodeConfig.getCacheManagerJNDIName(), hazelcastCachingProvider.getCacheManager()); Logger.getLogger(HazelcastCore.class.getName()).log(Level.INFO, "Hazelcast Instance Bound to JNDI at {0}", nodeConfig.getJNDIName()); Logger.getLogger(HazelcastCore.class.getName()).log(Level.INFO, "JSR107 Caching Provider Bound to JNDI at {0}", nodeConfig.getCachingProviderJNDIName()); Logger.getLogger(HazelcastCore.class.getName()).log(Level.INFO, "JSR107 Default Cache Manager Bound to JNDI at {0}", nodeConfig.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(nodeConfig.getJNDIName()); ctx.unbind(nodeConfig.getCacheManagerJNDIName()); ctx.unbind(nodeConfig.getCachingProviderJNDIName()); Logger.getLogger(HazelcastCore.class.getName()).log(Level.INFO, "Hazelcast Instance Unbound from JNDI at {0}", nodeConfig.getJNDIName()); Logger.getLogger(HazelcastCore.class.getName()).log(Level.INFO, "JSR107 Caching Provider Unbound from JNDI at {0}", nodeConfig.getCachingProviderJNDIName()); Logger.getLogger(HazelcastCore.class.getName()).log(Level.INFO, "JSR107 Cache Manager Unbound from JNDI at {0}", nodeConfig.getCacheManagerJNDIName()); } catch (NamingException ex) { Logger.getLogger(HazelcastCore.class.getName()).log(Level.SEVERE, null, ex); } } /** * Gets the port that Hazelcast in running on * @return The default is {@link 54327} */ public int getPort() { return theInstance.getCluster().getLocalMember().getSocketAddress().getPort(); } @Override public UnprocessedChangeEvents changed(PropertyChangeEvent[] pces) { List unprocessedChanges = new ArrayList<>(); for (PropertyChangeEvent pce : pces) { if (pce.getPropertyName().equalsIgnoreCase("datagrid-encryption-enabled")) { unprocessedChanges.add(new UnprocessedChangeEvent(pce, "Hazelcast encryption settings changed")); } } if (unprocessedChanges.isEmpty()) { return null; } return new UnprocessedChangeEvents(unprocessedChanges); } public boolean isDatagridEncryptionEnabled() { // We want to return the value as it was at boot time here to prevent the server changing encryption behaviour // without a restart return datagridEncryptionValue; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy