blob: f363542cbac860451809468544e4a31163fe2438 [file] [log] [blame]
/*********************************************************************
* 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>";
}
}