1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 package com.sun.star.comp.loader;
25 
26 import java.lang.reflect.Method;
27 
28 import java.lang.reflect.InvocationTargetException;
29 
30 import java.net.URLDecoder;
31 
32 import com.sun.star.loader.CannotActivateFactoryException;
33 import com.sun.star.loader.XImplementationLoader;
34 
35 import com.sun.star.registry.CannotRegisterImplementationException;
36 import com.sun.star.registry.XRegistryKey;
37 
38 import com.sun.star.lang.XSingleComponentFactory;
39 import com.sun.star.lang.XSingleServiceFactory;
40 import com.sun.star.lang.XMultiServiceFactory;
41 import com.sun.star.lang.XServiceInfo;
42 import com.sun.star.lang.XInitialization;
43 
44 import com.sun.star.uno.XComponentContext;
45 import com.sun.star.beans.XPropertySet;
46 import com.sun.star.util.XMacroExpander;
47 
48 import com.sun.star.uno.Type;
49 import com.sun.star.uno.UnoRuntime;
50 
51 import com.sun.star.lib.util.StringHelper;
52 
53 import com.sun.star.uno.AnyConverter;
54 
55 
56 /**
57  * The <code>JavaLoader</code> class provides the functionality of the <code>com.sun.star.loader.Java</code>
58  * service. Therefor the <code>JavaLoader</code> activates external UNO components which are implemented in Java.
59  * The loader is used by the <code>ServiceManger</code>.
60  * <p>
61  * @version 	$Revision: 1.16 $ $ $Date: 2008-04-11 11:10:31 $
62  * @author 	    Markus Herzog
63  * @see         com.sun.star.loader.XImplementationLoader
64  * @see         com.sun.star.loader.Java
65  * @see         com.sun.star.comp.servicemanager.ServiceManager
66  * @see			com.sun.star.lang.ServiceManager
67  * @since       UDK1.0
68  */
69 public class JavaLoader implements XImplementationLoader,
70                                    XServiceInfo,
71                                    XInitialization
72 {
73 	private static final boolean DEBUG = false;
74 
DEBUG(String dbg)75 	private static final void DEBUG(String dbg) {
76 	    if (DEBUG) System.err.println( dbg );
77 	}
78 
79     private static String[] supportedServices = {
80         "com.sun.star.loader.Java"
81     };
82 
83 	protected XMultiServiceFactory multiServiceFactory = null;
84 
85 	private XMacroExpander m_xMacroExpander = null;
86     private static final String EXPAND_PROTOCOL_PREFIX = "vnd.sun.star.expand:";
87 
88     /** Expands macrofied url using the macro expander singleton.
89      */
expand_url( String url )90     private String expand_url( String url ) throws RuntimeException
91     {
92         if (url != null && url.startsWith( EXPAND_PROTOCOL_PREFIX ))
93         {
94             try
95             {
96                 if (m_xMacroExpander == null)
97                 {
98                     XPropertySet xProps =
99                         UnoRuntime.queryInterface(
100                             XPropertySet.class, multiServiceFactory );
101                     if (xProps == null)
102                     {
103                         throw new com.sun.star.uno.RuntimeException(
104                             "service manager does not support XPropertySet!",
105                             this );
106                     }
107                     XComponentContext xContext = (XComponentContext)
108                         AnyConverter.toObject(
109                             new Type( XComponentContext.class ),
110                             xProps.getPropertyValue( "DefaultContext" ) );
111                     m_xMacroExpander = (XMacroExpander)AnyConverter.toObject(
112                         new Type( XMacroExpander.class ),
113                         xContext.getValueByName(
114                             "/singletons/com.sun.star.util.theMacroExpander" )
115                         );
116                 }
117                 // decode uric class chars
118                 String macro = URLDecoder.decode(
119                     StringHelper.replace(
120                         url.substring( EXPAND_PROTOCOL_PREFIX.length() ),
121                         '+', "%2B" ) );
122                 // expand macro string
123                 String ret = m_xMacroExpander.expandMacros( macro );
124                 if (DEBUG)
125                 {
126                     System.err.println(
127                         "JavaLoader.expand_url(): " + url + " => " +
128                         macro + " => " + ret );
129                 }
130                 return ret;
131             }
132             catch (com.sun.star.uno.Exception exc)
133             {
134                 throw new com.sun.star.uno.RuntimeException(
135                     exc.getMessage(), this );
136             }
137             catch (java.lang.Exception exc)
138             {
139                 throw new com.sun.star.uno.RuntimeException(
140                     exc.getMessage(), this );
141             }
142         }
143         return url;
144     }
145 
146 	/** default constructor
147 	 */
148 
149 	/**
150 	 * Creates a new instance of the <code>JavaLoader</code> class.
151 	 * <p>
152 	 * @return	new instance
153 	 */
JavaLoader()154 	public JavaLoader() {}
155 
156 	/**
157 	 * Creates a new <code>JavaLoader</code> object. The specified <code>com.sun.star.lang.XMultiServiceFactory</code>
158 	 * is the <code>ServiceManager</code> service which can be deliviert to all components the <code>JavaLoader</code> is
159 	 * loading.
160 	 * To set the <code>MultiServiceFactory</code> you can use the <code>com.sun.star.lang.XInitialization</code> interface, either.
161 	 * <p>
162 	 * @return	new instance
163 	 * @param	factory		the <code>ServiceManager</code>
164 	 * @see		com.sun.star.lang.ServiceManager
165 	 * @see		com.sun.star.lang.ServiceManager
166 	 * @see		com.sun.star.lang.XInitialization
167 	 */
JavaLoader(XMultiServiceFactory factory)168 	public JavaLoader(XMultiServiceFactory factory) {
169 	    multiServiceFactory = factory;
170 	}
171 
172 	/**
173      * Unlike the original intention, the method could be called every time a new
174      * <code>com.sun.star.lang.XMultiServiceFactory</code> should be set at the loader.
175 	 * <p>
176 	 * @param		args - the first parameter (args[0]) specifices the <code>ServiceManager</code>
177 	 * @see		    com.sun.star.lang.XInitialization
178 	 * @see		    com.sun.star.lang.ServiceManager
179 	 */
initialize( java.lang.Object[] args )180     public void initialize( java.lang.Object[] args )
181             throws com.sun.star.uno.Exception,
182                    com.sun.star.uno.RuntimeException
183     {
184         if (args.length == 0) throw new com.sun.star.lang.IllegalArgumentException("No arguments specified");
185 
186         try {
187             multiServiceFactory = (XMultiServiceFactory) AnyConverter.toObject(
188                 new Type(XMultiServiceFactory.class), args[0]);
189         }
190         catch (ClassCastException castEx) {
191             throw new com.sun.star.lang.IllegalArgumentException(
192                 "The argument must be an instance of XMultiServiceFactory");
193         }
194     }
195 
196 	/**
197 	 * Supplies the implementation name of the component.
198 	 * <p>
199 	 * @return      the implementation name - here the class name
200 	 * @see			com.sun.star.lang.XServiceInfo
201 	 */
getImplementationName()202 	public String getImplementationName()
203             throws com.sun.star.uno.RuntimeException
204     {
205 		return getClass().getName();
206 	}
207 
208 	/**
209 	 * Verifies if a given service is supported by the component.
210 	 * <p>
211 	 * @return		true,if service is suported - otherwise false
212 	 * @param		serviceName		the name of the service that should be checked
213 	 * @see			com.sun.star.lang.XServiceInfo
214 	 */
supportsService(String serviceName)215 	public boolean supportsService(String serviceName)
216 	        throws com.sun.star.uno.RuntimeException
217 	{
218 		for ( int i = 0; i < supportedServices.length; i++ ) {
219 			if ( supportedServices[i].equals(serviceName) )
220 				return true;
221 		}
222 		return false;
223 	}
224 
225 	/**
226 	 * Supplies a list of all service names supported by the component
227 	 * <p>
228 	 * @return		a String array with all supported services
229 	 * @see			com.sun.star.lang.XServiceInfo
230 	 */
getSupportedServiceNames()231 	public String[] getSupportedServiceNames()
232 	        throws com.sun.star.uno.RuntimeException
233 	{
234 		return supportedServices;
235 	}
236 
237 	/**
238 	 * Provides a components factory.
239 	 * The <code>JavaLoader</code> tries to load the class first. If a loacation URL is given the
240 	 * RegistrationClassFinder is used to load the class. Otherwise the class is loaded thru the Class.forName
241 	 * method.
242 	 * To get the factory the inspects the class for the optional static member functions __getServiceFactory resp.
243 	 * getServiceFactory (DEPRECATED).
244 	 * If the function can not be found a default factory @see ComponentFactoryWrapper will be created.
245 	 * <p>
246 	 * @return		the factory for the component (@see com.sun.star.lang.XSingleServiceFactory)
247 	 * @param      	implementationName			the implementation (class) name of the component
248 	 * @param      	implementationLoaderUrl		the URL of the implementation loader. Not used.
249 	 * @param      	locationUrl					points to an archive (JAR file) which contains a component
250 	 * @param      	xKey
251 	 * @see		   	com.sun.star.lang.XImplementationLoader
252 	 * @see			com.sun.star.com.loader.RegistrationClassFinder
253 	 */
activate( String implementationName, String implementationLoaderUrl, String locationUrl, XRegistryKey xKey )254 	public java.lang.Object activate( String implementationName,
255 	                                  String implementationLoaderUrl,
256 	                                  String locationUrl,
257 	                                  XRegistryKey xKey )
258         throws CannotActivateFactoryException,
259                com.sun.star.uno.RuntimeException
260     {
261         locationUrl = expand_url( locationUrl );
262 
263 		Object returnObject  = null;
264 	    Class clazz  ;
265 
266 	    DEBUG("try to get factory for " + implementationName);
267 
268 	    // first we must get the class of the implementation
269 	    // 1. If a location URL is given it is assumed that this points to a JAR file.
270 	    //    The components class name is stored in the manifest file.
271 	    // 2. If only the implementation name is given, the class is loaded with the
272 	    //    Class.forName() method. This is a hack to load bootstrap components.
273         //    Normally a string must no be null.
274 	    try {
275 		    if ( locationUrl != null ) {
276 		    	// 1.
277 	    		clazz = RegistrationClassFinder.find( locationUrl );
278 		    }
279 		    else {
280 		    	// 2.
281 	    		clazz = Class.forName( implementationName );
282 			}
283 		}
284 		catch (java.net.MalformedURLException e) {
285 			CannotActivateFactoryException cae = new CannotActivateFactoryException(
286 					"Can not activate factory because " + e.toString() );
287 			cae.fillInStackTrace();
288 			throw cae;
289 		}
290 		catch (java.io.IOException e) {
291 			CannotActivateFactoryException cae = new CannotActivateFactoryException(
292 					"Can not activate factory because " + e.toString() );
293 			cae.fillInStackTrace();
294 			throw cae;
295 		}
296 		catch (java.lang.ClassNotFoundException e) {
297 			CannotActivateFactoryException cae = new CannotActivateFactoryException(
298 					"Can not activate factory because " + e.toString() );
299 			cae.fillInStackTrace();
300 			throw cae;
301 		}
302 
303         if (null == clazz)
304         {
305             CannotActivateFactoryException cae =
306                 new CannotActivateFactoryException(
307                     "Cannot determine activation class!" );
308 			cae.fillInStackTrace();
309 			throw cae;
310         }
311 
312 		Class[] paramTypes = {String.class, XMultiServiceFactory.class, XRegistryKey.class};
313 		Object[] params = { implementationName, multiServiceFactory, xKey };
314 
315         // try to get factory from implemetation class
316         // latest style: use the public static method __getComponentFactory
317         // - new style: use the public static method __getServiceFactory
318         // - old style: use the public static method getServiceFactory ( DEPRECATED )
319 
320         Method compfac_method = null;
321 		try
322         {
323 		 	compfac_method = clazz.getMethod(
324                 "__getComponentFactory", new Class [] { String.class } );
325 		}
326 		catch ( NoSuchMethodException noSuchMethodEx) {}
327 		catch ( SecurityException secEx) {}
328 
329         Method method = null;
330         if (null == compfac_method)
331         {
332             try {
333                 method = clazz.getMethod("__getServiceFactory", paramTypes);
334             }
335             catch ( NoSuchMethodException noSuchMethodEx) {
336                 method = null;
337             }
338             catch ( SecurityException secEx) {
339                 method = null;
340             }
341         }
342 
343 		try {
344             if (null != compfac_method)
345             {
346                 Object ret = compfac_method.invoke( clazz, new Object [] { implementationName } );
347                 if (null == ret || !(ret instanceof XSingleComponentFactory))
348                 {
349                     throw new CannotActivateFactoryException(
350                         "No factory object for " + implementationName );
351                 }
352                 return (XSingleComponentFactory)ret;
353             }
354             else
355             {
356                 if ( method == null ) {
357                     method = clazz.getMethod("getServiceFactory", paramTypes);
358                 }
359 
360                 Object oRet = method.invoke(clazz, params);
361 
362                 if ( (oRet != null) && (oRet instanceof XSingleServiceFactory) ) {
363                     returnObject = (XSingleServiceFactory) oRet;
364                 }
365             }
366     	}
367 		catch ( NoSuchMethodException e) {
368             throw new CannotActivateFactoryException("Can not activate the factory for "
369                         + implementationName + " because " + e.toString() );
370 		}
371 		catch ( SecurityException e) {
372             throw new CannotActivateFactoryException("Can not activate the factory for "
373 						+ implementationName + " because " + e.toString() );
374 		}
375 		catch ( IllegalAccessException e ) {
376 			throw new CannotActivateFactoryException("Can not activate the factory for "
377 						+ implementationName + " because " + e.toString() );
378 		}
379 		catch ( IllegalArgumentException e ) {
380 			throw new CannotActivateFactoryException("Can not activate the factory for "
381 						+ implementationName + " because " + e.toString() );
382 		}
383 		catch ( InvocationTargetException e ) {
384 			throw new CannotActivateFactoryException("Can not activate the factory for "
385 						+ implementationName + " because " + e.getTargetException().toString() );
386 		}
387 
388 		return returnObject;
389 	}
390 
391 	/**
392 	 * Registers the component in a registry under a given root key. If the component supports the optional
393 	 * methods __writeRegistryServiceInfo, writeRegistryServiceInfo (DEPRECATED), the call is delegated to that
394 	 * method. Otherwise a default registration will be accomplished.
395 	 * <p>
396 	 * @return		true if registration is successfully - otherwise false
397 	 * @param		regKey					the root key under that the component should be registred.
398 	 * @param		implementationLoaderUrl	specifies the loader, the component is loaded by.
399 	 * @param		locationUrl				points to an archive (JAR file) which contains a component
400 	 * @see 		ComponentFactoryWrapper
401 	 */
writeRegistryInfo( XRegistryKey regKey, String implementationLoaderUrl, String locationUrl )402 	public boolean writeRegistryInfo( XRegistryKey regKey,
403 	                                  String implementationLoaderUrl,
404 	                                  String locationUrl )
405 	        throws CannotRegisterImplementationException,
406 	               com.sun.star.uno.RuntimeException
407 	{
408         locationUrl = expand_url( locationUrl );
409 
410 		boolean success = false;
411 
412         try {
413 
414     		Class clazz = RegistrationClassFinder.find(locationUrl);
415             if (null == clazz)
416             {
417                 throw new CannotRegisterImplementationException(
418                     "Cannot determine registration class!" );
419             }
420 
421 			Class[] paramTypes = { XRegistryKey.class };
422 			Object[] params = { regKey };
423 
424 			Method method  = clazz.getMethod("__writeRegistryServiceInfo", paramTypes);
425 			Object oRet = method.invoke(clazz, params);
426 
427 			if ( (oRet != null) && (oRet instanceof Boolean) )
428 				success = ((Boolean) oRet).booleanValue();
429         }
430 		catch (Exception e) {
431             throw new CannotRegisterImplementationException( e.getMessage());
432  		}
433 
434 		return success;
435 	}
436 
437 	/**
438 	 * Supplies the factory for the <code>JavaLoader</code>
439 	 * <p>
440 	 * @return	the factory for the <code>JavaLoader</code>
441 	 * @param	implName		the name of the desired component
442 	 * @param   multiFactory	the <code>ServiceManager</code> is delivered to the factory
443 	 * @param   regKey			not used - can be null
444 	 */
getServiceFactory( String implName, XMultiServiceFactory multiFactory, XRegistryKey regKey)445 	public static XSingleServiceFactory getServiceFactory( String implName,
446 	                                                       XMultiServiceFactory multiFactory,
447 	                                                       XRegistryKey regKey)
448 	{
449 	    if ( implName.equals(JavaLoader.class.getName()) )
450 	        return new JavaLoaderFactory( multiFactory );
451 
452 	    return null;
453 	}
454 
455 	/**
456 	 * Registers the <code>JavaLoader</code> at the registry.
457 	 * <p>
458 	 * @return     true if registration succseeded - otherwise false
459 	 * @param      regKey	root key under which the <code>JavaLoader</code> should be regidstered
460 	 */
writeRegistryServiceInfo(XRegistryKey regKey)461 	public static boolean writeRegistryServiceInfo(XRegistryKey regKey) {
462 	    boolean result = false;
463 
464 	    try {
465 	        XRegistryKey newKey = regKey.createKey("/" + JavaLoader.class.getName() + "/UNO/SERVICE");
466 
467 	        for (int i=0; i<supportedServices.length; i++)
468 	            newKey.createKey(supportedServices[i]);
469 
470 	        result = true;
471 	    }
472 	    catch (Exception ex) {
473 	        if (DEBUG) System.err.println(">>>JavaLoader.writeRegistryServiceInfo " + ex);
474 	    }
475 
476 	    return result;
477     }
478 }
479 
480