/******************************************************************************* * Copyright (c) 2004, 2009 Actuate Corporation. * 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: * Actuate Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.birt.core.framework.osgi; import java.io.File; import java.io.FileFilter; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Method; import java.net.MalformedURLException; import java.net.URL; import java.security.CodeSource; import java.security.Policy; import java.security.ProtectionDomain; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Properties; import java.util.StringTokenizer; import java.util.logging.Level; import java.util.logging.Logger; import org.eclipse.birt.core.exception.BirtException; import org.eclipse.birt.core.exception.CoreException; import org.eclipse.birt.core.framework.FrameworkException; import org.eclipse.birt.core.framework.IPlatformContext; import org.eclipse.birt.core.framework.PlatformConfig; import org.eclipse.birt.core.framework.URLClassLoader; /** * Patched OSGILauncher. * * @see http://mifosforge.jira.com/browse/MIFOS-5119 */ public class OSGILauncher { private static Logger logger = Logger.getLogger( OSGILauncher.class .getName( ) ); /** the class used to start the elipse framework */ private static final String ECLIPSE_STARTER = "org.eclipse.core.runtime.adaptor.EclipseStarter"; private static String PluginId = "org.eclipse.birt.core"; private PlatformConfig platformConfig; private File platformDirectory; private URL osgiFramework; private ChildFirstURLClassLoader frameworkClassLoader; private ClassLoader frameworkContextClassLoader; private HashMap properties; private Object bundleContext; // OSGi properties private static final String PROP_OSGI_INSTALL_AREA = "osgi.install.area";//$NON-NLS-1$ private static final String PROP_OSGI_INSTANCE_AREA = "osgi.instance.area";//$NON-NLS-1$ private static final String PROP_OSGI_CONFIGURATION_AREA = "osgi.configuration.area";//$NON-NLS-1$ private static final String PROP_ECLIPSE_IGNOREAPP = "eclipse.ignoreApp";//$NON-NLS-1$ private static final String PROP_OSGI_NOSHUTDOWN = "osgi.noShutdown";//$NON-NLS-1$ private static final String PROP_OSGI_CLEAN = "osgi.clean";//$NON-NLS-1$ private static final String PROP_ECLIPSE_SECURITY = "eclipse.security"; //$NON-NLS-1$ private static final String CONFIG_FILE = "config.ini";//$NON-NLS-1$ private static final String CONFIG_FOLDER = "configuration";//$NON-NLS-1$ private static final String INSTANCE_FOLDER = "workspace";//$NON-NLS-1$ public void startup( final PlatformConfig config ) throws BirtException { try { java.security.AccessController .doPrivileged( new java.security.PrivilegedExceptionAction<Object>( ) { public Object run( ) throws Exception { doStartup( config ); return null; } } ); } catch ( Exception ex ) { if ( ex instanceof BirtException ) { throw (BirtException) ex; } throw new CoreException( ex.getMessage( ), new Object[0], ex ); } } private void doStartup(PlatformConfig config) throws BirtException { if ( frameworkClassLoader != null ) { logger.log( Level.WARNING, "Framework is already started" ); //$NON-NLS-1$ return; } platformConfig = config; IPlatformContext context = config.getPlatformContext( ); if ( context == null ) { throw new FrameworkException( "PlatformContext is not setted - {0}", new Object[]{"PlatformConfig"} ); //$NON-NLS-1$ } // process install.area String root = context.getPlatform( ); platformDirectory = new File( root ); if ( !platformDirectory.exists( ) || !platformDirectory.isDirectory( ) ) { throw new FrameworkException( "Framework {0} doesn't exist or is not a directory", new Object[]{root} ); //$NON-NLS-1$ } String path = new File( platformDirectory, "plugins" ).toString( ); //$NON-NLS-1$ path = searchFor( "org.eclipse.osgi", path ); //$NON-NLS-1$ if ( path == null ) { throw new FrameworkException( "Could not find the Framework - {0}", new Object[]{"org.eclipse.osgi"} ); //$NON-NLS-1$ } try { osgiFramework = new File( path ).toURI( ).toURL( ); } catch(MalformedURLException ex) { //cannot be here } ClassLoader original = Thread.currentThread( ).getContextClassLoader( ); try { ClassLoader loader = this.getClass( ).getClassLoader( ); frameworkClassLoader = new ChildFirstURLClassLoader( new URL[]{osgiFramework}, loader ); // frameworkClassLoader = new OSGIClassLoader( // new URL[]{frameworkUrl}, loader ); // Weblogic 8.1SP6 contains old version JS.JAR, we need // set pref-web-inf to true, if we set it to true, the // URL classloader still loads the JS in weblogic, so // load the class explicitly. try { loader.loadClass( "org.mozilla.javascript.Context" ); loader.loadClass( "org.mozilla.javascript.Scriptable" ); loader.loadClass( "org.mozilla.javascript.ScriptableObject" ); // frameworkClassLoader.loadClass( // "org.mozilla.javascript.Context" } catch ( Exception ex ) { } Class clazz = frameworkClassLoader.loadClass( ECLIPSE_STARTER ); setupOSGiProperties( ); setupSecurityPolicy( ); Method initPropertiesMethod = clazz.getMethod( "setInitialProperties", new Class[]{Map.class} ); //$NON-NLS-1$ if ( initPropertiesMethod != null ) { System.setProperty( "osgi.framework.useSystemProperties", "false" ); //$NON-NLS-1$ //$NON-NLS-2$ properties.put( "osgi.framework.useSystemProperties", "false" ); initPropertiesMethod.invoke( null, new Object[]{properties} ); } else { Iterator iter = properties.entrySet( ).iterator( ); while ( iter.hasNext( ) ) { Map.Entry entry = (Map.Entry) iter.next( ); String key = (String) entry.getKey( ); String value = (String) entry.getValue( ); System.setProperty( key, value ); } System.setProperty( "osgi.framework.useSystemProperties", "true" ); //$NON-NLS-1$ //$NON-NLS-2$ } Method runMethod = clazz.getMethod( "startup", new Class[]{String[].class, Runnable.class} ); //$NON-NLS-1$ bundleContext = runMethod.invoke( null, new Object[]{new String[]{}, null} ); frameworkContextClassLoader = Thread.currentThread( ) .getContextClassLoader( ); } catch ( BirtException be ) { throw be; } catch ( Exception e ) { throw new FrameworkException( "Can not start up OSGI - {0}", new Object[]{e.getMessage( )}, e ); } finally { Thread.currentThread( ).setContextClassLoader( original ); } } private void setupOSGiProperties() { properties = new HashMap( ); //copy all system properties to the property. Properties systemProperties = System.getProperties( ); if ( systemProperties != null ) { for ( Iterator it = systemProperties.entrySet( ).iterator( ); it .hasNext( ); ) { Map.Entry entry = (Map.Entry) it.next( ); String key = (String) entry.getKey( ); Object value = entry.getValue( ); //Tomcat 6 setting some of property to Object instead of String if ( value == null || value instanceof String ) { if ( !key.startsWith( "osgi." ) && !key.startsWith( "eclipse." ) && !key.startsWith( "org.osgi." ) ) { properties.put( key, value ); } else { properties.put( key, null ); } } } } //load the config.ini distribued with BIRT system. File configFolder = new File( platformDirectory, CONFIG_FOLDER ); try { HashMap configProps = loadConfiguration( configFolder.toURL( ) ); properties.putAll( configProps ); } catch(Exception ex){/* do nothing */} //copy properties defined by the caller Map osgiConfig = platformConfig.getOSGiConfig( ); if ( osgiConfig != null ) { Iterator iter = osgiConfig.entrySet( ).iterator( ); while ( iter.hasNext( ) ) { Map.Entry entry = (Map.Entry) iter.next( ); Object key = entry.getKey( ); Object value = entry.getValue( ); if ( key instanceof String ) { if ( value == null || value instanceof String ) { properties.put( key, value ); } } } } //set up the properied defined by BIRT /* osgi.install.area, always using the one defined by BIRT_HOME. */ properties.put( PROP_OSGI_INSTALL_AREA, platformDirectory .getAbsolutePath( ) ); //$NON-NLS-1$ /* setup the osgi framework, it is the osgi.jar in the platform/plugins */ properties.put( "osgi.framework", osgiFramework.toExternalForm( ) ); //$NON-NLS-1$//$NON-NLS-2$ /* setup the config area, it is the /configuration under platform */ String configArea = getProperty( properties, PROP_OSGI_CONFIGURATION_AREA ); if (configArea == null) { File configDirectory = new File (platformDirectory, CONFIG_FOLDER ); properties.put( PROP_OSGI_CONFIGURATION_AREA, configDirectory.getAbsolutePath( ) ); } // instance.area, it is the workspace under platform String instanceArea = getProperty( properties, PROP_OSGI_INSTANCE_AREA ); if ( instanceArea == null ) { File workspaceDirectory = new File( platformDirectory, INSTANCE_FOLDER ); //$NON-NLS-1$ if ( !workspaceDirectory.exists( ) ) { workspaceDirectory.mkdirs( ); } properties.put( PROP_OSGI_INSTANCE_AREA, workspaceDirectory .getAbsolutePath( ) ); //$NON-NLS-1$ } properties.put( PROP_ECLIPSE_IGNOREAPP, "true" );//$NON-NLS-1$ properties.put( PROP_OSGI_NOSHUTDOWN, "true" ); //$NON-NLS-1$ //set -clean if the user doens't define it. String clean = getProperty(properties, PROP_OSGI_CLEAN); if (clean == null) { properties.put( PROP_OSGI_CLEAN, "true" ); } } private HashMap loadConfiguration( URL url ) { HashMap result = null; if ( url == null ) return result; try { url = new URL( url, CONFIG_FILE ); } catch ( MalformedURLException e ) { return result; } Properties tempProp = new Properties();; try { result = new HashMap( ); InputStream is = null; try { is = url.openStream( ); tempProp.load( is ); } finally { if ( is != null ) try { is.close( ); } catch ( IOException e ) { // ignore failure to close } } } catch ( IOException e ) { // do nothing so far } //copy all the properties Iterator ti = tempProp.entrySet( ).iterator( ); while ( ti.hasNext( ) ) { Map.Entry entry = (Map.Entry) ti.next( ); Object key = entry.getKey( ); Object value = entry.getValue( ); result.put( key, value ); } return result; } /** * return the property value. * * @param properties * @param name * @return value, must be none empty string or NULL. */ private String getProperty( Map properties, String name ) { Object value = properties.get( name ); if ( value instanceof String ) { String strValue = (String) value; strValue = strValue.trim( ); if ( strValue.length( ) > 0 ) { return strValue; } } return null; } public ClassLoader getFrameworkContextClassLoader( ) { return frameworkContextClassLoader; } public void shutdown( ) { java.security.AccessController .doPrivileged( new java.security.PrivilegedAction<Object>( ) { public Object run( ) { doShutdown( ); return null; } } ); } private void doShutdown() { if ( platformDirectory == null ) { logger.log( Level.WARNING, "Shutdown unnecessary. (not deployed)" ); //$NON-NLS-1$ return; } if ( frameworkClassLoader == null ) { logger.log( Level.WARNING, "Framework is already shutdown" ); //$NON-NLS-1$ return; } ClassLoader original = Thread.currentThread( ).getContextClassLoader( ); try { Class clazz = frameworkClassLoader.loadClass( ECLIPSE_STARTER ); Method method = clazz .getDeclaredMethod( "shutdown", (Class[]) null ); //$NON-NLS-1$ Thread.currentThread( ).setContextClassLoader( frameworkContextClassLoader ); method.invoke( clazz, (Object[]) null ); } catch ( Exception e ) { logger.log( Level.WARNING, "Error while stopping Framework", e ); //$NON-NLS-1$ return; } finally { frameworkClassLoader.close( ); frameworkClassLoader = null; frameworkContextClassLoader = null; Thread.currentThread( ).setContextClassLoader( original ); } } /*************************************************************************** * See org.eclipse.core.launcher [copy of searchFor, findMax, * compareVersion, getVersionElements] TODO: If these methods were made * public and static we could use them directly **************************************************************************/ /** * Searches for the given target directory starting in the "plugins" * subdirectory of the given location. If one is found then this location is * returned; otherwise an exception is thrown. * * @param target * * @return the location where target directory was found * @param start * the location to begin searching */ protected String searchFor( final String target, String start ) { FileFilter filter = new FileFilter( ) { public boolean accept( File candidate ) { return candidate.getName( ).equals( target ) || candidate.getName( ).startsWith( target + "_" ); //$NON-NLS-1$ } }; File[] candidates = new File( start ).listFiles( filter ); //$NON-NLS-1$ if ( candidates == null ) return null; String[] arrays = new String[candidates.length]; for ( int i = 0; i < arrays.length; i++ ) { arrays[i] = candidates[i].getName( ); } int result = findMax( arrays ); if ( result == -1 ) return null; return candidates[result].getAbsolutePath( ).replace( File.separatorChar, '/' ) + ( candidates[result].isDirectory( ) ? "/" : "" ); //$NON-NLS-1$//$NON-NLS-2$ } protected int findMax( String[] candidates ) { int result = -1; Object maxVersion = null; for ( int i = 0; i < candidates.length; i++ ) { String name = candidates[i]; String version = ""; //$NON-NLS-1$ // Note: directory with version suffix is always > than directory without version suffix int index = name.indexOf( '_' ); if ( index != -1 ) version = name.substring( index + 1 ); Object currentVersion = getVersionElements( version ); if ( maxVersion == null ) { result = i; maxVersion = currentVersion; } else { if ( compareVersion( (Object[]) maxVersion, (Object[]) currentVersion ) < 0 ) { result = i; maxVersion = currentVersion; } } } return result; } /** * Compares version strings. * * @param left * @param right * @return result of comparison, as integer; <code><0</code> if left < * right; <code>0</code> if left == right; <code>>0</code> if * left > right; */ private int compareVersion( Object[] left, Object[] right ) { int result = ( (Integer) left[0] ).compareTo( (Integer) right[0] ); // compare // major if ( result != 0 ) return result; result = ( (Integer) left[1] ).compareTo( (Integer) right[1] ); // compare // minor if ( result != 0 ) return result; result = ( (Integer) left[2] ).compareTo( (Integer) right[2] ); // compare // service if ( result != 0 ) return result; return ( (String) left[3] ).compareTo( (String) right[3] ); // compare // qualifier } /** * Do a quick parse of version identifier so its elements can be correctly * compared. If we are unable to parse the full version, remaining elements * are initialized with suitable defaults. * * @param version * @return an array of size 4; first three elements are of type Integer * (representing major, minor and service) and the fourth element is * of type String (representing qualifier). Note, that returning * anything else will cause exceptions in the caller. */ private Object[] getVersionElements( String version ) { if ( version.endsWith( ".jar" ) ) //$NON-NLS-1$ version = version.substring( 0, version.length( ) - 4 ); Object[] result = {new Integer( 0 ), new Integer( 0 ), new Integer( 0 ), ""}; //$NON-NLS-1$ StringTokenizer t = new StringTokenizer( version, "." ); //$NON-NLS-1$ String token; int i = 0; while ( t.hasMoreTokens( ) && i < 4 ) { token = t.nextToken( ); if ( i < 3 ) { // major, minor or service ... numeric values try { result[i++] = new Integer( token ); } catch ( Exception e ) { // invalid number format - use default numbers (0) for the // rest break; } } else { // qualifier ... string value result[i++] = token; } } return result; } /** * return the bundle named by symbolic name * * @param symbolicName * the bundle name * @return bundle object */ Object getBundle( String symbolicName ) { if ( bundleContext == null ) { return null; } try { Method methodLoadBundle = bundleContext.getClass( ).getMethod( "getBundles", new Class[]{} ); Object objects = methodLoadBundle.invoke( bundleContext, new Object[]{} ); if ( objects instanceof Object[] ) { Object[] bundles = (Object[]) objects; for ( int i = 0; i < bundles.length; i++ ) { Object bundle = bundles[i]; Method methodGetSymbolicName = bundle.getClass( ) .getMethod( "getSymbolicName", new Class[]{} ); Object name = methodGetSymbolicName.invoke( bundle, new Object[]{} ); if ( symbolicName.equals( name ) ) { return bundle; } } } } catch ( Exception ex ) { logger.log( Level.WARNING, ex.getMessage( ) ); } return null; } /** * The ChildFirstURLClassLoader alters regular ClassLoader delegation and * will check the URLs used in its initialization for matching classes * before delegating to it's parent. Sometimes also referred to as a * ParentLastClassLoader */ static protected class ChildFirstURLClassLoader extends URLClassLoader { public ChildFirstURLClassLoader( URL[] urls ) { super( urls ); } public ChildFirstURLClassLoader( URL[] urls, ClassLoader parent ) { super( urls, parent ); } public URL getResource( String name ) { URL resource = findResource( name ); if ( resource == null ) { ClassLoader parent = getParent( ); if ( parent != null ) resource = parent.getResource( name ); } return resource; } protected synchronized Class loadClass( String name, boolean resolve ) throws ClassNotFoundException { Class clazz = findLoadedClass( name ); if ( clazz == null ) { try { clazz = findClass( name ); } catch ( ClassNotFoundException e ) { ClassLoader parent = getParent( ); if ( parent != null ) clazz = parent.loadClass( name ); else clazz = getSystemClassLoader( ).loadClass( name ); } } if ( resolve ) resolveClass( clazz ); return clazz; } } protected void setupSecurityPolicy( ) throws FrameworkException { String eclipseSecurity = (String) properties .get( PROP_ECLIPSE_SECURITY ); if ( eclipseSecurity != null ) { // setup a policy that grants the launcher and path for the // framework AllPermissions. // Do not set the security manager, this will be done by the // framework itself. ProtectionDomain domain = OSGILauncher.class.getProtectionDomain( ); CodeSource source = null; if ( domain != null ) source = OSGILauncher.class.getProtectionDomain( ) .getCodeSource( ); if ( domain == null || source == null ) { throw new FrameworkException( "Can not automatically set the security manager. Please use a policy file." ); } // get the list of codesource URLs to grant AllPermission to URL[] rootURLs = new URL[]{source.getLocation( ), osgiFramework}; // replace the security policy Policy eclipsePolicy = new OSGIPolicy( Policy.getPolicy( ), rootURLs ); Policy.setPolicy( eclipsePolicy ); } } }