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

com.sun.ejb.containers.StatefulContainerFactory Maven / Gradle / Ivy

There is a newer version: 6.2024.6
Show newest version
/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 1997-2013 Oracle 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://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.
 *
 * GPL Classpath Exception:
 * Oracle designates this particular file as subject to the "Classpath"
 * exception as provided by Oracle 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 com.sun.ejb.containers;

import java.io.File;
import java.io.Serializable;
import java.net.InetAddress;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

import jakarta.inject.Inject;
import jakarta.inject.Named;

import com.sun.appserv.util.cache.CacheListener;
import com.sun.ejb.Container;
import com.sun.ejb.ContainerFactory;
import com.sun.ejb.base.container.util.CacheProperties;
import com.sun.ejb.base.sfsb.util.ScrambledKeyGenerator;
import com.sun.ejb.base.sfsb.util.SimpleKeyGenerator;
import com.sun.ejb.containers.util.cache.FIFOSessionCache;
import com.sun.ejb.containers.util.cache.LruSessionCache;
import com.sun.ejb.containers.util.cache.NRUSessionCache;
import com.sun.ejb.containers.util.cache.UnBoundedSessionCache;
import com.sun.enterprise.config.serverbeans.AvailabilityService;
import com.sun.enterprise.config.serverbeans.Config;
import com.sun.enterprise.security.SecurityManager;
import com.sun.enterprise.util.Utility;
import fish.payara.nucleus.hazelcast.HazelcastCore;
import org.glassfish.api.admin.ServerEnvironment;
import org.glassfish.api.deployment.DeployCommandParameters;
import org.glassfish.api.deployment.DeploymentContext;
import org.glassfish.ejb.LogFacade;
import org.glassfish.ejb.config.EjbContainer;
import org.glassfish.ejb.config.EjbContainerAvailability;
import org.glassfish.ejb.deployment.descriptor.EjbDescriptor;
import org.glassfish.ejb.deployment.descriptor.EjbSessionDescriptor;
import org.glassfish.ha.store.api.BackingStore;
import org.glassfish.ha.store.api.BackingStoreConfiguration;
import org.glassfish.ha.store.api.BackingStoreException;
import org.glassfish.ha.store.api.BackingStoreFactory;
import org.glassfish.ha.store.util.SimpleMetadata;
import org.jvnet.hk2.annotations.Optional;
import org.jvnet.hk2.annotations.Service;
import org.glassfish.hk2.api.PerLookup;
import org.glassfish.hk2.api.PostConstruct;
import org.glassfish.hk2.api.ServiceLocator;
import org.glassfish.logging.annotation.LogMessageInfo;

/**
 * A builder for StatefulSessionContainer. Takes care of
 * building / initializing the StatefulSessionContainer
 * with the following classes:
 * a) Cache (LRU / NRU / FIFO / UnBounded)
 * b) SFSBStoreManager (Using PersistenceStrategyBuilder)
 * c) Cache passivation task (if idle-timeout is greater than 0)
 * d) Passivated sessions removal task (if removal-timeout is greater than 0)
 * e) CheckpointPolicy (if ha enabled)
 * f) SFSBUUIDUtil
 * g) BeanLifecycleManager
 *
 * @author Mahesh Kannan
 */
@Service(name = "StatefulContainerFactory")
@PerLookup
public class StatefulContainerFactory extends BaseContainerFactory
        implements PostConstruct, ContainerFactory {
    protected static final Logger _logger  = LogFacade.getLogger();

    @LogMessageInfo(
        message = "TopLevel AvailabilityService.getAvailabilityEnabled: [{0}]",
        level = "INFO")
    private static final String SFSB_BUILDER_TOP_LEVEL_AVAILABILITY_SERVICE_ENABLED = "AS-EJB-00036";

    @LogMessageInfo(
        message = "TopLevel EjbAvailabilityService.getAvailabilityEnabled: [{0}]",
        level = "INFO")
    private static final String SFSB_BUILDER_EJB_AVAILABILITY_SERVICE_ENABLED = "AS-EJB-00037";

    @LogMessageInfo(
        message = "Global AvailabilityEnabled: [{0}], application AvailabilityEnabled: [{1}]",
        level = "INFO")
    private static final String SFSB_BUILDER_GLOBAL_AND_APP_AVAILABILITY_ENABLED = "AS-EJB-00038";

    @LogMessageInfo(
        message = "Exception while trying to determine availability-enabled settings for this app",
        level = "WARNING")
    private static final String SFSB_BUILDER_DETERMINE_AVAILABILITY_EXCEPTION = "AS-EJB-00039";

    @LogMessageInfo(
        message = "StatefulContainerBuilder AvailabilityEnabled [{0}] for this application",
        level = "INFO")
    private static final String SFSB_BUILDER_RESOLVED_AVAILABILITY_ENABLED = "AS-EJB-00040";

    @LogMessageInfo(
        message = "StatefulContainerBuilder.buildStoreManager() storeName: [{0}]",
        level = "INFO")
    private static final String SFSB_BUILDER_STORE_NAME = "AS-EJB-00041";

    @LogMessageInfo(
        message = "Could not instantiate backing store for type [{0}]",
        level = "WARNING")
    private static final String SFSB_BUILDER_INSTANTIATE_BACKING_STORE_EXCEPTION = "AS-EJB-00042";

    @LogMessageInfo(
        message = "StatefulContainerbuilder instantiated store: {0}, " +
                "with ha-enabled [{1}], and backing store configuration: {2}",
        level = "INFO")
    private static final String SFSB_BUILDER_INSTANTIATED_BACKING_STORE = "AS-EJB-00043";

    @LogMessageInfo(
        message = "Error while adding idle bean passivator task",
        level = "WARNING")
    private static final String SFSB_HELPER_ADD_IDLE_PASSIVATOR_TASK_FAILED = "AS-EJB-00044";

    @LogMessageInfo(
        message = "Error while adding idle bean removal task",
        level = "WARNING")
    private static final String SFSB_HELPER_ADD_REMOVE_PASSIVATOR_TASK_FAILED = "AS-EJB-00045";

    @LogMessageInfo(
        message = "Error while removing idle beans for [{0}]",
        level = "WARNING")
    static final String SFSB_HELPER_REMOVE_IDLE_BEANS_FAILED = "AS-EJB-00046";

    @LogMessageInfo(
        message = "Error while removing expired beans for [{0}]",
        level = "WARNING")
    static final String SFSB_HELPER_REMOVE_EXPIRED_BEANS_FAILED = "AS-EJB-00047";

    @LogMessageInfo(
        message = "Disabling high availability for the stateful session bean {0}, as its marked non passivatable",
        level = "WARNING")
    private static final String SFSB_HA_DISABLED_BY_PASSIVATION_SETTING = "AS-EJB-00051";

    private static final Level TRACE_LEVEL = Level.FINE;

    private EjbDescriptor		    ejbDescriptor;

    private StatefulSessionContainer sfsbContainer;

    @Inject
    private ServiceLocator services;

    @Inject
    private CacheProperties cacheProps;

    @Inject @Named(ServerEnvironment.DEFAULT_INSTANCE_NAME) @Optional
    private AvailabilityService availabilityService;

    @Inject @Optional
    private EjbContainerAvailability ejbAvailability;

    @Inject @Named(ServerEnvironment.DEFAULT_INSTANCE_NAME)
    private Config serverConfig;

    EjbContainer ejbContainerConfig;

    @Inject @Optional
    HazelcastCore hazelcast;
    
    private LruSessionCache sessionCache;

    private BackingStore backingStore;

    private boolean HAEnabled = false;

    private boolean asyncReplication = true;

    private SimpleKeyGenerator keyGen;

    public void postConstruct() {
        ejbContainerConfig = serverConfig.getExtensionByType(EjbContainer.class);
    }

    public void buildComponents(byte[] ipAddress, int port,
                                DeploymentContext dc)
            throws Exception {
        if (availabilityService != null) {
            this.HAEnabled = Boolean.valueOf(availabilityService.getAvailabilityEnabled());
            _logger.log(Level.FINE, SFSB_BUILDER_TOP_LEVEL_AVAILABILITY_SERVICE_ENABLED, this.HAEnabled);
            if ((this.HAEnabled) && (ejbAvailability != null)) {
                this.HAEnabled = Boolean.valueOf(ejbAvailability.getAvailabilityEnabled());
                _logger.log(Level.FINE, SFSB_BUILDER_EJB_AVAILABILITY_SERVICE_ENABLED, this.HAEnabled);
            }

            boolean appLevelHAEnabled = false;
            try {
                if (HAEnabled) {
                    if (dc != null) {
                        DeployCommandParameters params = dc.getCommandParameters(DeployCommandParameters.class);
                        if (params != null) {
                            appLevelHAEnabled = params.availabilityenabled;
                            asyncReplication = params.asyncreplication;
                        }
                    }
                    
                    _logger.log(Level.FINE, SFSB_BUILDER_GLOBAL_AND_APP_AVAILABILITY_ENABLED,
                            new Object[] {this.HAEnabled, appLevelHAEnabled});
                }
            } catch (Exception ex) {
                _logger.log(Level.WARNING, SFSB_BUILDER_DETERMINE_AVAILABILITY_EXCEPTION, ex);
                appLevelHAEnabled = false;
            }

            HAEnabled = HAEnabled && appLevelHAEnabled;
            _logger.log(Level.FINE, SFSB_BUILDER_RESOLVED_AVAILABILITY_ENABLED, this.HAEnabled);
        }

        EjbSessionDescriptor sessionDescriptor = (EjbSessionDescriptor)ejbDescriptor;
        //When passivation is disabled, we should also forbid ha.
        if (!sessionDescriptor.isPassivationCapable() && HAEnabled) {
            if (_logger.isLoggable(Level.WARNING)) {
                _logger.log(Level.WARNING, SFSB_HA_DISABLED_BY_PASSIVATION_SETTING, ejbDescriptor.getEjbClassName());
            }
            HAEnabled = false;
        }

        buildCheckpointPolicy(this.HAEnabled);
        buildSFSBUUIDUtil(ipAddress, port);

        //First build BackingStore before Cache is built
        if (sessionDescriptor.isPassivationCapable()){
            buildStoreManager();
        } else{
            if (_logger.isLoggable(TRACE_LEVEL)) {
                _logger.log(TRACE_LEVEL, "Stateful session bean passivation is disabled, so do not create store manger");
            }
        }

        buildCache();
        scheduleTimerTasks(sfsbContainer);
    }

    /************************* Private Methods *************************/
    /**
     * ***************************************************************
     */

    private final void buildCheckpointPolicy(boolean haEnabled) {
        sfsbContainer.setHAEnabled(haEnabled);
    }

    private void buildSFSBUUIDUtil(byte[] ipAddress, int port) {
        //Just for debugging purpose,  we instantiate
        //  two different key generators
        keyGen = HAEnabled
                ? new ScrambledKeyGenerator(ipAddress, port)
                : new SimpleKeyGenerator(ipAddress, port);
        sfsbContainer.setSFSBUUIDUtil(keyGen);
    }

    private void buildStoreManager()
        throws BackingStoreException {

        String persistenceStoreType = "file";

        if (ejbAvailability != null) {
            persistenceStoreType = HAEnabled
                ? ejbAvailability.getSfsbHaPersistenceType() : ejbAvailability.getSfsbPersistenceType();
            if ("ha".equals(persistenceStoreType)) {
                persistenceStoreType = "replicated";
            } else if ("memory".equals(persistenceStoreType)) {
                persistenceStoreType = "file";
            }
        }



        BackingStoreConfiguration conf = new BackingStoreConfiguration();
        String storeName = ejbDescriptor.getName() + "-" + ejbDescriptor.getUniqueId() + "-BackingStore";

        _logger.log(Level.FINE, SFSB_BUILDER_STORE_NAME, storeName);
        
        String subDirName = "";

/*        if (ejbDescriptor.getApplication().isVirtual()) {
            String archURI = ejbDescriptor.getEjbBundleDescriptor().
                    getModuleDescriptor().getArchiveUri();
            subDirName += FileUtils.makeFriendlyFilename(archURI);
            subDirName += "_" + FileUtils.makeFriendlyFilename(ejbDescriptor.getName());
        } else {
            subDirName += FileUtils.makeFriendlyFilename(ejbDescriptor.getApplication().getRegistrationName());
            subDirName += "_" + FileUtils.makeFriendlyFilename(ejbDescriptor.getEjbBundleDescriptor().getName());
            subDirName += "_" + FileUtils.makeFriendlyFilename(ejbDescriptor.getName());
        }*/

        subDirName += ejbDescriptor.getName() + "-" + ejbDescriptor.getUniqueId();
        
        conf.setShortUniqueName(""+ejbDescriptor.getUniqueId()).setStoreName(storeName)
                .setStoreType(persistenceStoreType)
                .setBaseDirectory(new File(ejbContainerConfig.getSessionStore(), subDirName))
                .setKeyClazz(Serializable.class)
                .setValueClazz(SimpleMetadata.class)
                .setClassLoader(StatefulContainerFactory.class.getClassLoader());


        Map vendorMap = conf.getVendorSpecificSettings();
        vendorMap.put("local.caching", true);
        vendorMap.put("start.gms", false);
        vendorMap.put("async.replication", asyncReplication);
        vendorMap.put("broadcast.remove.expired", false);
        vendorMap.put("value.class.is.thread.safe", true);
        vendorMap.put("key.transformer", keyGen);

        if (hazelcast != null) {
            if (hazelcast.isEnabled()) {
                conf.setClusterName(hazelcast.getMemberGroup());
                conf.setInstanceName(hazelcast.getMemberName());
            }
        }
        
        BackingStoreFactory factory = null;
        try {
            factory = services.getService(BackingStoreFactory.class, persistenceStoreType);
        } catch (Exception ex) {
            _logger.log(Level.WARNING, SFSB_BUILDER_INSTANTIATE_BACKING_STORE_EXCEPTION,
                    new Object[]{persistenceStoreType, ex});
        }

        try {
            if (factory == null) {
                factory = services.getService(BackingStoreFactory.class, "noop");
            }
            this.backingStore = factory.createBackingStore(conf);
        } catch (Exception ex) {
            _logger.log(Level.WARNING, SFSB_BUILDER_INSTANTIATE_BACKING_STORE_EXCEPTION,
                    new Object[]{persistenceStoreType, ex});
            throw new BackingStoreException("Could not instantiate backing store for type [" +
                    persistenceStoreType + "]", ex);
        }
        _logger.log(Level.INFO, SFSB_BUILDER_INSTANTIATED_BACKING_STORE, new Object[]{backingStore, HAEnabled, conf});
    }

    private void buildCache() {
        String cacheName = ejbDescriptor.getEjbClassName();
        String victimPolicy = cacheProps.getVictimSelectionPolicy();

        if (cacheProps.getMaxCacheSize() <= 0) {
            sessionCache = new UnBoundedSessionCache(cacheName, sfsbContainer,
                    cacheProps.getCacheIdleTimeoutInSeconds(),
                    cacheProps.getRemovalTimeoutInSeconds());
        } else if ("lru".equalsIgnoreCase(victimPolicy)) {
            sessionCache = new LruSessionCache(cacheName, sfsbContainer,
                    cacheProps.getCacheIdleTimeoutInSeconds(),
                    cacheProps.getRemovalTimeoutInSeconds());
        } else if ("fifo".equalsIgnoreCase(victimPolicy)) {
            sessionCache = new FIFOSessionCache(cacheName, sfsbContainer,
                    cacheProps.getCacheIdleTimeoutInSeconds(),
                    cacheProps.getRemovalTimeoutInSeconds());
        } else {
            sessionCache = new NRUSessionCache(cacheName, sfsbContainer,
                    cacheProps.getCacheIdleTimeoutInSeconds(),
                    cacheProps.getRemovalTimeoutInSeconds());
        }


        float ratio = (float) (1.0 * cacheProps.getNumberOfVictimsToSelect()
                / cacheProps.getMaxCacheSize());
        float loadFactor = (float) (1.0 - ratio);
        if (loadFactor < 0 || loadFactor > 1) {
            loadFactor = 0.75f;
        }

        if (cacheProps.getMaxCacheSize() <= 0) {
            sessionCache.init(16 * 1024, loadFactor, null);
        } else {
            sessionCache.init(cacheProps.getMaxCacheSize(), loadFactor, null);
        }

        sessionCache.addCacheListener((CacheListener) sfsbContainer);

        sfsbContainer.setSessionCache(sessionCache);
        sessionCache.setBackingStore(backingStore);
        sfsbContainer.setBackingStore(this.backingStore);
        if (cacheProps.getNumberOfVictimsToSelect() >
                sfsbContainer.MIN_PASSIVATION_BATCH_COUNT) {
            sfsbContainer.setPassivationBatchCount(
                    cacheProps.getNumberOfVictimsToSelect());
        }

        if (_logger.isLoggable(TRACE_LEVEL)) {
            _logger.log(TRACE_LEVEL,
                    "Created cache for {0}; cache properties: {1}; loadFactor: {2}; backingStore: {3}",
                    new Object[]{ejbDescriptor.getName(), cacheProps, loadFactor, this.backingStore});
        }
    }

    private void scheduleTimerTasks(StatefulSessionContainer container) {
        String ejbName = ejbDescriptor.getEjbClassName();

        if (cacheProps.getCacheIdleTimeoutInSeconds() > 0) {
            long timeout = cacheProps.getCacheIdleTimeoutInSeconds() * 1000L;
            try {
                sfsbContainer.invokePeriodically(timeout, timeout,
                        new CachePassivatorTask(ejbName, sessionCache, _logger));
                if (_logger.isLoggable(TRACE_LEVEL)) {
                    _logger.log(TRACE_LEVEL, "Added CachePassivator for {0} to run after {1} milliseconds",
                            new Object[]{ejbName, timeout});
                }

            } catch (Throwable th) {
                _logger.log(Level.WARNING, SFSB_HELPER_ADD_IDLE_PASSIVATOR_TASK_FAILED, th);
            }
        }

        if (cacheProps.getRemovalTimeoutInSeconds() > 0 && container.isPassivationCapable()) {
            long timeout = cacheProps.getRemovalTimeoutInSeconds() * 1000L;
            try {
                sfsbContainer.invokePeriodically(timeout, timeout,
                        new ExpiredSessionsRemovalTask(ejbName,
                                this.sfsbContainer, _logger));
                if (_logger.isLoggable(TRACE_LEVEL)) {
                    _logger.log(TRACE_LEVEL, "Added StorePassivator for {0} to run after {1} milliseconds",
                            new Object[]{ejbName, timeout});
                }
            } catch (Throwable th) {
                _logger.log(Level.WARNING, SFSB_HELPER_ADD_REMOVE_PASSIVATOR_TASK_FAILED, th);
            }
        }

    }

  @Override
  public Container createContainer(EjbDescriptor ejbDescriptor,
                                   ClassLoader loader,
                                   DeploymentContext deployContext)
          throws Exception {
    this.ejbDescriptor = ejbDescriptor;

    //FIXME: Read from domain.xml iiop-service ip-addr
    byte[] ipAddress = new byte[4];
    try {
      ipAddress = InetAddress.getLocalHost().getAddress();
    } catch (Exception ex) {
      long val = System.identityHashCode(ipAddress)
              + System.currentTimeMillis();
      Utility.longToBytes(val, ipAddress, 0);
    }

    //FIXME: Read from domain.xml
    int port = 8080;

    cacheProps.init(ejbDescriptor);
    SecurityManager sm = getSecurityManager(ejbDescriptor);
    sfsbContainer = new StatefulSessionContainer(ejbDescriptor, loader, sm);
    buildComponents(ipAddress, port, deployContext);
    sfsbContainer.initializeHome();
    return sfsbContainer;
  }

}

class CachePassivatorTask
        implements Runnable {

    private LruSessionCache cache;
    private Logger logger;
    private String name;

    CachePassivatorTask(String name, LruSessionCache cache, Logger logger) {
        this.name = name;
        this.cache = cache;
        this.logger = logger;
    }

    public void run() {
        try {
            cache.trimTimedoutItems(Integer.MAX_VALUE);
        } catch (Exception ex) {
            if (logger.isLoggable(Level.WARNING)) {
                logger.log(Level.WARNING, StatefulContainerFactory.SFSB_HELPER_REMOVE_IDLE_BEANS_FAILED,
                        new Object[]{name, ex});
            }
        }
    }
}

class ExpiredSessionsRemovalTask
        implements Runnable {
    private StatefulSessionContainer container;
    private Logger logger;
    private String name;

    ExpiredSessionsRemovalTask(String name,
                               StatefulSessionContainer container, Logger logger) {
        this.name = name;
        this.container = container;
        this.logger = logger;
    }

    public void run() {
        try {
            container.removeExpiredSessions();
        } catch (Exception ex) {
            if (logger.isLoggable(Level.WARNING)) {
                logger.log(Level.WARNING, StatefulContainerFactory.SFSB_HELPER_REMOVE_EXPIRED_BEANS_FAILED,
                        new Object[]{name, ex});
            }
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy