/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright (c) 1997-2012 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.deployapi; import com.sun.enterprise.util.HostAndPort; import com.sun.enterprise.util.StringUtils; import com.sun.enterprise.module.ModulesRegistry; import com.sun.enterprise.module.single.StaticModulesRegistry; import com.sun.enterprise.module.bootstrap.StartupContext; import org.glassfish.api.admin.ProcessEnvironment; import org.glassfish.deployapi.config.SunDeploymentConfiguration; import org.glassfish.deployment.client.DeploymentFacility; import org.glassfish.deployment.client.DeploymentFacilityFactory; import org.glassfish.deployment.client.ServerConnectionEnvironment; import org.glassfish.deployment.client.ServerConnectionIdentifier; import org.glassfish.api.deployment.archive.ReadableArchive; import org.glassfish.api.deployment.archive.Archive; import com.sun.enterprise.deploy.shared.ArchiveFactory; import com.sun.enterprise.deployment.deploy.shared.MemoryMappedArchive; import org.glassfish.deployment.client.DFDeploymentProperties; import org.glassfish.hk2.api.ServiceLocator; import org.glassfish.hk2.utilities.ServiceLocatorUtilities; import com.sun.enterprise.util.LocalStringManagerImpl; import java.io.File; import java.io.InputStream; import java.io.IOException; import java.net.URL; import java.net.MalformedURLException; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Properties; import java.util.Vector; import java.util.logging.Level; import java.util.logging.Logger; import java.net.URI; import javax.enterprise.deploy.spi.DeploymentManager; import javax.enterprise.deploy.model.DeployableObject; import javax.enterprise.deploy.shared.CommandType; import javax.enterprise.deploy.shared.DConfigBeanVersionType; import javax.enterprise.deploy.shared.ModuleType; import javax.enterprise.deploy.spi.DeploymentConfiguration; import javax.enterprise.deploy.spi.exceptions.ConfigurationException; import javax.enterprise.deploy.spi.exceptions.DConfigBeanVersionUnsupportedException; import javax.enterprise.deploy.spi.exceptions.InvalidModuleException; import javax.enterprise.deploy.spi.exceptions.TargetException; import javax.enterprise.deploy.spi.status.ProgressObject; import javax.enterprise.deploy.spi.status.ProgressEvent; import javax.enterprise.deploy.spi.status.DeploymentStatus; import javax.enterprise.deploy.spi.Target; import javax.enterprise.deploy.spi.TargetModuleID; /** * * @author Jerome Dochez * @author Tim Quinn */ public class SunDeploymentManager implements DeploymentManager { // store ID to server connection private ServerConnectionIdentifier serverId = null; /** cached reference to a connected DeploymentFacility */ private DeploymentFacility deploymentFacility = null; private static LocalStringManagerImpl localStrings = new LocalStringManagerImpl(SunDeploymentManager.class); private static Locale defaultLocale = Locale.US; private Locale currentLocale = defaultLocale; private static Locale[] supportedLocales = { Locale.US }; private String disconnectedMessage = localStrings.getLocalString( "enterprise.deployapi.spi.disconnectedDM", // NOI18N "Illegal operation for a disconnected DeploymentManager");// NOI18N private static final String ENABLED_ATTRIBUTE_NAME = "Enabled"; // NOI18N private ServiceLocator habitat; /** Creates a new instance of DeploymentManager */ public SunDeploymentManager() { } /** Creates a new instance of DeploymentManager */ public SunDeploymentManager(ServerConnectionIdentifier sci) { deploymentFacility = DeploymentFacilityFactory.getDeploymentFacility(); deploymentFacility.connect(sci); prepareHabitat(); } /** * Set additional env vars for the jmx https connector, provided * by the client. This method is expected to be called right after * the client retrieves the DeploymentManager, before * the client makes any calls on the DM that requires MBean Server * connection. */ public void setServerConnectionEnvironment(ServerConnectionEnvironment env) { serverId.setConnectionEnvironment(env); } /** * Retrieve the list of deployment targets supported by * this DeploymentManager. *<p> * @throws IllegalStateException is thrown when there is a problem getting * Connection Source * @return A list of deployment Target designators the * user may select for application deployment or 'null' * if there are none. */ public Target[] getTargets() throws IllegalStateException { verifyConnected(); try { return deploymentFacility.listTargets(); } catch (Throwable e) { IllegalStateException ex = new IllegalStateException(e.getMessage()); ex.initCause(e); throw ex; } } /** * Retrieve the list of J2EE application modules distributed * to the identified targets and that are currently running * on the associated server or servers. * * @param moduleType A predefined designator for a J2EE * module type. * * @param targetList A list of deployment Target designators * the user wants checked for module run * status. * * @return An array of TargetModuleID objects representing * the running modules or 'null' if there * are none. * * @throws IllegalStateException is thrown when the method is * called when running in disconnected mode. * @throws TargetException An invalid Target designator * encountered. */ public TargetModuleID[] getRunningModules(ModuleType moduleType, Target[] targetList) throws TargetException, IllegalStateException { return getModules(moduleType, targetList, DFDeploymentProperties.RUNNING); } /** * Retrieve the list of J2EE application modules distributed * to the identified targets and that are currently not * running on the associated server or servers. * * @param moduleType A predefined designator for a J2EE * module type. * * @param targetList A list of deployment Target designators * the user wants checked for module not * running status. * * @return An array of TargetModuleID objects representing * the non-running modules or 'null' if * there are none. * * @throws IllegalStateException is thrown when the method is * called when running in disconnected mode. * @throws TargetException An invalid Target designator * encountered. */ public TargetModuleID[] getNonRunningModules(ModuleType moduleType, Target[] targetList) throws TargetException, IllegalStateException { return getModules(moduleType, targetList, DFDeploymentProperties.NON_RUNNING); } /** * Retrieve the list of all J2EE application modules running * or not running on the identified targets. * * @param moduleType A predefined designator for a J2EE * module type. * * @param targetList A list of deployment Target designators * the user wants checked for module not * running status. * * @return An array of TargetModuleID objects representing * all deployed modules running or not or * 'null' if there are no deployed modules. * * @throws IllegalStateException is thrown when the method is * called when running in disconnected mode. * @throws TargetException An invalid Target designator * encountered. */ public TargetModuleID[] getAvailableModules(ModuleType moduleType, Target[] targetList) throws TargetException, IllegalStateException { return getModules(moduleType, targetList, DFDeploymentProperties.ALL); } /** *Single method used by several public methods to make sure the deployment manager is *connected and, if not, throw the IllegalStateException. * *@throws IllegalStateException if the deployment manager is not connected. */ private void verifyConnected() { if(isDisconnected()) { throw new IllegalStateException(disconnectedMessage); } } /** *Report whether the deployment manager is currently disconnected from the DAS. *@returns whether the deployment manager is disconnected from the DAS */ private boolean isDisconnected(){ if (deploymentFacility == null) { return true; } return (!deploymentFacility.isConnected()); } /** *Get all modules in the specified state from the targets specified in the argument list. *@param moduleType which returned modules must match *@param array of Target indicating which targets should be searched for matching modules *@param state state of the modules *have for the indicated attribute to be matched *@exception TargetException if a target was improperly formed *@exception IllegalStateException if the method is called after release was called */ private TargetModuleID[] getModules(ModuleType moduleType, Target[] targetList, String state) throws TargetException, IllegalStateException { verifyConnected(); if (moduleType==null) { return null; } try { Vector resultingTMIDs = new Vector(); for (int i = 0; i < targetList.length; i++) { TargetImpl aTarget = (TargetImpl) targetList[i]; TargetModuleID[] tmids = deploymentFacility._listAppRefs(new Target[] {aTarget}, state, getTypeFromModuleType(moduleType)); addToTargetModuleIDs(tmids, moduleType, aTarget, resultingTMIDs); } /* *Return an array of runtime type TargetModuleIDImpl []. */ TargetModuleID [] answer = (TargetModuleID []) resultingTMIDs.toArray(new TargetModuleIDImpl[resultingTMIDs.size()]); return answer; } catch(Exception e){ TargetException tg = new TargetException(localStrings.getLocalString( "enterprise.deployapi.spi.errorgetreqmods", "Error getting required modules" )); tg.initCause(e); throw tg; } } private String getTypeFromModuleType(ModuleType moduleType) { if (moduleType.equals(ModuleType.WAR)) { return "web"; } else if (moduleType.equals(ModuleType.EJB)) { return "ejb"; } else if (moduleType.equals(ModuleType.CAR)) { return "appclient"; } else if (moduleType.equals(ModuleType.RAR)) { return "connector"; } else if (moduleType.equals(ModuleType.EAR)) { return "ear"; } return null; } private ModuleType getModuleTypeFromType(String type) { if (type.equals("war")) { return ModuleType.WAR; } else if (type.equals("ejb")) { return ModuleType.EJB; } else if (type.equals("car")) { return ModuleType.CAR; } else if (type.equals("rar")) { return ModuleType.RAR; } else if (type.equals("ear")) { return ModuleType.EAR; } return null; } /** *Augments a Collection of TargetModuleIDs with new entries for target module IDs of a given module type on the specified target. *@param tmids array of TargetModuleIDs *@param type the ModuleType of interest *@param targetImpl the TargetImpl from which to retrieve modules of the selected type *@param resultingTMIDs pre-instantiated List to which TargetModuleIDs will be added */ private void addToTargetModuleIDs(TargetModuleID[] tmids, ModuleType type, TargetImpl targetImpl, Collection resultingTMIDs) throws IOException { for (int j = 0;j< tmids.length;j++) { // Get the host name and port where the application was deployed HostAndPort webHost = deploymentFacility.getHostAndPort(targetImpl.getName(), tmids[j].getModuleID(), false); if (tmids[j] instanceof TargetModuleIDImpl) { ((TargetModuleIDImpl)tmids[j]).setModuleType(type); } resultingTMIDs.add(tmids[j]); /* *Set additional information on the target module ID, depending on what type of *module this is. For J2EE apps, this includes constructing sub TargetModuleIDs. */ try { if (type.equals(ModuleType.EAR)) { setJ2EEApplicationTargetModuleIDInfo(tmids[j], webHost); } else if (type.equals(ModuleType.WAR)) { setWebApplicationTargetModuleIDInfo(tmids[j], webHost); } } catch(Exception exp){ Logger.getAnonymousLogger().log(Level.WARNING, exp.getLocalizedMessage(), exp); } } } /** *Attach child target module IDs to a J2EE application target module ID. *@param tmid the target module ID for the J2EE application. *@param targetImpl the target identifying which installation of this module is of interest *@param webHost the host and port for this target */ private void setJ2EEApplicationTargetModuleIDInfo(TargetModuleID tmid, HostAndPort hostAndPort) throws MalformedURLException, IOException { TargetImpl targetImpl = (TargetImpl) tmid.getTarget(); try { List<String> subModuleInfoList = deploymentFacility.getSubModuleInfoForJ2EEApplication(tmid.getModuleID()); for (String subModuleInfo : subModuleInfoList) { List<String> infoParts = StringUtils.parseStringList( subModuleInfo, ":"); String subModuleName = infoParts.get(0); String subModuleID = tmid.getModuleID() + "#" + subModuleName; String subType = infoParts.get(1); ModuleType subModuleType = getModuleTypeFromType(subType); TargetModuleIDImpl childTmid = new TargetModuleIDImpl( targetImpl, subModuleID); childTmid.setModuleType(subModuleType); if (subType.equals("war")) { // get the web url URL webURL = new URL("http", hostAndPort.getHost(), hostAndPort.getPort(), infoParts.get(2)); childTmid.setWebURL(webURL.toExternalForm()); } if (tmid instanceof TargetModuleIDImpl) { ((TargetModuleIDImpl)tmid).addChildTargetModuleID( childTmid); } } } catch(Exception exp){ Logger.getAnonymousLogger().log(Level.WARNING, exp.getLocalizedMessage(), exp); } } /** *Set additional type-specific information on the target module ID. *@param tmid the target module ID for the Web app *@param targetImpl the target identifying which installation of this module is of interest *@param webHost the host and port for this target */ private void setWebApplicationTargetModuleIDInfo(TargetModuleID tmid, HostAndPort webHost) throws MalformedURLException, IOException { String path = deploymentFacility.getContextRoot(tmid.getModuleID()); if (!path.startsWith("/")) { //NOI18N path = "/" + path; //NOI18N } URL webURL = new URL("http", webHost.getHost(), webHost.getPort(), path); //NOI18N if (tmid instanceof TargetModuleIDImpl) { ((TargetModuleIDImpl)tmid).setWebURL(webURL.toExternalForm()); } } /** * Retrieve the object that provides server-specific deployment * configuration information for the J2EE deployable component. * * @param dObj An object representing a J2EE deployable component. * @throws InvalidModuleException The DeployableObject is an * unknown or unsupport component for this * configuration tool. */ public DeploymentConfiguration createConfiguration(DeployableObject dObj) throws InvalidModuleException { try { SunDeploymentConfiguration deploymentConfiguration = new SunDeploymentConfiguration(dObj); deploymentConfiguration.setDeploymentManager(this); return deploymentConfiguration; } catch(ConfigurationException e) { InvalidModuleException ime = new InvalidModuleException(e.getMessage()); ime.initCause(e); throw ime; } } /** * The distribute method performs three tasks; it validates the * deployment configuration data, generates all container specific * classes and interfaces, and moves the fully baked archive to * the designated deployment targets. * * @param targetList A list of server targets the user is specifying * this application be deployed to. * @param moduleArchive The file name of the application archive * to be disrtibuted. * @param deploymentPlan The XML file containing the runtime * configuration information associated with * this application archive. * @throws IllegalStateException is thrown when the method is * called when running in disconnected mode. * @return ProgressObject an object that tracks and reports the * status of the distribution process. */ public ProgressObject distribute(Target[] targetList, File moduleArchive, File deploymentPlan) throws IllegalStateException { return deploy(targetList, moduleArchive, deploymentPlan, null /* presetOptions */); } /** * The distribute method performs three tasks; it validates the * deployment configuration data, generates all container specific * classes and interfaces, and moves the fully baked archive to * the designated deployment targets. * * @param targetList A list of server targets the user is specifying * this application be deployed to. * @param moduleArchive The input stream containing the application * archive to be disrtibuted. * @param deploymentPlan The input stream containing the deployment * configuration information associated with * this application archive. * @throws IllegalStateException is thrown when the method is * called when running in disconnected mode. * @return ProgressObject an object that tracks and reports the * status of the distribution process. * */ public ProgressObject distribute(Target[] targetList, InputStream moduleArchive, InputStream deploymentPlan) throws IllegalStateException { return deploy(targetList, moduleArchive, deploymentPlan, null /* presetOptions */); } /** * The distribute method performs three tasks; it validates the * deployment configuration data, generates all container specific * classes and interfaces, and moves the fully baked archive to * the designated deployment targets. * * @param targetList A list of server targets the user is specifying * this application be deployed to. * @param moduleType The module type of this application archive. * @param moduleArchive The input stream containing the application * archive to be disrtibuted. * @param deploymentPlan The input stream containing the deployment * configuration information associated with * this application archive. * @throws IllegalStateException is thrown when the method is * called when running in disconnected mode. * @return ProgressObject an object that tracks and reports the * status of the distribution process. * */ public ProgressObject distribute(Target[] targetList, ModuleType type, InputStream moduleArchive, InputStream deploymentPlan) throws IllegalStateException { DFDeploymentProperties dProps = new DFDeploymentProperties(); dProps.setProperty("type", getTypeFromModuleType(type)); return deploy(targetList, moduleArchive, deploymentPlan, (Properties)dProps); } /** * Start the application running. * * <p> Only the TargetModuleIDs which represent a root module * are valid for being started. A root TargetModuleID has no parent. * A TargetModuleID with a parent can not be individually started. * A root TargetModuleID module and all its child modules will be * started. * * @param moduleIDList A array of TargetModuleID objects * representing the modules to be started. * @throws IllegalStateException is thrown when the method is * called when running in disconnected mode. * @return ProgressObject an object that tracks and reports the * status of the start operation. */ public ProgressObject start(TargetModuleID[] moduleIDList) throws IllegalStateException { return executeCommandUsingFacility(CommandType.START, moduleIDList); } /** * Stop the application running. * * <p> Only the TargetModuleIDs which represent a root module * are valid for being stopped. A root TargetModuleID has no parent. * A TargetModuleID with a parent can not be individually stopped. * A root TargetModuleID module and all its child modules will be * stopped. * * @param moduleIDList A array of TargetModuleID objects * representing the modules to be stopped. * @throws IllegalStateException is thrown when the method is * called when running in disconnected mode. * @return ProgressObject an object that tracks and reports the * status of the stop operation. */ public ProgressObject stop(TargetModuleID [] moduleIDList) throws IllegalStateException { return executeCommandUsingFacility(CommandType.STOP, moduleIDList); } /** * Remove the application from the target server. * * <p> Only the TargetModuleIDs which represent a root module * are valid for undeployment. A root TargetModuleID has no parent. * A TargetModuleID with a parent can not be undeployed. A root * TargetModuleID module and all its child modules will be undeployed. * The root TargetModuleID module and all its child modules must * stopped before they can be undeployed. * * @param moduleIDList An array of TargetModuleID objects representing * the root modules to be stopped. * @throws IllegalStateException is thrown when the method is * called when running in disconnected mode. * @return ProgressObject an object that tracks and reports the * status of the stop operation. */ public ProgressObject undeploy(TargetModuleID[] moduleIDList) throws IllegalStateException { return executeCommandUsingFacility(CommandType.UNDEPLOY, moduleIDList); } /** * This method designates whether this platform vendor provides * application redeployment functionality. A value of true means * it is supported. False means it is not. * * @return A value of true means redeployment is supported by this * vendor's DeploymentManager. False means it * is not. */ public boolean isRedeploySupported() { return true; } /** * (optional) * The redeploy method provides a means for updating currently * deployed J2EE applications. This is an optional method for * vendor implementation. * * Redeploy replaces a currently deployed application with an * updated version. The runtime configuration information for * the updated application must remain identical to the application * it is updating. * * When an application update is redeployed, all existing client * connections to the original running application must not be disrupted; * new clients will connect to the application update. * * This operation is valid for TargetModuleIDs that represent a * root module. A root TargetModuleID has no parent. A root * TargetModuleID module and all its child modules will be redeployed. * A child TargetModuleID module cannot be individually redeployed. * The redeploy operation is complete only when this action for * all the modules has completed. * * @param moduleIDList An array of designators of the applications * to be updated. * @param moduleArchive The file name of the application archive * to be disrtibuted. * @param deploymentPlan The deployment configuration information * associated with this application archive. * @return ProgressObject an object that tracks and reports the * status of the redeploy operation. * @throws IllegalStateException is thrown when the method is * called when running in disconnected mode. * @throws UnsupportedOperationException this optional command * is not supported by this implementation. */ public ProgressObject redeploy(TargetModuleID[] moduleIDList, File moduleArchive, File deploymentPlan) throws UnsupportedOperationException, IllegalStateException { try { /* *To support multiple different modules in the module ID list, use a TargetModuleIDCollection to *organize them and work on each module one at a time. */ TargetModuleIDCollection coll = new TargetModuleIDCollection(moduleIDList); for (Iterator it = coll.iterator(); it.hasNext();) { /* *The iterator returns one work instance for each module present in the collection. */ DeploymentFacilityModuleWork work = (DeploymentFacilityModuleWork) it.next(); /* *Set the name in the properties according to the moduleID. The module is the same for all the *targets represented by this single work object. */ ProgressObject po = deploy(work.targets(), moduleArchive, deploymentPlan, getRedeployOptions(work.getModuleID())); /* *The work instance needs to know about its own progress object, and the *aggregate progress object needs to also. */ work.setProgressObject(po); coll.getProgressObjectSink().sinkProgressObject(po); } return coll.getProgressObjectSink(); } catch (Throwable e) { return prepareErrorProgressObject(CommandType.REDEPLOY, e); } } /** * (optional) * The redeploy method provides a means for updating currently * deployed J2EE applications. This is an optional method for * vendor implementation. * * Redeploy replaces a currently deployed application with an * updated version. The runtime configuration information for * the updated application must remain identical to the application * it is updating. * * When an application update is redeployed, all existing client * connections to the original running application must not be disrupted; * new clients will connect to the application update. * * This operation is valid for TargetModuleIDs that represent a * root module. A root TargetModuleID has no parent. A root * TargetModuleID module and all its child modules will be redeployed. * A child TargetModuleID module cannot be individually redeployed. * The redeploy operation is complete only when this action for * all the modules has completed. * * @param moduleIDList An array of designators of the applications * to be updated. * @param moduleArchive The input stream containing the application * archive to be disrtibuted. * @param deploymentPlan The input stream containing the runtime * configuration information associated with * this application archive. * @return ProgressObject an object that tracks and reports the * status of the redeploy operation. * @throws IllegalStateException is thrown when the method is * called when running in disconnected mode. * @throws UnsupportedOperationException this optional command * is not supported by this implementation. */ public ProgressObject redeploy(TargetModuleID[] moduleIDList, InputStream moduleArchive, InputStream deploymentPlan) throws UnsupportedOperationException, IllegalStateException { try { /* *To support multiple different modules in the module ID list, use a TargetModuleIDCollection to *organize them and work on each module one at a time. */ TargetModuleIDCollection coll = new TargetModuleIDCollection(moduleIDList); for (Iterator it = coll.iterator(); it.hasNext();) { /* *The iterator returns one work instance for each module present in the collection. */ DeploymentFacilityModuleWork work = (DeploymentFacilityModuleWork) it.next(); /* *Set the name in the properties according to the moduleID. The module is the same for all the *targets represented by this single work object. */ DFDeploymentProperties dProps = getRedeployOptions(work.getModuleID()); // type is not needed for v3 server code now // dProps.setType(deploymentFacility.getModuleType(work.getModuleID())); ProgressObject po = deploy(work.targets(), moduleArchive, deploymentPlan, dProps); /* *The work instance needs to know about its own progress object, and the *aggregate progress object needs to also. */ work.setProgressObject(po); coll.getProgressObjectSink().sinkProgressObject(po); } return coll.getProgressObjectSink(); } catch (Throwable e) { return prepareErrorProgressObject(CommandType.REDEPLOY, e); } } /** * The release method is the mechanism by which the tool signals * to the DeploymentManager that the tool does not need it to * continue running connected to the platform. * * The tool may be signaling it wants to run in a disconnected * mode or it is planning to shutdown. * * When release is called the DeploymentManager may close any * J2EE resource connections it had for deployment configuration * and perform other related resource cleanup. It should not * accept any new operation requests (i.e., distribute, start * stop, undeploy, redeploy. It should finish any operations * that are currently in process. Each ProgressObject associated * with a running operation should be marked as released (see * the ProgressObject). * */ public void release() { /* *Make sure multiple releases are handled gracefully. */ if ( ! isDisconnected() ) { deploymentFacility = null; } } /** * Returns the default locale supported by this implementation of * javax.enterprise.deploy.spi subpackages. * * @return Locale the default locale for this implementation. */ public Locale getDefaultLocale() { return defaultLocale; } /** * Returns the active locale this implementation of * javax.enterprise.deploy.spi subpackages is running. * * @return Locale the active locale of this implementation. */ public Locale getCurrentLocale() { return currentLocale; } /** * Set the active locale for this implementation of * javax.enterprise.deploy.spi subpackages to run. * * @throws UnsupportedOperationException the provide locale is * not supported. */ public void setLocale(Locale locale) throws UnsupportedOperationException { for (int i=0;i<supportedLocales.length;i++) { if (supportedLocales[i] == locale) { currentLocale = locale; return; } } throw new UnsupportedOperationException( localStrings.getLocalString("enterprise.deployapi.spi.localnotsupported", //NOI18N "Locale {0} is not supported", new Object[] {locale})); //NOI18N } /** * Returns an array of supported locales for this implementation. * * @return Locale[] the list of supported locales. */ public Locale[] getSupportedLocales() { return supportedLocales; } /** * Reports if this implementation supports the designated locale. * * @return A value of 'true' means it is support and 'false' it is * not. */ public boolean isLocaleSupported(Locale locale) { Locale[] locales = getSupportedLocales(); for (int i=0;i<locales.length;i++) { if (locales[i].equals(locale)) return true; } return false; } /** * Returns the J2EE platform version number for which the * configuration beans are provided. The beans must have * been compiled with the J2SE version required by the J2EE * platform. * * @return a DConfigBeanVersionType object representing the * platform version number for which these beans are provided. */ public DConfigBeanVersionType getDConfigBeanVersion() { return DConfigBeanVersionType.V5; } /** * Returns 'true' if the configuration beans support the J2EE platform * version specified. It returns 'false' if the version is * not supported. * * @param version a DConfigBeanVersionType object representing the * J2EE platform version for which support is requested. * @return 'true' if the version is supported and 'false if not. */ public boolean isDConfigBeanVersionSupported(DConfigBeanVersionType version) { return version.getValue()==getDConfigBeanVersion().getValue(); } /** * Set the configuration beans to be used to the J2EE platform * version specificed. * * @param version a DConfigBeanVersionType object representing the * J2EE platform version for which support is requested. * @throws DConfigBeanVersionUnsupportedException when the * requested bean version is not supported. */ public void setDConfigBeanVersion(DConfigBeanVersionType version) throws DConfigBeanVersionUnsupportedException { if (!isDConfigBeanVersionSupported(version)) { throw new DConfigBeanVersionUnsupportedException( localStrings.getLocalString( "enterprise.deployapi.spi.dconfigbeanversionnotsupported", //NOI18N "DConfigBean version {0} is not supported", //NOI18N new Object[] {version.toString()})); } } /** *Return deployment options for the DeploymentFacility preset for the needs of redeployment. *These properties will be merged with and will override the options set for normal deployment. *@return Properties with the conventional preset properties for redeployment */ private DFDeploymentProperties getRedeployOptions(String moduleID) { DFDeploymentProperties deplProps = new DFDeploymentProperties(); deplProps.setForce(true); deplProps.setName(moduleID); deplProps.setRedeploy(true); return deplProps; } /** *Deploy the specified module to the list of targets. *The deployment plan archive can be null. *@param Target[] the targets to which to deploy the module *@param File the archive stream to be deployed *@param File the (optional) deployment plan stream *@param options set by the caller to override and augment any settings made here *@return ProgressObject to communicate progress and results of the deployment *@exception IllegalStateException if the DeploymentManager has disconnected *@exception IOException if there are problems working with the input streams */ private ProgressObject deploy(Target[] targetList, InputStream moduleStream, InputStream deploymentPlanStream, Properties presetOptions) throws IllegalStateException { /* *Create archives for the module's input stream and, if present, the deployment plan's *input stream, and then delegate to the variant of deploy that accepts archives as *arguments. */ MemoryMappedArchive moduleArchive = null; MemoryMappedArchive deploymentPlanArchive = null; try { moduleArchive = new MemoryMappedArchive(moduleStream); if (deploymentPlanStream != null) { deploymentPlanArchive = new MemoryMappedArchive(deploymentPlanStream); } return deploy(targetList, moduleArchive, deploymentPlanArchive, presetOptions); } catch (Throwable e) { String msg = localStrings.getLocalString( "enterprise.deployapi.spi.errpreparearchstream", "Could not prepare archives for module and/or deployment plan input streams"); IllegalArgumentException ex = new IllegalArgumentException(msg); ex.initCause(e); return prepareErrorProgressObject(CommandType.DISTRIBUTE, ex); } } /** *Deploy the specified module to the list of targets. *The deployment plan archive can be null. *@param Target[] the targets to which to deploy the module *@param File the archive file to be deployed *@param File the (optional) deployment plan file *@param options set by the caller to override and augment any settings made here *@return ProgressObject to communicate progress and results of the deployment *@exception IllegalStateException if the DeploymentManager has disconnected *@exception IOException if there are problems opening the archive files */ private ProgressObject deploy(Target[] targetList, File moduleArchive, File deploymentPlan, Properties presetOptions) throws IllegalStateException { /* *Create archives for the module file and, if present, the deployment plan file, and *then delegate to the variant of deploy that accepts archives as arguments. */ ReadableArchive appArchive = null; ReadableArchive planArchive = null; ArchiveFactory archiveFactory = getArchiveFactory(); try { appArchive = archiveFactory.openArchive(moduleArchive); if(deploymentPlan != null && deploymentPlan.length() != 0) { planArchive = archiveFactory.openArchive(deploymentPlan); if (planArchive == null) { throw new IllegalArgumentException(localStrings.getLocalString( "enterprise.deployapi.spi.noarchivisthandlesplan", "No archivist is able to handle the deployment plan {0}", new Object [] {deploymentPlan.getAbsolutePath()} )); } } ProgressObject po = deploy(targetList, appArchive, planArchive, presetOptions); return po; } catch (Exception se) { String msg = localStrings.getLocalString( "enterprise.deployapi.spi.errpreparearchfile", "Could not prepare archives for module and/or deployment plan files"); IllegalArgumentException ex = new IllegalArgumentException(msg); ex.initCause(se); return prepareErrorProgressObject(CommandType.DISTRIBUTE, ex); } finally { closeArchives(CommandType.DISTRIBUTE, appArchive, moduleArchive.getAbsolutePath(), planArchive, (deploymentPlan != null) ? deploymentPlan.getAbsolutePath() : null); } } /** *Deploy the specified module to the list of targets. *The deployment plan archive can be null. *@param Target[] the targets to which to deploy the module *@param ReadableArchive the archive to be deployed *@param ReadableArchive the (optional) deployment plan *@param options set by the caller to override and augment any settings made here *@return ProgressObject to communicate progress and results of the deployment *@exception IllegalStateException if the DeploymentManager has disconnected */ private ProgressObject deploy(Target[] targetList, ReadableArchive moduleArchive, ReadableArchive planArchive, Properties presetOptions) throws IllegalStateException { verifyConnected(); ProgressObject progressObj = null; try { Properties options = getProperties(); /* *If any preset options were specified by the caller, use them to *override or augment the just-assigned set. */ if (presetOptions != null) { options.putAll(presetOptions); } progressObj = deploymentFacility.deploy(targetList, moduleArchive, planArchive, options); } catch(Throwable e) { /* *Prepare a progress object with a deployment status "wrapper" around this exception. */ progressObj = prepareErrorProgressObject(CommandType.DISTRIBUTE, e); } return progressObj; } /** *Closes the module archive and the plan archive, if any, preparing a *ProgressObject if any error occurred. *@param commandType the CommandType in progress - used in preparing the progress object (if needed) *@param moduleArchive the main module archive to be closed *@param moduleArchiveSpec a String representation of the main module archive *@param planArchive the deployment plan archive (if any) to be closed *@param planArchiveSpec a String representation of the deployment plan archive (if any) to be closed *@return ProgressObject an error progress object if any error ocurred trying to close the archive(s) */ private ProgressObject closeArchives(CommandType commandType, ReadableArchive moduleArchive, String moduleArchiveSpec, ReadableArchive planArchive, String planArchiveSpec) { ProgressObject errorPO = null; IOException moduleIOE = closeArchive(moduleArchive); IOException planIOE = closeArchive(planArchive); IOException excForProgressObject = null; String errorMsg = null; /* *If the module could not be closed, record the IOException resulting from the attempt for *use in the error progress object returned to the caller. */ if (moduleIOE != null) { excForProgressObject = moduleIOE; /* *If there was a problem with both the module archive and the plan archive, *compose an appropriate message that says both failed. */ if (planIOE != null) { errorMsg = localStrings.getLocalString( "enterprise.deployapi.spi.errclosearchs", "Could not close module archive {0} or deployment plan archive {1}", new Object[] {moduleArchiveSpec, planArchiveSpec} ); } else { /* *Either the plan was closed or there was no plan to close. To build *a message about only the module archive. */ errorMsg = localStrings.getLocalString( "enterprise.deployapi.spi.errclosemodulearch", "Could not close module archive {0}", new Object[] {moduleArchiveSpec} ); } } else if (planIOE != null) { /* *The module archive was closed fine. If the plan archive exists and *could not be closed, compose an error message to that effect and *record the IOException that occurred during the attempt to close the *deployment plan archive for use in the error progress object returned *to the caller. */ excForProgressObject = planIOE; errorMsg = localStrings.getLocalString( "enterprise.deployapi.spi.errcloseplanarch", "Could not close deployment plan archive {0}", new Object[] {planArchiveSpec} ); } /** *If an error occurred trying to close either archive, build an error progress object *for return to the caller. */ if (errorMsg != null) { IOException ioe = new IOException(errorMsg); ioe.initCause(excForProgressObject); // Only reflects the module exception if both occurred, but the msg describes both. errorPO = prepareErrorProgressObject(commandType, ioe); } return errorPO; } /** *Closes the specified archive, returning any IOException encountered in the process. *@param archive the archive to be closed *@return IOException describing any error; null if the close succeeded */ private IOException closeArchive(ReadableArchive archive) { IOException errorIOE = null; if (archive != null) { try { archive.close(); } catch (IOException ioe) { errorIOE = ioe; } } return errorIOE; } /** *Perform the selected command on the DeploymentFacility using the specified target module IDs. *<p> *Several of the deployment facility methods have the same signature except for the name. *This method collects the pre- and post-processing around those method calls in one place, then *chooses which of the deployment facility methods to actually invoke based on the *command type provided as an argument. * *@param commandType selects which method should be invoked *@param moduleIDList array of TargetModuleID to be started *@exception IllegalArgumentException if the command type is not supported *@exception IllegalStateException if the deployment manager had been released previously */ private ProgressObject executeCommandUsingFacility( CommandType commandType, TargetModuleID[] targetModuleIDList) throws IllegalStateException { verifyConnected(); try { /* *Create a temporary collection based on the target module IDs to make it easier to deal *with the different modules and the set of targets. */ TargetModuleIDCollection coll = new TargetModuleIDCollection(targetModuleIDList); /* *For each distinct module ID present in the list, ask the deployment facility to *operate on that module on all the relevant targets. */ for (Iterator it = coll.iterator(); it.hasNext();) { /* *The iterator returns one work instance for each module present in the collection. *Each work instance reflects one invocation of a method on the DeploymentFacility. */ DeploymentFacilityModuleWork work = (DeploymentFacilityModuleWork) it.next(); ProgressObject po = null; if (commandType.equals(CommandType.START)) { po = deploymentFacility.enable(work.targets(), work.getModuleID()); } else if (commandType.equals(CommandType.STOP)) { po = deploymentFacility.disable(work.targets(), work.getModuleID()); } else if (commandType.equals(CommandType.UNDEPLOY)) { po = deploymentFacility.undeploy(work.targets(), work.getModuleID()); } else { throw new IllegalArgumentException(localStrings.getLocalString( "enterprise.deployapi.spi.unexpcommand", "Received unexpected deployment facility command ${0}", new Object [] {commandType.toString()} )); } /* *The new work instance needs to know about its own progress object, and the *aggregate progress object needs to also. */ work.setProgressObject(po); coll.getProgressObjectSink().sinkProgressObject(po); } /* *Return the single progress object to return to the caller. */ return coll.getProgressObjectSink(); } catch (Throwable thr) { return prepareErrorProgressObject(commandType, thr); } } /** *Prepare a ProgressObject that reflects an error, with a related Throwable cause. *@param commandType being processed at the time of the error *@param throwable that occurred *@return ProgressObject set to FAILED with linked cause reporting full error info */ private ProgressObject prepareErrorProgressObject (CommandType commandType, Throwable thr) { DeploymentStatus ds = new DeploymentStatusImplWithError(CommandType.DISTRIBUTE, thr); SimpleProgressObjectImpl progressObj = new SimpleProgressObjectImpl(ds); ProgressEvent event = new ProgressEvent(progressObj, null /*targetModuleID */, ds); progressObj.fireProgressEvent(event); return progressObj; } protected Properties getProperties() { // we don't set name from client side and will let server side // determine it DFDeploymentProperties dProps = new DFDeploymentProperties(); dProps.setEnabled(false); return (Properties)dProps; } /** * The distribute method performs three tasks; it validates the * deployment configuration data, generates all container specific * classes and interfaces, and moves the fully baked archive to * the designated deployment targets. * * @param targetList A list of server targets the user is specifying * this application be deployed to. * @param moduleArchive The abstraction for the application * archive to be disrtibuted. * @param deploymentPlan The archive containing the deployment * configuration information associated with * this application archive. * @param deploymentOptions is a JavaBeans compliant component * containing all deployment options for this deployable * unit. This object must be created using the * BeanInfo instance returned by * DeploymentConfiguration.getDeploymentOptions * @throws IllegalStateException is thrown when the method is * called when running in disconnected mode. * @return ProgressObject an object that tracks and reports the * status of the distribution process. * */ public ProgressObject distribute(Target[] targetList, Archive moduleArchive, Archive deploymentPlan, Object deploymentOptions) throws IllegalStateException { return null; } /** * Creates a new instance of Archive which can be used to * store application elements in a layout that can be directly used by * the application server. Implementation of this method should carefully * return the appropriate implementation of the interface that suits * the server needs and provide the fastest deployment time. * An archive may already exist at the location and elements may be * read but not changed or added depending on the underlying medium. * @param path the directory in which to create this archive if local * storage is a possibility. * @param name is the desired name for the archive * @return the writable archive instance */ public Archive getArchive(URI path, String name) throws IOException { if (path==null) { // no particular path was provided, using tmp jar file File root = File.createTempFile(name,".jar"); //NOI18N path = root.toURI(); } ArchiveFactory factory = getArchiveFactory(); boolean exists = false; if ((path.getScheme().equals("file")) || //NOI18N (path.getScheme().equals("jar"))) { //NOI18N File target = new File(path); exists = target.exists(); } else { return null; } if (exists) { return factory.openArchive(path); } else { return factory.createArchive(path); } } private void prepareHabitat() { ModulesRegistry registry = new StaticModulesRegistry(getClass().getClassLoader()); ServiceLocator serviceLocator = registry.createServiceLocator("default"); habitat = serviceLocator.getService(ServiceLocator.class); StartupContext startupContext = new StartupContext(); ServiceLocatorUtilities.addOneConstant(habitat, startupContext); ServiceLocatorUtilities.addOneConstant(habitat, new ProcessEnvironment(ProcessEnvironment.ProcessType.Other)); } private ArchiveFactory getArchiveFactory() { return habitat.getService(ArchiveFactory.class); } /** *Organizes the target module IDs passed by a JSR88 client for easy processing one module ID *at a time. *<p> *Several methods in the JSR88 DeploymentManager interface accept a list of TargetModuleID values, *and these lists can refer to multiple module IDs and multiple targets. *Each invocation of a DeploymentFacility method, on the other hand, can work on only a single module *although with perhaps multiple targets. This class provides a central way of organizing the *target module IDs as passed from the JSR88 client and making the information for a single *module ID readily available. *<p> *Typically, a client will use three methods: *<ul> *<le>the constructor - pass a TargetModuleID array as supplied by a client *<le>the iterator() method, which the client uses to step through the DeploymentFacilityModuleWork *instances, each representing a single module and perhaps multiple targets. *<le>the getProgressObjectSink which returns the aggregator for the ProgressObjects *from each work element *</ul> */ protected static class TargetModuleIDCollection { /* Maps the module ID to that module's instance of DeploymentFacilityModuleWork. */ private HashMap moduleIDToInfoMap = new HashMap(); /* Collects together the individual progress objects into a single aggregate one. */ ProgressObjectSink progressObjectSink = null; /** *Create a new instance of TargetModuleIDCollection. *Accept the array of targetModuleIDs as passed by the JSR88 client and set up the *internal data structures. *@param targetModuleIDs array of {@link javax.deployment.api.TargetModuleID TargetModuleID} provided from the calling JSR88 client */ public TargetModuleIDCollection(TargetModuleID [] targetModuleIDs) throws IllegalArgumentException { for (int i = 0; i < targetModuleIDs.length; i++) { /* *Make sure that this target module ID has a target that is a TargetImpl and was created by this DM. */ Target candidateTarget = targetModuleIDs[i].getTarget(); if ( ! (candidateTarget instanceof TargetImpl)) { throw new IllegalArgumentException( localStrings.getLocalString("enterprise.deployapi.spi.nott", //NOI18N "Expected TargetImpl instance but found instance of {0}", new Object[] {candidateTarget.getClass().getName() } )); //NOI18N } String moduleID = targetModuleIDs[i].getModuleID(); /* *Look for the entry in the hash map for this module. */ DeploymentFacilityModuleWork work = (DeploymentFacilityModuleWork) moduleIDToInfoMap.get(moduleID); if (work == null) { /* *This module ID is not yet in the map. Add a work instance for it with the module ID as the key. */ work = new DeploymentFacilityModuleWork(moduleID); moduleIDToInfoMap.put(moduleID, work); } /* *Either the entry already exists or one has been created. *In either case, add the target to the work to be done with this module. */ work.addTarget(candidateTarget); } } /** *Provides an Iterator over the module work items in the collection. *The iterator provides one element for each distinct module that appeared in the original *array of TargetModuleIDs. * *@return Iterator over the DeploymentFacilityModuleWork elements in the collection */ public Iterator iterator() { return moduleIDToInfoMap.values().iterator(); } /** *Reports the number of elements in the collection. *This is also a measure of the number of distinct module IDs specified in the TargetModuleID array *passed to the constructor of the collection. *@return the number of DeploymentFacilityModuleWork elements contained in the collection */ public int size() { return moduleIDToInfoMap.size(); } /** *Returns the aggregate progress object for the collection. *Creates a new ProgressObjectSink if needed. *@return ProgressObjectSink */ public ProgressObjectSink getProgressObjectSink() { if (progressObjectSink == null) { progressObjectSink = new ProgressObjectSink(); } return progressObjectSink; } } /** *Encapsulates information used with a single invocation of a DeploymentFacility method-- *that is, one item of "work" the DeploymentFacility is being asked to perform. *This includes the single target ID of interest (because the DF methods operate on a *single module), a collection of all the targets to be included in the operation on that *module, and the progress object resulting from the DF method invocation. */ protected static class DeploymentFacilityModuleWork { /** The module ID this work handles */ private String moduleID = null; /** The targets this work should affect. */ private Collection targets = new Vector(); /** The ProgressObject for this work returned by the DeploymentFacility method invocation. */ private ProgressObject progressObject = null; /** *Creates a new instance of DeploymentFacilityModuleWork. *@param the module ID common to all work recorded in this instance */ public DeploymentFacilityModuleWork(String moduleID) { this.moduleID = moduleID; } /** *Adds a target to the collection of targets for the work to be done for this distinct module. *@param the {@link javax.enterprise.deploy.spi.Target Target} to be added for this module */ public void addTarget(Target target) { if ( ! (target instanceof TargetImpl) ) { throw new IllegalArgumentException(localStrings.getLocalString( "enterprise.deployapi.spi.unexptargettyp", "Target must be of type TargetImpl but encountered {0}", new Object [] {target.getClass().getName()} )); } targets.add(target); } /** *Returns an array of {@link javax.enterprise.deploy.spi.Target Target} instances recorded for *this module. Note the return of an array of runtime type TargetImpl[]. *@return array of Target */ public Target [] targets() { return (Target []) targets.toArray(new TargetImpl[targets.size()]); } /** *Returns the {@link javax.enterprise.deploy.spi.status.ProgressObject ProgressObject} that the *DeploymentFacility method returned when it was invoked. *@return the ProgressObject */ public ProgressObject getProgressObject() { return this.progressObject; } /** *Records the {@link javax.enterprise.deploy.spi.status.ProgressObject ProgressObject} that the *DeploymentFacility returned when its method was invoked. *@param the ProgressObject provided by the DeploymentFacility *method */ public void setProgressObject (ProgressObject progressObject) { this.progressObject = progressObject; } /** *Reports the module ID for this instance of DeploymentFacilityModuleWork *@return the module ID */ public String getModuleID() { return this.moduleID; } } }