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

com.sun.enterprise.config.serverbeans.Server Maven / Gradle / Ivy

There is a newer version: 10.0-b28
Show newest version
/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 1997-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 com.sun.enterprise.config.serverbeans;

import com.sun.enterprise.config.util.InstanceRegisterInstanceCommandParameters;
import static com.sun.enterprise.config.util.RegisterInstanceCommandParameters.ParameterNames.*;
import com.sun.enterprise.config.serverbeans.customvalidators.ConfigRefConstraint;
import com.sun.enterprise.config.serverbeans.customvalidators.ConfigRefValidator;
import com.sun.enterprise.config.serverbeans.customvalidators.NotTargetKeyword;
import com.sun.enterprise.config.serverbeans.customvalidators.NotDuplicateTargetName;
import com.sun.enterprise.config.util.ServerHelper;
import com.sun.enterprise.config.util.PortBaseHelper;
import com.sun.enterprise.config.util.PortManager;
import com.sun.enterprise.util.StringUtils;
import com.sun.enterprise.util.LocalStringManagerImpl;
import com.sun.enterprise.util.io.FileUtils;
import com.sun.enterprise.util.net.NetUtils;
import com.sun.grizzly.config.dom.NetworkListener;
import com.sun.logging.LogDomains;
import java.io.*;
import org.glassfish.api.Param;
import org.glassfish.api.admin.AdminCommandContext;
import org.glassfish.config.support.*;
import org.jvnet.hk2.annotations.Inject;
import org.jvnet.hk2.annotations.Scoped;
import org.jvnet.hk2.annotations.Service;
import org.jvnet.hk2.component.Injectable;
import org.jvnet.hk2.component.PerLookup;
import org.jvnet.hk2.config.*;
import org.jvnet.hk2.config.types.Property;
import org.jvnet.hk2.config.types.PropertyBag;
import org.glassfish.api.ActionReport;
import org.glassfish.api.admin.config.Named;
import org.glassfish.api.admin.config.PropertiesDesc;
import org.glassfish.api.admin.config.ReferenceContainer;
import org.glassfish.quality.ToDo;
import static org.glassfish.config.support.Constants.*;

import java.beans.PropertyVetoException;
import java.util.List;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.validation.Payload;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;

import org.glassfish.api.admin.ServerEnvironment;
import org.glassfish.api.admin.CommandRunner;

/**
 *
 * Java EE Application Server Configuration
 *
 * Each Application Server instance is a Java EE compliant container. One
 * server instance is specially designated as the Administration Server in SE/EE
 *
 * User applications cannot be deployed to an Administration Server instance
 */
@Configured
@ConfigRefConstraint(message="{configref.invalid}", payload=ConfigRefValidator.class)
@SuppressWarnings("unused")
@NotDuplicateTargetName(message="{server.duplicate.name}", payload=Server.class)
public interface Server extends ConfigBeanProxy, Injectable, PropertyBag, Named, SystemPropertyBag, ReferenceContainer, RefContainer, Payload {

    String lbEnabledSystemProperty = "org.glassfish.lb-enabled-default";

    @Param(name = OPERAND_NAME, primary = true)
    @Override
    public void setName(String value) throws PropertyVetoException;

    @NotTargetKeyword(message="{server.reserved.name}", payload=Server.class)
    @Pattern(regexp=NAME_SERVER_REGEX, message="{server.invalid.name}", payload=Server.class)
    @Override
    public String getName();

    /**
     * Gets the value of the configRef property.
     *
     * Points to a named config. Needed for stand-alone servers. If server
     * instance is part of a cluster, then it points to the cluster config
     *
     * @return possible object is
     *         {@link String }
     */
    @Attribute
    @NotNull
    @NotTargetKeyword(message="{server.reserved.name}", payload=Server.class)
    @Pattern(regexp = NAME_SERVER_REGEX)
    String getConfigRef();

    /**
     * Sets the value of the configRef property.
     *
     * @param value allowed object is
     *              {@link String }
     * @throws PropertyVetoException if a listener vetoes the change
     */
    @Param(name = PARAM_CONFIG, optional = true)
    void setConfigRef(String value) throws PropertyVetoException;

    /**
     * Gets the value of the nodeAgentRef property.
     *
     * SE/EE only. Specifies name of node agent where server instance is hosted
     *
     * @return possible object is
     *         {@link String }
     */
    @Attribute
    @Deprecated
    String getNodeAgentRef();

    /**
     * Sets the value of the nodeAgentRef property.
     *
     * @param value allowed object is
     *              {@link String }
     * @throws PropertyVetoException if a listener vetoes the change
     */
    @Deprecated
    void setNodeAgentRef(String value) throws PropertyVetoException;

    /**
     * Sets the value of the node property.
     *
     * @param value allowed object is
     *              {@link String }
     * @throws PropertyVetoException if a listener vetoes the change
     */
    @Param(name = PARAM_NODE, optional = true)
    void setNodeRef(String value) throws PropertyVetoException;

    /**
     * Gets the value of the node property.
     *
     * SE/EE only. Specifies name of node agent where server instance is hosted
     *
     * @return possible object is
     *         {@link String }
     */
    @Attribute
    String getNodeRef();
    
    /**
     * Gets the value of the lbWeight property.
     *
     * Each server instance in a cluster has a weight, which may be used to
     * represent the relative processing capacity of that instance. Default
     * weight is 100 for every instance. Weighted load balancing policies will
     * use this weight while load balancing requests within the cluster.
     * It is the responsibility of the administrator to set the relative weights
     * correctly, keeping in mind deployed hardware capacity
     *
     * @return possible object is
     *         {@link String }
     */
    @Attribute(defaultValue = "100")
    @Min(value = 1)
    String getLbWeight();

    /**
     * Sets the value of the lbWeight property.
     *
     * @param value allowed object is
     *              {@link String }
     * @throws PropertyVetoException if a listener vetoes the change
     */
    void setLbWeight(String value) throws PropertyVetoException;

    /**
     * Gets the value of the systemProperty property.
     * 

*

* This accessor method returns a reference to the live list, * not a snapshot. Therefore any modification you make to the * returned list will be present inside the JAXB object. * This is why there is not a set method for the systemProperty property. *

*

* For example, to add a new item, do as follows: *

     *    getSystemProperty().add(newItem);
     * 
*

*

*

* Objects of the following type(s) are allowed in the list * {@link SystemProperty } */ @ToDo(priority = ToDo.Priority.IMPORTANT, details = "Provide PropertyDesc for legal system properties") @Element @Param(name = InstanceRegisterInstanceCommandParameters.ParameterNames.PARAM_SYSTEMPROPERTIES, optional = true) List getSystemProperty(); /** Properties as per {@link PropertyBag} */ @ToDo(priority = ToDo.Priority.IMPORTANT, details = "Provide PropertyDesc for legal props") @PropertiesDesc(props = {}) @Element @Param(name = "properties", optional = true) List getProperty(); @DuckTyped String getReference(); @DuckTyped ResourceRef getResourceRef(String name); @DuckTyped boolean isResourceRefExists(String refName); @DuckTyped void deleteResourceRef(String name) throws TransactionFailure; @DuckTyped void createResourceRef(String enabled, String refName) throws TransactionFailure; @DuckTyped ApplicationRef getApplicationRef(String appName); /** * Returns the cluster instance this instance is referenced in or null * if there is no cluster referencing this server instance. * * @return the cluster owning this instance or null if this is a standalone * instance */ @DuckTyped Cluster getCluster(); // four trivial methods that ReferenceContainer's need to implement @DuckTyped @Override boolean isCluster(); @DuckTyped @Override boolean isServer(); @DuckTyped @Override boolean isDas(); @DuckTyped @Override boolean isInstance(); @DuckTyped String getAdminHost(); @DuckTyped int getAdminPort(); @DuckTyped Config getConfig(); @DuckTyped boolean isRunning(); class Duck { public static boolean isCluster(Server server) { return false; } public static boolean isServer(Server server) { return true; } public static boolean isInstance(Server server) { String name = (server == null) ? null : server.getName(); return name != null && !name.equals("server"); } public static boolean isDas(Server server) { String name = (server == null) ? null : server.getName(); return "server".equals(name); } public static Cluster getCluster(Server server) { Dom serverDom = Dom.unwrap(server); Clusters clusters = serverDom.getHabitat().getComponent(Clusters.class); if (clusters != null) { for (Cluster cluster : clusters.getCluster()) { for (ServerRef serverRef : cluster.getServerRef()) { if (serverRef.getRef().equals(server.getName())) { return cluster; } } } } return null; } public static String getReference(Server server) { return server.getConfigRef(); } public static ApplicationRef getApplicationRef(Server server, String appName) { for (ApplicationRef appRef : server.getApplicationRef()) { if (appRef.getRef().equals(appName)) { return appRef; } } return null; } public static ResourceRef getResourceRef(Server server, String refName) { for (ResourceRef ref : server.getResourceRef()) { if (ref.getRef().equals(refName)) { return ref; } } return null; } public static boolean isResourceRefExists(Server server, String refName) { return getResourceRef(server, refName) != null; } public static void deleteResourceRef(Server server, String refName) throws TransactionFailure { final ResourceRef ref = getResourceRef(server, refName); if (ref != null) { ConfigSupport.apply(new SingleConfigCode() { public Object run(Server param) { return param.getResourceRef().remove(ref); } }, server); } } public static void createResourceRef(Server server, final String enabled, final String refName) throws TransactionFailure { ConfigSupport.apply(new SingleConfigCode() { public Object run(Server param) throws PropertyVetoException, TransactionFailure { ResourceRef newResourceRef = param.createChild(ResourceRef.class); newResourceRef.setEnabled(enabled); newResourceRef.setRef(refName); param.getResourceRef().add(newResourceRef); return newResourceRef; } }, server); } public static Config getConfig(Server server) { try { if (server == null) return null; Dom serverDom = Dom.unwrap(server); Configs configs = serverDom.getHabitat().getComponent(Configs.class); String configName = getReference(server); Config theConfig = null; for (Config config : configs.getConfig()) { if (configName.equals(config.getName())) { return config; } } } catch (Exception e) { // drop through... } return null; } public static int getAdminPort(Server server) { try { ServerHelper helper = new ServerHelper(server, getConfig(server)); return helper.getAdminPort(); } catch (Exception e) { // drop through... } return -1; } public static String getAdminHost(Server server) { try { ServerHelper helper = new ServerHelper(server, getConfig(server)); return helper.getAdminHost(); } catch (Exception e) { // drop through... } return null; } /** this is NOT a reliable test. It just checks if ANYTHING has setup shop * on the host and port. * I wanted to run RemoteAdminCommand but that is (inexplicably) in * admin/util -- and we would have a circular dependency * @param server * @return */ public static boolean isRunning(Server server) { try { ServerHelper helper = new ServerHelper(server, getConfig(server)); return helper.isRunning(); } catch (Exception e) { // drop through... } return false; } } @Service @Scoped(PerLookup.class) class CreateDecorator implements CreationDecorator { @Param(name = PARAM_CLUSTER, optional = true) String clusterName; @Param(name = PARAM_NODE, optional = true) String node = null; @Param(name = PARAM_LBENABLED, optional = true) String lbEnabled = null; @Param(name = PARAM_CHECKPORTS, optional = true, defaultValue = "true") boolean checkPorts = true; @Param(name = PARAM_PORTBASE, optional = true) private String portBase; @Param(optional=true, defaultValue="false", shortName="t") public Boolean terse = false; @Inject Domain domain; @Inject private ServerEnvironment env; @Inject CommandRunner runner; @Override public void decorate(AdminCommandContext context, final Server instance) throws TransactionFailure, PropertyVetoException { Config ourConfig = null; Cluster ourCluster = null; Logger logger = LogDomains.getLogger(Cluster.class, LogDomains.ADMIN_LOGGER); LocalStringManagerImpl localStrings = new LocalStringManagerImpl(Server.class); Transaction tx = Transaction.getTransaction(instance); String configRef = instance.getConfigRef(); Clusters clusters = domain.getClusters(); if (tx == null) { throw new TransactionFailure(localStrings.getLocalString( "noTransaction", "Internal Error - Cannot obtain transaction object")); } /* if (node == null) { instance.setNode("localhost"); //remove? } else { */ if (node != null){ if (domain.getNodeNamed(node) == null) { throw new TransactionFailure(localStrings.getLocalString( "noSuchNode", "Node {0} does not exist.", node)); } } if (portBase != null) { PortBaseHelper pbh = new PortBaseHelper(instance, portBase, false, logger); pbh.verifyPortBase(); pbh.setPorts(); } // cluster instance using cluster config if (clusterName != null) { if (configRef != null) { throw new TransactionFailure(localStrings.getLocalString( "Server.cannotSpecifyBothConfigAndCluster", "A configuration name and cluster name cannot both be specified.")); } boolean clusterExists = false; if (clusters != null) { for (Cluster cluster : clusters.getCluster()) { if (cluster != null && clusterName.equals(cluster.getName())) { ourCluster = cluster; String configName = cluster.getConfigRef(); instance.setConfigRef(configName); clusterExists = true; ourConfig = domain.getConfigNamed(configName); break; } } } if (!clusterExists) { throw new TransactionFailure(localStrings.getLocalString( "noSuchCluster", "Cluster {0} does not exist.", clusterName)); } Cluster cluster = domain.getClusterNamed(clusterName); final String instanceName = instance.getName(); try { File configConfigDir = new File(env.getConfigDirPath(), cluster.getConfigRef()); new File(configConfigDir, "docroot").mkdirs(); new File(configConfigDir, "lib/ext").mkdirs(); } catch (Exception e) { // no big deal - just ignore } if (cluster != null) { if (tx != null) { Cluster c = tx.enroll(cluster); ServerRef newServerRef = c.createChild(ServerRef.class); newServerRef.setRef(instanceName); if(lbEnabled != null){ newServerRef.setLbEnabled(lbEnabled); } else { //check whether all instances in cluster had lb-enabled set to false List serverRefs = c.getServerRef(); Iterator serverRefIter = serverRefs.iterator(); boolean allLBEnabled = false; while (!allLBEnabled && serverRefIter.hasNext()) { ServerRef serverRef = serverRefIter.next(); allLBEnabled = allLBEnabled || Boolean.parseBoolean(serverRef.getLbEnabled()); } //if there are existing instances in cluster //and they all have lb-enabled to false, set it //false for new instance as well if (!allLBEnabled && serverRefs.size() > 0) { newServerRef.setLbEnabled("false"); } else { //check if system property exists and use that String lbEnabledDefault = System.getProperty(lbEnabledSystemProperty); if(lbEnabledDefault != null){ newServerRef.setLbEnabled(lbEnabledDefault); } } } c.getServerRef().add(newServerRef); } } } // instance using specified config if (configRef != null) { Config specifiedConfig = domain.getConfigs().getConfigByName(configRef); if (specifiedConfig == null) { throw new TransactionFailure(localStrings.getLocalString( "noSuchConfig", "Configuration {0} does not exist.", configRef)); } ourConfig = specifiedConfig; try { File configConfigDir = new File(env.getConfigDirPath(), specifiedConfig.getName()); new File(configConfigDir, "docroot").mkdirs(); new File(configConfigDir, "lib/ext").mkdirs(); } catch (Exception e) { // no big deal - just ignore } } //stand-alone instance using default-config if config not specified if (configRef == null && clusterName == null) { Config defaultConfig = domain.getConfigs().getConfigByName("default-config"); if (defaultConfig == null) { final String msg = localStrings.getLocalString(Server.class, "Cluster.noDefaultConfig", "Can''t find the default config (an element named \"default-config\") " + "in domain.xml. You may specify the name of an existing config element next time."); logger.log(Level.SEVERE, msg); throw new TransactionFailure(msg); } final String configName = instance.getName() + "-config"; instance.setConfigRef(configName); final CopyConfig command = (CopyConfig) runner .getCommand("copy-config", context.getActionReport(), context.getLogger()); Configs configs = domain.getConfigs(); Configs writableConfigs = tx.enroll(configs); ourConfig = command.copyConfig(writableConfigs,defaultConfig,configName,logger); } for (Resource resource : domain.getResources().getResources()) { if (resource.getObjectType().equals("system-all") || resource.getObjectType().equals("system-instance")) { String name = null; if (resource instanceof BindableResource) { name = ((BindableResource) resource).getJndiName(); } if (resource instanceof Named) { name = ((Named) resource).getName(); } if (name == null) { throw new TransactionFailure("Cannot add un-named resources to the new server instance"); } ResourceRef newResourceRef = instance.createChild(ResourceRef.class); newResourceRef.setRef(name); instance.getResourceRef().add(newResourceRef); } } for (Application application : domain.getApplications().getApplications()) { if (application.getObjectType().equals("system-all") || application.getObjectType().equals("system-instance")) { ApplicationRef newAppRef = instance.createChild(ApplicationRef.class); newAppRef.setRef(application.getName()); // todo : what about virtual-servers ? instance.getApplicationRef().add(newAppRef); } } this.addClusterRefs(ourCluster, instance); if (checkPorts) { PortManager pm = new PortManager(ourCluster, ourConfig, domain, instance); String message = pm.process(); if (message != null && !terse) { ActionReport report = context.getActionReport(); report.setMessage(message); } } checkAdminPort(instance, ourConfig, localStrings); setupSupplemental(context, instance); } private void checkAdminPort(final Server instance, final Config config, LocalStringManagerImpl localStrings) throws TransactionFailure { if (node != null) { Node n = domain.getNodeNamed(node); if (n != null) { String nodeHost = n.getNodeHost(); if (NetUtils.isThisHostLocal(nodeHost)) { // instance on same host as DAS int dasAdminPort = domain.getServerNamed("server").getAdminPort(); // Don't use the getAdminPort duck type method directly on the instance being created int instanceAdminPort = new ServerHelper(instance, config).getAdminPort(); if (instanceAdminPort != -1 && dasAdminPort != -1) { if (instanceAdminPort == dasAdminPort) { throw new TransactionFailure(localStrings.getLocalString( "Server.cannotHaveSameAdminPortAsDAS", "Cannot create an instance on the same host as DAS with the same admin port as DAS: {0}.", instanceAdminPort + "")); } } } } } } private void setupSupplemental(AdminCommandContext context, final Server instance) { if (clusterName != null) { InstanceRegisterInstanceCommandParameters cp = new InstanceRegisterInstanceCommandParameters(); context.getActionReport(). setResultType(InstanceRegisterInstanceCommandParameters.class, cp); Node instNode = domain.getNodeNamed(node); cp.config = instance.getConfigRef(); cp.nodehost = instNode.getNodeHost(); cp.nodedir = instNode.getNodeDir(); cp.installdir = instNode.getInstallDir(); List spList = instance.getSystemProperty(); if (spList != null) { Properties p = new Properties(); for (SystemProperty sp : spList) { p.put(sp.getName(), sp.getValue()); } cp.systemProperties = p; } } } private void addClusterRefs(Cluster cluster, Server instance) throws TransactionFailure, PropertyVetoException { if (cluster != null) { for (ApplicationRef appRef : cluster.getApplicationRef()) { if (instance.getApplicationRef(appRef.getRef()) == null) { ApplicationRef newAppRef = instance.createChild(ApplicationRef.class); newAppRef.setRef(appRef.getRef()); newAppRef.setDisableTimeoutInMinutes(appRef.getDisableTimeoutInMinutes()); newAppRef.setEnabled(appRef.getEnabled()); newAppRef.setLbEnabled(appRef.getLbEnabled()); newAppRef.setVirtualServers(appRef.getVirtualServers()); instance.getApplicationRef().add(newAppRef); } } for (ResourceRef rr : cluster.getResourceRef()) { if (instance.getResourceRef(rr.getRef()) == null) { ResourceRef newRR = instance.createChild(ResourceRef.class); newRR.setRef(rr.getRef()); newRR.setEnabled(rr.getEnabled()); instance.getResourceRef().add(newRR); } } } } } @Service @Scoped(PerLookup.class) class DeleteDecorator implements DeletionDecorator { @Param(optional=true) String target; //TODO - add support for node? @Param(name = PARAM_NODE, optional = true) String node; @Inject Configs configs; @Inject private Domain domain; @Inject private ServerEnvironment env; @Override public void decorate(AdminCommandContext context, Servers parent, final Server child) throws PropertyVetoException, TransactionFailure { final Logger logger = LogDomains.getLogger(Server.class, LogDomains.ADMIN_LOGGER); LocalStringManagerImpl localStrings = new LocalStringManagerImpl(Server.class); final ActionReport report = context.getActionReport(); Transaction t = Transaction.getTransaction(parent); Cluster cluster = domain.getClusterForInstance(child.getName()); boolean isStandAlone = cluster == null ? true : false; /* setup supplemental */ if (!isStandAlone && env.isDas()) { context.getActionReport(). setResultType(String.class, cluster.getName()); } if (isStandAlone) { // remove config -config String instanceConfig = child.getConfigRef(); final Config config = configs.getConfigByName(instanceConfig); // bnevins June 2010 // don't delete the config is someone else holds a reference to it! if (config != null && domain.getReferenceContainersOf(config).size() > 1) { return; } // bnevins September 30, 2010 // don't delete the config if it wasn't auto-generated. final String autoGeneratedName = child.getName() + "-config"; if (!autoGeneratedName.equals(instanceConfig)) return; try { if (config != null) { File configConfigDir = new File(env.getConfigDirPath(), config.getName()); FileUtils.whack(configConfigDir); } } catch (Exception e) { // no big deal - just ignore } try { if (t != null) { Configs c = t.enroll(configs); List configList = c.getConfig(); configList.remove(config); } } catch (TransactionFailure ex) { logger.log(Level.SEVERE, localStrings.getLocalString("deleteConfigFailed", "Unable to remove config {0}", instanceConfig), ex); String msg = ex.getMessage() != null ? ex.getMessage() : localStrings.getLocalString("deleteConfigFailed", "Unable to remove config {0}", instanceConfig); report.setMessage(msg); report.setActionExitCode(ActionReport.ExitCode.FAILURE); report.setFailureCause(ex); throw ex; } } else { // remove server-ref from cluster final String instanceName = child.getName(); if (t != null) { try { Cluster c = t.enroll(cluster); List serverRefList = c.getServerRef(); ServerRef serverRef = null; for (ServerRef sr : serverRefList) { if (sr.getRef().equals(instanceName)) { serverRef = sr; break; } } if (serverRef != null) { serverRefList.remove(serverRef); } } catch (TransactionFailure ex) { logger.log(Level.SEVERE, localStrings.getLocalString("deleteServerRefFailed", "Unable to remove server-ref {0} from cluster {1}", instanceName, cluster.getName()), ex); String msg = ex.getMessage() != null ? ex.getMessage() : localStrings.getLocalString("deleteServerRefFailed", "Unable to remove server-ref {0} from cluster {1}", instanceName, cluster.getName()); report.setMessage(msg); report.setActionExitCode(ActionReport.ExitCode.FAILURE); report.setFailureCause(ex); throw ex; } } } } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy