/*************************************************************************
*
* $RCSfile: JavaLoader.java,v $
*
* $Revision: 1.4 $
*
* last change: $Author: dbo $ $Date: 2002-06-14 13:09:52 $
*
* The Contents of this file are made available subject to the terms of
* either of the following licenses
*
* - GNU Lesser General Public License Version 2.1
* - Sun Industry Standards Source License Version 1.1
*
* Sun Microsystems Inc., October, 2000
*
* GNU Lesser General Public License Version 2.1
* =============================================
* Copyright 2000 by Sun Microsystems, Inc.
* 901 San Antonio Road, Palo Alto, CA 94303, USA
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1, as published by the Free Software Foundation.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
*
* Sun Industry Standards Source License Version 1.1
* =================================================
* The contents of this file are subject to the Sun Industry Standards
* Source License Version 1.1 (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.openoffice.org/license.html.
*
* Software provided under this License is provided on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
* WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
* MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
* See the License for the specific provisions governing your rights and
* obligations concerning the Software.
*
* The Initial Developer of the Original Code is: Sun Microsystems, Inc.
*
* Copyright: 2000 by Sun Microsystems, Inc.
*
* All Rights Reserved.
*
* Contributor(s): _______________________________________
*
*
************************************************************************/
package com.sun.star.comp.loader;
import java.lang.reflect.Method;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.URLDecoder;
import com.sun.star.loader.CannotActivateFactoryException;
import com.sun.star.loader.XImplementationLoader;
import com.sun.star.registry.CannotRegisterImplementationException;
import com.sun.star.registry.RegistryKeyType;
import com.sun.star.registry.RegistryValueType;
import com.sun.star.registry.InvalidRegistryException;
import com.sun.star.registry.InvalidValueException;
import com.sun.star.registry.XRegistryKey;
import com.sun.star.registry.XSimpleRegistry;
import com.sun.star.lang.XSingleServiceFactory;
import com.sun.star.lang.XMultiServiceFactory;
import com.sun.star.lang.XServiceInfo;
import com.sun.star.lang.ServiceNotRegisteredException;
import com.sun.star.lang.WrappedTargetException;
import com.sun.star.lang.XInitialization;
import com.sun.star.uno.XComponentContext;
import com.sun.star.beans.XPropertySet;
import com.sun.star.util.XMacroExpander;
import com.sun.star.uno.XInterface;
import com.sun.star.uno.Type;
import com.sun.star.uno.UnoRuntime;
import com.sun.star.uno.AnyConverter;
import java.io.IOException;
import java.net.MalformedURLException;
/**
* The JavaLoader
class provides the functionality of the com.sun.star.loader.Java
* service. Therefor the JavaLoader
activates external UNO components which are implemented in Java.
* The loader is used by the ServiceManger
.
*
* @version $Revision: 1.4 $ $ $Date: 2002-06-14 13:09:52 $
* @author Markus Herzog
* @see com.sun.star.loader.XImplementationLoader
* @see com.sun.star.loader.Java
* @see com.sun.star.comp.servicemanager.ServiceManager
* @see com.sun.star.lang.ServiceManager
* @since UDK1.0
*/
public class JavaLoader implements XImplementationLoader,
XServiceInfo,
XInitialization
{
private static final boolean DEBUG = true;
private static final void DEBUG(String dbg) {
if (DEBUG) System.err.println( dbg );
}
private static String[] supportedServices = {
"com.sun.star.loader.Java"
};
protected XMultiServiceFactory multiServiceFactory = null;
private XMacroExpander m_xMacroExpander = null;
private static final String EXPAND_PROTOCOL_PREFIX = "vnd.sun.star.expand:";
/** Expands macrofied url using the macro expander singleton.
*/
private String expand_url( String url ) throws RuntimeException
{
if (url != null && url.startsWith( EXPAND_PROTOCOL_PREFIX ))
{
try
{
if (m_xMacroExpander == null)
{
XPropertySet xProps = (XPropertySet)UnoRuntime.queryInterface(
XPropertySet.class, multiServiceFactory );
if (xProps == null)
{
throw new com.sun.star.uno.RuntimeException(
"service manager does not support XPropertySet!", this );
}
XComponentContext xContext = (XComponentContext)AnyConverter.toObject(
new Type( XComponentContext.class ),
xProps.getPropertyValue( "DefaultContext" ) );
m_xMacroExpander = (XMacroExpander)AnyConverter.toObject(
new Type( XMacroExpander.class ),
xContext.getValueByName( "/singletons/com.sun.star.util.theMacroExpander" ) );
// decode uric class chars
String macro = URLDecoder.decode(
url.substring( EXPAND_PROTOCOL_PREFIX.length() ) /* cut protocol */ );
// expand macro string
String ret = m_xMacroExpander.expandMacros( macro );
if (DEBUG)
{
System.err.println(
"JavaLoader.expand_url(): " + url + " => " + macro + " => " + ret );
}
return ret;
}
}
catch (com.sun.star.uno.Exception exc)
{
throw new com.sun.star.uno.RuntimeException( exc.getMessage(), this );
}
}
return url;
}
/** default constructor
*/
/**
* Creates a new instance of the JavaLoader
class.
*
* @return new instance
*/
public JavaLoader() {}
/**
* Creates a new JavaLoader
object. The specified com.sun.star.lang.XMultiServiceFactory
* is the ServiceManager
service which can be deliviert to all components the JavaLoader
is
* loading.
* To set the MultiServiceFactory
you can use the com.sun.star.lang.XInitialization
interface, either.
*
* @return new instance
* @param factory the ServiceManager
* @see com.sun.star.lang.ServiceManager
* @see com.sun.star.lang.ServiceManager
* @see com.sun.star.lang.XInitialization
*/
public JavaLoader(XMultiServiceFactory factory) {
multiServiceFactory = factory;
}
/**
* Unlike the original intention, the method could be called every time a new
* com.sun.star.lang.XMultiServiceFactory
should be set at the loader.
*
* @param args - the first parameter (args[0]) specifices the ServiceManager
* @see com.sun.star.lang.XInitialization
* @see com.sun.star.lang.ServiceManager
*/
public void initialize( java.lang.Object[] args )
throws com.sun.star.uno.Exception,
com.sun.star.uno.RuntimeException
{
if (args.length == 0) throw new com.sun.star.lang.IllegalArgumentException("No arguments specified");
try {
multiServiceFactory = (XMultiServiceFactory) UnoRuntime.queryInterface(XMultiServiceFactory.class, args[0]);
}
catch (ClassCastException castEx) {
throw new com.sun.star.lang.IllegalArgumentException(
"The argument must be an instance of XMultiServiceFactory");
}
}
/**
* Supplies the implementation name of the component.
*
* @return the implementation name - here the class name * @see com.sun.star.lang.XServiceInfo */ public String getImplementationName() throws com.sun.star.uno.RuntimeException { return getClass().getName(); } /** * Verifies if a given service is supported by the component. *
* @return true,if service is suported - otherwise false * @param serviceName the name of the service that should be checked * @see com.sun.star.lang.XServiceInfo */ public boolean supportsService(String serviceName) throws com.sun.star.uno.RuntimeException { for ( int i = 0; i < supportedServices.length; i++ ) { if ( supportedServices[i].equals(serviceName) ) return true; } return false; } /** * Supplies a list of all service names supported by the component *
* @return a String array with all supported services
* @see com.sun.star.lang.XServiceInfo
*/
public String[] getSupportedServiceNames()
throws com.sun.star.uno.RuntimeException
{
return supportedServices;
}
/**
* Provides a components factory.
* The JavaLoader
tries to load the class first. If a loacation URL is given the
* RegistrationClassFinder is used to load the class. Otherwise the class is loaded thru the Class.forName
* method.
* To get the factory the inspects the class for the optional static member functions __getServiceFactory resp.
* getServiceFactory (DEPRECATED).
* If the function can not be found a default factory @see ComponentFactoryWrapper will be created.
*
* @return the factory for the component (@see com.sun.star.lang.XSingleServiceFactory) * @param implementationName the implementation (class) name of the component * @param implementationLoaderUrl the URL of the implementation loader. Not used. * @param locationUrl points to an archive (JAR file) which contains a component * @param xKey * @see com.sun.star.lang.XImplementationLoader * @see com.sun.star.com.loader.RegistrationClassFinder */ public java.lang.Object activate( String implementationName, String implementationLoaderUrl, String locationUrl, XRegistryKey xKey ) throws CannotActivateFactoryException, com.sun.star.uno.RuntimeException { locationUrl = expand_url( locationUrl ); boolean needFactoryWrapper = false; Object returnObject = null; Class clazz = null; DEBUG("try to get factory for " + implementationName); // first we must get the class of the implementation // 1. If a location URL is given it is assumed that this points to a JAR file. // The components class name is stored in the manifest file. // 2. If only the implementation name is given, the class is loaded with the Class.forName() method try { if ( locationUrl != null ) { RegistrationClassFinder classFinder = new RegistrationClassFinder( locationUrl ); // 1. clazz = classFinder.getRegistrationClass(); } else { // 2. clazz = Class.forName( implementationName ); } } catch (java.net.MalformedURLException e) { CannotActivateFactoryException cae = new CannotActivateFactoryException( "Can not activate factory because " + e.toString() ); cae.fillInStackTrace(); throw cae; } catch (java.io.IOException e) { CannotActivateFactoryException cae = new CannotActivateFactoryException( "Can not activate factory because " + e.toString() ); cae.fillInStackTrace(); throw cae; } catch (java.lang.ClassNotFoundException e) { CannotActivateFactoryException cae = new CannotActivateFactoryException( "Can not activate factory because " + e.toString() ); cae.fillInStackTrace(); throw cae; } Class[] paramTypes = {String.class, XMultiServiceFactory.class, XRegistryKey.class}; Object[] params = { implementationName, multiServiceFactory, xKey }; // try to get factory from implemetation class // - new style: use the public static method __getServiceFactory // - old style: use the public static method getServiceFactory ( DEPRECATED ) Method method = null; try { method = clazz.getMethod("__getServiceFactory", paramTypes); } catch ( NoSuchMethodException noSuchMethodEx) { method = null; } catch ( SecurityException secEx) { method = null; } try { if ( method == null ) { method = clazz.getMethod("getServiceFactory", paramTypes); } Object oRet = method.invoke(clazz, params); if ( (oRet != null) && (oRet instanceof XSingleServiceFactory) ) { returnObject = (XSingleServiceFactory) oRet; } } catch ( NoSuchMethodException noSuchMethodEx) { needFactoryWrapper = true; } catch ( SecurityException secEx) { needFactoryWrapper = true; } catch ( IllegalAccessException e ) { throw new CannotActivateFactoryException("Can not activate the factory for " + implementationName + " because " + e.toString() ); } catch ( IllegalArgumentException e ) { throw new CannotActivateFactoryException("Can not activate the factory for " + implementationName + " because " + e.toString() ); } catch ( InvocationTargetException e ) { throw new CannotActivateFactoryException("Can not activate the factory for " + implementationName + " because " + e.getTargetException().toString() ); } // if no method is found make a factory wrapper for the implementation and return it if ( needFactoryWrapper ) { DEBUG ("create factory wrapper for " + implementationName); ComponentFactoryWrapper wrapp = new ComponentFactoryWrapper( implementationName, locationUrl ); returnObject = wrapp.getServiceFactory(implementationName, multiServiceFactory, xKey); } return returnObject; } /** * Registers the component in a registry under a given root key. If the component supports the optional * methods __writeRegistryServiceInfo, writeRegistryServiceInfo (DEPRECATED), the call is delegated to that * method. Otherwise a default registration will be accomplished. *
* @return true if registration is successfully - otherwise false
* @param regKey the root key under that the component should be registred.
* @param implementationLoaderUrl specifies the loader, the component is loaded by.
* @param locationUrl points to an archive (JAR file) which contains a component
* @see ComponentFactoryWrapper
*/
public boolean writeRegistryInfo( XRegistryKey regKey,
String implementationLoaderUrl,
String locationUrl )
throws CannotRegisterImplementationException,
com.sun.star.uno.RuntimeException
{
locationUrl = expand_url( locationUrl );
boolean success = false;
try {
RegistrationClassFinder classFinder = new RegistrationClassFinder(locationUrl);
Class clazz = classFinder.getRegistrationClass();
Class[] paramTypes = { XRegistryKey.class };
Object[] params = { regKey };
Method method = clazz.getMethod("__writeRegistryServiceInfo", paramTypes);
Object oRet = method.invoke(clazz, params);
if ( (oRet != null) && (oRet instanceof Boolean) )
success = ((Boolean) oRet).booleanValue();
}
catch (Exception e) {
// default registration
ComponentFactoryWrapper wrapp = new ComponentFactoryWrapper(null, locationUrl);
success = wrapp.writeRegistryServiceInfo(regKey);
}
return success;
}
/**
* Supplies the factory for the JavaLoader
*
* @return the factory for the JavaLoader
* @param implName the name of the desired component
* @param multiFactory the ServiceManager
is delivered to the factory
* @param regKey not used - can be null
*/
public static XSingleServiceFactory getServiceFactory( String implName,
XMultiServiceFactory multiFactory,
XRegistryKey regKey)
{
if ( implName.equals(JavaLoader.class.getName()) )
return new JavaLoaderFactory( multiFactory );
return null;
}
/**
* Registers the JavaLoader
at the registry.
*
* @return true if registration succseeded - otherwise false
* @param regKey root key under which the
* @version $Revision: 1.4 $ $ $Date: 2002-06-14 13:09:52 $
* @author Markus Herzog
* @since UDK1.0
*/
class ComponentFactoryWrapper
implements XServiceInfo,
XSingleServiceFactory
{
private static final boolean DEBUG = false;
private String serviceName = null;
private String implName = null;
private String locationUrl = null;
private Class serviceClass = null;
private XMultiServiceFactory aServiceManager = null;
private boolean bServiceManagerAlreadySet = false;
/**
* Constructor for a
* @param name specifies the name of the implementation. If no loaction URL is given
* the implementation name must be the class name of the component.
* @param lUrl points to an archive (JAR file) which contains a component.
*/
public ComponentFactoryWrapper( String name, String lUrl ) {
implName = name;
locationUrl = lUrl;
}
private static final void DEBUG( String dbg ) {
if (DEBUG) System.err.println(">>>ComponentFactoryWrapper - " + dbg);
}
/**
* Registers the component at the registry. First it is verified if component includes the optional
* method __writeRegistryServiceInfo ( writeRegistryServiceInfo - DEPRECATED ). If so the call is delegated
* to this method. Otherwise the component will be registered under its implementation name.
*
* @return true if registration succseeded - otherwise false
* @param regKey root key under which the
* @param obj the newly created component
* @see createInstanceWithArguments
* @see createInstance
*/
private void setServiceManager( Object obj ) {
Class clazz = getServiceClass();
Class paramTypes[] = { XMultiServiceFactory.class };
Object[] args = { aServiceManager };
try {
clazz.getDeclaredMethod( "__setServiceManager", paramTypes).invoke(obj, args);
}
catch (NoSuchMethodException e) {}
catch (SecurityException e) {}
catch (IllegalAccessException e) {}
catch (IllegalArgumentException e) {}
catch (InvocationTargetException e) {}
}
/**
* Tries to instantiate a component by its implementation name. For it the Beans.instantiate
* method is called. If any exception occured a com.sun.star.uno.Exception will be thrown.
*
* @return
* @see java.beans.Beans
*/
private Object createInstanceWithDefaultConstructor()
throws com.sun.star.uno.Exception,
com.sun.star.uno.RuntimeException
{
Object resObj = null;
try {
DEBUG ("try to instantiate " + implName );
resObj = java.beans.Beans.instantiate(getClass().getClassLoader(), implName);
}
catch (IOException e) {
if (DEBUG) e.printStackTrace();
throw new com.sun.star.uno.Exception("Can not create an instance of " + implName + "\n" +
e.toString());
}
catch (ClassNotFoundException e) {
if (DEBUG) e.printStackTrace();
throw new com.sun.star.uno.Exception("Can not create an instance of " + implName + "\n" +
e.toString());
}
catch (NoSuchMethodError e) {
if (DEBUG) e.printStackTrace();
throw new com.sun.star.uno.Exception("Can not create an instance of " + implName + "\n" +
e.toString());
}
return resObj;
}
/**
* Creates a new instance of the component.
*
* @return newly instanciated component
* @see com.sun.star.lang.XSimpleServiceFactory
*/
public Object createInstance()
throws com.sun.star.uno.Exception,
com.sun.star.uno.RuntimeException
{
Object resObj = createInstanceWithDefaultConstructor();
setServiceManager( resObj );
return resObj;
}
/**
* Creates a new component with arguments. How the arguments are commited depends on the component.
* For that the following order is used:
* 1. The component supports the XInitialization interface: An object is created using Beans.instantiate.
* The arguments are set with the
* @return newly created component
* @param args the arguments which should be used
*/
public Object createInstanceWithArguments( Object[] args )
throws com.sun.star.uno.Exception,
com.sun.star.uno.RuntimeException
{
java.lang.Object resObj = null;
boolean useDefaultCtor = false;
// first we try if the component supports the XInitialization interface
Class clazz = getServiceClass();
if ( clazz.isAssignableFrom( XInitialization.class ) )
useDefaultCtor = true;
else {
try {
Class parameterTypes[] = { Class.class, Object.class };
Method queryMeth = clazz.getMethod("queryInterface", parameterTypes );
useDefaultCtor = true;
}
catch (NoSuchMethodException e) {}
catch (SecurityException e) {}
}
if (useDefaultCtor) {
resObj = createInstanceWithDefaultConstructor();
XInitialization iniObj = (XInitialization) UnoRuntime.queryInterface( XInitialization.class, resObj );
iniObj.initialize( args );
}
else {
// try the constructor A(java.lang.Object[])
Class[] clazzParams = { Object[].class };
java.lang.reflect.Constructor ctor = null;
try {
ctor = clazz.getConstructor( clazzParams );
}
catch (NoSuchMethodException noSuchMethodEx) {
ctor = null;
}
catch (SecurityException securityEx) {
ctor = null;
}
if (ctor == null) {
// look for the first matching constructor
java.lang.reflect.Constructor ctors[] = clazz.getDeclaredConstructors();
int i=0;
while ( (i
* @return true if the service is supported - otherwise false
* @param requestedService name of the requested service
*/
public boolean supportsService(String requestedService)
throws com.sun.star.uno.RuntimeException
{
boolean found = false;
int i = 0;
if (requestedService == null)
throw new com.sun.star.uno.RuntimeException("no service requested");
String names[] = getServiceNames();
if (names.length == 0)
throw new com.sun.star.uno.RuntimeException("no service name found");
while (i
* @return the class of the component
* @see com.sun.star.comp.loader.RegistrationClassFinder
*/
Class getServiceClass()
throws com.sun.star.uno.RuntimeException
{
if (serviceClass == null) {
try {
if ( locationUrl != null ) {
RegistrationClassFinder classFinder = new RegistrationClassFinder( locationUrl );
serviceClass = classFinder.getRegistrationClass();
}
else {
serviceClass = Class.forName( implName );
}
}
catch ( java.net.MalformedURLException e ) {
throw new com.sun.star.uno.RuntimeException( "can't get the class " + implName + ".\n" + e );
}
catch ( java.io.IOException e ) {
throw new com.sun.star.uno.RuntimeException( "can't get the class " + implName + ".\n" + e );
}
catch ( java.lang.ClassNotFoundException e ) {
throw new com.sun.star.uno.RuntimeException( "can't get the class " + implName + ".\n" + e );
}
}
return serviceClass;
}
/**
* Extract the service names supported by the component. The optional static member
*
* @return a list with the supported service names
*/
public String[] getServiceNames()
throws com.sun.star.uno.RuntimeException
{
String result[] = null;
try {
Class clazz = getServiceClass();
Object attrib = null;
try {
attrib = clazz.getDeclaredField("__serviceName").get(clazz);
} catch (NoSuchFieldException e) {
//********************* DEPRECATED ******************************************
attrib = clazz.getDeclaredField("serviceName").get(clazz);
//***************************************************************************
}
if ( attrib instanceof String ) {
String str = (String) attrib;
result = new String[1];
result[0] = str;
} else
result = (String[]) attrib; // expecting an array of Strings - otherwise an exception will be thrown
}
catch (NoSuchFieldException e) {}
catch (SecurityException e) {}
catch (IllegalAccessException e) {}
if (result == null) {
result = new String[1];
result[0] = implName;
}
return result;
}
public String toString() {
String result = super.toString();
result += " implementation name: " +getImplementationName();
result += " supported services:";
String[] services = getSupportedServiceNames();
for (int i=0; iJavaLoader
should be regidstered
*/
public static boolean writeRegistryServiceInfo(XRegistryKey regKey) {
boolean result = false;
try {
XRegistryKey newKey = regKey.createKey("/" + JavaLoader.class.getName() + "/UNO/SERVICE");
for (int i=0; iComponentFactoryWrapper
class provides methods to create a factory for a component and
* the registration at a registry in a default manner. The class is used by the JavaLoader
if the
* a component does not comes with its own methods for creating a factory or for the registration.
* ComponentFactoryWrapper
object.
* JavaLoader
should be regidstered
*/
public boolean writeRegistryServiceInfo( XRegistryKey regKey )
throws CannotRegisterImplementationException,
com.sun.star.uno.RuntimeException
{
boolean success = false;
boolean defaultRegistration = false;
Class clazz = getServiceClass();
if ( clazz != null ) {
try {
Class[] paramTypes = { XRegistryKey.class };
Object[] params = { regKey };
Method method = null;
try {
method = clazz.getMethod("__writeRegistryServiceInfo", paramTypes);
}
catch (NoSuchMethodException noSuchMethodEx) {
method = null;
}
catch (SecurityException securityEx) {
method = null;
}
if (method == null) {
method = clazz.getMethod("writeRegistryServiceInfo", paramTypes);
}
Object oRet = method.invoke(clazz, params);
if ( (oRet != null) && (oRet instanceof Boolean) )
success = ((Boolean) oRet).booleanValue();
}
catch (NoSuchMethodException noSuchMethodEx) {
defaultRegistration = true;
}
catch (SecurityException securityEx) {
defaultRegistration = true;
}
catch (IllegalAccessException e) {
throw new CannotRegisterImplementationException("Can not register " + implName + " because " + e.toString() );
}
catch (IllegalArgumentException e) {
throw new CannotRegisterImplementationException("Can not register " + implName + " because " + e.toString() );
}
catch (InvocationTargetException e) {
throw new CannotRegisterImplementationException("Can not register " + implName + " because " + e.getTargetException() );
}
if (defaultRegistration) {
try {
XRegistryKey newKey = regKey.createKey("/" + implName + "/UNO/SERVICES");
String names[] = getServiceNames();
for (int i=0; iServiceManager
at the component. For that the component must support
* the __setServiceManager
methode. The method is called after a new instance of the
* component is created.
* initialize
method.
* 2. The component has a constructor with an array of objects as a argument.
* 3. The component has any other constructor which can take the arguments.
* com.sun.star.comp.loader.RegistrationClassFinder
is used to obtain the class.
* Otherwise the java.lang.Class.forName
is called with implementation name.
* __serviceName
( serviceName
DEPRECATED ) is used to specify the supported
* service names. If no member can be found the implementation name is return.
*