| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| package org.netbeans.modules.glassfish.common; |
| |
| import org.netbeans.modules.glassfish.tooling.admin.ResultProcess; |
| import org.netbeans.modules.glassfish.tooling.admin.CommandException; |
| import org.netbeans.modules.glassfish.tooling.admin.CommandStartInstance; |
| import org.netbeans.modules.glassfish.tooling.admin.CommandVersion; |
| import org.netbeans.modules.glassfish.tooling.admin.CommandStartCluster; |
| import org.netbeans.modules.glassfish.tooling.admin.ResultString; |
| import java.io.File; |
| import java.io.FileNotFoundException; |
| import java.io.IOException; |
| import java.net.ServerSocket; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.logging.Level; |
| import java.util.logging.Logger; |
| import javax.swing.SwingUtilities; |
| import org.netbeans.modules.glassfish.tooling.GlassFishIdeException; |
| import org.netbeans.modules.glassfish.tooling.GlassFishStatus; |
| import static org.netbeans.modules.glassfish.tooling.GlassFishStatus.OFFLINE; |
| import static org.netbeans.modules.glassfish.tooling.GlassFishStatus.ONLINE; |
| import static org.netbeans.modules.glassfish.tooling.GlassFishStatus.SHUTDOWN; |
| import static org.netbeans.modules.glassfish.tooling.GlassFishStatus.STARTUP; |
| import org.netbeans.modules.glassfish.tooling.TaskEvent; |
| import org.netbeans.modules.glassfish.tooling.TaskState; |
| import org.netbeans.modules.glassfish.tooling.TaskStateListener; |
| import org.netbeans.modules.glassfish.tooling.data.GlassFishServerStatus; |
| import org.netbeans.modules.glassfish.tooling.data.StartupArgs; |
| import org.netbeans.modules.glassfish.tooling.data.StartupArgsEntity; |
| import org.netbeans.modules.glassfish.tooling.server.FetchLogSimple; |
| import org.netbeans.modules.glassfish.tooling.server.ServerTasks; |
| import org.netbeans.modules.glassfish.tooling.utils.NetUtils; |
| import org.netbeans.modules.glassfish.tooling.utils.ServerUtils; |
| import org.netbeans.api.extexecution.startup.StartupExtender; |
| import static org.netbeans.modules.glassfish.common.BasicTask.START_TIMEOUT; |
| import org.netbeans.modules.glassfish.common.ui.JavaSEPlatformPanel; |
| import org.netbeans.modules.glassfish.common.utils.AdminKeyFile; |
| import org.netbeans.modules.glassfish.common.utils.JavaUtils; |
| import org.netbeans.modules.glassfish.common.utils.Util; |
| import org.netbeans.modules.glassfish.spi.*; |
| import org.netbeans.modules.glassfish.spi.GlassfishModule.ServerState; |
| import org.openide.execution.NbProcessDescriptor; |
| import org.openide.filesystems.FileObject; |
| import org.openide.filesystems.FileUtil; |
| import org.openide.util.Lookup; |
| import org.openide.util.NbBundle; |
| import org.openide.util.RequestProcessor; |
| import org.openide.util.Utilities; |
| import org.openide.util.lookup.Lookups; |
| |
| /** |
| * Asynchronous GlassFish server startup command execution. |
| * <p/> |
| * @author Ludovic Chamenois, Peter Williams, Tomas Kraus |
| */ |
| public class StartTask extends BasicTask<TaskState> { |
| |
| //////////////////////////////////////////////////////////////////////////// |
| // Class attributes // |
| //////////////////////////////////////////////////////////////////////////// |
| |
| /** Local logger. */ |
| private static final Logger LOGGER = GlassFishLogger.get(StartTask.class); |
| |
| private static RequestProcessor NODE_REFRESHER |
| = new RequestProcessor("nodes to refresh"); |
| |
| //////////////////////////////////////////////////////////////////////////// |
| // Static methods // |
| //////////////////////////////////////////////////////////////////////////// |
| |
| private static String[] removeEscapes(String[] args) { |
| for (int i = 0; i < args.length; i++) { |
| args[i] = args[i].replace("\\\"", ""); // NOI18N |
| } |
| return args; |
| } |
| |
| private static StartupExtender.StartMode getMode(String gfMode) { |
| if (GlassfishModule.PROFILE_MODE.equals(gfMode)) { |
| return StartupExtender.StartMode.PROFILE; |
| } else if (GlassfishModule.DEBUG_MODE.equals(gfMode)) { |
| return StartupExtender.StartMode.DEBUG; |
| } else { |
| return StartupExtender.StartMode.NORMAL; |
| } |
| } |
| |
| //////////////////////////////////////////////////////////////////////////// |
| // Instance attributes // |
| //////////////////////////////////////////////////////////////////////////// |
| |
| private final CommonServerSupport support; |
| private List<Recognizer> recognizers; |
| private List<String> jvmArgs = null; |
| private final VMIntrospector vmi; |
| |
| /** internal Java SE platform home cache. */ |
| private FileObject jdkHome; |
| |
| //////////////////////////////////////////////////////////////////////////// |
| // Constructors // |
| //////////////////////////////////////////////////////////////////////////// |
| |
| /** |
| * Constructs an instance of asynchronous GlassFish server startup command |
| * execution support object. |
| * <p/> |
| * @param support Common support object for the server instance being |
| * started. |
| * @param recognizers Output recognizers to pass to log processors, if any. |
| * @param stateListener State monitor to track start progress. |
| */ |
| public StartTask(CommonServerSupport support, List<Recognizer> recognizers, |
| VMIntrospector vmi, |
| TaskStateListener... stateListener) { |
| this(support, recognizers, vmi, null, stateListener); |
| } |
| |
| /** |
| * Constructs an instance of asynchronous GlassFish server startup command |
| * execution support object. |
| * <p/> |
| * @param support Common support object for the server instance being |
| * started. |
| * @param recognizers Output recognizers to pass to log processors, if any. |
| * @param jdkRoot Java SE Development Kit to start server in profiling |
| * mode. |
| * @param jvmArgs JVM arguments used to start server in profiling |
| * mode. |
| * @param stateListener State monitor to track start progress. |
| */ |
| public StartTask(final CommonServerSupport support, |
| List<Recognizer> recognizers, VMIntrospector vmi, String[] jvmArgs, |
| TaskStateListener... stateListener) { |
| super(support.getInstance(), stateListener); |
| List<TaskStateListener> listeners = new ArrayList<>(); |
| listeners.addAll(Arrays.asList(stateListener)); |
| listeners.add(new TaskStateListener() { |
| |
| @Override |
| public void operationStateChanged(TaskState newState, |
| TaskEvent event, String... args) { |
| if (TaskState.COMPLETED.equals(newState)) { |
| // attempt to sync the comet support |
| RequestProcessor.getDefault().post( |
| new EnableComet(support.getInstance())); |
| } |
| } |
| }); |
| this.stateListener = listeners.toArray(new TaskStateListener[listeners.size()]); |
| this.support = support; |
| this.recognizers = recognizers; |
| this.jvmArgs = (jvmArgs != null) ? Arrays.asList(removeEscapes(jvmArgs)) : null; |
| this.vmi = vmi; |
| this.jdkHome = null; |
| LOGGER.log(Level.FINE, "VMI == {0}", vmi); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////// |
| // ExecutorService call() Method // |
| //////////////////////////////////////////////////////////////////////////// |
| |
| /** |
| * Asynchronous task method started by {@link Executors}. |
| * <p/> |
| * @return Task execution result. |
| */ |
| @Override |
| public TaskState call() { |
| // Save the current time so that we can deduct that the startup |
| // Failed due to timeout |
| LOGGER.log(Level.FINEST, "StartTask.call() called on thread \"{0}\"", |
| Thread.currentThread().getName()); |
| final long start = System.currentTimeMillis(); |
| |
| final String host = instance.getHost(); |
| final int adminPort = instance.getAdminPort(); |
| StateChange change; |
| if ((change = validateAdminHostAndPort(host, adminPort)) != null) { |
| return change.fireOperationStateChanged(); |
| } |
| // Remote server. |
| if (support.isRemote()) { |
| if (GlassFishState.isOnline(instance)) { |
| if (Util.isDefaultOrServerTarget(instance.getProperties())) { |
| return restartDAS(host, adminPort, start); |
| } else { |
| return startClusterOrInstance(host, adminPort); |
| } |
| } else { |
| return fireOperationStateChanged(TaskState.FAILED, |
| TaskEvent.CMD_FAILED, |
| "MSG_START_SERVER_FAILED_DASDOWN", instanceName); |
| } |
| // Local server. |
| } else |
| // Our server is offline. |
| if (GlassFishState.isOffline(instance)) { |
| // But administrator port is occupied. |
| if (ServerUtils.isAdminPortListening( |
| instance, NetUtils.PORT_CHECK_TIMEOUT)) { |
| ResultString version; |
| try { |
| version = CommandVersion.getVersion(instance); |
| } catch (CommandException ce) { |
| version = null; |
| } |
| // Got version response from DAS. |
| if (version != null) { |
| // There is server with matching version. |
| if (CommandVersion.verifyResult(version, instance)) { |
| fireOperationStateChanged(TaskState.RUNNING, |
| TaskEvent.CMD_COMPLETED, |
| "StartTask.call.matchVersion", |
| version.getValue()); |
| return startClusterOrInstance(host, adminPort); |
| // There is server with non matching version. |
| } else { |
| if (!version.isAuth()) { |
| return fireOperationStateChanged(TaskState.FAILED, |
| TaskEvent.CMD_FAILED, |
| "StartTask.call.authFailed", |
| instanceName, version.getValue()); |
| } else { |
| return fireOperationStateChanged(TaskState.FAILED, |
| TaskEvent.CMD_FAILED, |
| "StartTask.call.anotherVersion", |
| instanceName, version.getValue()); |
| } |
| } |
| // Got no version response from DAS. |
| } else { |
| return fireOperationStateChanged(TaskState.FAILED, |
| TaskEvent.CMD_FAILED, |
| "StartTask.call.unknownVersion", instanceName); |
| } |
| } else { |
| return startDAS(host, adminPort); |
| } |
| // Our server is online. |
| } else { |
| return startClusterOrInstance(host, adminPort); |
| } |
| } |
| |
| //////////////////////////////////////////////////////////////////////////// |
| // Methods // |
| //////////////////////////////////////////////////////////////////////////// |
| |
| /** |
| * Validate <code>host</code> and <code>port</code> values |
| * for DAS listener. |
| * <p/> |
| * @return State change request data when server shall not be started |
| * and listeners should be notified about it or <code>null</code> |
| * otherwise. |
| */ |
| private StateChange validateAdminHostAndPort( |
| final String host, final int adminPort) { |
| if (host == null || host.length() == 0) { |
| return new StateChange(this, TaskState.FAILED, TaskEvent.CMD_FAILED, |
| "MSG_START_SERVER_FAILED_NOHOST", instanceName); |
| } |
| if (adminPort < 0 || adminPort > 65535) { |
| return new StateChange(this, TaskState.FAILED, TaskEvent.CMD_FAILED, |
| "MSG_START_SERVER_FAILED_BADPORT", instanceName); |
| } |
| return null; |
| } |
| |
| private TaskState restartDAS(String adminHost, int adminPort, final long start) { |
| // deal with the remote case here... |
| TaskStateListener[] listeners = { |
| new TaskStateListener() { |
| // if the http command is successful, we are not done yet... |
| // The server still has to stop. If we signal success to the 'stateListener' |
| // for the task, it may be premature. |
| @SuppressWarnings("SleepWhileInLoop") |
| @Override |
| public void operationStateChanged(TaskState newState, |
| TaskEvent event, String... args) { |
| if (newState == TaskState.RUNNING) { |
| support.setServerState(ServerState.STARTING); |
| } |
| if (newState == TaskState.FAILED) { |
| fireOperationStateChanged(newState, event, |
| instanceName, args); |
| support.setServerState(ServerState.STOPPED); |
| //support.refresh(); |
| } else if (args != null && newState == TaskState.COMPLETED) { |
| for (String message : args) { |
| if (message.matches("[sg]et\\?.*\\=configs\\..*")) { |
| return; |
| } |
| } |
| long startTime = System.currentTimeMillis(); |
| TaskState state = TaskState.RUNNING; |
| try { |
| Thread.sleep(2000); |
| } catch (InterruptedException e) { |
| // no op |
| } |
| while (TaskState.RUNNING == state && System.currentTimeMillis() - start < START_TIMEOUT) { |
| // Send the 'completed' event and return when the server is running |
| boolean httpLive = GlassFishState.isOnline(instance); //CommonServerSupport.isRunning(host, port,instance.getProperty(GlassfishModule.DISPLAY_NAME_ATTR)); |
| |
| // Sleep for a little so that we do not make our checks too often |
| // |
| // Doing this before we check httpAlive also prevents us from |
| // pinging the server too quickly after the ports go live. |
| // |
| try { |
| Thread.sleep(1000); |
| } catch (InterruptedException e) { |
| // no op |
| } |
| |
| if (httpLive) { |
| try { |
| Thread.sleep(2000); |
| } catch (InterruptedException e) { |
| // no op |
| } |
| state = TaskState.COMPLETED; |
| } |
| } |
| if (state == TaskState.COMPLETED) { //support.isReady(false, 120, TimeUnit.SECONDS)) { |
| support.setServerState(ServerState.RUNNING); |
| } else { |
| support.setServerState(ServerState.STOPPED); |
| } |
| } |
| } |
| }}; |
| int debugPort = -1; |
| if (GlassfishModule.DEBUG_MODE.equals(instance.getProperty(GlassfishModule.JVM_MODE))) { |
| debugPort = instance.getDebugPort(); |
| } |
| support.restartServer(debugPort, |
| support.supportsRestartInDebug() && debugPort >= 0, listeners); |
| return fireOperationStateChanged( |
| TaskState.RUNNING, TaskEvent.CMD_FAILED, |
| "StartTask.restartDAS.inProgress", instanceName); |
| |
| } |
| |
| /** |
| * Reset administrator password when needed. |
| */ |
| private void resetPassword() { |
| AdminKeyFile keyFile = new AdminKeyFile(instance); |
| keyFile.read(); |
| if (keyFile.isReset()) { |
| String password = AdminKeyFile.randomPassword( |
| AdminKeyFile.RANDOM_PASSWORD_LENGTH); |
| instance.setAdminPassword(password); |
| keyFile.setPassword(password); |
| try { |
| GlassfishInstance.writeInstanceToFile(instance); |
| } catch(IOException ex) { |
| LOGGER.log(Level.INFO, |
| "Could not store GlassFish server attributes", ex); |
| } |
| keyFile.write(); |
| } |
| } |
| |
| /** |
| * Initialize JDK used to start GlassFish server. |
| * <p/> |
| * @return State change request data when JDK could not be initialized |
| * or <code>null</code> otherwise. |
| */ |
| private StateChange initJDK() { |
| try { |
| if (null == jdkHome) { |
| jdkHome = getJavaPlatformRoot(); |
| File jdkHomeFile = FileUtil.toFile(jdkHome); |
| if (!JavaUtils.isJavaPlatformSupported(instance, jdkHomeFile)) { |
| jdkHome = JavaSEPlatformPanel.selectServerSEPlatform( |
| instance, jdkHomeFile); |
| } |
| } |
| if (jdkHome == null) { |
| return new StateChange(this, TaskState.FAILED, |
| TaskEvent.CMD_FAILED, "StartTask.initJDK.null", |
| instanceName); |
| } |
| } catch (IOException ex) { |
| LOGGER.log(Level.INFO, null, ex); // NOI18N |
| return new StateChange(this, TaskState.FAILED, |
| TaskEvent.CMD_FAILED, "StartTask.initJDK.exception", |
| new String[] {instanceName, ex.getLocalizedMessage()}); |
| } |
| return null; |
| } |
| |
| /** |
| * Verify GlassFish server status before starting it. |
| * <p/> |
| * @return State change request data when server shall not be started |
| * and listeners should be notified about it or <code>null</code> |
| * otherwise. |
| */ |
| private StateChange checkBeforeStart() { |
| GlassFishServerStatus status = GlassFishState.getStatus(instance); |
| String msgKey = null; |
| switch (status.getStatus()) { |
| case ONLINE: |
| TaskState result; |
| TaskEvent event; |
| if (GlassfishModule.PROFILE_MODE.equals( |
| instance.getProperty(GlassfishModule.JVM_MODE))) { |
| result = TaskState.FAILED; |
| event = TaskEvent.CMD_FAILED; |
| } else { |
| result = TaskState.COMPLETED; |
| event = TaskEvent.CMD_COMPLETED; |
| } |
| return new StateChange(this, result, event, |
| "StartTask.startDAS.alreadyRunning"); |
| case OFFLINE: |
| if (ServerUtils.isAdminPortListening( |
| instance, NetUtils.PORT_CHECK_TIMEOUT)) { |
| msgKey = "StartTask.startDAS.adminPortOccupied"; |
| } else { |
| final int httpPort = instance.getPort(); |
| if (httpPort >= 0 && httpPort <= 65535 |
| && NetUtils.isPortListeningLocal( |
| instance.getHost(), httpPort)) { |
| msgKey = "StartTask.startDAS.httpPortOccupied"; |
| } |
| } |
| break; |
| case SHUTDOWN: |
| msgKey = "StartTask.startDAS.shutdown"; |
| break; |
| case STARTUP: |
| msgKey = "StartTask.startDAS.startup"; |
| } |
| return msgKey != null |
| ? new StateChange(this, TaskState.FAILED, |
| TaskEvent.CMD_FAILED, msgKey, this.instance.getDisplayName()) |
| : null; |
| } |
| |
| /** |
| * Check for server status in profiling mode during startup. |
| * <p/> |
| * @return State change request about server startup success. |
| */ |
| @SuppressWarnings("SleepWhileInLoop") |
| private StateChange profilingServerStatus() { |
| LOGGER.log(Level.FINE, |
| "Profiling mode status hack for {0}", |
| new Object[]{instance.getName()}); |
| // Save process to be able to stop process waiting for profiler |
| // to attach. |
| support.setLocalStartProcess(instance.getProcess()); |
| // Try to sync the states after the profiler attaches. |
| NODE_REFRESHER.post(new Runnable() { |
| @Override |
| public void run() { |
| while (!GlassFishState.isOnline(instance)) { |
| try { |
| Thread.sleep(200); |
| } catch (InterruptedException ex) { |
| } |
| } |
| SwingUtilities.invokeLater(new Runnable() { |
| @Override |
| public void run() { |
| support.refresh(); |
| } |
| }); |
| } |
| }); |
| return new StateChange(this, TaskState.COMPLETED, |
| TaskEvent.CMD_COMPLETED, |
| "MSG_SERVER_STARTED", instanceName); |
| } |
| |
| private TaskState startDAS(String adminHost, int adminPort) { |
| StateChange change; |
| resetPassword(); |
| if ((change = initJDK()) != null) { |
| return change.fireOperationStateChanged(); |
| } |
| // Time must be measured after Java SE platform selection is done. |
| long start = System.currentTimeMillis(); |
| StartStateListener listener; |
| try { |
| // lookup the javadb start service and use it here. |
| RegisteredDerbyServer db |
| = Lookup.getDefault().lookup(RegisteredDerbyServer.class); |
| if (null != db && "true".equals( |
| instance.getProperty(GlassfishModule.START_DERBY_FLAG))) { // NOI18N |
| db.start(); |
| } |
| // This may be an autheticated server... so we will say it is |
| // started. Other operations will fail if the process on the port |
| // is not a GF v3 server. |
| LOGGER.log(Level.FINEST, |
| "Checking if GlassFish {0} is running.", |
| instance.getName()); |
| if ((change = checkBeforeStart()) != null) { |
| return change.fireOperationStateChanged(); |
| } else if (upgradeFailed()) { |
| return fireOperationStateChanged(TaskState.FAILED, |
| TaskEvent.CMD_FAILED, |
| "StartTask.startDAS.domainUpgrade", instanceName); |
| } |
| // We should be listening for reaching ONLINE state before process |
| // is started. |
| listener = prepareStartMonitoring( |
| instance.getJvmMode() == GlassFishJvmMode.PROFILE); |
| if (listener == null) { |
| return fireOperationStateChanged(TaskState.FAILED, |
| TaskEvent.CMD_FAILED, |
| "StartTask.startDAS.startupMonitoring", instanceName); |
| } |
| Process process = createProcess(); |
| instance.setProcess(process); |
| listener.setProcess(process); |
| } catch (ProcessCreationException ex) { |
| Logger.getLogger("glassfish").log(Level.INFO, |
| "Could not start process for " + instanceName, ex); |
| return fireOperationStateChanged(TaskState.FAILED, |
| TaskEvent.CMD_FAILED, "MSG_PASS_THROUGH", |
| ex.getLocalizedMessage()); |
| } |
| |
| fireOperationStateChanged(TaskState.RUNNING, TaskEvent.CMD_RUNNING, |
| "StartTask.startDAS.inProgress", instanceName); |
| // create a logger to the server's output stream so that a user |
| // can observe the progress |
| LogViewMgr logger = LogViewMgr.getInstance(instance.getProperty( |
| GlassfishModule.URL_ATTR)); |
| logger.readInputStreams(recognizers, false, null, |
| new FetchLogSimple(instance.getProcess().getInputStream()), |
| new FetchLogSimple(instance.getProcess().getErrorStream())); |
| |
| // Waiting for server to start |
| LOGGER.log(Level.FINER, "Waiting for server to start for {0} ms", |
| new Object[] {Integer.toString(START_TIMEOUT)}); |
| try { |
| synchronized(listener) { |
| while (!listener.isWakeUp() |
| && (System.currentTimeMillis() |
| - start < START_TIMEOUT)) { |
| listener.wait(System.currentTimeMillis() - start); |
| } |
| } |
| } catch (InterruptedException ie) { |
| LOGGER.log(Level.INFO, |
| "Caught InterruptedException while waiting for {0} to start: {1}", |
| new Object[] {instance.getName(), ie.getLocalizedMessage()}); |
| } finally { |
| GlassFishStatus.removeListener(instance, listener); |
| } |
| // We need to lie about server status in profiling mode. |
| if (null != jvmArgs && (change = profilingServerStatus()) != null) { |
| return change.fireOperationStateChanged(); |
| } |
| if (!GlassFishState.isOnline(instance)) { |
| return fireOperationStateChanged( |
| TaskState.FAILED, TaskEvent.CMD_FAILED, |
| "StartTask.startDAS.startFailed", instanceName); |
| } else { |
| return startClusterOrInstance(adminHost, adminPort); |
| } |
| } |
| |
| private TaskState startClusterOrInstance(String adminHost, int adminPort) { |
| String target = Util.computeTarget(instance.getProperties()); |
| if (Util.isDefaultOrServerTarget(instance.getProperties())) { |
| return fireOperationStateChanged(TaskState.COMPLETED, |
| TaskEvent.CMD_COMPLETED, |
| "MSG_SERVER_STARTED", instanceName); |
| } else { |
| TaskState state; |
| try { |
| ResultString result |
| = CommandStartCluster.startCluster(instance, target); |
| state = result.getState(); |
| } catch (GlassFishIdeException gfie) { |
| state = TaskState.FAILED; |
| LOGGER.log(Level.INFO, gfie.getMessage(), gfie); |
| } |
| if (state == TaskState.FAILED) { |
| try { |
| ResultString result |
| = CommandStartInstance.startInstance(instance, target); |
| state = result.getState(); |
| } catch (GlassFishIdeException gfie) { |
| state = TaskState.FAILED; |
| LOGGER.log(Level.INFO, gfie.getMessage(), gfie); |
| } |
| if (state == TaskState.FAILED) { |
| // if start instance not suscessful fail |
| return fireOperationStateChanged(TaskState.FAILED, |
| TaskEvent.CMD_FAILED, |
| "MSG_START_TARGET_FAILED", instanceName, target); |
| } |
| } |
| support.updateHttpPort(); |
| return fireOperationStateChanged(TaskState.COMPLETED, |
| TaskEvent.CMD_COMPLETED, |
| "MSG_SERVER_STARTED", instanceName); |
| } |
| } |
| |
| /** |
| * Search for Java SE platform to be used for running GlassFish server. |
| * <p/> |
| * GlassFish instance Java SE platform property is checked first |
| * and Java SE platform used to run NetBeans as a fallback option. |
| * <p/> |
| * @return Java SE platform to be used for running GlassFish server. |
| * @throws IOException when GlassFish instance Java SE platform property |
| * does not point to existing directory. |
| */ |
| private FileObject getJavaPlatformRoot() throws IOException { |
| FileObject retVal; |
| String javaHome = instance.getJavaHome(); |
| if (null == javaHome || javaHome.trim().length() < 1) { |
| File dir = new File(getJdkHome()); |
| retVal = FileUtil.createFolder(FileUtil.normalizeFile(dir)); |
| } else { |
| File f = new File(javaHome); |
| if (f.exists()) { |
| retVal = FileUtil.createFolder(FileUtil.normalizeFile(f)); |
| } else { |
| throw new FileNotFoundException( |
| NbBundle.getMessage(StartTask.class, |
| "MSG_INVALID_JAVA", instanceName, javaHome)); |
| } |
| } |
| return retVal; |
| } |
| |
| /** |
| * Get Java SE platform used to run NetBeans. |
| * <p/> |
| * @return Java SE platform used to run NetBeans. |
| */ |
| private String getJdkHome() { |
| String result; |
| result = System.getProperty("java.home"); |
| if (result.endsWith(File.separatorChar + "jre")) { |
| result = result.substring(0, result.length() - 4); |
| } |
| return result; |
| } |
| |
| private StartupArgs createProcessDescriptor() throws ProcessCreationException { |
| List<String> glassfishArgs = new ArrayList<>(2); |
| String domainDir = Util.quote(getDomainFolder().getAbsolutePath()); |
| glassfishArgs.add(ServerUtils.cmdLineArgument( |
| ServerUtils.GF_DOMAIN_ARG, getDomainName())); |
| glassfishArgs.add(ServerUtils.cmdLineArgument( |
| ServerUtils.GF_DOMAIN_DIR_ARG, domainDir)); |
| |
| ArrayList<String> optList = new ArrayList<>(); |
| // append debug options |
| if (GlassfishModule.DEBUG_MODE.equals(instance.getProperty(GlassfishModule.JVM_MODE))) { |
| appendDebugOptions(optList); |
| } |
| |
| // append other options from startup extenders, e.g. for profiling |
| appendStartupExtenderParams(optList); |
| |
| return new StartupArgsEntity( |
| glassfishArgs, |
| optList, |
| (Map<String, String>) null, |
| FileUtil.toFile(jdkHome).getAbsolutePath()); |
| } |
| |
| /** |
| * Appends debug options for server start. |
| * If the port read from instance properties is not valid (null or out of the range), |
| * it offers to the user a different free port. |
| * |
| * @param optList |
| * @throws ProcessCreationException |
| */ |
| private void appendDebugOptions(List<String> optList) throws ProcessCreationException { |
| String debugPortString = instance.getProperty(GlassfishModule.DEBUG_PORT); |
| String debugTransport = "dt_socket"; // NOI18N |
| if ("true".equals(instance.getProperty(GlassfishModule.USE_SHARED_MEM_ATTR))) { // NOI18N |
| debugTransport = "dt_shmem"; // NOI18N |
| } else { |
| if (null != debugPortString && debugPortString.trim().length() > 0) { |
| int t = Integer.parseInt(debugPortString); |
| if (t != 0 && (t < GlassfishInstance.LOWEST_USER_PORT |
| || t > 65535)) { |
| throw new NumberFormatException(); |
| } |
| } |
| } |
| if (null == debugPortString |
| || "0".equals(debugPortString) || "".equals(debugPortString)) { |
| if ("true".equals(instance.getProperty(GlassfishModule.USE_SHARED_MEM_ATTR))) { // NOI18N |
| debugPortString = Integer.toString( |
| Math.abs((instance.getProperty(GlassfishModule.GLASSFISH_FOLDER_ATTR) |
| + instance.getDomainsRoot() |
| + instance.getProperty(GlassfishModule.DOMAIN_NAME_ATTR)).hashCode() + 1)); |
| } else { |
| try { |
| debugPortString = selectDebugPort(); |
| } catch (IOException ioe) { |
| throw new ProcessCreationException(ioe, |
| "MSG_START_SERVER_FAILED_INVALIDPORT", instanceName, debugPortString); //NOI18N |
| } |
| } |
| } |
| support.setEnvironmentProperty(GlassfishModule.DEBUG_PORT, debugPortString, true); |
| StringBuilder opt = new StringBuilder(); |
| opt.append("-agentlib:jdwp=transport="); // NOI18N |
| opt.append(debugTransport); |
| opt.append(",address="); // NOI18N |
| opt.append(debugPortString); |
| opt.append(",server=y,suspend=n"); // NOI18N |
| optList.add(opt.toString()); |
| } |
| |
| private void appendStartupExtenderParams(List<String> optList) { |
| for (StartupExtender args : StartupExtender.getExtenders( |
| Lookups.singleton(support.getInstanceProvider().getInstance(instance.getProperty("url"))), |
| getMode(instance.getProperty(GlassfishModule.JVM_MODE)))) { |
| for (String arg : args.getArguments()) { |
| String[] argSplitted = arg.trim().split("\\s+(?=-)"); |
| optList.addAll(Arrays.asList(argSplitted)); |
| } |
| } |
| } |
| |
| private String selectDebugPort() throws IOException { |
| int debugPort = 9009; |
| ServerSocket t = null; |
| try { |
| // try to use the 'standard port' |
| t = new ServerSocket(debugPort); |
| return Integer.toString(debugPort); |
| } catch (IOException ex) { |
| // log this... but don't panic |
| Logger.getLogger("glassfish").fine("9009 is in use... going random"); |
| } finally { |
| if (null != t) { |
| try { |
| t.close(); |
| } catch (IOException ioe) { |
| } |
| } |
| } |
| try { |
| // try to find a different port... if this fails, |
| // it is a great time to panic. |
| t = new ServerSocket(0); |
| debugPort = t.getLocalPort(); |
| return Integer.toString(debugPort); |
| } finally { |
| if (null != t) { |
| try { |
| t.close(); |
| } catch (IOException ioe) { |
| } |
| } |
| } |
| } |
| |
| private Process createProcess() throws ProcessCreationException { |
| StartupArgs args = createProcessDescriptor(); |
| // JDK checks and Java VM process startup were moved to GF Tooling SDK. |
| ResultProcess process = ServerTasks.startServer(instance, args); |
| if (process.getState() != TaskState.COMPLETED) { |
| throw new ProcessCreationException(null, "MSG_START_SERVER_FAILED_PD", instanceName); |
| } |
| return process.getValue().getProcess(); |
| } |
| |
| private File getDomainFolder() { |
| return new File(instance.getDomainsRoot() + File.separatorChar + getDomainName()); |
| } |
| |
| private String getDomainName() { |
| return instance.getProperty(GlassfishModule.DOMAIN_NAME_ATTR); |
| } |
| |
| private boolean upgradeFailed() { |
| // get server install version |
| File glassfishDir = new File(instance.getGlassfishRoot()); |
| int installVersion = ServerDetails.getVersionFromInstallDirectory(glassfishDir); |
| |
| if (installVersion < 0) { |
| return false; // no upgrade attempted, so it DID NOT fail. |
| } |
| // get domain.xml 'version' |
| File domainXmlFile = new File(getDomainFolder(), "config" + File.separator + "domain.xml"); // NOI18N |
| int domainVersion = ServerDetails.getVersionFromDomainXml(domainXmlFile); |
| |
| if (domainVersion < 0) { |
| return false; // no upgrade attempted, so it DID NOT fail. |
| } |
| if (domainVersion / 10 < installVersion / 10 && domainVersion < 310) { |
| return executeUpgradeProcess() != 0; |
| } |
| return false; |
| } |
| |
| private int executeUpgradeProcess() { |
| int retVal = -1; |
| File asadmin = findFirstExecutableFile(new File(instance.getGlassfishRoot()), "asadmin", "bin"); |
| if (null == asadmin) { |
| return retVal; |
| } |
| NbProcessDescriptor upgrader = new NbProcessDescriptor(asadmin.getAbsolutePath(), |
| "start-domain --upgrade --domaindir " + Util.quote(instance.getDomainsRoot()) + " " + // NOI18N |
| instance.getDomainName()); |
| try { |
| Process p = upgrader.exec(); |
| p.waitFor(); |
| retVal = p.exitValue(); |
| } catch (InterruptedException ex) { |
| LOGGER.log(Level.INFO, upgrader.toString(), ex); // NOI18N |
| } catch (IOException ex) { |
| LOGGER.log(Level.INFO, upgrader.toString(), ex); // NOI18N |
| } |
| return retVal; |
| } |
| |
| // TODO : refactor and remove 'similar' methods post 7.0 |
| private File findFirstExecutableFile(File installRoot, String executableName, String... directories) { |
| File result = null; |
| if (installRoot != null && installRoot.exists()) { |
| for (String dir : directories) { |
| File updateCenterBin = new File(installRoot, dir); // NOI18N |
| if (updateCenterBin.exists()) { |
| if (Utilities.isWindows()) { |
| File launcherPath = new File(updateCenterBin, executableName + ".exe"); // NOI18N |
| if (launcherPath.exists()) { |
| result = launcherPath; |
| } else { |
| launcherPath = new File(updateCenterBin, executableName + ".bat"); // NOI18N |
| result = (launcherPath.exists()) ? launcherPath : null; |
| } |
| } else { |
| File launcherPath = new File(updateCenterBin, executableName); // NOI18N |
| result = (launcherPath.exists()) ? launcherPath : null; |
| } |
| if (null != result) { |
| break; |
| } |
| } |
| } |
| } |
| return result; |
| } |
| |
| } |