1 /**
2 * This file Copyright (c) 2015 Magnolia International
3 * Ltd. (http://www.magnolia-cms.com). All rights reserved.
4 *
5 *
6 * This file is dual-licensed under both the Magnolia
7 * Network Agreement and the GNU General Public License.
8 * You may elect to use one or the other of these licenses.
9 *
10 * This file is distributed in the hope that it will be
11 * useful, but AS-IS and WITHOUT ANY WARRANTY; without even the
12 * implied warranty of MERCHANTABILITY or FITNESS FOR A
13 * PARTICULAR PURPOSE, TITLE, or NONINFRINGEMENT.
14 * Redistribution, except as permitted by whichever of the GPL
15 * or MNA you select, is prohibited.
16 *
17 * 1. For the GPL license (GPL), you can redistribute and/or
18 * modify this file under the terms of the GNU General
19 * Public License, Version 3, as published by the Free Software
20 * Foundation. You should have received a copy of the GNU
21 * General Public License, Version 3 along with this program;
22 * if not, write to the Free Software Foundation, Inc., 51
23 * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
24 *
25 * 2. For the Magnolia Network Agreement (MNA), this file
26 * and the accompanying materials are made available under the
27 * terms of the MNA which accompanies this distribution, and
28 * is available at http://www.magnolia-cms.com/mna.html
29 *
30 * Any modifications to this file must keep this entire header
31 * intact.
32 *
33 */
34 package info.magnolia.imaging;
35
36 import info.magnolia.module.ModuleLifecycle;
37 import info.magnolia.module.ModuleLifecycleContext;
38
39 import java.util.ArrayList;
40 import java.util.Iterator;
41 import java.util.LinkedHashMap;
42 import java.util.List;
43 import java.util.Map;
44
45 import javax.imageio.ImageIO;
46 import javax.imageio.spi.IIORegistry;
47 import javax.imageio.spi.ServiceRegistry;
48
49 import org.slf4j.Logger;
50 import org.slf4j.LoggerFactory;
51
52 /**
53 * Keeps the configuration of the imaging module.
54 */
55 public class ImagingModule implements ModuleLifecycle {
56
57 private static final Logger log = LoggerFactory.getLogger(ImagingModule.class);
58
59 private Map<String, ImageGenerator> generators = new LinkedHashMap<String, ImageGenerator>();
60
61 public void addGenerator(String name, ImageGenerator generator) {
62 generators.put(name, generator);
63 }
64
65 public void setGenerators(Map<String, ImageGenerator> generators) {
66 this.generators = generators;
67 }
68
69 public Map<String, ImageGenerator> getGenerators() {
70 return generators;
71 }
72
73 /**
74 * Scan for additional plugins when module starts.
75 *
76 * @see ImageIO#scanForPlugins()
77 */
78 @Override
79 public void start(ModuleLifecycleContext moduleLifecycleContext) {
80 ImageIO.scanForPlugins();
81 }
82
83 /**
84 * De-registers all plugins which have the {@link Thread#getContextClassLoader() current thread's context class loader}
85 * as its class loader when the module is stopped, to avoid class/resource leak.
86 *
87 * This is taken from com.twelvemonkeys.servlet.image.IIOProviderContextListener and the details are explained here:
88 * https://github.com/haraldk/TwelveMonkeys#deploying-the-plugins-in-a-web-app .
89 */
90 @Override
91 public void stop(ModuleLifecycleContext moduleLifecycleContext) {
92
93 // De-register any locally registered IIO plugins. Relies on each web app having its own context class loader.
94 final IIORegistry registry = IIORegistry.getDefaultInstance();
95 final LocalFilter localFilter = new LocalFilter(Thread.currentThread().getContextClassLoader());
96
97 Iterator<Class<?>> categories = registry.getCategories();
98
99 while (categories.hasNext()) {
100 Class<?> category = categories.next();
101 Iterator<?> providers = registry.getServiceProviders(category, localFilter, false);
102
103 // Copy the providers, as de-registering while iterating over providers will lead to ConcurrentModificationExceptions.
104 List<Object> providersCopy = new ArrayList<>();
105 while (providers.hasNext()) {
106 providersCopy.add(providers.next());
107 }
108
109 for (Object provider : providersCopy) {
110 registry.deregisterServiceProvider(provider);
111 log.debug("Unregistered locally installed provider class: {}", provider.getClass());
112 }
113 }
114 }
115
116 /**
117 * Filters the providers based on their class loader.
118 */
119 static class LocalFilter implements ServiceRegistry.Filter {
120 private final ClassLoader loader;
121
122 public LocalFilter(ClassLoader loader) {
123 this.loader = loader;
124 }
125
126 @Override
127 public boolean filter(Object provider) {
128 return provider.getClass().getClassLoader() == loader;
129 }
130 }
131 }