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