/*
* Adito
*
* Copyright (C) 2003-2006 3SP LTD. All Rights Reserved
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package com.adito.applications.types;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.jdom.Element;
import com.adito.applications.ApplicationLauncherType;
import com.adito.applications.ApplicationShortcut;
import com.adito.applications.server.ApplicationServerType;
import com.adito.applications.server.ProcessMonitor;
import com.adito.applications.server.ServerApplicationLauncher;
import com.adito.applications.server.ServerLauncher;
import com.adito.applications.server.ServerLauncherEvents;
import com.adito.boot.SystemProperties;
import com.adito.boot.XMLElement;
import com.adito.extensions.ExtensionDescriptor;
import com.adito.extensions.ExtensionException;
import com.adito.policyframework.LaunchSession;
import com.adito.policyframework.Resource.LaunchRequirement;
import com.adito.security.SessionInfo;
/**
* Implementation of an
* {@link com.adito.applications.ApplicationLauncherType} that allows
* execution of Java applications using a running VPN client.
* <p>
* This launcher will provide links to launch the VPN client if it is not
* running before launching the applicaiton itself.
*/
public class JavasType implements ApplicationLauncherType,
ApplicationServerType {
final static Log log = LogFactory.getLog(JavasType.class);
/**
* Type name
*/
public final static String TYPE = "javas";
// Private instance variables
private String jre;
private ExtensionDescriptor descriptor;
private String classpath = "";
private String mainclass;
private File workingDir;
private String[] jvm;
private List<String> programArgs = new ArrayList<String>();
private List<String> jvmArgs = new ArrayList<String>();
private ProcessMonitor process;
private String javaLibraryPath = "";
protected ServerLauncherEvents events;
protected ServerLauncher launcher;
/*
* (non-Javadoc)
*
* @see com.adito.extensions.ExtensionType#start(com.adito.extensions.ExtensionDescriptor,
* org.jdom.Element)
*/
public void start(ExtensionDescriptor descriptor, Element element)
throws ExtensionException {
this.descriptor = descriptor;
if (element.getName().equals(TYPE)) {
jre = element.getAttribute("jre").getValue();
if (jre == null) {
throw new ExtensionException(
ExtensionException.FAILED_TO_PROCESS_DESCRIPTOR,
"<application> element requires attribute 'jre'");
}
try {
ExtensionDescriptor.getVersion(jre);
} catch (Throwable ex) {
throw new ExtensionException(
ExtensionException.FAILED_TO_PROCESS_DESCRIPTOR,
"Invalid value '" + jre
+ "' specified for 'jre' attribute");
}
for (Iterator it = element.getChildren().iterator(); it.hasNext();) {
Element e = (Element) it.next();
if (e.getName().equalsIgnoreCase("classpath")) {
verifyClasspath(e);
} else if (e.getName().equalsIgnoreCase("main")) {
verifyMain(e);
} else {
throw new ExtensionException(
ExtensionException.FAILED_TO_PROCESS_DESCRIPTOR,
"Unexpected element <" + e.getName()
+ "> found in <application>");
}
}
}
}
/*
* (non-Javadoc)
*
* @see com.adito.extensions.ExtensionType#verifyRequiredElements()
*/
public void verifyRequiredElements() throws ExtensionException {
}
/*
* (non-Javadoc)
*
* @see com.adito.extensions.ExtensionType#isHidden()
*/
public boolean isHidden() {
return false;
}
/*
* (non-Javadoc)
*
* @see com.adito.extensions.ExtensionType#getType()
*/
public String getType() {
return TYPE;
}
/*
* (non-Javadoc)
*
* @see com.adito.applications.server.ApplicationServerType#prepare(com.adito.applications.server.ServerLauncher,
* com.adito.applications.server.ServerLauncherEvents,
* com.adito.boot.XMLElement)
*/
public void prepare(ServerLauncher launcher, ServerLauncherEvents events,
XMLElement element) throws IOException {
if (events != null)
events.debug("Processing <" + element.getName()
+ "> for java application type");
this.launcher = launcher;
this.events = events;
if (element.getName().equals("java")) {
String jre = (String) element.getAttribute("jre");
if (events != null)
events
.debug("Checking our version against the required application version "
+ jre);
if (!ServerLauncher.checkVersion(jre)) {
throw new IOException(
"Application requires Java Runtime Environment " + jre);
}
/**
* LDP - Don't reset the classpath as this stops extended extensions
* (such as the agent extension itself) from adding addtional
* classpath entries.
*/
if (SystemProperties.get("java.version").startsWith("1.1")
&& !SystemProperties.get("java.vendor").startsWith(
"Microsoft"))
classpath = SystemProperties.get("java.home")
+ File.pathSeparator + "lib" + File.pathSeparator
+ "classes.zip";
Enumeration e = element.enumerateChildren();
while (e.hasMoreElements()) {
XMLElement el = (XMLElement) e.nextElement();
if (el.getName().equalsIgnoreCase("classpath")) {
buildClassPath(el);
} else if (el.getName().equalsIgnoreCase("main")) {
mainclass = (String) el.getAttribute("class");
if (events != null)
events.debug("Main class is " + mainclass);
String dir = (String) el.getAttribute("dir");
if (events != null)
events.debug("Dir is " + dir);
if (dir != null) {
workingDir = new File(launcher.replaceTokens(dir));
} else {
workingDir = null;
}
buildProgramArguments(el);
}
}
if (events != null)
events.debug("Finished preparing application descriptor.");
} else {
if (events != null)
events.debug("Ignoring <" + element.getName()
+ "> tag as it is not a java application tag");
}
}
/*
* (non-Javadoc)
*
* @see com.adito.applications.server.ApplicationServerType#start()
*/
public void start() {
execute(classpath, mainclass, workingDir);
}
/*
* (non-Javadoc)
*
* @see com.adito.vpn.util.ApplicationType#checkFileCondition(com.adito.vpn.util.XMLElement)
*/
public boolean checkFileCondition(XMLElement el) throws IOException,
IllegalArgumentException {
String jre = (String) el.getAttribute("jre");
if (jre == null) {
throw new IllegalArgumentException(
"No supported attributes in condition.");
} else {
return isSupportedJRE(jre);
}
}
/*
* (non-Javadoc)
*
* @see com.adito.vpn.util.ApplicationType#getProcessMonitor()
*/
public ProcessMonitor getProcessMonitor() {
return process;
}
/*
* (non-Javadoc)
*
* @see com.adito.extensions.ExtensionType#stop()
*/
public void stop() throws ExtensionException {
}
/*
* (non-Javadoc)
*
* @see com.adito.extensions.ExtensionType#activate()
*/
public void activate() throws ExtensionException {
}
/*
* (non-Javadoc)
*
* @see com.adito.extensions.ExtensionType#canStop()
*/
public boolean canStop() throws ExtensionException {
return true;
}
/*
* (non-Javadoc)
*
* @see com.adito.applications.ApplicationLauncherType#launch(java.util.Map,
* com.adito.extensions.ExtensionDescriptor,
* com.adito.applications.ApplicationShortcut,
* org.apache.struts.action.ActionMapping,
* com.adito.policyframework.LaunchSession, java.lang.String,
* javax.servlet.http.HttpServletRequest)
*/
public ActionForward launch(Map<String, String> parameters,
ExtensionDescriptor descriptor, ApplicationShortcut shortcut,
ActionMapping mapping, LaunchSession launchSession,
String returnTo, HttpServletRequest request)
throws ExtensionException {
if (log.isInfoEnabled())
log.info("Starting Java server application "
+ shortcut.getResourceName());
ServerApplicationLauncher app;
try {
app = new ServerApplicationLauncher(parameters, shortcut
.getApplication(), launchSession.getSession(), shortcut);
app.start();
} catch (Exception e) {
throw new ExtensionException(ExtensionException.FAILED_TO_LAUNCH, e);
}
return null;
}
/*
* (non-Javadoc)
*
* @see com.adito.applications.ApplicationLauncherType#isAgentRequired(com.adito.applications.ApplicationShortcut,
* com.adito.extensions.ExtensionDescriptor)
*/
public boolean isAgentRequired(ApplicationShortcut shortcut,
ExtensionDescriptor descriptor) {
return false;
}
protected void addClasspathEntry(XMLElement e) throws IOException {
addClasspathEntry(e, null);
}
protected void addClasspathEntry(XMLElement e, String app)
throws IOException {
events.debug("Adding "
+ launcher.getInstallDir()
+ (e.getContent() != null ? File.separatorChar + e.getContent()
: "") + " to CLASSPATH");
classpath += (!classpath.equals("") ? File.pathSeparator : "")
+ launcher.getInstallDir()
+ (e.getContent() != null ? File.separatorChar + e.getContent()
: "");
}
protected void buildClassPath(XMLElement element) throws IOException {
buildClassPath(element, null);
}
protected void buildClassPath(XMLElement element, String app)
throws IOException {
if (events != null)
events.debug("Building classpath");
Enumeration en = element.enumerateChildren();
XMLElement e;
while (en.hasMoreElements()) {
e = (XMLElement) en.nextElement();
if (e.getName().equalsIgnoreCase("jar")) {
addClasspathEntry(e, app);
} else if (e.getName().equals("if")) {
String jre = (String) e.getAttribute("jre");
if (jre == null) {
String parameter = (String) e.getAttribute("parameter");
if (parameter != null) {
String requiredValue = (String) e.getAttribute("value");
boolean not = "true".equalsIgnoreCase(((String) e
.getAttribute("not")));
// Check the parameter
String value = (String) launcher.getDescriptorParams()
.get(parameter);
if ((!not && requiredValue.equalsIgnoreCase(value))
|| (not && !requiredValue
.equalsIgnoreCase(value))) {
buildClassPath(e, app);
}
} else
throw new IOException(
"<if> element requires jre or parameter attribute");
} else {
if (isSupportedJRE(jre)) {
buildClassPath(e, app);
}
}
} else
throw new IOException("Invalid element <" + e.getName()
+ "> found in <classpath>");
}
}
private boolean isSupportedJRE(String jre) {
int[] ourVersion = ServerLauncher.getVersion(System
.getProperty("java.version"));
if (jre.startsWith(">")) {
// Our JRE must be greater than the value specified
int[] requiredVersion = ServerLauncher.getVersion(jre.substring(1));
for (int i = 0; i < ourVersion.length && i < requiredVersion.length; i++) {
if (ourVersion[i] < requiredVersion[i])
return false;
}
return true;
} else if (jre.startsWith("<")) {
// Our JRE must be less than the value specified
int[] requiredVersion = ServerLauncher.getVersion(jre.substring(1));
for (int i = 0; i < ourVersion.length && i < requiredVersion.length; i++) {
if (ourVersion[i] > requiredVersion[i])
return false;
}
return true;
} else {
// Direct comparison
int[] requiredVersion = ServerLauncher.getVersion(jre);
for (int i = 0; i < ourVersion.length && i < requiredVersion.length; i++) {
if (ourVersion[i] != requiredVersion[i])
return false;
}
return true;
}
}
protected void addArgument(String arg) {
if (arg != null)
programArgs.add(launcher.replaceTokens(arg));
}
protected void addJVMArgument(String arg) {
if (arg != null) {
if (arg.startsWith("java.library.path")) {
int idx = arg.indexOf('=');
if (idx > -1) {
String val = arg.substring(idx + 1).replace('/',
File.separatorChar);
javaLibraryPath += (javaLibraryPath.equals("") ? val
: SystemProperties.get("path.separator") + val);
if (events != null)
events
.debug(val
+ " has been appened to system property java.library.path");
} else if (events != null)
events.debug("Invalid java.library.path system property: "
+ arg);
} else
jvmArgs.add(launcher.replaceTokens(arg));
}
}
private void addArgument(XMLElement e) throws IOException {
if (e.getName().equalsIgnoreCase("arg"))
addArgument(e.getContent());
else if (e.getName().equalsIgnoreCase("jvm")) {
addJVMArgument(e.getContent());
} else {
throw new IOException("Unexpected element <" + e.getName()
+ "> found");
}
}
private void buildProgramArguments(XMLElement element) throws IOException {
Enumeration en = element.enumerateChildren();
while (en.hasMoreElements()) {
XMLElement e = (XMLElement) en.nextElement();
if (e.getName().equalsIgnoreCase("arg"))
addArgument(e);
else if (e.getName().equalsIgnoreCase("jvm")) {
addArgument(e);
} else if (e.getName().equalsIgnoreCase("if")) {
String jre = (String) e.getAttribute("jre");
if (jre == null) {
String parameter = (String) e.getAttribute("parameter");
boolean not = "true".equalsIgnoreCase((String) e
.getAttribute("not"));
if (parameter != null) {
String requiredValue = (String) e.getAttribute("value");
// Check the parameter
String value = (String) launcher.getDescriptorParams()
.get(parameter);
if ((!not && requiredValue.equalsIgnoreCase(value))
|| (not && !requiredValue
.equalsIgnoreCase(value))) {
buildProgramArguments(e);
}
} else
throw new IOException(
"<if> element requires jre or parameter attribute");
} else {
// Check the jre
if (isSupportedJRE(jre)) {
buildProgramArguments(e);
}
}
} else
throw new IOException("Unexpected element <" + e.getName()
+ "> found in <main>");
}
}
private void execute(String classpath, String mainclass, File workingDir) {
String[] args = new String[programArgs.size()];
programArgs.toArray(args);
if (!javaLibraryPath.equals(""))
jvmArgs.add("java.library.path="
+ launcher.replaceTokens(javaLibraryPath));
jvm = new String[jvmArgs.size()];
jvmArgs.toArray(jvm);
String[] cmdargs = new String[jvm.length + args.length + 4];
/**
* Setup the command line in the format expected by Sun Microsystems
* java command line interpreter
*/
cmdargs[0] = SystemProperties.get("java.home") + File.separator + "bin"
+ File.separator + "java";
cmdargs[1] = "-classpath";
cmdargs[2] = classpath;
for (int i = 0; i < jvm.length; i++) {
cmdargs[3 + i] = "-D" + jvm[i];
}
cmdargs[jvm.length + 3] = mainclass;
System.arraycopy(args, 0, cmdargs, jvm.length + 4, args.length);
String cmdline = "";
for (int i = 0; i < cmdargs.length; i++)
cmdline += " " + cmdargs[i];
if (events != null)
events.debug("Executing command: " + cmdline);
try {
if (events != null)
events.executingApplication(launcher.getName(), cmdline.trim());
// Can we change the working directory of the process?
Process prc = Runtime.getRuntime().exec(cmdargs, null, workingDir);
process = new ProcessMonitor(launcher.getName(), prc);
} catch (IOException ex) {
if (events != null)
events.debug("Process execution failed: " + ex.getMessage());
}
}
private void verifyClasspath(Element element) throws ExtensionException {
for (Iterator it = element.getChildren().iterator(); it.hasNext();) {
Element e = (Element) it.next();
if (e.getName().equalsIgnoreCase("jar")) {
descriptor.processFile(e);
} else if (e.getName().equals("if")) {
verifyClasspath(e);
} else {
throw new ExtensionException(
ExtensionException.FAILED_TO_PROCESS_DESCRIPTOR,
"Invalid element <" + e.getName()
+ "> found in <classpath>");
}
}
}
private void verifyMain(Element element) throws ExtensionException {
for (Iterator it = element.getChildren().iterator(); it.hasNext();) {
Element e = (Element) it.next();
if (e.getName().equalsIgnoreCase("if")) {
verifyMain(e);
} else if (!e.getName().equalsIgnoreCase("arg")
&& !e.getName().equalsIgnoreCase("jvm")) {
throw new ExtensionException(
ExtensionException.FAILED_TO_PROCESS_DESCRIPTOR,
"Unexpected element <" + e.getName()
+ "> found in <main>");
}
}
}
/*
* (non-Javadoc)
*
* @see com.adito.extensions.ExtensionType#descriptorCreated(org.jdom.Element)
*/
public void descriptorCreated(Element element, SessionInfo session)
throws IOException {
}
/*
* (non-Javadoc)
*
* @see com.adito.extensions.ExtensionType#getTypeBundle()
*/
public String getTypeBundle() {
return "applications";
}
/*
* (non-Javadoc)
*
* @see com.adito.applications.ApplicationLauncherType#isServiceSide()
*/
public boolean isServerSide() {
return true;
}
/* (non-Javadoc)
* @see com.adito.extensions.ExtensionType#getLaunchRequirement()
*/
public LaunchRequirement getLaunchRequirement() {
return LaunchRequirement.LAUNCHABLE;
}
}