/* license-start * * Copyright (C) 2008 - 2013 Crispico, <http://www.crispico.com/>. * * 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 version 3. * * 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, at <http://www.gnu.org/licenses/>. * * Contributors: * Crispico - Initial API and implementation * * license-end */ package org.flowerplatform.web.app; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.lang.reflect.Field; import java.security.Policy; import java.util.Map; import java.util.Properties; import javax.servlet.ServletContext; import org.eclipse.equinox.servletbridge.FrameworkLauncher; /** * Customized launcher class that disables the "deploy" process * (done by the original class), and processes some additional parameters * from web.xml. * * <p> * The original classes (form the *.servletbridge package) are not modified * (for the moment), in order to allow an easy merge from their original source * repository. We even had to access a private field using reflection. * * <p> * <strong>NOTE:</strong> * The source files from *.servlet bridge are taken from Eclipse's CVS: * dev.eclipse.org; /cvsroot/rt; org.eclipse.equinox/server-side/bundles/org.eclipse.equinox.servletbridge * * The version of servletbridge in use is v20080929-1800. Before we used a newer version (v20110502), * but this one did not worked when using the security manager (java.lang.ClassCircularityError appeared). * We changed to an older servletbridge version as indicated here: * http://www.eclipse.org/forums/index.php?t=msg&goto=127453&S=5647d1811f8b25b9cbb9757fb10ab336 * * <p> * In case we will need to use the newer version again, here is what we investigated until now: * <ul> * <li>Breakpoint at org.eclipse.osgi.framework.util.SecureAction@345, and then breakpoint * at java.net.JarURLConnection@161 * <li>This line fails (i.e. new URL("file:/D:/Java/eclipse_3.5_modeling/plugins/org.eclipse.osgi_3.5.2.R35x_v20100126.jar");) * because it will arrive again in "SecureAction" => infinite loop * <li>It's interesting to see why this fail for this call, being given that other classes are already loaded * from Eclipse. A parallel debug might be interesting, between the version that doesn't use this pass * </ul> * * The newer version might be needed if jar connections need to be closed (i.e. to physically delete the jar file). * * <p> * <strong>NOTE/UPDATE:</strong><br> * We needed to modify the original <code>FrameworkLauncher</code> class. Be <strong>CAREFUL</strong> when/if merging with * a newer version. Modifications are marked with "MODIF_FROM_ORIGINAL" * * @see web.xml for boot parameter details * @author Cristian Spiescu */ public class FlowerFrameworkLauncher extends FrameworkLauncher { private static final String WEB_APP_CONTEXT_TO_DEV_WS_PATH = "../../../../../../"; private static final String POLICY_FILE = "/WEB-INF/all.policy"; @Override public void init() { super.init(); // set security manager String policyFile = context.getRealPath(POLICY_FILE); System.setProperty("java.security.policy", policyFile); Policy.getPolicy().refresh(); // ensure that the policy is reloaded System.setSecurityManager(new SecurityManager()); } /** * Do nothing. The original implementation was deploying (copying) * Eclipse files into the webapp's working directory. Accesses a private field. */ @Override public synchronized void deploy() { // modify this private field; otherwise the start() method would throw an exception Field platformDirectory; try { platformDirectory = FrameworkLauncher.class.getDeclaredField("platformDirectory"); platformDirectory.setAccessible(true); platformDirectory.set(this, new File("")); } catch (Exception e) { throw new RuntimeException("Unexpected error while accessing field platformDirectory", e); } } /** * Reads the configuration file and generates some properties if in dev. mode. * @see web.xml file for parameter documentation * */ @SuppressWarnings("rawtypes") @Override protected Map buildInitialPropertyMap() { Properties properties = new Properties(); String configFileLocation; String developmentLaunchConfigurationProperty = "developmentLaunchConfiguration" + (System.getProperty("os.name").startsWith("Windows") ? "Windows" : "Linux"); // if in development mode, generate some properties + config file path String developmentLaunchConfiguration = config.getInitParameter(developmentLaunchConfigurationProperty); String osgiConfigurationArea = null; String developmentWorkspaceRoot = null; if (developmentLaunchConfiguration != null) { // dev mode developmentWorkspaceRoot = context.getRealPath(WEB_APP_CONTEXT_TO_DEV_WS_PATH); // osgi.configuration.area a.k.a. -configuration osgiConfigurationArea = String.format("%s/.metadata/.plugins/org.eclipse.pde.core/%s", developmentWorkspaceRoot, developmentLaunchConfiguration); if (!(new File(osgiConfigurationArea).exists())) throw new RuntimeException(osgiConfigurationArea + " file not found. Either the '" + developmentLaunchConfigurationProperty + "' is not correctly set or the Eclipse configuration was not generated (=> you need to launch the target launch config " + developmentLaunchConfiguration + " at least once directly from Eclipse)."); } else { // prod mode String relativeOsgiConfigurationArea = config.getInitParameter("eclipseConfigurationLocation"); osgiConfigurationArea = context.getRealPath(relativeOsgiConfigurationArea); if (!(new File(osgiConfigurationArea).exists())) { throw new RuntimeException(osgiConfigurationArea + " file not found."); } String pluginsDir = osgiConfigurationArea + "/../plugins"; if (!(new File(pluginsDir).exists())) { throw new RuntimeException(pluginsDir + " file not found."); } // bundles with relative paths defined in "osgi.bundles" use the "osgi.syspath" property as current // dir (when resolving the absolute path from the relative path). This allows us to specify relative // paths for FDC plugins (that will be searched in: e.g. /tomcat/webapps/_contex_/WEB-INF/eclipse/plugins) properties.put("osgi.syspath", pluginsDir); } properties.put("osgi.configuration.area", "file:" + osgiConfigurationArea); // osgi.dev a.k.a. -dev String osgiDev = osgiConfigurationArea + "/dev.properties"; if (!(new File(osgiDev).exists())) { if (developmentLaunchConfiguration != null) { // for dev, this is mandatory; for production = optional throw new RuntimeException(osgiDev + " file not found. Either the 'developmentLaunchConfiguration' is not correctly set or the Eclipse configuration was not generated (=> you need to launch the target launch config " + developmentLaunchConfiguration + " at least once directly from Eclipse)."); } } else { properties.put("osgi.dev", "file:" + osgiDev); } // config file path configFileLocation = osgiConfigurationArea + "/config.ini"; // load the properties from the config file File file = new File(configFileLocation); FileInputStream is = null; try { is = new FileInputStream(file); properties.load(is); } catch (Exception e) { throw new RuntimeException("'configFileLocation' error; it points towards an unaccesible file or there was another error!", e); } finally { if (is != null) try { is.close(); } catch (IOException e) { // ignore } } // generate osgi.instance.area a.k.a. -data a.k.a. workspace location String workspaceLocation = config.getInitParameter("workspaceLocation"); if (workspaceLocation == null) throw new RuntimeException("'workspaceLocation' must be specified."); String osgiInstanceArea; // if in dev mode, the ws location is relative to the eclipse project/WebContent if (developmentWorkspaceRoot != null) // osgiInstanceArea = developmentWorkspaceRoot + context.getContextPath() + "/WebContent/" + workspaceLocation; osgiInstanceArea = developmentWorkspaceRoot + "/" + workspaceLocation; else osgiInstanceArea = context.getRealPath(workspaceLocation); if (!(new File(osgiInstanceArea).exists())) throw new RuntimeException(osgiInstanceArea + " workspace location not found"); properties.put("osgi.instance.area", "file:" + osgiInstanceArea); properties.put("flower.server.app.context", context.getContextPath().substring(1)); properties.put("flower.server.app.location", context.getRealPath(File.separator)); properties.put("flower.server.tmpdir", ((File) context.getAttribute(ServletContext.TEMPDIR)).getAbsolutePath()); return properties; } }