0001: /*
0002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
0003: *
0004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
0005: *
0006: * The contents of this file are subject to the terms of either the GNU
0007: * General Public License Version 2 only ("GPL") or the Common
0008: * Development and Distribution License("CDDL") (collectively, the
0009: * "License"). You may not use this file except in compliance with the
0010: * License. You can obtain a copy of the License at
0011: * http://www.netbeans.org/cddl-gplv2.html
0012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
0013: * specific language governing permissions and limitations under the
0014: * License. When distributing the software, include this License Header
0015: * Notice in each file and include the License file at
0016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
0017: * particular file as subject to the "Classpath" exception as provided
0018: * by Sun in the GPL Version 2 section of the License file that
0019: * accompanied this code. If applicable, add the following below the
0020: * License Header, with the fields enclosed by brackets [] replaced by
0021: * your own identifying information:
0022: * "Portions Copyrighted [year] [name of copyright owner]"
0023: *
0024: * Contributor(s):
0025: *
0026: * The Original Software is NetBeans. The Initial Developer of the Original
0027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
0028: * Microsystems, Inc. All Rights Reserved.
0029: *
0030: * If you wish your version of this file to be governed by only the CDDL
0031: * or only the GPL Version 2, indicate your decision by adding
0032: * "[Contributor] elects to include this software in this distribution
0033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
0034: * single choice of license, a recipient has the option to distribute
0035: * your version of this file under either the CDDL, the GPL Version 2 or
0036: * to extend the choice of license to its licensees as provided above.
0037: * However, if you add GPL Version 2 code and therefore, elected the GPL
0038: * Version 2 license, then the option applies only if the new code is
0039: * made subject to such option by the copyright holder.
0040: */
0041:
0042: package org.netbeans.modules.tomcat5.ide;
0043:
0044: import java.io.BufferedInputStream;
0045: import java.io.BufferedOutputStream;
0046: import java.io.File;
0047: import java.io.FileInputStream;
0048: import java.io.FileNotFoundException;
0049: import java.io.FileOutputStream;
0050: import java.io.IOException;
0051: import java.io.InputStream;
0052: import java.util.Collections;
0053: import java.util.HashMap;
0054: import java.util.Map;
0055: import java.util.logging.Level;
0056: import java.util.logging.Logger;
0057: import javax.enterprise.deploy.shared.ActionType;
0058: import javax.enterprise.deploy.shared.CommandType;
0059: import javax.enterprise.deploy.shared.StateType;
0060: import javax.enterprise.deploy.spi.DeploymentManager;
0061: import javax.enterprise.deploy.spi.Target;
0062: import javax.enterprise.deploy.spi.TargetModuleID;
0063: import javax.enterprise.deploy.spi.exceptions.OperationUnsupportedException;
0064: import javax.enterprise.deploy.spi.status.ClientConfiguration;
0065: import javax.enterprise.deploy.spi.status.DeploymentStatus;
0066: import javax.enterprise.deploy.spi.status.ProgressListener;
0067: import javax.enterprise.deploy.spi.status.ProgressObject;
0068: import org.netbeans.api.java.platform.JavaPlatform;
0069: import org.netbeans.modules.tomcat5.progress.ProgressEventSupport;
0070: import org.netbeans.modules.tomcat5.progress.Status;
0071: import org.netbeans.modules.tomcat5.util.LogManager;
0072: import org.netbeans.modules.tomcat5.util.Utils;
0073: import org.openide.execution.NbProcessDescriptor;
0074: import org.openide.util.NbBundle;
0075: import org.openide.util.RequestProcessor;
0076: import org.netbeans.modules.j2ee.deployment.plugins.api.ServerDebugInfo;
0077: import org.netbeans.modules.j2ee.deployment.plugins.spi.StartServer;
0078: import org.netbeans.modules.j2ee.deployment.profiler.api.ProfilerServerSettings;
0079: import org.netbeans.modules.j2ee.deployment.profiler.api.ProfilerSupport;
0080: import org.netbeans.modules.tomcat5.TomcatFactory;
0081: import org.netbeans.modules.tomcat5.TomcatManager;
0082: import org.netbeans.modules.tomcat5.TomcatManager.TomcatVersion;
0083: import org.netbeans.modules.tomcat5.util.EditableProperties;
0084: import org.netbeans.modules.tomcat5.util.TomcatProperties;
0085: import org.openide.filesystems.FileObject;
0086: import org.openide.filesystems.FileUtil;
0087: import org.openide.util.Utilities;
0088: import org.xml.sax.SAXException;
0089:
0090: /** Extension to Deployment API that enables starting of Tomcat.
0091: *
0092: * @author Radim Kubacki, Pavel Buzek
0093: */
0094: public final class StartTomcat extends StartServer implements
0095: ProgressObject {
0096:
0097: public static final String CATALINA_BAT = "catalina.bat"; // NOI18N
0098: public static final String CATALINA_SH = "catalina.sh"; // NOI18N
0099: public static final String CATALINA_50_BAT = "catalina.50.bat"; // NOI18N
0100: public static final String CATALINA_50_SH = "catalina.50.sh"; // NOI18N
0101:
0102: public static final String SETCLASSPATH_BAT = "setclasspath.bat"; // NOI18N
0103: public static final String SETCLASSPATH_SH = "setclasspath.sh"; // NOI18N
0104:
0105: public static final String TAG_CATALINA_HOME = "catalina_home"; // NOI18N
0106: public static final String TAG_CATALINA_BASE = "catalina_base"; // NOI18N
0107:
0108: public static final String TAG_JPDA = "jpda"; // NOI18N
0109: public static final String TAG_JPDA_STARTUP = "jpda_startup"; // NOI18N
0110:
0111: /** Startup command tag. */
0112: public static final String TAG_EXEC_CMD = "catalina"; // NOI18N
0113: public static final String TAG_EXEC_STARTUP = "exec_startup"; // NOI18N
0114: public static final String TAG_EXEC_SHUTDOWN = "exec_shutdown"; // NOI18N
0115: public static final String TAG_SECURITY_OPT = "security_option"; //NOI18N
0116: public static final String TAG_FORCE_OPT = "force_option"; //NOI18N
0117:
0118: /** Debug startup/shutdown tag */
0119: public static final String TAG_DEBUG_CMD = "catalina"; // NOI18N
0120:
0121: /** Normal mode */
0122: private static final int MODE_RUN = 0;
0123: /** Debug mode */
0124: private static final int MODE_DEBUG = 1;
0125: /** Profile mode */
0126: private static final int MODE_PROFILE = 2;
0127:
0128: /** For how long should we keep trying to get response from the server. */
0129: private static final long TIMEOUT_DELAY = 180000;
0130:
0131: private static final Logger LOGGER = Logger
0132: .getLogger(StartTomcat.class.getName());
0133:
0134: private TomcatManager tm;
0135:
0136: private ProgressEventSupport pes;
0137: private int currentServerPort; // current server port Tomcat is running on
0138:
0139: private static Map isDebugModeUri = Collections
0140: .synchronizedMap((Map) new HashMap(2, 1));
0141:
0142: public StartTomcat(DeploymentManager manager) {
0143: assert manager instanceof TomcatManager : "Illegal DeploymentManager instance: "
0144: + manager.getClass().getName(); // NIO18N
0145: tm = (TomcatManager) manager;
0146: tm.setStartTomcat(this );
0147: pes = new ProgressEventSupport(this );
0148: currentServerPort = tm.getServerPort();
0149: }
0150:
0151: public boolean supportsStartDeploymentManager() {
0152: return true;
0153: }
0154:
0155: public boolean supportsStartProfiling(Target target) {
0156: return true;
0157: }
0158:
0159: /** Start Tomcat server if the TomcatManager is not connected.
0160: */
0161: public ProgressObject startDeploymentManager() {
0162: LOGGER.log(Level.FINE,
0163: "StartTomcat.startDeploymentManager called on " + tm); // NOI18N
0164: pes.fireHandleProgressEvent(null, new Status(
0165: ActionType.EXECUTE, CommandType.START, "",
0166: StateType.RUNNING));
0167: RequestProcessor.getDefault().post(
0168: new StartRunnable(MODE_RUN, CommandType.START, null),
0169: 0, Thread.NORM_PRIORITY);
0170: isDebugModeUri.remove(tm.getUri());
0171: return this ;
0172: }
0173:
0174: /**
0175: * Returns true if the admin server is also a target server (share the same vm).
0176: * Start/stopping/debug apply to both servers.
0177: * @return true when admin is also target server
0178: */
0179: public boolean isAlsoTargetServer(Target target) {
0180: return true;
0181: }
0182:
0183: /**
0184: * Returns true if the admin server should be started before configure.
0185: */
0186: public boolean needsStartForConfigure() {
0187: return false;
0188: }
0189:
0190: /**
0191: * Returns true if the admin server should be started before asking for
0192: * target list.
0193: */
0194: public boolean needsStartForTargetList() {
0195: return false;
0196: }
0197:
0198: /**
0199: * Returns true if the admin server should be started before admininistrative configuration.
0200: */
0201: public boolean needsStartForAdminConfig() {
0202: return false;
0203: }
0204:
0205: public boolean needsRestart(Target target) {
0206: return tm.getNeedsRestart();
0207: }
0208:
0209: /**
0210: * Returns true if this admin server is running.
0211: */
0212: public boolean isRunning() {
0213: return tm.isRunning(true);
0214: }
0215:
0216: /**
0217: * Returns true if this target is in debug mode.
0218: */
0219: public boolean isDebuggable(Target target) {
0220: if (!isDebugModeUri.containsKey(tm.getUri())) {
0221: return false;
0222: }
0223: if (!isRunning()) {
0224: isDebugModeUri.remove(tm.getUri());
0225: return false;
0226: }
0227: return true;
0228: }
0229:
0230: /**
0231: * Stops the admin server. The DeploymentManager object will be disconnected.
0232: * All diagnostic should be communicated through ServerProgres with no
0233: * exceptions thrown.
0234: * @return ServerProgress object used to monitor start server progress
0235: */
0236: public ProgressObject stopDeploymentManager() {
0237: LOGGER.log(Level.FINE,
0238: "StartTomcat.stopDeploymentManager called on " + tm); // NOI18N
0239: pes.fireHandleProgressEvent(null, new Status(
0240: ActionType.EXECUTE, CommandType.STOP, "",
0241: StateType.RUNNING));
0242: RequestProcessor.getDefault().post(
0243: new StartRunnable(MODE_RUN, CommandType.STOP, null), 0,
0244: Thread.NORM_PRIORITY);
0245: isDebugModeUri.remove(tm.getUri());
0246: return this ;
0247: }
0248:
0249: /**
0250: * Start or restart the target in debug mode.
0251: * If target is also domain admin, the amdin is restarted in debug mode.
0252: * All diagnostic should be communicated through ServerProgres with no exceptions thrown.
0253: * @param target the target server
0254: * @return ServerProgress object to monitor progress on start operation
0255: */
0256: public ProgressObject startDebugging(Target target) {
0257: LOGGER.log(Level.FINE, "StartTomcat.startDebugging called on "
0258: + tm); // NOI18N
0259: pes.fireHandleProgressEvent(null, new Status(
0260: ActionType.EXECUTE, CommandType.START, "",
0261: StateType.RUNNING));
0262: RequestProcessor.getDefault().post(
0263: new StartRunnable(MODE_DEBUG, CommandType.START, null),
0264: 0, Thread.NORM_PRIORITY);
0265: return this ;
0266: }
0267:
0268: public ProgressObject startProfiling(Target target,
0269: ProfilerServerSettings settings) {
0270: LOGGER.log(Level.FINE, "StartTomcat.startProfiling called on "
0271: + tm); // NOI18N
0272: pes.fireHandleProgressEvent(null, new Status(
0273: ActionType.EXECUTE, CommandType.START, "", // NOI18N
0274: StateType.RUNNING));
0275: RequestProcessor.getDefault().post(
0276: new StartRunnable(MODE_PROFILE, CommandType.START,
0277: settings), 0, Thread.NORM_PRIORITY);
0278: return this ;
0279: }
0280:
0281: public ServerDebugInfo getDebugInfo(Target target) {
0282: ServerDebugInfo sdi;
0283: TomcatProperties tp = tm.getTomcatProperties();
0284: if (tp.getDebugType().toLowerCase().indexOf("socket") != -1) { // NOI18N
0285: sdi = new ServerDebugInfo("localhost", tp.getDebugPort()); // NOI18N
0286: } else {
0287: sdi = new ServerDebugInfo("localhost", tp.getSharedMem()); // NOI18N
0288: }
0289: return sdi;
0290: }
0291:
0292: private class StartRunnable implements Runnable {
0293:
0294: private int mode;
0295: private CommandType command = CommandType.START;
0296: private ProfilerServerSettings profilerSettings;
0297:
0298: public StartRunnable(int mode, CommandType command,
0299: ProfilerServerSettings profilerSettings) {
0300: this .mode = mode;
0301: this .command = command;
0302: this .profilerSettings = profilerSettings;
0303: }
0304:
0305: public synchronized void run() {
0306: // PENDING check whether is runs or not
0307: TomcatProperties tp = tm.getTomcatProperties();
0308: File homeDir = tp.getCatalinaHome();
0309: if (homeDir == null || !homeDir.exists()) {
0310: fireCmdExecProgressEvent(
0311: command == CommandType.START ? "MSG_NoHomeDirStart"
0312: : "MSG_NoHomeDirStop", StateType.FAILED);
0313: return;
0314: }
0315: File baseDir = tp.getCatalinaBase();
0316: if (baseDir == null) {
0317: baseDir = homeDir;
0318: } else {
0319: if (baseDir != null) {
0320: String[] files = baseDir.list();
0321: if (files == null || files.length == 0) {
0322: baseDir = tm.createBaseDir(baseDir, homeDir);
0323: }
0324: }
0325: if (baseDir == null) {
0326: fireCmdExecProgressEvent(
0327: command == CommandType.START ? "MSG_NoBaseDirStart"
0328: : "MSG_NoBaseDirStop",
0329: StateType.FAILED);
0330: return;
0331: }
0332: }
0333:
0334: // check whether the startup script - catalina.sh/bat exists
0335: File startupScript = getStartupScript();
0336: if (!startupScript.exists()) {
0337: final String MSG = NbBundle
0338: .getMessage(
0339: StartTomcat.class,
0340: command == CommandType.START ? "MSG_StartFailedNoStartScript"
0341: : "MSG_StopFailedNoStartScript",
0342: startupScript.getAbsolutePath());
0343: pes.fireHandleProgressEvent(null, new Status(
0344: ActionType.EXECUTE, command, MSG,
0345: StateType.FAILED));
0346: return;
0347: }
0348:
0349: // install the monitor
0350: if (command == CommandType.START) {
0351: try {
0352: MonitorSupport.synchronizeMonitorWithFlag(tm, true);
0353: } catch (IOException e) {
0354: if (MonitorSupport.getMonitorFlag(tm)) {
0355: // tomcat has been started with monitor enabled
0356: MonitorSupport.setMonitorFlag(tm, false);
0357: fireCmdExecProgressEvent(
0358: tm.isTomcat60() ? "MSG_enableMonitorSupportErr60"
0359: : "MSG_enableMonitorSupportErr",
0360: StateType.FAILED);
0361: } else {
0362: // tomcat has been started with monitor disabled
0363: fireCmdExecProgressEvent(
0364: "MSG_disableMonitorSupportErr",
0365: StateType.FAILED);
0366: }
0367: LOGGER.log(Level.INFO, null, e);
0368: return;
0369: } catch (SAXException e) {
0370: // fault, but not a critical one
0371: LOGGER.log(Level.INFO, null, e);
0372: }
0373: try {
0374: DebugSupport.allowDebugging(tm);
0375: } catch (IOException e) {
0376: // fault, but not a critical one
0377: LOGGER.log(Level.INFO, null, e);
0378: } catch (SAXException e) {
0379: // fault, but not a critical one
0380: LOGGER.log(Level.INFO, null, e);
0381: }
0382: }
0383:
0384: currentServerPort = tm.getServerPort(); // remember the server port
0385: int shutdownPort = tm.getShutdownPort();
0386:
0387: if (command == CommandType.START) {
0388: // check whether the server ports are free
0389: if (!Utils.isPortFree(currentServerPort)) {
0390: fireCmdExecProgressEvent(
0391: "MSG_StartFailedServerPortInUse", String
0392: .valueOf(currentServerPort),
0393: StateType.FAILED);
0394: return;
0395: }
0396: if (!Utils.isPortFree(shutdownPort)) {
0397: fireCmdExecProgressEvent(
0398: "MSG_StartFailedShutdownPortInUse", String
0399: .valueOf(shutdownPort),
0400: StateType.FAILED);
0401: return;
0402: }
0403: }
0404:
0405: // set the JAVA_OPTS value
0406: String javaOpts = tp.getJavaOpts();
0407: // use the IDE proxy settings if the 'use proxy' checkbox is selected
0408: // do not override a property if it was set manually by the user
0409: if (tp.getProxyEnabled()) {
0410: StringBuilder sb = new StringBuilder(javaOpts);
0411: final String[] PROXY_PROPS = { "http.proxyHost", // NOI18N
0412: "http.proxyPort", // NOI18N
0413: "http.nonProxyHosts", // NOI18N
0414: "https.proxyHost", // NOI18N
0415: "https.proxyPort", // NOI18N
0416: };
0417: boolean isWindows = Utilities.isWindows();
0418: for (String prop : PROXY_PROPS) {
0419: if (javaOpts.indexOf(prop) == -1) {
0420: String value = System.getProperty(prop);
0421: if (value != null) {
0422: if (isWindows
0423: && "http.nonProxyHosts"
0424: .equals(prop)) { // NOI18N
0425: // enclose in double quotes to escape the pipes separating the hosts on windows
0426: value = "\"" + value + "\""; // NOI18N
0427: }
0428: sb.append(" -D").append(prop).append("=")
0429: .append(value); // NOI18N
0430: }
0431: }
0432: }
0433: javaOpts = sb.toString();
0434: }
0435:
0436: JavaPlatform platform = mode == MODE_PROFILE ? profilerSettings
0437: .getJavaPlatform()
0438: : getJavaPlatform();
0439: String jdkVersion = platform.getSpecification()
0440: .getVersion().toString();
0441:
0442: if (tm.isBundledTomcat()) {
0443: // work-arounding problems caused by the compatibility pack when running on 1.5
0444: // ensure that the catalina class loader is set properly
0445: patchCatalinaProperties(tp.getCatalinaDir(), "1.4"
0446: .equals(jdkVersion)); // NOI18N
0447: }
0448:
0449: if ((mode == MODE_DEBUG) && (command == CommandType.START)) {
0450:
0451: NbProcessDescriptor pd = null;
0452: if (tp.getSecManager()) {
0453: pd = defaultDebugStartDesc(TAG_DEBUG_CMD,
0454: TAG_JPDA_STARTUP, TAG_SECURITY_OPT);
0455: } else {
0456: pd = defaultDebugStartDesc(TAG_DEBUG_CMD,
0457: TAG_JPDA_STARTUP);
0458: }
0459: try {
0460: fireCmdExecProgressEvent("MSG_startProcess",
0461: StateType.RUNNING);
0462: Process p = null;
0463:
0464: String address;
0465: String transport;
0466: if (tp.getDebugType().toLowerCase().indexOf(
0467: "socket") != -1) { // NOI18N
0468: transport = "dt_socket"; // NOI18N
0469: address = Integer.toString(tp.getDebugPort());
0470: } else {
0471: transport = "dt_shmem"; // NOI18N
0472: address = tp.getSharedMem();
0473: }
0474: LOGGER.log(Level.FINE, "transport: " + transport); // NOI18N
0475: LOGGER.log(Level.FINE, "address: " + address); // NOI18N
0476: p = pd
0477: .exec(
0478: new TomcatFormat(startupScript,
0479: homeDir),
0480: new String[] {
0481: "JAVA_HOME="
0482: + getJavaHome(platform), // NOI18N
0483: "JRE_HOME=", // NOI18N ensure that JRE_HOME system property won't be used instead of JAVA_HOME
0484: "JAVA_OPTS=" + javaOpts, // NOI18N
0485: "JPDA_TRANSPORT="
0486: + transport, // NOI18N
0487: "JPDA_ADDRESS=" + address, // NOI18N
0488: "CATALINA_HOME="
0489: + homeDir
0490: .getAbsolutePath(), // NOI18N
0491: "CATALINA_BASE="
0492: + baseDir
0493: .getAbsolutePath(), // NOI18N
0494: // this is used in the setclasspath.sb/bat script for work-arounding
0495: // problems caused by the compatibility pack when running on 1.5
0496: "NB_TOMCAT_JDK="
0497: + jdkVersion // NOI18N
0498: }, true, new File(homeDir, "bin") // NOI18N
0499: );
0500: tm.setTomcatProcess(p);
0501: openLogs();
0502: } catch (java.io.IOException ioe) {
0503: LOGGER.log(Level.FINE, null, ioe);
0504: fireCmdExecProgressEvent(
0505: command == CommandType.START ? "MSG_StartFailedIOE"
0506: : "MSG_StopFailedIOE",
0507: startupScript.getAbsolutePath(),
0508: StateType.FAILED);
0509: return;
0510: }
0511: } else if ((mode == MODE_PROFILE)
0512: && (command == CommandType.START)) {
0513: NbProcessDescriptor pd = null;
0514: if (tp.getSecManager()) {
0515: pd = defaultExecDesc(TAG_EXEC_CMD,
0516: TAG_EXEC_STARTUP, TAG_SECURITY_OPT);
0517: } else {
0518: pd = defaultExecDesc(TAG_EXEC_CMD, TAG_EXEC_STARTUP);
0519: }
0520: try {
0521: fireCmdExecProgressEvent(
0522: "MSG_StartingInProfileMode",
0523: StateType.RUNNING);
0524: Process p = null;
0525:
0526: String[] profJvmArgs = profilerSettings
0527: .getJvmArgs();
0528: // TODO solve conflicts between profiler and tomcat vm args
0529: StringBuffer catalinaOpts = new StringBuffer();
0530: for (int i = 0; i < profJvmArgs.length; i++) {
0531: catalinaOpts.append(profJvmArgs[i]).append(" "); // NOI18N
0532: }
0533: String[] defaultEnv = new String[] {
0534: "JAVA_HOME=" + getJavaHome(platform), // NOI18N
0535: "JRE_HOME=", // NOI18N ensure that JRE_HOME system property won't be used instead of JAVA_HOME
0536: "JAVA_OPTS=" + javaOpts, // NOI18N
0537: "CATALINA_OPTS=" + catalinaOpts.toString(), // NOI18N
0538: "CATALINA_HOME="
0539: + homeDir.getAbsolutePath(), // NOI18N
0540: "CATALINA_BASE="
0541: + baseDir.getAbsolutePath(), // NOI18N
0542: // this is used in the setclasspath.sb/bat script for work-arounding
0543: // problems caused by the compatibility pack when running on 1.5
0544: "NB_TOMCAT_JDK=" + jdkVersion // NOI18N
0545: };
0546: String[] profEnv = profilerSettings.getEnv();
0547: // merge Tomcat and profiler env properties
0548: String[] envp = new String[defaultEnv.length
0549: + profEnv.length];
0550: System.arraycopy(profEnv, 0, envp, 0,
0551: profEnv.length);
0552: System.arraycopy(defaultEnv, 0, envp,
0553: profEnv.length, defaultEnv.length);
0554: p = pd.exec(
0555: new TomcatFormat(startupScript, homeDir),
0556: envp, true, new File(homeDir, "bin") // NOI18N
0557: );
0558: tm.setTomcatProcess(p);
0559: openLogs();
0560: } catch (java.io.IOException ioe) {
0561: LOGGER.log(Level.FINE, null, ioe);
0562: fireCmdExecProgressEvent(
0563: command == CommandType.START ? "MSG_StartFailedIOE"
0564: : "MSG_StopFailedIOE",
0565: startupScript.getAbsolutePath(),
0566: StateType.FAILED);
0567: return;
0568: }
0569: } else {
0570: NbProcessDescriptor pd = null;
0571: if (command == CommandType.START) {
0572: if (tp.getSecManager()) {
0573: pd = defaultExecDesc(TAG_EXEC_CMD,
0574: TAG_EXEC_STARTUP, TAG_SECURITY_OPT);
0575: } else {
0576: pd = defaultExecDesc(TAG_EXEC_CMD,
0577: TAG_EXEC_STARTUP);
0578: }
0579: } else {
0580: if (tp.getForceStop() && Utilities.isUnix()) {
0581: pd = defaultExecDesc(TAG_EXEC_CMD,
0582: TAG_EXEC_SHUTDOWN, TAG_FORCE_OPT);
0583: } else {
0584: pd = defaultExecDesc(TAG_EXEC_CMD,
0585: TAG_EXEC_SHUTDOWN);
0586: }
0587: }
0588: try {
0589: fireCmdExecProgressEvent(
0590: command == CommandType.START ? "MSG_startProcess"
0591: : "MSG_stopProcess",
0592: StateType.RUNNING);
0593: Process p = pd.exec(new TomcatFormat(startupScript,
0594: homeDir), new String[] {
0595: "JAVA_HOME=" + getJavaHome(platform), // NOI18N
0596: "JRE_HOME=", // NOI18N ensure that JRE_HOME system property won't be used instead of JAVA_HOME
0597: "JAVA_OPTS=" + javaOpts, // NOI18N
0598: "CATALINA_HOME="
0599: + homeDir.getAbsolutePath(), // NOI18N
0600: "CATALINA_BASE="
0601: + baseDir.getAbsolutePath(), // NOI18N
0602: // this is used in the setclasspath.sb/bat script for work-arounding
0603: // problems caused by the compatibility pack when running on 1.5
0604: "NB_TOMCAT_JDK=" + jdkVersion // NOI18N
0605: }, true, new File(homeDir, "bin"));
0606: if (command == CommandType.START) {
0607: tm.setTomcatProcess(p);
0608: openLogs();
0609: } else {
0610: // #58554 workaround
0611: RequestProcessor.getDefault().post(
0612: new StreamConsumer(p.getInputStream()),
0613: 0, Thread.MIN_PRIORITY);
0614: RequestProcessor.getDefault().post(
0615: new StreamConsumer(p.getErrorStream()),
0616: 0, Thread.MIN_PRIORITY);
0617: }
0618: } catch (java.io.IOException ioe) {
0619: LOGGER.log(Level.FINE, null, ioe); // NOI18N
0620: fireCmdExecProgressEvent(
0621: command == CommandType.START ? "MSG_StartFailedIOE"
0622: : "MSG_StopFailedIOE",
0623: startupScript.getAbsolutePath(),
0624: StateType.FAILED);
0625: return;
0626: }
0627: }
0628: fireCmdExecProgressEvent("MSG_waiting", StateType.RUNNING);
0629: if (hasCommandSucceeded()) {
0630: if (command == CommandType.START) {
0631: // reset the need restart flag
0632: tm.setNeedsRestart(false);
0633: if (mode == MODE_DEBUG) {
0634: isDebugModeUri.put(tm.getUri(), new Object());
0635: }
0636: }
0637: fireCmdExecProgressEvent(
0638: command == CommandType.START ? "MSG_Started"
0639: : "MSG_Stopped", StateType.COMPLETED);
0640: } else {
0641: fireCmdExecProgressEvent(
0642: command == CommandType.START ? "MSG_StartFailed"
0643: : "MSG_StopFailed", StateType.FAILED);
0644: }
0645: }
0646:
0647: /** Open JULI log and server output */
0648: private void openLogs() {
0649: LogManager logManager = tm.logManager();
0650: if (logManager.hasJuliLog()) {
0651: logManager.openJuliLog();
0652: }
0653: logManager.closeServerLog();
0654: logManager.openServerLog();
0655: }
0656:
0657: /**
0658: * Fires command progress event of action type <code>ActionType.EXECUTE</code>.
0659: *
0660: * @param resName event status message from the bundle, specified by the
0661: * resource name.
0662: * @param stateType event state type.
0663: */
0664: private void fireCmdExecProgressEvent(String resName,
0665: StateType stateType) {
0666: String msg = NbBundle
0667: .getMessage(StartTomcat.class, resName);
0668: pes.fireHandleProgressEvent(null, new Status(
0669: ActionType.EXECUTE, command, msg, stateType));
0670: }
0671:
0672: /**
0673: * Fires command progress event of action type <code>ActionType.EXECUTE</code>.
0674: *
0675: * @param resName event status message from the bundle, specified by the
0676: * resource name.
0677: * @param arg1 the argument to use when formating the message
0678: * @param stateType event state type.
0679: */
0680: private void fireCmdExecProgressEvent(String resName,
0681: Object arg1, StateType stateType) {
0682: String msg = NbBundle.getMessage(StartTomcat.class,
0683: resName, arg1);
0684: pes.fireHandleProgressEvent(null, new Status(
0685: ActionType.EXECUTE, command, msg, stateType));
0686: }
0687:
0688: /**
0689: * Try to get response from the server, whether the START/STOP command has
0690: * succeeded.
0691: *
0692: * @return <code>true</code> if START/STOP command completion was verified,
0693: * <code>false</code> if time-out ran out.
0694: */
0695: private boolean hasCommandSucceeded() {
0696: long timeout = System.currentTimeMillis() + TIMEOUT_DELAY;
0697: while (true) {
0698: boolean isRunning = isRunning();
0699: if (command == CommandType.START) {
0700: if (isRunning) {
0701: return true;
0702: }
0703: if (isStopped()) {
0704: // Tomcat failed to start, process is finished
0705: return false;
0706: }
0707: if (mode == MODE_PROFILE) {
0708: int state = ProfilerSupport.getState();
0709: if (state == ProfilerSupport.STATE_BLOCKING
0710: || state == ProfilerSupport.STATE_RUNNING
0711: || state == ProfilerSupport.STATE_PROFILING) {
0712: return true;
0713: } else if (state == ProfilerSupport.STATE_INACTIVE) {
0714: return false;
0715: }
0716: }
0717: }
0718: if (command == CommandType.STOP) {
0719: if (isStopped()) {
0720: // give server a few secs to finish its shutdown, not responding
0721: // does not necessarily mean its is still not running
0722: try {
0723: Thread.sleep(2000);
0724: } catch (InterruptedException ie) {
0725: }
0726: return true;
0727: }
0728: }
0729: // if time-out ran out, suppose command failed
0730: if (System.currentTimeMillis() > timeout) {
0731: return false;
0732: }
0733: try {
0734: Thread.sleep(1000); // take a nap before next retry
0735: } catch (InterruptedException ie) {
0736: }
0737: }
0738: }
0739: }
0740:
0741: /** Return true if the server is stopped. If the server was started from within
0742: * the IDE, determin the server state from the process exit code, otherwise try
0743: * to ping it. */
0744: private boolean isStopped() {
0745: Process proc = tm.getTomcatProcess();
0746: if (proc != null) {
0747: try {
0748: proc.exitValue();
0749: // process is stopped
0750: return true;
0751: } catch (IllegalThreadStateException e) {
0752: // process is still running
0753: return false;
0754: }
0755: } else {
0756: int timeout = tm.getTomcatProperties()
0757: .getRunningCheckTimeout();
0758: return !Utils.pingTomcat(tm.getServerPort(), timeout);
0759: }
0760: }
0761:
0762: /** This implementation does nothing.
0763: * Target is already started when Tomcat starts.
0764: */
0765: public ProgressObject startServer(Target target) {
0766: return null;
0767: }
0768:
0769: public boolean supportsStartDebugging(Target target) {
0770: return true;
0771: }
0772:
0773: public ClientConfiguration getClientConfiguration(
0774: TargetModuleID targetModuleID) {
0775: return null;
0776: }
0777:
0778: public DeploymentStatus getDeploymentStatus() {
0779: return pes.getDeploymentStatus();
0780: }
0781:
0782: public TargetModuleID[] getResultTargetModuleIDs() {
0783: return new TargetModuleID[] {};
0784: }
0785:
0786: public boolean isCancelSupported() {
0787: return false;
0788: }
0789:
0790: public void cancel() throws OperationUnsupportedException {
0791: throw new OperationUnsupportedException("");
0792: }
0793:
0794: public boolean isStopSupported() {
0795: return false;
0796: }
0797:
0798: public void stop() throws OperationUnsupportedException {
0799: throw new OperationUnsupportedException("");
0800: }
0801:
0802: public void addProgressListener(ProgressListener pl) {
0803: pes.addProgressListener(pl);
0804: }
0805:
0806: public void removeProgressListener(ProgressListener pl) {
0807: pes.removeProgressListener(pl);
0808: }
0809:
0810: public String toString() {
0811: return "StartTomcat [" + tm + "]"; // NOI18N
0812: }
0813:
0814: public int getCurrentServerPort() {
0815: return currentServerPort;
0816: }
0817:
0818: // private helper methods -------------------------------------------------
0819:
0820: private static NbProcessDescriptor defaultExecDesc(String command,
0821: String argCommand, String option) {
0822: return new NbProcessDescriptor("{" + command + "}", // NOI18N
0823: "{" + argCommand + "}" + " {" + option + "}", // NOI18N
0824: NbBundle.getMessage(StartTomcat.class,
0825: "MSG_TomcatExecutionCommand"));
0826: }
0827:
0828: private static NbProcessDescriptor defaultExecDesc(String command,
0829: String argCommand) {
0830: return new NbProcessDescriptor("{" + command + "}", // NOI18N
0831: "{" + argCommand + "}", // NOI18N
0832: NbBundle.getMessage(StartTomcat.class,
0833: "MSG_TomcatExecutionCommand"));
0834: }
0835:
0836: private static NbProcessDescriptor defaultDebugStartDesc(
0837: String command, String jpdaCommand, String option) {
0838: return new NbProcessDescriptor("{" + command + "}", // NOI18N
0839: "{" + TAG_JPDA + "}" + " {" + jpdaCommand + "}" + " {"
0840: + option + "}", // NOI18N
0841: NbBundle.getMessage(StartTomcat.class,
0842: "MSG_TomcatExecutionCommand"));
0843: }
0844:
0845: private static NbProcessDescriptor defaultDebugStartDesc(
0846: String command, String jpdaCommand) {
0847: return new NbProcessDescriptor("{" + command + "}", // NOI18N
0848: "{" + TAG_JPDA + "}" + " {" + jpdaCommand + "}", // NOI18N
0849: NbBundle.getMessage(StartTomcat.class,
0850: "MSG_TomcatExecutionCommand"));
0851: }
0852:
0853: private String getJavaHome(JavaPlatform platform) {
0854: FileObject fo = (FileObject) platform.getInstallFolders()
0855: .iterator().next();
0856: return FileUtil.toFile(fo).getAbsolutePath();
0857: }
0858:
0859: /** Return the catalina startup script file. */
0860: private File getStartupScript() {
0861: TomcatProperties tp = tm.getTomcatProperties();
0862: if (tp.getCustomScript()) {
0863: return new File(tp.getScriptPath());
0864: }
0865: // use catalina50.sh/bat for Tomcat 5.0 on jdk1.5
0866: if (tm.getTomcatVersion() == TomcatVersion.TOMCAT_50
0867: && "1.5".equals(getJavaPlatform().getSpecification()
0868: .getVersion().toString())) { // NOI18N
0869: String startupScript = Utilities.isWindows() ? CATALINA_50_BAT
0870: : CATALINA_50_SH;
0871: File scriptFile = new File(tp.getCatalinaHome(), "/bin/"
0872: + startupScript); // NOI18N
0873: if (scriptFile.exists()) {
0874: return scriptFile;
0875: }
0876: }
0877: String startupScript = Utilities.isWindows() ? CATALINA_BAT
0878: : CATALINA_SH;
0879: return new File(tp.getCatalinaHome(), "/bin/" + startupScript); // NOI18N
0880: }
0881:
0882: private JavaPlatform getJavaPlatform() {
0883: return tm.getTomcatProperties().getJavaPlatform();
0884: }
0885:
0886: /** enable/disable ${catalina.home}/common/endorsed/*.jar in the catalina class
0887: loader in the catalina.properties file */
0888: private void patchCatalinaProperties(File catalinaBase,
0889: final boolean endorsedEnabled) {
0890: File catalinaProp = new File(catalinaBase,
0891: "conf/catalina.properties"); // NOI18N
0892: if (!catalinaProp.exists()) {
0893: return; // catalina.properties does not exist, can't do anything
0894: }
0895: EditableProperties props = new EditableProperties();
0896: try {
0897: InputStream is = new BufferedInputStream(
0898: new FileInputStream(catalinaProp));
0899: try {
0900: props.load(is);
0901: String COMMON_LOADER = "common.loader"; // NOI18N
0902: String commonLoader = props.getProperty(COMMON_LOADER);
0903: if (commonLoader != null) {
0904: String COMMON_ENDORSED = "${catalina.home}/common/endorsed/*.jar"; // NOI18N
0905: int idx = commonLoader.indexOf(COMMON_ENDORSED);
0906: if (endorsedEnabled) {
0907: if (idx == -1) { // common/endorsed/*.jar is not present, add it
0908: String COMMON_LIB = "${catalina.home}/"
0909: + tm.libFolder() + "/*.jar"; // NOI18N
0910: int commonLibIdx = commonLoader
0911: .indexOf(COMMON_LIB);
0912: StringBuffer sb = new StringBuffer(
0913: commonLibIdx == -1 ? commonLoader
0914: : commonLoader.substring(0,
0915: commonLibIdx));
0916: if (commonLibIdx != -1) {
0917: sb
0918: .append(COMMON_ENDORSED)
0919: .append(',')
0920: .append(
0921: commonLoader
0922: .substring(commonLibIdx));
0923: } else {
0924: if (commonLoader.trim().length() != 0) {
0925: sb.append(',');
0926: }
0927: sb.append(COMMON_ENDORSED);
0928: }
0929: props.setProperty(COMMON_LOADER, sb
0930: .toString());
0931: } else {
0932: return;
0933: }
0934: } else {
0935: if (idx != -1) { // common/endorsed/*.jar is present, remove it
0936: String strBefore = commonLoader.substring(
0937: 0, idx);
0938: int commaIdx = strBefore.lastIndexOf(',');
0939: StringBuffer sb = new StringBuffer(
0940: commonLoader.substring(0,
0941: commaIdx == -1 ? idx
0942: : commaIdx));
0943: String strAfter = commonLoader
0944: .substring(idx
0945: + COMMON_ENDORSED.length());
0946: if (commaIdx == -1) {
0947: // we have to cut off the trailing comman after the endorsed lib
0948: int trailingCommaIdx = strAfter
0949: .indexOf(',');
0950: if (trailingCommaIdx != -1) {
0951: strAfter = strAfter
0952: .substring(trailingCommaIdx + 1);
0953: }
0954: }
0955: sb.append(strAfter);
0956: props.setProperty(COMMON_LOADER, sb
0957: .toString());
0958: } else {
0959: return;
0960: }
0961: }
0962: }
0963: } finally {
0964: is.close();
0965: }
0966: // store changes
0967: BufferedOutputStream out = new BufferedOutputStream(
0968: new FileOutputStream(catalinaProp));
0969: try {
0970: props.store(out);
0971: } finally {
0972: out.close();
0973: }
0974: } catch (FileNotFoundException fnfe) {
0975: LOGGER.log(Level.INFO, null, fnfe);
0976: } catch (IOException ioe) {
0977: LOGGER.log(Level.INFO, null, ioe);
0978: }
0979: }
0980:
0981: /** Utility class that just "consumes" the input stream - #58554 workaround
0982: */
0983: private static class StreamConsumer implements Runnable {
0984:
0985: private BufferedInputStream in;
0986:
0987: public StreamConsumer(InputStream is) {
0988: in = new BufferedInputStream(is);
0989: }
0990:
0991: public void run() {
0992: try {
0993: byte buffer[] = new byte[1024];
0994: while (true) {
0995: int n = in.read(buffer);
0996: if (n < 0) {
0997: break;
0998: }
0999: LOGGER.log(Level.FINE, new String(buffer, 0, n));
1000: }
1001: } catch (IOException ioe) {
1002: LOGGER.log(Level.FINE, null, ioe);
1003: } finally {
1004: try {
1005: in.close();
1006: } catch (IOException ioe) {
1007: }
1008: ;
1009: }
1010: }
1011: };
1012:
1013: /** Format that provides value usefull for Tomcat execution.
1014: * Currently this is only the name of startup wrapper.
1015: */
1016: private static class TomcatFormat extends
1017: org.openide.util.MapFormat {
1018:
1019: private static final long serialVersionUID = 992972967554321415L;
1020:
1021: public TomcatFormat(File startupScript, File homeDir) {
1022: super (new java.util.HashMap());
1023: java.util.Map map = getMap();
1024: String scriptPath = startupScript.getAbsolutePath();
1025: map.put(TAG_EXEC_CMD, scriptPath);
1026: map.put(TAG_EXEC_STARTUP, "run"); // NOI18N
1027: map.put(TAG_EXEC_SHUTDOWN, "stop"); // NOI18N
1028: map.put(TAG_DEBUG_CMD, scriptPath);
1029: map.put(TAG_JPDA, "jpda"); // NOI18N
1030: map.put(TAG_JPDA_STARTUP, "run"); // NOI18N
1031: map.put(TAG_SECURITY_OPT, "-security"); // NOI18N
1032: map.put(TAG_FORCE_OPT, "-force"); // NOI18N
1033: map.put(TAG_CATALINA_HOME, homeDir.getAbsolutePath());
1034: }
1035: }
1036: }
|