alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Glassfish example source code file (JPADeployer.java)

This example Glassfish source code file (JPADeployer.java) is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Java - Glassfish tags/keywords

application, applicationinfo, deploymentcontext, deploymentcontext, inject, inject, jpapplicationcontainer, log, logging, override, override, persistenceunitdescriptor, persistenceunitdescriptoriterator, persistenceunitdescriptoriterator, string, util, we

The Glassfish JPADeployer.java source code

/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 2008-2011 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 org.glassfish.persistence.jpa;

import com.sun.appserv.connectors.internal.api.ConnectorRuntime;
import com.sun.enterprise.deployment.*;
import com.sun.enterprise.module.bootstrap.StartupContext;
import com.sun.logging.LogDomains;
import org.glassfish.api.deployment.DeployCommandParameters;
import org.glassfish.api.event.EventListener;
import org.glassfish.api.event.Events;
import org.glassfish.internal.data.ApplicationInfo;
import org.glassfish.internal.data.ApplicationRegistry;
import org.glassfish.internal.deployment.Deployment;
import org.glassfish.internal.deployment.ExtendedDeploymentContext;
import org.glassfish.server.ServerEnvironmentImpl;
import org.glassfish.api.deployment.DeploymentContext;
import org.glassfish.api.deployment.MetaData;
import org.glassfish.api.deployment.OpsParams;
import org.glassfish.deployment.common.SimpleDeployer;
import org.glassfish.deployment.common.DeploymentException;
import org.glassfish.persistence.common.Java2DBProcessorHelper;
import org.jvnet.hk2.annotations.Inject;
import org.jvnet.hk2.annotations.Service;
import org.jvnet.hk2.component.Habitat;
import org.jvnet.hk2.component.PostConstruct;
import javax.persistence.EntityManagerFactory;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;


/**
 * Deployer for JPA applications
 * @author Mitesh Meswani
 */
@Service
public class JPADeployer extends SimpleDeployer<JPAContainer, JPApplicationContainer> implements PostConstruct, EventListener {

    @Inject
    private ConnectorRuntime connectorRuntime;

    @Inject
    private Habitat habitat;

    @Inject
    private ServerEnvironmentImpl serverEnvironment;

    @Inject
    private volatile StartupContext sc = null;

    @Inject
    private Events events;

    @Inject
    private ApplicationRegistry applicationRegistry;

    private static Logger logger = LogDomains.getLogger(PersistenceUnitLoader.class, LogDomains.PERSISTENCE_LOGGER + ".jpadeployer");

    /** Key used to get/put emflists in transientAppMetadata */
    private static final String EMF_KEY = EntityManagerFactory.class.toString();

    @Override public MetaData getMetaData() {

        return new MetaData(true /*invalidateCL */ ,
                null /* provides */,
                new Class[] {Application.class} /* requires Application from dol */);
    }

    protected void generateArtifacts(DeploymentContext dc) throws DeploymentException {
        // Noting to generate yet!!
    }

    protected void cleanArtifacts(DeploymentContext dc) throws DeploymentException {
        // Drop tables if needed on undeploy.
        OpsParams params = dc.getCommandParameters(OpsParams.class);
        if (params.origin.isUndeploy() && isDas()) {

            boolean hasScopedResource = false;
            String appName = params.name();
            ApplicationInfo appInfo = applicationRegistry.get(appName);
            Application application = appInfo.getMetaData(Application.class);
            Set<BundleDescriptor> bundles = application.getBundleDescriptors();

             // Iterate through all the bundles of the app and collect pu references in referencedPus
             for (BundleDescriptor bundle : bundles) {
                 Collection<? extends PersistenceUnitDescriptor> pusReferencedFromBundle = bundle.findReferencedPUs();
                 for(PersistenceUnitDescriptor pud : pusReferencedFromBundle) {
                     hasScopedResource = hasScopedResource(pud);
                     if(hasScopedResource) {
                         break;
                     }
                 }
             }

            // if there are scoped resources, deploy them so that they are accessible for Java2DB to
            // delete tables.
            if(hasScopedResource){
                connectorRuntime.registerDataSourceDefinitions(application);
            }

             Java2DBProcessorHelper helper = new Java2DBProcessorHelper(dc);
             helper.init();
             helper.createOrDropTablesInDB(false, "JPA"); // NOI18N

            //if there are scoped resources, undeploy them.
            if(hasScopedResource){
                connectorRuntime.unRegisterDataSourceDefinitions(application);
            }
        }
    }

    /**
     * @inheritDoc
     */
    public <V> V loadMetaData(Class type, DeploymentContext context) {
        return null;
    }

    /**
     * EMFs for refered pus are created and stored in JPAApplication instance.
     * The JPAApplication instance is stored in given DeploymentContext to be retrieved by load
     */
    @Override public boolean prepare(DeploymentContext context) {
        boolean prepared = super.prepare(context);
        if(prepared) {
            if(isEMFCreationRequired(context)) {
                createEMFs(context);
            }
        }
        return prepared;
    }

    /**
     * CreateEMFs and save them in persistence
     * @param context
     */
    private void createEMFs(DeploymentContext context) {
        Application application = context.getModuleMetaData(Application.class);
        Set<BundleDescriptor> bundles = application.getBundleDescriptors();

        // Iterate through all the bundles for the app and collect pu references in referencedPus
        boolean hasScopedResource = false;
        final List<PersistenceUnitDescriptor> referencedPus = new ArrayList();
        for (BundleDescriptor bundle : bundles) {
            Collection<? extends PersistenceUnitDescriptor> pusReferencedFromBundle = bundle.findReferencedPUs();
            for(PersistenceUnitDescriptor pud : pusReferencedFromBundle) {
                referencedPus.add(pud);
                if( hasScopedResource(pud) ) {
                    hasScopedResource = true;
                }
            }
        }
        if (hasScopedResource) {
            // Scoped resources are registered by connectorruntime after prepare(). That is too late for JPA
            // This is a hack to initialize connectorRuntime for scoped resources
            connectorRuntime.registerDataSourceDefinitions(application);
        }

        //Iterate through all the PUDs for this bundle and if it is referenced, load the corresponding pu
        PersistenceUnitDescriptorIterator pudIterator = new PersistenceUnitDescriptorIterator() {
            @Override void visitPUD(PersistenceUnitDescriptor pud, DeploymentContext context) {
                if(referencedPus.contains(pud)) {
                    boolean isDas = isDas();

                    // While running in embedded mode, it is not possible to guarantee that entity classes are not loaded by the app classloader before transformers are installed
                    // If that happens, weaving will not take place and EclipseLink will throw up. Provide users an option to disable weaving by passing the flag.
                    // Note that we enable weaving if not explicitly disabled by user
                    boolean weavingEnabled = Boolean.valueOf(sc.getArguments().getProperty("org.glassfish.persistence.embedded.weaving.enabled", "true"));

                    ProviderContainerContractInfo providerContainerContractInfo = weavingEnabled ?
                            new ServerProviderContainerContractInfo(context, connectorRuntime, isDas) :
                            new EmbeddedProviderContainerContractInfo(context, connectorRuntime, isDas);

                    PersistenceUnitLoader puLoader = new PersistenceUnitLoader(pud, providerContainerContractInfo);
                    // Store the puLoader in context. It is retrieved to execute java2db and to
                    // store the loaded emfs in a JPAApplicationContainer object for cleanup
                    context.addTransientAppMetaData(getUniquePuIdentifier(pud), puLoader );
                }
            }
        };
        pudIterator.iteratePUDs(context);
    }

    /**
     * @return true if given <code>pud is using scoped resource
     */
    private boolean hasScopedResource(PersistenceUnitDescriptor pud) {
        boolean hasScopedResource = false;
        String jtaDataSource = pud.getJtaDataSource();
        if(jtaDataSource != null && jtaDataSource.startsWith("java:")){
            hasScopedResource = true;
        }
        return hasScopedResource;
    }

    /**
     * @param context
     * @return true if emf creation is required false otherwise
     */
    private boolean isEMFCreationRequired(DeploymentContext context) {
/*
  Here are various use cases that needs to be handled.
  This method handles EMF creation part, APPLOCATION_PREPARED event handle handles java2db and closing of emf

  To summarize,
  -Unconditionally create EMFs on DAS for java2db if it is deploy. We will close this EMF in APPLICATION_PREPARED after java2db if (target!= DAS || enable=false)
  -We will not create EMFs on instance if application is not enabled

        ------------------------------------------------------------------------------------
            Scenario                                       Expcted Behavior
        ------------------------------------------------------------------------------------
        deploy --target=server   --enabled=true.   DAS(EMF created, java2db, EMF remains open)
           -restart                                DAS(EMF created, EMF remains open)
           -undeploy                               DAS(EMF closed. Drop tables)
           -create-application-ref instance1       DAS(No action)
                                                   INSTANCE1(EMF created)

        deploy --target=server   --enabled=false.  DAS(EMF created,java2db, EMF closed in APPLICATION_PREPARED)
           -restart                                DAS(No EMF created)
           -undelpoy                               DAS(No EMF to close, Drop tables)

           -enable                                 DAS(EMF created)
           -undelpoy                               DAS(EMF closed, Drop tables)

           -create-application-ref instance1       DAS(No action)
                                                   INSTANCE1(EMF created)

        deploy --target=instance1 --enabled=true   DAS(EMF created, java2db, EMF closed in APPLICATION_PREPARED)
                                                   INSTANCE1(EMF created)
            -create-application-ref instance2      INSTANCE2(EMF created)
            -restart                               DAS(No EMF created)
                                                   INSTANCE1(EMF created)
                                                   INSTANCE2(EMF created)
            -undelpoy                              DAS(No EMF to close, Drop tables)
                                                   INSTANCE1(EMF closed)

            -create-application-ref server         DAS(EMF created)
            -delete-application-ref server         DAS(EMF closed)
            undeploy                               INSTANCE1(EMF closed)


        deploy --target=instance --enabled=false.  DAS(EMF created, java2db, EMF closed in APPLICATION_PREPARED)
                                                   INSTANCE1(No EMF created)
            -create-application-ref instance2      DAS(No action)
                                                   INSTANCE2(No Action)
            -restart                               DAS(No EMF created)
                                                   INSTANCE1(No EMF created)
                                                   INSTANCE2(No EMF created)
            -undeploy                              DAS(No EMF to close, Drop tables)
                                                   INSTANCE1(No EMF to close)
                                                   INSTANCE2(No EMF to close)

            -enable --target=instance1             DAS(No EMF created)
                                                   INSTANCE1(EMF created)

*/

        boolean createEMFs = false;
        DeployCommandParameters deployCommandParameters = context.getCommandParameters(DeployCommandParameters.class);
        boolean deploy  = deployCommandParameters.origin.isDeploy();
        boolean enabled = deployCommandParameters.enabled;
        boolean isDas = isDas();

        if(logger.isLoggable(Level.FINER)) {
            logger.finer("isEMFCreationRequired(): deploy: " + deploy + " enabled: " + enabled + " isDas: " + isDas);
        }

        if(isDas) {
            if(deploy) {
                createEMFs = true; // Always create emfs on DAS while deploying to take care of java2db -- TODO - future  optimize by not creating it for pus that do not require java2db
            } else {
                //We reach here for (!deploy && das) => server restart or enabling a disabled app on DAS
                boolean isTargetDas = isTargetDas(deployCommandParameters);
                if(logger.isLoggable(Level.FINER)) {
                    logger.finer("isEMFCreationRequired(): isTargetDas: " + isTargetDas);
                }
                
                if(enabled && isTargetDas) {
                    createEMFs = true;
                }
            }
        } else { //!das => on an instance
            if(enabled) {
                createEMFs = true;
            }
        }

        if(logger.isLoggable(Level.FINER)) {
            logger.finer("isEMFCreationRequired(): returning createEMFs:" + createEMFs);
        }

        return createEMFs;
    }

    private static boolean isTargetDas(DeployCommandParameters deployCommandParameters) {
        return "server".equals(deployCommandParameters.target); // TODO discuss with Hong. This comparison should be encapsulated somewhere
    }

    /**
     * @inheritDoc
     */
    //@Override
    public JPApplicationContainer load(JPAContainer container, DeploymentContext context) {
        return new JPApplicationContainer();
    }

    /**
     * Returns unique identifier for this pu within application
     * @param pud The given pu
     * @return Absolute pu root + pu name
     */
    private static String getUniquePuIdentifier(PersistenceUnitDescriptor pud) {
        return pud.getAbsolutePuRoot() + pud.getName();
     }

    private boolean isDas() {
        return serverEnvironment.isDas() || serverEnvironment.isEmbedded();
    }

    @Override
    public void postConstruct() {
        events.register(this);
    }

    @Override
    public void event(Event event) {
        if(logger.isLoggable(Level.FINEST)) {
            logger.finest("JpaDeployer.event():" + event.name());
        }
        if (event.is(Deployment.APPLICATION_PREPARED) ) {
            ExtendedDeploymentContext context = (ExtendedDeploymentContext)event.hook();
            DeployCommandParameters deployCommandParameters = context.getCommandParameters(DeployCommandParameters.class);
            if(logger.isLoggable(Level.FINE)) {
                logger.fine("JpaDeployer.event(): Handling APPLICATION_PREPARED origin is:" + deployCommandParameters.origin);
            }

            // When create-application-ref is called for an already deployed app, APPLICATION_PREPARED will be sent on DAS
            // Obviously there is no new emf created for this event and we need not do java2db also. Ignore the event
            // However, if target for create-application-ref is DAS => the app was deployed on other instance but now
            // an application-ref is being created on DAS. Process the app
            if(!deployCommandParameters.origin.isCreateAppRef() || isTargetDas(deployCommandParameters)) {
                Map<String, ExtendedDeploymentContext> deploymentContexts = context.getModuleDeploymentContexts();

                for (DeploymentContext deploymentContext : deploymentContexts.values()) {
                    //bundle level pus
                    iterateInitializedPUsAtApplicationPrepare(deploymentContext);
                }
                //app level pus
                iterateInitializedPUsAtApplicationPrepare(context);
            }
        } else if(event.is(Deployment.APPLICATION_DISABLED)) {
            logger.fine("JpaDeployer.event(): APPLICATION_DISABLED");
            // APPLICATION_DISABLED will be generated when an app is disabled/undeployed/appserver goes down.
            //close all the emfs created for this app
            ApplicationInfo appInfo = (ApplicationInfo) event.hook();
            closeEMFs(appInfo);
        }
    }

    private void closeEMFs(ApplicationInfo appInfo) {
        //Suppress warning required as there is no way to pass equivalent of List<EMF>.class to the method
        @SuppressWarnings("unchecked") List<EntityManagerFactory> emfsCreatedForThisApp = appInfo.getTransientAppMetaData(EMF_KEY, List.class);
        if(emfsCreatedForThisApp != null) { // Events are always dispatched to all registered listeners. emfsCreatedForThisApp will be null for an app that does not have PUs.
            for (EntityManagerFactory entityManagerFactory : emfsCreatedForThisApp) {
                entityManagerFactory.close();
            }
            // We no longer have the emfs in open state clear the list.
            // On app enable(after a disable), for a cluster, the deployment framework calls prepare() for instances but not for DAS.
            // So on DAS, at a disable, the emfs will be closed and we will not attempt to close emfs when appserver goes down even if the app is re-enabled.
            emfsCreatedForThisApp.clear();
        }
    }

    /**
     * Does java2db on DAS and saves emfs created during prepare to ApplicationInfo maintained by DOL.
     * ApplicationInfo is not available during prepare() so we can not directly use it there.
     * @param context
     */
    private void iterateInitializedPUsAtApplicationPrepare(final DeploymentContext context) {

        final DeployCommandParameters deployCommandParameters = context.getCommandParameters(DeployCommandParameters.class);
        String appName = deployCommandParameters.name;
        final ApplicationInfo appInfo = applicationRegistry.get(appName);

        //iterate through all the PersistenceUnitDescriptor for this bundle.
        PersistenceUnitDescriptorIterator pudIterator = new PersistenceUnitDescriptorIterator() {
            @Override void visitPUD(PersistenceUnitDescriptor pud, DeploymentContext context) {
                //PersistenceUnitsDescriptor corresponds to  persistence.xml. A bundle can only have one persitence.xml except
                // when the bundle is an application which can have multiple persitence.xml under jars in root of ear and lib.
                PersistenceUnitLoader puLoader = context.getTransientAppMetaData(getUniquePuIdentifier(pud), PersistenceUnitLoader.class);
                if (puLoader != null) { // We have initialized PU
                    boolean saveEMF = true;
                    if(isDas()) { //We execute Java2DB only on DAS
                        if(deployCommandParameters.origin.isDeploy()) { //APPLICATION_PREPARED will be called for create-application-ref also. We should perform java2db only on first deploy
                            puLoader.doJava2DB();

                            boolean enabled = deployCommandParameters.enabled;
                            boolean isTargetDas = isTargetDas(deployCommandParameters);
                            if(logger.isLoggable(Level.FINER)) {
                                logger.finer("iterateInitializedPUsAtApplicationPrepare(): enabled: " + enabled + " isTargetDas: " + isTargetDas);
                            }
                            if(!isTargetDas || !enabled) {
                                // we are on DAS but target != das or app is not enabled on das => The EMF was just created for Java2Db. Close it. 
                                puLoader.getEMF().close();
                                saveEMF = false; // Do not save EMF. We have already closed it
                            }
                        }
                    }

                    if(saveEMF) {
                        // Save emf in ApplicationInfo so that it can be retrieved and closed for cleanup
                        @SuppressWarnings("unchecked") //Suppress warning required as there is no way to pass equivalent of List<EMF>.class to the method
                        List<EntityManagerFactory> emfsCreatedForThisApp = appInfo.getTransientAppMetaData(EMF_KEY, List.class );
                        if(emfsCreatedForThisApp == null) {
                            //First EMF for this app, initialize
                            emfsCreatedForThisApp = new ArrayList<EntityManagerFactory>();
                            appInfo.addTransientAppMetaData(EMF_KEY, emfsCreatedForThisApp);
                        }
                        emfsCreatedForThisApp.add(puLoader.getEMF());
                    } // if (saveEMF)
                } // if(puLoader != null)
            }
        };

        pudIterator.iteratePUDs(context);

    }

    /**
     * Helper class to centralize the code for loop that iterates through all the PersistenceUnitDescriptor for a given DeploymentContext (and hence the corresponding bundle)
     */
    private static abstract class PersistenceUnitDescriptorIterator {
        /**
         * Iterate through all the PersistenceUnitDescriptors for the given context (and hence corresponding bundle) and call visitPUD for each of them
         * @param context
         */
        void iteratePUDs(DeploymentContext context) {
            RootDeploymentDescriptor currentBundle = context.getModuleMetaData(BundleDescriptor.class);
            if (currentBundle == null) {
                    // We are being called for an application
                    currentBundle = context.getModuleMetaData(Application.class);
                }

            Collection<PersistenceUnitsDescriptor> pusDescriptorForThisBundle = currentBundle.getExtensionsDescriptors(PersistenceUnitsDescriptor.class);
            for (PersistenceUnitsDescriptor persistenceUnitsDescriptor : pusDescriptorForThisBundle) {
                    for (PersistenceUnitDescriptor pud : persistenceUnitsDescriptor.getPersistenceUnitDescriptors()) {
                        visitPUD(pud, context);
                    }
            }

        }

        /**
         * Called for each PersistenceUnitDescriptor visited by this iterator.
         */
        abstract void visitPUD(PersistenceUnitDescriptor pud, DeploymentContext context);

    }
}

Other Glassfish examples (source code examples)

Here is a short list of links related to this Glassfish JPADeployer.java source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.