1
24 package net.sf.jasperreports.extensions;
25
26 import java.net.URL;
27 import java.util.ArrayList;
28 import java.util.Collections;
29 import java.util.HashMap;
30 import java.util.IdentityHashMap;
31 import java.util.Iterator;
32 import java.util.LinkedHashMap;
33 import java.util.List;
34 import java.util.Map;
35 import java.util.Map.Entry;
36
37 import org.apache.commons.collections4.map.ReferenceMap;
38 import org.apache.commons.logging.Log;
39 import org.apache.commons.logging.LogFactory;
40
41 import net.sf.jasperreports.annotations.properties.Property;
42 import net.sf.jasperreports.annotations.properties.PropertyScope;
43 import net.sf.jasperreports.engine.JRPropertiesMap;
44 import net.sf.jasperreports.engine.JRPropertiesUtil;
45 import net.sf.jasperreports.engine.JRPropertiesUtil.PropertySuffix;
46 import net.sf.jasperreports.engine.util.ClassLoaderResource;
47 import net.sf.jasperreports.engine.util.ClassUtils;
48 import net.sf.jasperreports.engine.util.JRLoader;
49 import net.sf.jasperreports.engine.util.ObjectUtils;
50 import net.sf.jasperreports.properties.PropertyConstants;
51
52
79 public class DefaultExtensionsRegistry implements ExtensionsRegistry
80 {
81
82 private final Log log = LogFactory.getLog(DefaultExtensionsRegistry.class);
83
84
88 public final static String EXTENSION_RESOURCE_NAME =
89 "jasperreports_extension.properties";
90
91
94 @Property(
95 name = "net.sf.jasperreports.extension.registry.factory.{arbitrary_name}",
96 category = PropertyConstants.CATEGORY_EXTENSIONS,
97 scopes = {PropertyScope.EXTENSION},
98 sinceVersion = PropertyConstants.VERSION_3_1_0
99 )
100 public final static String PROPERTY_REGISTRY_FACTORY_PREFIX =
101 JRPropertiesUtil.PROPERTY_PREFIX + "extension.registry.factory.";
102
103
107 @Property(
108 name = "net.sf.jasperreports.extension.{registry_id}.{property_suffix}",
109 category = PropertyConstants.CATEGORY_EXTENSIONS,
110 scopes = {PropertyScope.EXTENSION},
111 sinceVersion = PropertyConstants.VERSION_3_1_0
112 )
113 public static final String PROPERTY_REGISTRY_PREFIX =
114 JRPropertiesUtil.PROPERTY_PREFIX + "extension.";
115
116 private final ReferenceMap<Object, List<ExtensionsRegistry>> registrySetCache =
117 new ReferenceMap<Object, List<ExtensionsRegistry>>(
118 ReferenceMap.ReferenceStrength.WEAK, ReferenceMap.ReferenceStrength.HARD
119 );
120
121 private final ReferenceMap<ClassLoader, Map<URL, URLRegistries>> registryCache =
122 new ReferenceMap<ClassLoader, Map<URL, URLRegistries>>(
123 ReferenceMap.ReferenceStrength.WEAK, ReferenceMap.ReferenceStrength.HARD
124 );
125
126 @Override
127 public <T> List<T> getExtensions(Class<T> extensionType)
128 {
129 List<ExtensionsRegistry> registries = getRegistries();
130 List<T> extensions = new ArrayList<T>(registries.size());
131 for (Iterator<ExtensionsRegistry> it = registries.iterator(); it.hasNext();)
132 {
133 ExtensionsRegistry registry = it.next();
134 List<T> registryExtensions = registry.getExtensions(extensionType);
135 if (registryExtensions != null && !registryExtensions.isEmpty())
136 {
137 extensions.addAll(registryExtensions);
138 }
139 }
140 return extensions;
141 }
142
143 protected List<ExtensionsRegistry> getRegistries()
144 {
145 List<ExtensionsRegistry> registries;
146 Object cacheKey = ExtensionsEnvironment.getExtensionsCacheKey();
147 synchronized (registrySetCache)
148 {
149 registries = registrySetCache.get(cacheKey);
150 if (registries == null)
151 {
152 if (log.isDebugEnabled())
153 {
154 log.debug("Loading registries for cache key " + cacheKey);
155 }
156
157 registries = loadRegistries();
158 registrySetCache.put(cacheKey, registries);
159 }
160 }
161 return registries;
162 }
163
164 protected List<ExtensionsRegistry> loadRegistries()
165 {
166
167 IdentityHashMap<ExtensionsRegistry, Object> registrySet = new IdentityHashMap<>();
168 List<ExtensionsRegistry> allRegistries = new ArrayList<ExtensionsRegistry>();
169
170 List<ClassLoaderResource> extensionResources = loadExtensionPropertyResources();
171 for (ClassLoaderResource extensionResource : extensionResources)
172 {
173 ClassLoader classLoader = extensionResource.getClassLoader();
174 Map<URL, URLRegistries> classLoaderRegistries = getClassLoaderRegistries(classLoader);
175
176 URL url = extensionResource.getUrl();
177 List<ExtensionsRegistry> registries;
178 Map<String, Exception> registryExceptions = new LinkedHashMap<String, Exception>();
179 synchronized (classLoaderRegistries)
180 {
181 URLRegistries urlRegistries = classLoaderRegistries.get(url);
182 if (urlRegistries == null)
183 {
184 if (log.isDebugEnabled())
185 {
186 log.debug("Loading JasperReports extension properties resource "
187 + url);
188 }
189
190 JRPropertiesMap properties = JRPropertiesMap.loadProperties(url);
191 URL duplicateURL = detectDuplicate(properties, classLoaderRegistries);
192 if (duplicateURL == null)
193 {
194 registries = loadRegistries(properties, registryExceptions);
195 }
196 else
197 {
198 log.warn("Extension resource " + url + " was found to be a duplicate of "
199 + duplicateURL + " in classloader " + classLoader);
200 registries = Collections.emptyList();
201 }
202
203 classLoaderRegistries.put(url, new URLRegistries(properties, registries));
204 }
205 else
206 {
207 registries = urlRegistries.registries;
208 }
209 }
210
211 for (Map.Entry<String, Exception> entry : registryExceptions.entrySet())
212 {
213 log.error("Error instantiating extensions registry for "
214 + entry.getKey() + " from " + url, entry.getValue());
215 }
216
217 for (ExtensionsRegistry extensionsRegistry : registries)
218 {
219
220 boolean added = registrySet.put(extensionsRegistry, Boolean.FALSE) == null;
221 if (added)
222 {
223 allRegistries.add(extensionsRegistry);
224 }
225 else if (log.isDebugEnabled())
226 {
227 log.debug("Found duplicate extension registry " + extensionsRegistry);
228 }
229 }
230 }
231 return allRegistries;
232 }
233
234 protected List<ClassLoaderResource> loadExtensionPropertyResources()
235 {
236 return JRLoader.getClassLoaderResources(
237 EXTENSION_RESOURCE_NAME);
238 }
239
240 protected Map<URL, URLRegistries> getClassLoaderRegistries(ClassLoader classLoader)
241 {
242 synchronized (registryCache)
243 {
244 Map<URL, URLRegistries> registries = registryCache.get(classLoader);
245 if (registries == null)
246 {
247 registries = new HashMap<URL, URLRegistries>();
248 registryCache.put(classLoader, registries);
249 }
250 return registries;
251 }
252 }
253
254 protected List<ExtensionsRegistry> loadRegistries(JRPropertiesMap properties,
255 Map<String, Exception> registryExceptions)
256 {
257 List<ExtensionsRegistry> registries = new ArrayList<ExtensionsRegistry>();
258 List<PropertySuffix> factoryProps = JRPropertiesUtil.getProperties(properties,
259 PROPERTY_REGISTRY_FACTORY_PREFIX);
260 for (Iterator<PropertySuffix> it = factoryProps.iterator(); it.hasNext();)
261 {
262 PropertySuffix factoryProp = it.next();
263 String registryId = factoryProp.getSuffix();
264 String factoryClass = factoryProp.getValue();
265
266 if (log.isDebugEnabled())
267 {
268 log.debug("Instantiating registry of type " + factoryClass
269 + " for property " + factoryProp.getKey());
270 }
271
272 try
273 {
274 ExtensionsRegistry registry = instantiateRegistry(
275 properties, registryId, factoryClass);
276 registries.add(registry);
277 }
278 catch (Exception e)
279 {
280
281
282 registryExceptions.put(registryId, e);
283 }
284 }
285 return registries;
286 }
287
288 protected ExtensionsRegistry instantiateRegistry(
289 JRPropertiesMap props, String registryId, String factoryClass)
290 {
291 if (log.isDebugEnabled())
292 {
293 log.debug("Instantiating extensions registry for " + registryId
294 + " using factory class " + factoryClass);
295 }
296
297 ExtensionsRegistryFactory factory = (ExtensionsRegistryFactory)
298 ClassUtils.instantiateClass(factoryClass, ExtensionsRegistryFactory.class);
299 return factory.createRegistry(registryId, props);
300 }
301
302 protected URL detectDuplicate(JRPropertiesMap properties, Map<URL, URLRegistries> registries)
303 {
304 URL duplicateURL = null;
305 for (Entry<URL, URLRegistries> registryEntry : registries.entrySet())
306 {
307 JRPropertiesMap entryProperties = registryEntry.getValue().properties;
308 if (ObjectUtils.equals(properties, entryProperties))
309 {
310 duplicateURL = registryEntry.getKey();
311 break;
312 }
313 }
314 return duplicateURL;
315 }
316
317 protected static class URLRegistries
318 {
319 JRPropertiesMap properties;
320 List<ExtensionsRegistry> registries;
321
322 public URLRegistries(JRPropertiesMap properties, List<ExtensionsRegistry> registries)
323 {
324 this.properties = properties;
325 this.registries = registries;
326 }
327 }
328
329 }
330