xref: /AOO41X/main/jurt/com/sun/star/comp/loader/JavaLoader.java (revision cdf0e10c4e3984b49a9502b011690b615761d4a3)
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