| /********************************************************************* |
| * Copyright (c) 2009 - 2012 SpringSource, a division of VMware, Inc. and others and others. |
| * |
| * This program and the accompanying materials are made |
| * available under the terms of the Eclipse Public License 2.0 |
| * which is available at https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| **********************************************************************/ |
| |
| package org.eclipse.virgo.ide.runtime.internal.core; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.io.RandomAccessFile; |
| import java.net.URL; |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.Dictionary; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.concurrent.ConcurrentHashMap; |
| import java.util.concurrent.TimeoutException; |
| |
| import org.eclipse.core.resources.IProject; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IPath; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.Path; |
| import org.eclipse.core.runtime.Status; |
| import org.eclipse.core.runtime.jobs.Job; |
| import org.eclipse.debug.core.DebugEvent; |
| import org.eclipse.debug.core.DebugPlugin; |
| import org.eclipse.debug.core.IDebugEventSetListener; |
| import org.eclipse.debug.core.ILaunch; |
| import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy; |
| import org.eclipse.debug.core.model.IProcess; |
| import org.eclipse.debug.internal.ui.views.console.ProcessConsole; |
| import org.eclipse.jdt.core.JavaCore; |
| import org.eclipse.jdt.launching.IJavaLaunchConfigurationConstants; |
| import org.eclipse.jdt.launching.IRuntimeClasspathEntry; |
| import org.eclipse.jdt.launching.IVMInstall; |
| import org.eclipse.jdt.launching.JavaRuntime; |
| import org.eclipse.libra.framework.editor.core.IOSGiFrameworkAdmin; |
| import org.eclipse.libra.framework.editor.core.IOSGiFrameworkConsole; |
| import org.eclipse.libra.framework.editor.core.model.IBundle; |
| import org.eclipse.ui.console.ConsolePlugin; |
| import org.eclipse.ui.console.IConsole; |
| import org.eclipse.ui.console.IOConsoleOutputStream; |
| import org.eclipse.virgo.ide.facet.core.FacetCorePlugin; |
| import org.eclipse.virgo.ide.facet.core.FacetUtils; |
| import org.eclipse.virgo.ide.manifest.core.BundleManifestCorePlugin; |
| import org.eclipse.virgo.ide.runtime.core.IServerBehaviour; |
| import org.eclipse.virgo.ide.runtime.core.IServerDeployer; |
| import org.eclipse.virgo.ide.runtime.core.IServerRuntime; |
| import org.eclipse.virgo.ide.runtime.core.IServerRuntimeProvider; |
| import org.eclipse.virgo.ide.runtime.core.ServerUtils; |
| import org.eclipse.virgo.util.osgi.manifest.BundleManifest; |
| import org.eclipse.wst.common.project.facet.core.FacetedProjectFramework; |
| import org.eclipse.wst.server.core.IModule; |
| import org.eclipse.wst.server.core.IServer; |
| import org.eclipse.wst.server.core.model.IModuleResource; |
| import org.eclipse.wst.server.core.model.IModuleResourceDelta; |
| import org.eclipse.wst.server.core.model.ServerBehaviourDelegate; |
| |
| /** |
| * Default dm server behavior. |
| * |
| * @author Christian Dupuis |
| * @author Kaloyan Raev |
| * @since 1.0.0 |
| */ |
| @SuppressWarnings("restriction") |
| public class ServerBehaviour extends ServerBehaviourDelegate implements IServerBehaviour, IOSGiFrameworkAdmin, IOSGiFrameworkConsole { |
| |
| private final static String WEB_CONTEXT_PATH_MANIFEST_HEADER = "Web-ContextPath"; |
| |
| protected Map<String, DeploymentIdentity> deploymentIdentities = new ConcurrentHashMap<String, DeploymentIdentity>(); |
| |
| protected transient ILaunch launch; |
| |
| protected transient ServerStatusPingThread pingThread; |
| |
| protected transient IDebugEventSetListener processListener; |
| |
| private transient ProcessConsole processConsole; |
| |
| private transient IServerDeployer serverDeployer; |
| |
| private transient List<ServerLogTail> traceTailJobs = Collections.synchronizedList(new ArrayList<ServerLogTail>()); |
| |
| private transient Map<String, Long> traceFileSizes = new ConcurrentHashMap<String, Long>(); |
| |
| public IStatus cleanServerWorkDir(IProgressMonitor monitor) throws CoreException { |
| String path = getServerDeployDirectory().toString(); |
| File file = new File(path); |
| file.delete(); |
| return Status.OK_STATUS; |
| } |
| |
| public IPath getModuleDeployDirectory(IModule module) { |
| return ServerUtils.getServer(this).getModuleDeployDirectory(module); |
| } |
| |
| public IPath getModuleDeployUri(IModule module) { |
| return getModuleDeployDirectory(module); |
| } |
| |
| public IPath getRuntimeBaseDirectory() { |
| return ServerUtils.getServer(this).getRuntimeBaseDirectory(); |
| } |
| |
| public IPath getServerDeployDirectory() { |
| return ServerUtils.getServer(this).getServerDeployDirectory(); |
| } |
| |
| public IServerRuntimeProvider getVersionHandler() { |
| return ServerUtils.getServer(this).getVersionHandler(); |
| } |
| |
| @Override |
| public void handleResourceChange() { |
| if (getServer().getServerRestartState()) { |
| return; |
| } |
| |
| Iterator<IModule[]> iterator = getAllModules().iterator(); |
| while (iterator.hasNext()) { |
| IModule[] module = iterator.next(); |
| IModuleResourceDelta[] delta = getPublishedResourceDelta(module); |
| if (delta == null || delta.length == 0) { |
| continue; |
| } |
| } |
| } |
| |
| public void onModulePublishStateChange(IModule[] module, int state) { |
| setModulePublishState(module, state); |
| } |
| |
| public void onModuleStateChange(IModule[] module, int state) { |
| setModuleState(module, state); |
| } |
| |
| public void setupLaunch(ILaunch launch, String launchMode, IProgressMonitor monitor) throws CoreException { |
| IStatus status = ServerUtils.getServerRuntime(this).validate(); |
| if (status != null && status.getSeverity() == IStatus.ERROR) { |
| throw new CoreException(status); |
| } |
| |
| getVersionHandler().preStartup(this); |
| |
| setServerRestartState(false); |
| setServerState(IServer.STATE_STARTING); |
| setMode(launchMode); |
| this.launch = launch; |
| this.pingThread = new ServerStatusPingThread(this); |
| } |
| |
| @Override |
| public void setupLaunchConfiguration(ILaunchConfigurationWorkingCopy workingCopy, IProgressMonitor monitor) throws CoreException { |
| |
| String existingProgArgs = workingCopy.getAttribute(IJavaLaunchConfigurationConstants.ATTR_PROGRAM_ARGUMENTS, (String) null); |
| workingCopy.setAttribute(IJavaLaunchConfigurationConstants.ATTR_PROGRAM_ARGUMENTS, |
| LaunchArgumentUtils.mergeArguments(existingProgArgs, getRuntimeProgramArguments(), getExcludedRuntimeProgramArguments(true), true)); |
| |
| String existingVMArgs = workingCopy.getAttribute(IJavaLaunchConfigurationConstants.ATTR_VM_ARGUMENTS, (String) null); |
| String[] configVMArgs = getRuntimeVMArguments(); |
| |
| workingCopy.setAttribute(IJavaLaunchConfigurationConstants.ATTR_VM_ARGUMENTS, |
| LaunchArgumentUtils.mergeArguments(existingVMArgs, configVMArgs, null, false)); |
| |
| IServerRuntime runtime = ServerUtils.getServerRuntime(this); |
| IVMInstall vmInstall = runtime.getVMInstall(); |
| if (vmInstall != null) { |
| workingCopy.setAttribute(IJavaLaunchConfigurationConstants.ATTR_JRE_CONTAINER_PATH, |
| JavaRuntime.newJREContainerPath(vmInstall).toPortableString()); |
| } |
| |
| workingCopy.setAttribute(IJavaLaunchConfigurationConstants.ATTR_WORKING_DIRECTORY, getRuntimeBaseDirectory().toOSString()); |
| |
| IRuntimeClasspathEntry[] originalClasspath = JavaRuntime.computeUnresolvedRuntimeClasspath(workingCopy); |
| int size = originalClasspath.length; |
| List<IRuntimeClasspathEntry> oldCp = new ArrayList<IRuntimeClasspathEntry>(originalClasspath.length + 2); |
| for (int i = 0; i < size; i++) { |
| oldCp.add(originalClasspath[i]); |
| } |
| |
| List<IRuntimeClasspathEntry> cp2 = runtime.getRuntimeClasspath(); |
| Iterator<IRuntimeClasspathEntry> iterator = cp2.iterator(); |
| while (iterator.hasNext()) { |
| IRuntimeClasspathEntry entry = iterator.next(); |
| LaunchArgumentUtils.mergeClasspath(oldCp, entry); |
| } |
| |
| if (vmInstall != null) { |
| try { |
| String typeId = vmInstall.getVMInstallType().getId(); |
| LaunchArgumentUtils.replaceJREContainer(oldCp, JavaRuntime.newRuntimeContainerClasspathEntry( |
| new Path(JavaRuntime.JRE_CONTAINER).append(typeId).append(vmInstall.getName()), IRuntimeClasspathEntry.BOOTSTRAP_CLASSES)); |
| } catch (Exception e) { |
| // ignore |
| } |
| |
| IPath jrePath = new Path(vmInstall.getInstallLocation().getAbsolutePath()); |
| if (jrePath != null) { |
| IPath toolsPath = jrePath.append("lib").append("tools.jar"); |
| if (toolsPath.toFile().exists()) { |
| IRuntimeClasspathEntry toolsJar = JavaRuntime.newArchiveRuntimeClasspathEntry(toolsPath); |
| int toolsIndex; |
| for (toolsIndex = 0; toolsIndex < oldCp.size(); toolsIndex++) { |
| IRuntimeClasspathEntry entry = oldCp.get(toolsIndex); |
| if (entry.getType() == IRuntimeClasspathEntry.ARCHIVE && entry.getPath().lastSegment().equals("tools.jar")) { |
| break; |
| } |
| } |
| if (toolsIndex < oldCp.size()) { |
| oldCp.set(toolsIndex, toolsJar); |
| } else { |
| LaunchArgumentUtils.mergeClasspath(oldCp, toolsJar); |
| } |
| } |
| } |
| } |
| |
| iterator = oldCp.iterator(); |
| List<String> list = new ArrayList<String>(); |
| while (iterator.hasNext()) { |
| IRuntimeClasspathEntry entry = iterator.next(); |
| try { |
| list.add(entry.getMemento()); |
| } catch (Exception e) { |
| } |
| } |
| |
| workingCopy.setAttribute(IJavaLaunchConfigurationConstants.ATTR_CLASSPATH, list); |
| workingCopy.setAttribute(IJavaLaunchConfigurationConstants.ATTR_DEFAULT_CLASSPATH, false); |
| } |
| |
| @Override |
| public void stop(boolean force) { |
| if (force) { |
| immediateShutdown(); |
| } else { |
| shutdown(); |
| } |
| } |
| |
| public void stopServer() { |
| |
| // if the server is in starting mode make sure that the state transition |
| // is allowed |
| if (getServer().getServerState() != IServer.STATE_STARTED) { |
| setServerState(IServer.STATE_STOPPING); |
| } |
| |
| if (this.pingThread != null) { |
| this.pingThread.stop(); |
| this.pingThread = null; |
| } |
| if (this.processListener != null) { |
| DebugPlugin.getDefault().removeDebugEventListener(this.processListener); |
| this.processListener = null; |
| } |
| if (this.processConsole != null && this.traceTailJobs.size() > 0) { |
| for (ServerLogTail job : this.traceTailJobs) { |
| job.stopTailing(); |
| } |
| this.traceTailJobs.clear(); |
| this.traceFileSizes.clear(); |
| this.processConsole = null; |
| } |
| |
| setServerState(IServer.STATE_STOPPED); |
| } |
| |
| public void tail(DeploymentIdentity identity) { |
| // add setting to enable/disable tailing |
| if (this.processConsole != null && ServerUtils.getServer(this).shouldTailTraceFiles() && identity != null |
| && identity.getSymbolicName() != null && identity.getVersion() != null) { |
| ServerLogTail tail = new ServerLogTail(this, identity, this.processConsole); |
| tail.setSystem(true); |
| tail.schedule(); |
| this.traceTailJobs.add(tail); |
| } |
| } |
| |
| @Override |
| public String toString() { |
| return "dm Server"; |
| } |
| |
| protected void addProcessListener(final IProcess newProcess) { |
| if (this.processListener != null || newProcess == null) { |
| return; |
| } |
| |
| this.processListener = new IDebugEventSetListener() { |
| |
| public void handleDebugEvents(DebugEvent[] events) { |
| if (events != null) { |
| int size = events.length; |
| for (int i = 0; i < size; i++) { |
| if (newProcess != null && newProcess.equals(events[i].getSource()) && events[i].getKind() == DebugEvent.TERMINATE) { |
| stopServer(); |
| } |
| } |
| } |
| } |
| }; |
| DebugPlugin.getDefault().addDebugEventListener(this.processListener); |
| |
| for (IConsole console : ConsolePlugin.getDefault().getConsoleManager().getConsoles()) { |
| if (console instanceof ProcessConsole && newProcess.equals(((ProcessConsole) console).getProcess())) { |
| this.processConsole = (ProcessConsole) console; |
| } |
| } |
| |
| File traceDirectory = new File(getRuntimeBaseDirectory().toOSString() + File.separator + "serviceability" + File.separator + "trace"); |
| if (traceDirectory.exists()) { |
| for (File applicationTraceDirectory : traceDirectory.listFiles()) { |
| if (applicationTraceDirectory.isDirectory()) { |
| File traceFile = new File(applicationTraceDirectory.getAbsolutePath() + File.separator + "trace.log"); |
| if (traceFile.exists()) { |
| this.traceFileSizes.put(traceFile.getAbsolutePath(), traceFile.length()); |
| } |
| } |
| } |
| } |
| |
| } |
| |
| public URL getModuleRootURL(IModule module) { |
| try { |
| // check pre condition; only dynamic web projects and java projects |
| // are allowed |
| IProject project = module.getProject(); |
| if (!FacetedProjectFramework.hasProjectFacet(project, FacetCorePlugin.WEB_FACET_ID) |
| || !FacetUtils.hasProjectFacet(project, JavaCore.NATURE_ID)) { |
| return null; |
| } |
| |
| String contextPath = null; |
| |
| BundleManifest bundleManifest = BundleManifestCorePlugin.getBundleManifestManager().getBundleManifest(JavaCore.create(project)); |
| if (bundleManifest != null) { |
| Dictionary<String, String> manifest = bundleManifest.toDictionary(); |
| if (manifest != null && manifest.get(WEB_CONTEXT_PATH_MANIFEST_HEADER) != null) { |
| contextPath = manifest.get(WEB_CONTEXT_PATH_MANIFEST_HEADER); |
| } |
| |
| } |
| if (contextPath == null) { |
| contextPath = module.getName(); |
| } |
| |
| // TODO: CD make port configurable |
| int port = 8080; |
| StringBuilder urlBuilder = new StringBuilder(); |
| urlBuilder.append("http://localhost:"); |
| urlBuilder.append(port); |
| urlBuilder.append("/"); |
| urlBuilder.append(contextPath); |
| |
| String url = urlBuilder.toString(); |
| if (!url.endsWith("/")) { |
| urlBuilder.append("/"); |
| } |
| |
| return new URL(urlBuilder.toString()); |
| } catch (Exception e) { |
| return null; |
| } |
| } |
| |
| public Map<String, DeploymentIdentity> getDeploymentIdentities() { |
| return this.deploymentIdentities; |
| } |
| |
| protected String[] getExcludedRuntimeProgramArguments(boolean starting) { |
| return getVersionHandler().getExcludedRuntimeProgramArguments(starting); |
| } |
| |
| @Override |
| public IModuleResourceDelta[] getPublishedResourceDelta(IModule[] module) { |
| return super.getPublishedResourceDelta(module); |
| } |
| |
| @Override |
| public IModuleResource[] getResources(IModule[] module) { |
| return super.getResources(module); |
| } |
| |
| protected String[] getRuntimeProgramArguments() { |
| return getVersionHandler().getRuntimeProgramArguments(this); |
| } |
| |
| protected String[] getRuntimeVMArguments() { |
| IPath installPath = getServer().getRuntime().getLocation(); |
| IPath configPath = getRuntimeBaseDirectory(); |
| IPath deployPath = getServerDeployDirectory(); |
| return getVersionHandler().getRuntimeVMArguments(this, installPath, configPath, deployPath); |
| } |
| |
| protected void immediateShutdown() { |
| if (getServer().getServerState() == IServer.STATE_STOPPED) { |
| return; |
| } |
| |
| try { |
| setServerState(IServer.STATE_STOPPING); |
| // Revise once on WTP 3 |
| // ILaunch launch = getServer().getLaunch(); |
| if (this.launch != null) { |
| this.launch.terminate(); |
| stopServer(); |
| } |
| } catch (Exception e) { |
| } |
| } |
| |
| @Override |
| protected void publishServer(int kind, IProgressMonitor monitor) throws CoreException { |
| if (getServer().getRuntime() == null) { |
| return; |
| } |
| setServerPublishState(IServer.PUBLISH_STATE_NONE); |
| } |
| |
| protected void setServerStarted() { |
| // Deploy modules |
| this.serverDeployer.deploy(getServer().getModules()); |
| |
| // Set server started |
| setServerState(IServer.STATE_STARTED); |
| } |
| |
| protected void shutdown() { |
| if (getServer().getServerState() == IServer.STATE_STOPPED) { |
| return; |
| } |
| |
| setServerState(IServer.STATE_STOPPING); |
| // Revise once on WTP 3 |
| // ILaunch launch = getServer().getLaunch(); |
| if (this.launch != null) { |
| try { |
| getServerDeployer().shutdown(); |
| } catch (TimeoutException e) { |
| immediateShutdown(); |
| } catch (IOException e) { |
| immediateShutdown(); |
| } |
| } |
| } |
| |
| public synchronized IServerDeployer getServerDeployer() { |
| if (this.serverDeployer == null) { |
| this.serverDeployer = new DefaultServerDeployer(this); |
| } |
| return this.serverDeployer; |
| } |
| |
| public String getMBeanServerIp() { |
| return this.launch.getAttribute(PROPERTY_MBEAN_SERVER_IP); |
| } |
| |
| class ServerLogTail extends Job { |
| |
| private final IOConsoleOutputStream stream; |
| |
| public ServerLogTail(ServerBehaviour server, DeploymentIdentity identity, ProcessConsole console) { |
| super(server.getServer().getName()); |
| this.logfile = new File(server.getRuntimeBaseDirectory().toOSString() + File.separator + "serviceability" + File.separator + "trace" |
| + File.separator + identity.getSymbolicName() + "-" + identity.getVersion() + File.separator + "trace.log"); |
| this.stream = console.newOutputStream(); |
| Long fileSize = ServerBehaviour.this.traceFileSizes.get(this.logfile.getAbsolutePath()); |
| if (fileSize != null) { |
| this.filePointer = fileSize; |
| } |
| } |
| |
| /** How frequently to check for file changes; defaults to 5 seconds */ |
| private final long sampleInterval = 1000; |
| |
| /** The log file to tail */ |
| private final File logfile; |
| |
| /** Is the tailer currently tailing? */ |
| private boolean tailing = false; |
| |
| /** The file pointer keeps track of where we are in the file */ |
| private long filePointer = 0; |
| |
| @Override |
| protected IStatus run(IProgressMonitor monitor) { |
| |
| this.tailing = true; |
| while (this.tailing && !this.logfile.exists()) { |
| try { |
| Thread.sleep(this.sampleInterval); |
| } catch (InterruptedException e) { |
| } |
| } |
| |
| try { |
| // Start tailing |
| RandomAccessFile file = new RandomAccessFile(this.logfile, "r"); |
| while (this.tailing) { |
| try { |
| // Compare the length of the file to the file pointer |
| long fileLength = this.logfile.length(); |
| if (fileLength < this.filePointer) { |
| // Log file must have been rotated or deleted; |
| // reopen the file and reset the file pointer |
| file = new RandomAccessFile(this.logfile, "r"); |
| this.filePointer = 0; |
| } |
| |
| if (fileLength > this.filePointer) { |
| // There is data to read |
| file.seek(this.filePointer); |
| byte[] line = new byte[1024]; |
| int length = file.read(line); |
| while (length > 0) { |
| this.stream.write(line, 0, length); |
| line = new byte[1024]; |
| length = file.read(line); |
| } |
| this.filePointer = file.getFilePointer(); |
| } |
| |
| // Sleep for the specified interval |
| Thread.sleep(this.sampleInterval); |
| } catch (Exception e) { |
| } |
| } |
| |
| // Close the file that we are tailing |
| file.close(); |
| } catch (Exception e) { |
| |
| } |
| |
| return Status.OK_STATUS; |
| } |
| |
| public void stopTailing() { |
| this.tailing = false; |
| } |
| } |
| |
| public Map<Long, IBundle> getBundles(IProgressMonitor monitor) throws CoreException { |
| try { |
| return getVersionHandler().getServerBundleAdminCommand(this).execute(); |
| } catch (IOException e) { |
| } catch (TimeoutException e) { |
| } |
| return Collections.emptyMap(); |
| } |
| |
| public void startBundle(long bundleId) throws CoreException { |
| executeCommand("start " + bundleId); |
| } |
| |
| public void stopBundle(long bundleId) throws CoreException { |
| executeCommand("stop " + bundleId); |
| } |
| |
| public void refreshBundle(long bundleId) throws CoreException { |
| executeCommand("refresh " + bundleId); |
| } |
| |
| public void updateBundle(long bundleId) throws CoreException { |
| executeCommand("update " + bundleId); |
| } |
| |
| public String executeCommand(String command) throws CoreException { |
| try { |
| return getVersionHandler().getServerBundleAdminExecuteCommand(this, command).execute(); |
| } catch (IOException e) { |
| } catch (TimeoutException e) { |
| } |
| return "<error>"; |
| } |
| |
| } |