/*
* $Id: JettyLaunchConfigurationType.java 97 2011-03-21 21:29:48Z tonylovejava@gmail.com $
* $HeadURL: https://run-jetty-run.googlecode.com/svn/trunk/plugin/src/runjettyrun/JettyLaunchConfigurationType.java $
*
* ==============================================================================
* Licensed 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 runjettyrun;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.debug.core.ILaunch;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.launching.AbstractJavaLaunchConfigurationDelegate;
import org.eclipse.jdt.launching.ExecutionArguments;
import org.eclipse.jdt.launching.IRuntimeClasspathEntry;
import org.eclipse.jdt.launching.JavaRuntime;
import org.eclipse.jdt.launching.VMRunnerConfiguration;
import runjettyrun.utils.RunJettyRunClasspathResolver;
import runjettyrun.utils.RunJettyRunClasspathUtil;
/**
* Launch configuration type for Jetty. Based on
* org.eclipse.jdt.launching.JavaLaunchDelegate.
*
* @author hillenius
*/
public class JettyLaunchConfigurationType extends
AbstractJavaLaunchConfigurationDelegate {
private static HashMap<String,ILaunch> launcher = new HashMap<String,ILaunch>();
/**
* Here's what the WebApp classpath. That means all the classpath here just like WEB-INF/classes or WEB-INF/lib ,
* which is only for the specific webapp project and will not used for the Jetty Instance.
*
* @param configuration
* @return
* @throws CoreException
*/
private String getWebappClasspath(ILaunchConfiguration configuration) throws CoreException{
String[] webAppClasspathArray = getProjectClasspath(configuration);
String webAppClasspath = "";
StringBuilder sb = new StringBuilder();
for (int i = 0; i < webAppClasspathArray.length; i++) {
String path = webAppClasspathArray[i];
if (sb.length() > 0)
sb.append(File.pathSeparator);
sb.append(path);
}
webAppClasspath = sb.toString();
/**
* The smallest limit for windows XP is 2048
*/
if(webAppClasspath.length() > 1024){
File f = prepareClasspathFile(configuration,webAppClasspath);
webAppClasspath = "file://"+f.getAbsolutePath();
}
return webAppClasspath;
}
/**
* Get working directory's absolute folder path.
*
* @param configuration
* @return return the path if exist, or return null.
* @throws CoreException
*/
private String getWorkingDirectoryAbsolutePath(ILaunchConfiguration configuration) throws CoreException{
File workingDir = verifyWorkingDirectory(configuration);
String workingDirName = null;
if (workingDir != null)
workingDirName = workingDir.getAbsolutePath();
return workingDirName;
}
/**
* I prefer to change the name to JettyClasspath to make it more clear.
* This classpath means how the RunJettyRun to get the Jetty bundle.
*
* If you change the classpath , that might means you are changing the Jetty version or something on it.
* @param configuration
* @return
* @throws CoreException
*/
private String[] getJettyClasspath(ILaunchConfiguration configuration) throws CoreException {
return getClasspath(configuration);
}
/**
* get Runtime arguments , and prepare the webapp classpath for the program.
* @param configuration
* @param oringinalVMArguments
* @return
* @throws CoreException
*/
private String[] getRuntimeArguments(ILaunchConfiguration configuration,String[] oringinalVMArguments,String webappClasspath ) throws CoreException{
List<String> runtimeVmArgs = getJettyArgs(configuration);
//Here the classpath is really for web app.
runtimeVmArgs.add("-Drjrclasspath=" + webappClasspath);
runtimeVmArgs.addAll(Arrays.asList(oringinalVMArguments));
return runtimeVmArgs.toArray(new String[runtimeVmArgs.size()]);
}
/**
* The launcher !
*/
public void launch(ILaunchConfiguration configuration, String mode,
ILaunch launch, IProgressMonitor monitor) throws CoreException {
if (monitor == null) {
monitor = new NullProgressMonitor();
}
monitor.beginTask(
MessageFormat.format("{0}...", configuration.getName()), 3); //$NON-NLS-1$
// check for cancellation
if (monitor.isCanceled()) return;
try {
monitor.subTask("verifying installation");
// Program & VM arguments
ExecutionArguments execArgs = new ExecutionArguments(getVMArguments(configuration),
getProgramArguments(configuration));
// Create VM configuration
//here the classpath means for the Jetty Server , not for the application! by TonyQ 2011/3/7
VMRunnerConfiguration runConfig = new VMRunnerConfiguration(
Plugin.BOOTSTRAP_CLASS_NAME , getJettyClasspath(configuration));
runConfig.setProgramArguments(execArgs.getProgramArgumentsArray());
// Environment variables
runConfig.setEnvironment(getEnvironment(configuration));
//Here prepare the classpath is really for webapp in Runtime Arguments , too.
runConfig.setVMArguments(getRuntimeArguments(configuration,execArgs.getVMArgumentsArray(),
getWebappClasspath(configuration)));
runConfig.setWorkingDirectory(getWorkingDirectoryAbsolutePath(configuration));
runConfig.setVMSpecificAttributesMap(getVMSpecificAttributesMap(configuration));
// Boot path
runConfig.setBootClassPath(getBootpath(configuration));
// check for cancellation
if (monitor.isCanceled()) return;
// stop in main
prepareStopInMain(configuration);
// done the verification phase
monitor.worked(1);
monitor.subTask("Creating source locator");
// set the default source locator if required
setDefaultSourceLocator(launch, configuration);
monitor.worked(1);
terminateOldRJRLauncher(configuration, launch);
registerRJRLauncher(configuration, launch);
// Launch the configuration - 1 unit of work
getVMRunner(configuration, mode).run(runConfig, launch, monitor);
// check for cancellation
if (monitor.isCanceled()) return;
} finally {
monitor.done();
}
}
/**
* A private helper to prepare a classpath file to workspace metadata,
* we use this to prevent classpath too long which was caused the problem
* for reaching Windows command length limitation.
*
* @param configuration
* @param classpath
* @return
*/
private File prepareClasspathFile(ILaunchConfiguration configuration,String classpath){
IPath path = Plugin.getDefault().getStateLocation().append(configuration.getName()+".classpath");
File f = path.toFile();
try{
BufferedWriter out = new BufferedWriter(new OutputStreamWriter
(new FileOutputStream(f,false),"UTF8"));
out.write(classpath);
out.close();
return f;
}catch(IOException e){
return null;
}
}
/**
* Terminate old Run-Jetty-Run launcher which use same port if exist.
*
* @param configuration
* @param launch
* @throws CoreException
*/
private static void terminateOldRJRLauncher(ILaunchConfiguration configuration, ILaunch launch) throws CoreException{
String port = configuration.getAttribute(Plugin.ATTR_PORT,"");
String sslPort = configuration.getAttribute(Plugin.ATTR_SSL_PORT,"");
boolean enableSSL = configuration.getAttribute(Plugin.ATTR_ENABLE_SSL,false);
if(!"".equals(port) && launcher.containsKey(port)){
if(!launcher.get(port).isTerminated()){
launcher.get(port).terminate();
launcher.remove(port);
}
}
if(enableSSL && !"".equals(sslPort) && launcher.containsKey(sslPort)){
launcher.get(sslPort).terminate();
launcher.remove(sslPort);
}
}
/**
* register a port for RJR Launcher
* @param configuration
* @param launch
* @throws CoreException
*/
private static void registerRJRLauncher(ILaunchConfiguration configuration, ILaunch launch) throws CoreException{
String port = configuration.getAttribute(Plugin.ATTR_PORT,"");
String sslPort = configuration.getAttribute(Plugin.ATTR_SSL_PORT,"");
boolean enableSSL = configuration.getAttribute(Plugin.ATTR_ENABLE_SSL,false);
if(!"".equals(port) ) launcher.put(port,launch);
if(enableSSL && !"".equals(sslPort))
launcher.put(sslPort,launch);
}
private List<String> getJettyArgs(ILaunchConfiguration configuration)
throws CoreException {
List<String> runtimeVmArgs = new ArrayList<String>();
addOptionalAttr(configuration, runtimeVmArgs, Plugin.ATTR_CONTEXT,
"context");
addOptionalAttr(configuration, runtimeVmArgs, Plugin.ATTR_WEBAPPDIR,
"webapp");
addOptionalAttr(configuration, runtimeVmArgs, Plugin.ATTR_PORT, "port");
addOptionalAttr(configuration, runtimeVmArgs, Plugin.ATTR_SSL_PORT,
"sslport");
addOptionalAttr(configuration, runtimeVmArgs, Plugin.ATTR_KEYSTORE,
"keystore");
addOptionalAttr(configuration, runtimeVmArgs, Plugin.ATTR_KEY_PWD,
"keypassword");
addOptionalAttr(configuration, runtimeVmArgs, Plugin.ATTR_PWD,
"password");
addOptionalAttr(configuration, runtimeVmArgs,
Plugin.ATTR_SCANINTERVALSECONDS, "scanintervalseconds");
addOptionalAttrx(configuration, runtimeVmArgs,
Plugin.ATTR_ENABLE_SCANNER, "enablescanner");
addOptionalAttrx(configuration, runtimeVmArgs,
Plugin.ATTR_ENABLE_SSL, "enablessl");
addOptionalAttrx(configuration, runtimeVmArgs,
Plugin.ATTR_ENABLE_NEED_CLIENT_AUTH, "needclientauth");
addOptionalAttrx(configuration, runtimeVmArgs,
Plugin.ATTR_ENABLE_PARENT_LOADER_PRIORITY, "parentloaderpriority");
return runtimeVmArgs;
}
private void addOptionalAttr(ILaunchConfiguration configuration,
List<String> runtimeVmArgs, String cfgAttr, String argName)
throws CoreException {
String value = configuration.getAttribute(cfgAttr, "");
if("webapp".equals(argName)&& "/".equals(value)){
value="./";
}
if (value.length() == 0)
return;
String arg = "-Drjr" + argName + "=" + value;
runtimeVmArgs.add(arg);
return;
}
private void addOptionalAttrx(ILaunchConfiguration configuration,
List<String> runtimeVmArgs, String cfgAttr, String argName)
throws CoreException {
Boolean value = configuration.getAttribute(cfgAttr, false);
String arg = "-Drjr" + argName + "=" + value;
runtimeVmArgs.add(arg);
return;
}
/**
* Returns the class path to be used by the web app context (not by Jetty,
* or as JRE Bootstrap).
* <p>
* Added by James Synge.
*
* Copied from {@link AbstractJavaLaunchConfigurationDelegate} so that I can
* eliminate everything that should be in WEB-INF/lib, but is not supposed
* to be in the project's classpath.
*
* (non-Javadoc)
*
* @see org.eclipse.jdt.launching.AbstractJavaLaunchConfigurationDelegate#getClasspath(org.eclipse.debug.core.ILaunchConfiguration)
*/
private String[] getProjectClasspath(ILaunchConfiguration configuration)
throws CoreException {
IJavaProject proj = JavaRuntime.getJavaProject(configuration);
if (proj == null) {
Plugin.logError("No project!");
return new String[0];
}
IRuntimeClasspathEntry[] entries =
RunJettyRunClasspathUtil.filterWebInfLibs(JavaRuntime.computeUnresolvedRuntimeClasspath(proj),configuration);
// Remove JRE entry/entries.
IRuntimeClasspathEntry stdJreEntry = JavaRuntime
.computeJREEntry(configuration);
IRuntimeClasspathEntry projJreEntry = JavaRuntime.computeJREEntry(proj);
List<IRuntimeClasspathEntry> entryList = new ArrayList<IRuntimeClasspathEntry>(
entries.length);
for (int i = 0; i < entries.length; i++) {
IRuntimeClasspathEntry entry = entries[i];
if (entry.equals(stdJreEntry))
continue;
if (entry.equals(projJreEntry))
continue;
entryList.add(entry);
}
// Resolve the entries to actual file/folder locations.
entries = entryList.toArray(new IRuntimeClasspathEntry[0]);
entries = RunJettyRunClasspathResolver.resolveClasspath(entries, configuration);
// entries = JavaRuntime.resolveRuntimeClasspath(entries,
// configuration);
Set<String> locations = new LinkedHashSet<String>();
for (int i = 0; i < entries.length; i++) {
IRuntimeClasspathEntry entry = entries[i];
if (entry.getClasspathProperty() == IRuntimeClasspathEntry.USER_CLASSES) {
String location = entry.getLocation();
if (location != null) {
locations.add(location);
}
}
}
locations.addAll( RunJettyRunClasspathUtil.getWebInfLibLocations(configuration) );
return (String[]) locations.toArray(new String[locations.size()]);
}
}