/******************************************************************************* * Copyright (c) 2010, 2011 Eteration A.S. and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation * - This code is based on WTP SDK frameworks and Tomcat Server Adapters * org.eclipse.jst.server.core * org.eclipse.jst.server.ui * * Naci Dai and Murat Yener, Eteration A.S. * Kaloyan Raev, SAP AG - integration with OSGi Framework Editor parts *******************************************************************************/ package org.eclipse.libra.framework.core; import java.io.FileInputStream; import java.io.FileOutputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Properties; 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.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.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.libra.framework.editor.integration.admin.osgijmx.LaunchOSGiJMXFrameworkAdmin; import org.eclipse.libra.framework.editor.integration.console.basic.LaunchBasicOSGiFrameworkConsole; import org.eclipse.wst.server.core.IModule; import org.eclipse.wst.server.core.IServer; import org.eclipse.wst.server.core.internal.IModulePublishHelper; import org.eclipse.wst.server.core.model.IModuleFile; import org.eclipse.wst.server.core.model.IModuleResource; import org.eclipse.wst.server.core.model.IModuleResourceDelta; import org.eclipse.wst.server.core.model.ServerBehaviourDelegate; @SuppressWarnings("restriction") public abstract class OSGIFrameworkInstanceBehaviorDelegate extends ServerBehaviourDelegate implements IModulePublishHelper, IOSGiFrameworkAdmin, IOSGiFrameworkConsole { private static final String ATTR_STOP = "stop-server"; // the thread used to ping the server to check for startup protected transient PingThread ping = null; protected transient IDebugEventSetListener processListener; private IOSGiFrameworkAdmin admin = null; private IOSGiFrameworkConsole console = null; public abstract String[] getFrameworkProgramArguments(boolean starting) ; public abstract String[] getExcludedFrameworkProgramArguments(boolean starting); public abstract String[] getFrameworkVMArguments(); public abstract String getFrameworkClass() ; protected static int getNextToken(String s, int start) { int i = start; int length = s.length(); char lookFor = ' '; while (i < length) { char c = s.charAt(i); if (lookFor == c) { if (lookFor == '"') return i + 1; return i; } if (c == '"') lookFor = '"'; i++; } return -1; } /** * Merge the given arguments into the original argument string, replacing * invalid values if they have been changed. Special handling is provided if * the keepActionLast argument is true and the last vmArg is a simple * string. The vmArgs will be merged such that the last vmArg is guaranteed * to be the last argument in the merged string. * * @param originalArg * String of original arguments. * @param vmArgs * Arguments to merge into the original arguments string * @param excludeArgs * Arguments to exclude from the original arguments string * @param keepActionLast * If <b>true</b> the vmArguments are assumed to be Framework program * arguments, the last of which is the action to perform which * must remain the last argument. This only has an impact if the * last vmArg is a simple string argument, like * "start". * @return merged argument string */ public static String mergeArguments(final String originalArg, String[] vmArgs, String[] excludeArgs, boolean keepActionLast) { if (vmArgs == null) return originalArg; String arg = originalArg; if (arg == null) arg = ""; arg = concatArgs(originalArg, vmArgs, keepActionLast) ; arg = excludeArgs(originalArg, excludeArgs); return arg; } private static String concatArgs(String initialArgs, String[] newArgs, boolean keepLast) { if(newArgs == null || newArgs.length <= 0) return initialArgs; StringBuffer args = new StringBuffer(); List<String> initArgsList = Arrays.asList(DebugPlugin.parseArguments(initialArgs)); int i= 0; if (initialArgs != null && initialArgs.length() > 0) { int last= (keepLast? initArgsList.size()-1 : initArgsList.size() ); for (i=0; i< last ; i++) { if(i != 0) args.append(' '); args.append(initArgsList.get(i)); } } for (String anArg: newArgs) { if(!initArgsList.contains(anArg)){ if(i++ != 0) args.append(' '); args.append(anArg); } } if(keepLast && initArgsList.size() >0 ){ args.append(' '); args.append(initArgsList.get(initArgsList.size()-1)); } return args.toString(); } private static String excludeArgs(String initialArgs, String[] excludeArgs) { if(excludeArgs == null || excludeArgs.length <= 0) return initialArgs; StringBuffer args = new StringBuffer(); List<String> excludeArgsList = Arrays.asList(excludeArgs); if (initialArgs != null && initialArgs.length() > 0) { String[] initArgsArray = DebugPlugin.parseArguments(initialArgs); int i=0; for (String anArg : initArgsArray) { if (!excludeArgsList.contains(anArg)) { if(i++ != 0) args.append(' '); args.append(anArg); } } } return args.toString(); } /** * Replace the current JRE container classpath with the given entry. * * @param cp * @param entry */ public static void replaceJREContainer(List<IRuntimeClasspathEntry> cp, IRuntimeClasspathEntry entry) { int size = cp.size(); for (int i = 0; i < size; i++) { IRuntimeClasspathEntry entry2 = cp.get(i); if (entry2.getPath().uptoSegment(2).isPrefixOf(entry.getPath())) { cp.set(i, entry); return; } } cp.add(0, entry); } /** * Merge a single classpath entry into the classpath list. * * @param cp * @param entry */ public static void mergeClasspath(List<IRuntimeClasspathEntry> cp, IRuntimeClasspathEntry entry) { Iterator<IRuntimeClasspathEntry> iterator = cp.iterator(); while (iterator.hasNext()) { IRuntimeClasspathEntry entry2 = iterator.next(); if (entry2.getPath().equals(entry.getPath())) return; } cp.add(entry); } public OSGIFrameworkInstanceBehaviorDelegate() { super(); } public void setServerStarted() { setServerState(IServer.STATE_STARTED); } public void stopImpl() { if (ping != null) { ping.stop(); ping = null; } if (processListener != null) { DebugPlugin.getDefault().removeDebugEventListener(processListener); processListener = null; } admin = null; console = null; setServerState(IServer.STATE_STOPPED); } /** * Cleanly shuts down and terminates the server. * * @param force * <code>true</code> to kill the server */ @Override public void stop(boolean force) { if (force) { terminate(); return; } int state = getServer().getServerState(); // If stopped or stopping, no need to run stop command again if (state == IServer.STATE_STOPPED || state == IServer.STATE_STOPPING) return; else if (state == IServer.STATE_STARTING) { terminate(); return; } try { if (Trace.isTraceEnabled()) Trace.trace(Trace.FINER, "Stopping OSGi Framework"); if (state != IServer.STATE_STOPPED) setServerState(IServer.STATE_STOPPING); // ILaunchConfiguration launchConfig = ((Server) // getServer()).getLaunchConfiguration(true, null); // ILaunchConfigurationWorkingCopy wc = // launchConfig.getWorkingCopy(); // // String args = // renderCommandLine(getRuntimeProgramArguments(false)," "); // wc.setAttribute(IJavaLaunchConfigurationConstants.ATTR_PROGRAM_ARGUMENTS,args); // wc.setAttribute("org.eclipse.debug.ui.private", true); // wc.setAttribute(ATTR_STOP, "true"); // wc.launch(ILaunchManager.RUN_MODE, new NullProgressMonitor()); this.terminate(); } catch (Exception e) { Trace.trace(Trace.SEVERE, "Error stopping OSGi Framework", e); } } /** * Terminates the server. */ protected void terminate() { if (getServer().getServerState() == IServer.STATE_STOPPED) return; try { setServerState(IServer.STATE_STOPPING); if (Trace.isTraceEnabled()) Trace.trace(Trace.FINER, "Killing the OSGi Framework process"); ILaunch launch = getServer().getLaunch(); if (launch != null) { launch.terminate(); stopImpl(); } } catch (Exception e) { Trace.trace(Trace.SEVERE, "Error killing the process", e); } } @Override public IPath getTempDirectory() { return super.getTempDirectory(false); } public IPath getBaseDirectory() { IPath confDir = getTempDirectory(true); String instancePathStr = getFrameworkInstance().getInstanceDirectory(); if (instancePathStr != null) { IPath instanceDir = new Path(getFrameworkInstance() .getInstanceDirectory()); confDir = instanceDir; } return confDir; } @Override public void initialize(IProgressMonitor monitor) { // do nothing } public IOSGIFramework getFramework() { if (getServer().getRuntime() == null) return null; return (IOSGIFramework) getServer().getRuntime().loadAdapter( IOSGIFramework.class, null); } public IOSGIFrameworkInstance getFrameworkInstance() { return (IOSGIFrameworkInstance) getServer().loadAdapter( IOSGIFrameworkInstance.class, null); } public void addProcessListener(final IProcess newProcess) { if (processListener != null || newProcess == null) return; processListener = new IDebugEventSetListener() { public void handleDebugEvents(DebugEvent[] events) { if (events != null) { int size = events.length; for (int i = 0; i < size; i++) { if (true && newProcess.equals(events[i].getSource()) && events[i].getKind() == DebugEvent.TERMINATE) { stopImpl(); } } } } }; DebugPlugin.getDefault().addDebugEventListener(processListener); } /** * Setup for starting the server. * * @param launch * ILaunch * @param launchMode * String * @param monitor * IProgressMonitor * @throws CoreException * if anything goes wrong */ public void setupLaunch(ILaunch launch, String launchMode, IProgressMonitor monitor) throws CoreException { if ("true".equals(launch.getLaunchConfiguration().getAttribute(ATTR_STOP, "false"))) return; if (getFramework() == null) throw new CoreException(new Status(IStatus.ERROR, FrameworkCorePlugin.PLUGIN_ID, "Can't launch for OSGi Framework")); IStatus status = getFramework().validate(); if (status != null && status.getSeverity() == IStatus.ERROR) throw new CoreException(status); setServerRestartState(false); setServerState(IServer.STATE_STARTING); setMode(launchMode); // ping server to check for startup try { String url = "http://" + getServer().getHost(); ping = new PingThread(launch, getServer(), url, -1, this); } catch (Exception e) { Trace.trace(Trace.SEVERE, "Can't ping for OSGi Framework startup."); } } /** * Return a string representation of this object. * * @return java.lang.String */ @Override public String toString() { return "OSGiFrameworkInstance"; } @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, mergeArguments(existingProgArgs, getFrameworkProgramArguments(true), getExcludedFrameworkProgramArguments(true), true)); String existingVMArgs = workingCopy.getAttribute( IJavaLaunchConfigurationConstants.ATTR_VM_ARGUMENTS, (String) null); String[] parsedVMArgs = null; if (null != existingVMArgs) { parsedVMArgs = DebugPlugin.parseArguments(existingVMArgs); } String[] configVMArgs = getFrameworkVMArguments(); workingCopy.setAttribute( IJavaLaunchConfigurationConstants.ATTR_VM_ARGUMENTS, mergeArguments(existingVMArgs, configVMArgs, null, false)); IOSGIFramework runtime = getFramework(); IVMInstall vmInstall = runtime.getVMInstall(); if (vmInstall != null) workingCopy.setAttribute( IJavaLaunchConfigurationConstants.ATTR_JRE_CONTAINER_PATH, JavaRuntime.newJREContainerPath(vmInstall) .toPortableString()); // update classpath 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]); @SuppressWarnings("unchecked") List<IRuntimeClasspathEntry> cp2 = runtime.getFrameworkClasspath(null); Iterator<IRuntimeClasspathEntry> iterator = cp2.iterator(); while (iterator.hasNext()) { IRuntimeClasspathEntry entry = iterator .next(); mergeClasspath(oldCp, entry); } if (vmInstall != null) { try { String typeId = vmInstall.getVMInstallType().getId(); 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.toFile().exists()){ IPath toolsPath = jrePath.append("lib").append("tools.jar"); if (toolsPath.toFile().exists()) { IRuntimeClasspathEntry toolsJar = JavaRuntime .newArchiveRuntimeClasspathEntry(toolsPath); // Search for index to any existing tools.jar entry 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 existing tools.jar found, replace in case it's // different. Otherwise add. if (toolsIndex < oldCp.size()) oldCp.set(toolsIndex, toolsJar); else 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) { Trace.trace(Trace.SEVERE, "Could not resolve classpath entry: " + entry, e); } } workingCopy.setAttribute( IJavaLaunchConfigurationConstants.ATTR_CLASSPATH, list); workingCopy .setAttribute( IJavaLaunchConfigurationConstants.ATTR_DEFAULT_CLASSPATH, false); } @Override protected IModuleResource[] getResources(IModule[] module) { return super.getResources(module); } @Override protected IModuleResourceDelta[] getPublishedResourceDelta(IModule[] module) { return super.getPublishedResourceDelta(module); } /** * @see ServerBehaviourDelegate#handleResourceChange() */ @Override public void handleResourceChange() { if (getServer().getServerRestartState()) return; List<IModule[]> modules = getAllModules(); for(IModule[] module: modules) { IModuleResourceDelta[] delta = getPublishedResourceDelta(module); if (delta == null || delta.length == 0) continue; if (containsNonResourceChange(delta)) { setServerRestartState(true); return; } } } protected boolean containsNonResourceChange(IModuleResourceDelta[] delta) { int size = delta.length; for (int i = 0; i < size; i++) { IModuleResourceDelta d = delta[i]; if (d.getModuleRelativePath().segmentCount() == 0) { if ("WEB-INF".equals(d.getModuleResource().getName())) { return containsNonResourceChange(d.getAffectedChildren()); } continue; } if (d.getModuleResource() instanceof IModuleFile) return true; boolean b = containsNonAddChange(d.getAffectedChildren()); if (b) return true; } return false; } protected boolean containsNonAddChange(IModuleResourceDelta[] delta) { if (delta == null) return false; int size = delta.length; for (int i = 0; i < size; i++) { IModuleResourceDelta d = delta[i]; if (d.getModuleResource() instanceof IModuleFile) { if (d.getKind() != IModuleResourceDelta.ADDED) return true; } boolean b = containsNonAddChange(d.getAffectedChildren()); if (b) return true; } return false; } /** * Cleans the entire work directory for this server. This involves deleting * all subdirectories of the server's work directory. * * @param monitor * a progress monitor * @return results of the clean operation * @throws CoreException */ public IStatus cleanFrameworkInstanceWorkDir(IProgressMonitor monitor) throws CoreException { return Status.OK_STATUS; } public IPath getPublishDirectory(IModule[] module) { return getServerDeployDirectory(); } /** * Gets the directory to which modules should be deployed for this server. * * @return full path to deployment directory for the server */ public IPath getServerDeployDirectory() { return new Path(getFrameworkInstance().getInstanceDirectory()); } /** * Gets the directory to which to deploy a module's web application. * * @param module * a module * @return full path to deployment directory for the module */ public IPath getModuleDeployDirectory(IModule module) { return getServerDeployDirectory().append(module.getName()); } public void setModulePublishState2(IModule[] module, int state) { setModulePublishState(module, state); } public Properties loadModulePublishLocations() { Properties p = new Properties(); IPath path = getTempDirectory().append("publish.txt"); FileInputStream fin = null; try { fin = new FileInputStream(path.toFile()); p.load(fin); } catch (Exception e) { // ignore } finally { try { if (fin!=null) fin.close(); } catch (Exception ex) { // ignore } } return p; } public void saveModulePublishLocations(Properties p) { IPath path = getTempDirectory().append("publish.txt"); FileOutputStream fout = null; try { fout = new FileOutputStream(path.toFile()); p.store(fout, "OSGi Framework publish data"); } catch (Exception e) { // ignore } finally { try { if (fout!=null) fout.close(); } catch (Exception ex) { // ignore } } } public Map<Long, IBundle> getBundles(IProgressMonitor monitor) throws CoreException { return getAdmin().getBundles(monitor); } public void startBundle(long bundleId) throws CoreException { getAdmin().startBundle(bundleId); } public void stopBundle(long bundleId) throws CoreException { getAdmin().stopBundle(bundleId); } public void refreshBundle(long bundleId) throws CoreException { getAdmin().refreshBundle(bundleId); } public void updateBundle(long bundleId) throws CoreException { getAdmin().updateBundle(bundleId); } public String executeCommand(String command) throws CoreException { return getConsole().executeCommand(command); } private IOSGiFrameworkAdmin getAdmin() throws CoreException { if (admin == null) { admin = new LaunchOSGiJMXFrameworkAdmin(getLaunch()); } return admin; } private IOSGiFrameworkConsole getConsole() throws CoreException { if (console == null) { console = new LaunchBasicOSGiFrameworkConsole(getLaunch()); } return console; } private ILaunch getLaunch() throws CoreException { IServer server = getServer(); if (server == null) { throw new CoreException(new Status(IStatus.ERROR, FrameworkCorePlugin.PLUGIN_ID, Messages.OSGIFrameworkInstanceBehaviorDelegate_ServerNotInitialized)); } ILaunch launch = server.getLaunch(); if (launch == null) { throw new CoreException(new Status(IStatus.ERROR, FrameworkCorePlugin.PLUGIN_ID, Messages.OSGIFrameworkInstanceBehaviorDelegate_ServerNotStarted)); } return launch; } }