/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright (c) 1997-2013 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development * and Distribution License("CDDL") (collectively, the "License"). You * may not use this file except in compliance with the License. You can * obtain a copy of the License at * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html * or packager/legal/LICENSE.txt. See the License for the specific * language governing permissions and limitations under the License. * * When distributing the software, include this License Header Notice in each * file and include the License file at packager/legal/LICENSE.txt. * * GPL Classpath Exception: * Oracle designates this particular file as subject to the "Classpath" * exception as provided by Oracle in the GPL Version 2 section of the License * file that accompanied this code. * * Modifications: * If applicable, add the following below the License Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyright [year] [name of copyright owner]" * * Contributor(s): * If you wish your version of this file to be governed by only the CDDL or * only the GPL Version 2, indicate your decision by adding "[Contributor] * elects to include this software in this distribution under the [CDDL or GPL * Version 2] license." If you don't indicate a single choice of license, a * recipient has the option to distribute your version of this file under * either the CDDL, the GPL Version 2 or to extend the choice of license to * its licensees as provided above. However, if you add GPL Version 2 code * and therefore, elected the GPL Version 2 license, then the option applies * only if the new code is made subject to such option by the copyright * holder. */ package org.glassfish.web.admin.cli; import java.beans.PropertyVetoException; import java.text.MessageFormat; import java.util.List; import java.util.ResourceBundle; import java.util.logging.Level; import java.util.logging.Logger; import javax.inject.Inject; import javax.inject.Named; import com.sun.enterprise.config.serverbeans.Config; import com.sun.enterprise.config.serverbeans.Domain; import com.sun.enterprise.config.serverbeans.HttpService; import com.sun.enterprise.config.serverbeans.VirtualServer; import com.sun.enterprise.util.SystemPropertyConstants; import org.glassfish.api.ActionReport; import org.glassfish.api.ActionReport.ExitCode; import org.glassfish.api.I18n; import org.glassfish.api.Param; import org.glassfish.api.admin.AdminCommand; import org.glassfish.api.admin.AdminCommandContext; import org.glassfish.api.admin.CommandRunner; import org.glassfish.api.admin.ExecuteOn; import org.glassfish.api.admin.RuntimeType; import org.glassfish.api.admin.ServerEnvironment; import org.glassfish.config.support.CommandTarget; import org.glassfish.config.support.TargetType; import org.glassfish.grizzly.config.dom.NetworkConfig; import org.glassfish.grizzly.config.dom.NetworkListener; import org.glassfish.grizzly.config.dom.NetworkListeners; import org.glassfish.grizzly.config.dom.ThreadPool; import org.glassfish.grizzly.config.dom.Transport; import org.glassfish.internal.api.Target; import org.glassfish.hk2.api.ServiceLocator; import org.glassfish.hk2.api.PerLookup; import org.glassfish.logging.annotation.LogMessageInfo; import org.glassfish.web.admin.monitor.HttpServiceStatsProviderBootstrap; import org.jvnet.hk2.annotations.Service; import org.jvnet.hk2.config.ConfigSupport; import org.jvnet.hk2.config.SingleConfigCode; import org.jvnet.hk2.config.TransactionFailure; import org.jvnet.hk2.config.Transactions; /** * Create Http Listener Command */ @Service(name = "create-http-listener") @PerLookup @I18n("create.http.listener") @ExecuteOn({RuntimeType.DAS, RuntimeType.INSTANCE}) @TargetType({CommandTarget.DAS,CommandTarget.STANDALONE_INSTANCE,CommandTarget.CLUSTER,CommandTarget.CONFIG}) public class CreateHttpListener implements AdminCommand { @Param(name = "listeneraddress") String listenerAddress; @Param(name = "listenerport") String listenerPort; @Param(name = "defaultvs", optional = true) String defaultVS; @Param(name = "default-virtual-server", optional = true) String defaultVirtualServer; @Param(name = "servername", optional = true) String serverName; @Param(name = "xpowered", optional = true, defaultValue = "true") Boolean xPoweredBy; @Param(name = "serverHeader", optional = true, defaultValue = "true", alias="serverHeader") Boolean serverHeader; @Param(name = "acceptorthreads", optional = true) String acceptorThreads; @Param(name = "redirectport", optional = true) String redirectPort; @Param(name = "securityenabled", optional = true, defaultValue = "false") Boolean securityEnabled; @Param(optional = true, defaultValue = "true") Boolean enabled; @Param(optional = true, defaultValue = "false") Boolean secure; //FIXME @Param(name = "listener_id", primary = true) String listenerId; @Param(name = "target", optional = true, defaultValue = SystemPropertyConstants.DEFAULT_SERVER_INSTANCE_NAME) String target; @Inject @Named(ServerEnvironment.DEFAULT_INSTANCE_NAME) Config config; @Inject ServiceLocator services; @Inject CommandRunner runner; @Inject Domain domain; private static final String DEFAULT_TRANSPORT = "tcp"; private NetworkConfig networkConfig = null; private static final Logger logger = HttpServiceStatsProviderBootstrap.logger; private static final ResourceBundle rb = logger.getResourceBundle(); @LogMessageInfo( message = "The acceptor threads must be at least 1", level = "INFO") private static final String ACCEPTOR_THREADS_TOO_LOW = "AS-WEB-ADMIN-00003"; @LogMessageInfo( message = "Listener {0} could not be created, actual reason: {1}", level = "INFO") private static final String CREATE_HTTP_LISTENER_FAIL = "AS-WEB-ADMIN-00004"; @LogMessageInfo( message = "A default virtual server is required. Please use --default-virtual-server to specify this value.", level = "INFO") private static final String CREATE_HTTP_LISTENER_VS_BLANK = "AS-WEB-ADMIN-00005"; @LogMessageInfo( message = "--defaultVS and --default-virtual-server conflict. Please use only --default-virtual-server to specify this value.", level = "INFO") private static final String CREATE_HTTP_LISTENER_VS_BOTH_PARAMS = "AS-WEB-ADMIN-00006"; @LogMessageInfo( message = "Attribute value (default-virtual-server = {0}) is not found in list of virtual servers defined in config.", level = "INFO") private static final String CREATE_HTTP_LISTENER_VS_NOTEXISTS = "AS-WEB-ADMIN-00007"; @LogMessageInfo( message = "Http Listener named {0} already exists.", level = "INFO") private static final String CREATE_HTTP_LISTENER_DUPLICATE = "AS-WEB-ADMIN-00008"; @LogMessageInfo( message = "Port [{0}] is already taken for address [{1}], please choose another port.", level = "INFO") protected static final String PORT_IN_USE = "AS-WEB-ADMIN-00009"; /** * Executes the command with the command parameters passed as Properties where the keys are the paramter names and * the values the parameter values * * @param context information */ public void execute(AdminCommandContext context) { final ActionReport report = context.getActionReport(); if(!validateInputs(report)) { return; } Target targetUtil = services.getService(Target.class); Config newConfig = targetUtil.getConfig(target); if (newConfig!=null) { config = newConfig; } networkConfig = config.getNetworkConfig(); HttpService httpService = config.getHttpService(); if (!(verifyUniqueName(report, networkConfig) && verifyUniquePort(report, networkConfig) && verifyDefaultVirtualServer(report))) { return; } VirtualServer vs = httpService.getVirtualServerByName(defaultVirtualServer); boolean listener = false; boolean protocol = false; boolean transport = false; try { transport = createOrGetTransport(null); protocol = createProtocol(context); createHttp(context); final ThreadPool threadPool = getThreadPool(networkConfig); listener = createNetworkListener(networkConfig, transport, threadPool); updateVirtualServer(vs); } catch (TransactionFailure e) { try { if (listener) { deleteListener(context); } if (protocol) { deleteProtocol(context); } if (transport) { deleteTransport(context); } } catch (Exception e1) { if (logger.isLoggable(Level.INFO)) { logger.log(Level.INFO, e.getMessage(), e); } throw new RuntimeException(e.getMessage()); } if (logger.isLoggable(Level.INFO)) { logger.log(Level.INFO, e.getMessage(), e); } report.setMessage(MessageFormat.format(rb.getString(CREATE_HTTP_LISTENER_FAIL), listenerId, e.getMessage())); report.setActionExitCode(ActionReport.ExitCode.FAILURE); report.setFailureCause(e); return; } report.setActionExitCode(ActionReport.ExitCode.SUCCESS); } private void updateVirtualServer(VirtualServer vs) throws TransactionFailure { //now change the associated virtual server ConfigSupport.apply(new SingleConfigCode<VirtualServer>() { public Object run(VirtualServer avs) throws PropertyVetoException { String DELIM = ","; String lss = avs.getNetworkListeners(); boolean listenerShouldBeAdded = true; if (lss == null || lss.length() == 0) { lss = listenerId; //the only listener in the list } else if (!lss.contains(listenerId)) { //listener does not already exist if (!lss.endsWith(DELIM)) { lss += DELIM; } lss += listenerId; } else { //listener already exists in the list, do nothing listenerShouldBeAdded = false; } if (listenerShouldBeAdded) { avs.setNetworkListeners(lss); } return avs; } }, vs); } private boolean createNetworkListener(NetworkConfig networkConfig, final boolean newTransport, final ThreadPool threadPool) throws TransactionFailure { ConfigSupport.apply(new SingleConfigCode<NetworkListeners>() { public Object run(NetworkListeners listenersParam) throws TransactionFailure { final NetworkListener newListener = listenersParam.createChild(NetworkListener.class); newListener.setName(listenerId); newListener.setAddress(listenerAddress); newListener.setPort(listenerPort); newListener.setTransport(newTransport ? listenerId : DEFAULT_TRANSPORT); newListener.setProtocol(listenerId); newListener.setThreadPool(threadPool.getName()); newListener.setEnabled(enabled.toString()); listenersParam.getNetworkListener().add(newListener); return newListener; } }, networkConfig.getNetworkListeners()); services.<Transactions>getService(Transactions.class).waitForDrain(); return true; } private boolean verifyDefaultVirtualServer(ActionReport report) { if (defaultVS == null && defaultVirtualServer == null) { report.setMessage(rb.getString(CREATE_HTTP_LISTENER_VS_BLANK)); report.setActionExitCode(ActionReport.ExitCode.FAILURE); return false; } if (defaultVS != null && defaultVirtualServer != null && !defaultVS.equals(defaultVirtualServer)) { report.setMessage(rb.getString(CREATE_HTTP_LISTENER_VS_BOTH_PARAMS)); report.setActionExitCode(ActionReport.ExitCode.FAILURE); return false; } else if (defaultVirtualServer == null && defaultVS != null) { defaultVirtualServer = defaultVS; } //no need to check the other things (e.g. id) for uniqueness // ensure that the specified default virtual server exists if (!defaultVirtualServerExists()) { report.setMessage(MessageFormat.format(rb.getString(CREATE_HTTP_LISTENER_VS_NOTEXISTS), defaultVirtualServer)); report.setActionExitCode(ActionReport.ExitCode.FAILURE); return false; } return true; } private boolean verifyUniquePort(ActionReport report, NetworkConfig networkConfig) { //check port uniqueness, only for same address for (NetworkListener listener : networkConfig.getNetworkListeners() .getNetworkListener()) { if (listener.getPort().trim().equals(listenerPort) && listener.getAddress().trim().equals(listenerAddress)) { String def = "Port is already taken by another listener, choose another port."; String msg = MessageFormat.format(rb.getString(PORT_IN_USE), listenerPort, listenerAddress); report.setMessage(msg); report.setActionExitCode(ActionReport.ExitCode.FAILURE); return false; } } return true; } private boolean validateInputs(final ActionReport report) { if(acceptorThreads != null && Integer.parseInt(acceptorThreads) < 1) { report.setMessage(MessageFormat.format(rb.getString(ACCEPTOR_THREADS_TOO_LOW), listenerId)); report.setActionExitCode(ActionReport.ExitCode.FAILURE); return false; } return true; } private boolean verifyUniqueName(ActionReport report, NetworkConfig networkConfig) { // ensure we don't already have one of this name for (NetworkListener listener : networkConfig.getNetworkListeners().getNetworkListener()) { if (listener.getName().equals(listenerId)) { report.setMessage(MessageFormat.format(rb.getString(CREATE_HTTP_LISTENER_DUPLICATE), listenerId)); report.setActionExitCode(ActionReport.ExitCode.FAILURE); return false; } } return true; } private ThreadPool getThreadPool(NetworkConfig config) { final List<ThreadPool> pools = config.getParent(Config.class).getThreadPools().getThreadPool(); ThreadPool target = null; for (ThreadPool pool : pools) { if ("http-thread-pool".equals(pool.getName())) { target = pool; } } if (target == null && !pools.isEmpty()) { target = pools.get(0); } return target; } private boolean createOrGetTransport(final AdminCommandContext context) throws TransactionFailure { boolean newTransport = false; for (Transport t : networkConfig.getTransports().getTransport()) { if (!t.getName().equals(DEFAULT_TRANSPORT)) { newTransport = true; } } if (newTransport) { final CreateTransport command = (CreateTransport) runner .getCommand("create-transport", context.getActionReport(), context.getLogger()); command.transportName = listenerId; command.acceptorThreads = acceptorThreads; command.target = target; command.execute(context); checkProgress(context); newTransport = true; } return newTransport; } private boolean createProtocol(final AdminCommandContext context) throws TransactionFailure { final CreateProtocol command = (CreateProtocol) runner .getCommand("create-protocol", context.getActionReport(), context.getLogger()); command.protocolName = listenerId; command.securityEnabled = securityEnabled; command.target = target; command.execute(context); checkProgress(context); return true; } private boolean createHttp(final AdminCommandContext context) throws TransactionFailure { final CreateHttp command = (CreateHttp) runner .getCommand("create-http", context.getActionReport(), context.getLogger()); command.protocolName = listenerId; command.defaultVirtualServer = defaultVirtualServer; command.xPoweredBy = xPoweredBy; command.serverHeader = serverHeader; command.serverName = serverName; command.target = target; command.execute(context); checkProgress(context); return true; } private void checkProgress(final AdminCommandContext context) throws TransactionFailure { if(context.getActionReport().getActionExitCode() != ExitCode.SUCCESS) { throw new TransactionFailure(context.getActionReport().getMessage()); } } private boolean deleteProtocol(final AdminCommandContext context) { final DeleteProtocol command = (DeleteProtocol) runner .getCommand("delete-protocol", context.getActionReport(), context.getLogger()); command.protocolName = listenerId; command.target = target; command.execute(context); return true; } private boolean deleteTransport(final AdminCommandContext context) { final DeleteTransport command = (DeleteTransport) runner .getCommand("delete-transport", context.getActionReport(), context.getLogger()); command.transportName = listenerId; command.target = target; command.execute(context); return true; } private boolean deleteListener(final AdminCommandContext context) { final DeleteNetworkListener command = (DeleteNetworkListener) runner .getCommand("delete-network-listener", context.getActionReport(), context.getLogger()); command.networkListenerName = listenerId; command.target = target; command.execute(context); return true; } private boolean defaultVirtualServerExists() { return (defaultVirtualServer != null) && ( config.getHttpService().getVirtualServerByName(defaultVirtualServer) != null); } }