1. Project Clover database Tue Dec 20 2016 21:24:09 CET
  2. Package org.xwiki.extension.jar.internal.handler

File JarExtensionHandler.java

 

Coverage histogram

../../../../../../img/srcFileCovDistChart9.png
38% of files have more coverage

Code metrics

22
55
11
1
266
163
26
0.47
5
11
2.36

Classes

Class Line # Actions
JarExtensionHandler 62 55 0% 26 14
0.8409090684.1%
 

Contributing tests

This file is covered by 23 tests. .

Source view

1    /*
2    * See the NOTICE file distributed with this work for additional
3    * information regarding copyright ownership.
4    *
5    * This is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU Lesser General Public License as
7    * published by the Free Software Foundation; either version 2.1 of
8    * the License, or (at your option) any later version.
9    *
10    * This software is distributed in the hope that it will be useful,
11    * but WITHOUT ANY WARRANTY; without even the implied warranty of
12    * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13    * Lesser General Public License for more details.
14    *
15    * You should have received a copy of the GNU Lesser General Public
16    * License along with this software; if not, write to the Free
17    * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
18    * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
19    */
20    package org.xwiki.extension.jar.internal.handler;
21   
22    import java.io.File;
23    import java.io.IOException;
24    import java.io.InputStream;
25    import java.net.MalformedURLException;
26    import java.net.URL;
27    import java.util.List;
28   
29    import javax.inject.Inject;
30    import javax.inject.Singleton;
31   
32    import org.xwiki.classloader.ClassLoaderManager;
33    import org.xwiki.classloader.NamespaceURLClassLoader;
34    import org.xwiki.component.annotation.Component;
35    import org.xwiki.component.annotation.ComponentAnnotationLoader;
36    import org.xwiki.component.annotation.ComponentDeclaration;
37    import org.xwiki.component.internal.StackingComponentEventManager;
38    import org.xwiki.component.internal.multi.ComponentManagerManager;
39    import org.xwiki.component.manager.ComponentEventManager;
40    import org.xwiki.component.manager.ComponentManager;
41    import org.xwiki.component.phase.Initializable;
42    import org.xwiki.component.phase.InitializationException;
43    import org.xwiki.extension.Extension;
44    import org.xwiki.extension.ExtensionException;
45    import org.xwiki.extension.InstallException;
46    import org.xwiki.extension.InstalledExtension;
47    import org.xwiki.extension.LocalExtension;
48    import org.xwiki.extension.LocalExtensionFile;
49    import org.xwiki.extension.UninstallException;
50    import org.xwiki.extension.handler.internal.AbstractExtensionHandler;
51    import org.xwiki.job.Request;
52    import org.xwiki.observation.ObservationManager;
53   
54    /**
55    * Add support for JAR extensions.
56    *
57    * @version $Id: 7f1ec66d2057636f65e2f9bcd723138c945153fd $
58    * @since 4.0M1
59    */
60    @Component(hints = {JarExtensionHandler.JAR, JarExtensionHandler.WEBJAR})
61    @Singleton
 
62    public class JarExtensionHandler extends AbstractExtensionHandler implements Initializable
63    {
64    /**
65    * Type {@code jar}.
66    *
67    * @since 9.0RC1
68    */
69    public static final String JAR = "jar";
70   
71    /**
72    * Type {@code webjar}.
73    *
74    * @since 9.0RC1
75    */
76    public static final String WEBJAR = "webjar";
77   
78    /**
79    * Custom property containing a JAR sub type (for example {@code webjar}).
80    *
81    * @since 9.0RC1
82    */
83    public static final String PROPERTY_TYPE = "xwiki.extension.jar.type";
84   
85    @Inject
86    private ComponentManagerManager componentManagerManager;
87   
88    @Inject
89    private ClassLoaderManager jarExtensionClassLoader;
90   
91    private ComponentAnnotationLoader jarLoader;
92   
93    /**
94    * @param type the type to test
95    * @return true of the passed extension type is supported by this handler
96    * @since 9.0RC1
97    */
 
98  148 toggle public static boolean isSupported(String type)
99    {
100  148 return type != null && (type.equals(JarExtensionHandler.JAR) || type.equals(JarExtensionHandler.WEBJAR));
101    }
102   
103    /**
104    * Find of the passes extension if a webjar.
105    *
106    * @param extension the extension to test
107    * @return true of the passed extension is a webjar, false otherwise
108    * @since 9.0RC1
109    */
 
110  133 toggle public static boolean isWebjar(Extension extension)
111    {
112    // Ideally webjar extensions should have "webjar" type
113  133 if (extension.getType().equals(WEBJAR)) {
114  0 return true;
115    }
116   
117    ///////////////////////////////
118    // But it's not the case for:
119   
120    // ** webjar.org releases (i.e. most of the webjars). We assume "org.webjars:*" id means it's a webjar
121  133 if (extension.getId().getId().startsWith("org.webjars:")) {
122  1 return true;
123    }
124    // ** contrib extensions which support version of XWiki older than 9.0RC1. We support a custom property which
125    // does not have any effect on older versions of XWiki
126  132 if (JarExtensionHandler.WEBJAR.equals(extension.getProperty(JarExtensionHandler.PROPERTY_TYPE))) {
127  0 return true;
128    }
129   
130  132 return false;
131    }
132   
 
133  26 toggle @Override
134    public void initialize() throws InitializationException
135    {
136  26 this.jarLoader = new ComponentAnnotationLoader();
137    }
138   
 
139  79 toggle @Override
140    public void initialize(LocalExtension localExtension, String namespace) throws ExtensionException
141    {
142  79 install(localExtension, namespace, null);
143    }
144   
 
145  135 toggle private static URL getExtensionURL(LocalExtension localExtension) throws MalformedURLException
146    {
147  135 return new File(localExtension.getFile().getAbsolutePath()).toURI().toURL();
148    }
149   
 
150  135 toggle @Override
151    public void install(LocalExtension localExtension, String namespace, Request request) throws InstallException
152    {
153  135 NamespaceURLClassLoader classLoader = this.jarExtensionClassLoader.getURLClassLoader(namespace, true);
154   
155    // 1) load jar into classloader
156  135 try {
157  135 classLoader.addURL(getExtensionURL(localExtension));
158    } catch (MalformedURLException e) {
159  0 throw new InstallException("Failed to load jar file", e);
160    }
161   
162    // 2) load and register components (only for standard jars)
163  135 if (containsComponents(localExtension)) {
164  132 loadComponents(localExtension.getFile(), classLoader, namespace);
165    }
166    }
167   
 
168  135 toggle private boolean containsComponents(Extension extension)
169    {
170  135 return extension != null && extension.getType().equals(JAR) && !isWebjar(extension);
171    }
172   
 
173  89 toggle @Override
174    public void uninstall(InstalledExtension installedExtension, String namespace, Request request)
175    throws UninstallException
176    {
177  89 if (installedExtension.isValid(namespace)) {
178  70 NamespaceURLClassLoader classLoader = this.jarExtensionClassLoader.getURLClassLoader(namespace, false);
179   
180  70 if (namespace == null || classLoader.getNamespace().equals(namespace)) {
181    // unregister components
182  70 try {
183  70 unloadComponents(installedExtension.getFile(), classLoader, namespace);
184    } catch (Throwable e) {
185    // We failed to unregister some components, we probably failed to register them in the first
186    // place too so let's just ignore it. Better than making impossible to uninstall the extension.
187    // We catch Throwable because most of the time we end up with a LinkageError
188  0 this.logger.warn("Failed to unregister some components of the JAR extension [{}]",
189    installedExtension.getId(), e);
190    }
191   
192    // The ClassLoader(s) will be replaced and reloaded at the end of the job
193    // @see org.xwiki.extension.jar.internal.handler.JarExtensionJobFinishedListener
194    }
195    }
196    }
197   
 
198  132 toggle private void loadComponents(LocalExtensionFile jarFile, NamespaceURLClassLoader classLoader, String namespace)
199    throws InstallException
200    {
201  132 try {
202  132 List<ComponentDeclaration> componentDeclarations = getDeclaredComponents(jarFile);
203   
204  132 if (componentDeclarations == null) {
205  2 this.logger.debug("[{}] does not contain any components to load", jarFile.getName());
206  2 return;
207    }
208   
209  130 ComponentManager componentManager = this.componentManagerManager.getComponentManager(namespace, true);
210   
211  130 ComponentEventManager componentEventManager = componentManager.getComponentEventManager();
212   
213    // Make sure to send events only when the extension is fully ready
214  130 StackingComponentEventManager stackingComponentEventManager = null;
215  130 try {
216  130 if (componentEventManager instanceof StackingComponentEventManager) {
217  130 stackingComponentEventManager = (StackingComponentEventManager) componentEventManager;
218    } else {
219  0 stackingComponentEventManager = new StackingComponentEventManager();
220  0 componentManager.setComponentEventManager(stackingComponentEventManager);
221    }
222  130 stackingComponentEventManager.shouldStack(true);
223   
224  130 this.jarLoader.initialize(componentManager, classLoader, componentDeclarations);
225    } finally {
226  130 if (stackingComponentEventManager != null) {
227  130 if (componentEventManager != stackingComponentEventManager) {
228  0 componentManager.setComponentEventManager(componentEventManager);
229    }
230   
231  130 stackingComponentEventManager.setObservationManager(
232    componentManager.<ObservationManager>getInstance(ObservationManager.class));
233  130 stackingComponentEventManager.shouldStack(false);
234  130 stackingComponentEventManager.flushEvents();
235    }
236    }
237    } catch (Exception e) {
238  0 throw new InstallException("Failed to load jar file components", e);
239    }
240    }
241   
 
242  202 toggle private List<ComponentDeclaration> getDeclaredComponents(LocalExtensionFile jarFile) throws IOException
243    {
244  202 InputStream is = jarFile.openStream();
245   
246  202 try {
247  202 return this.jarLoader.getDeclaredComponentsFromJAR(is);
248    } finally {
249  202 is.close();
250    }
251    }
252   
 
253  70 toggle private void unloadComponents(LocalExtensionFile jarFile, NamespaceURLClassLoader classLoader, String namespace)
254    throws IOException
255    {
256  70 List<ComponentDeclaration> componentDeclarations = getDeclaredComponents(jarFile);
257   
258  70 if (componentDeclarations == null) {
259  2 this.logger.debug("[{}] does not contain any component", jarFile.getName());
260  2 return;
261    }
262   
263  68 this.jarLoader.unregister(this.componentManagerManager.getComponentManager(namespace, false), classLoader,
264    componentDeclarations);
265    }
266    }