1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.myfaces.tobago.internal.context;
21
22 import org.apache.myfaces.tobago.application.ProjectStage;
23 import org.apache.myfaces.tobago.component.RendererTypes;
24 import org.apache.myfaces.tobago.config.Configurable;
25 import org.apache.myfaces.tobago.context.Markup;
26 import org.apache.myfaces.tobago.context.ResourceManager;
27 import org.apache.myfaces.tobago.context.Theme;
28 import org.apache.myfaces.tobago.context.UserAgent;
29 import org.apache.myfaces.tobago.internal.config.TobagoConfigImpl;
30 import org.apache.myfaces.tobago.layout.Measure;
31 import org.apache.myfaces.tobago.util.LocaleUtils;
32 import org.slf4j.Logger;
33 import org.slf4j.LoggerFactory;
34
35 import javax.faces.component.UIViewRoot;
36 import javax.faces.context.FacesContext;
37 import javax.faces.render.Renderer;
38 import java.util.ArrayList;
39 import java.util.List;
40 import java.util.Map;
41 import java.util.concurrent.ConcurrentHashMap;
42
43 public class ResourceManagerImpl implements ResourceManager {
44
45 private static final Logger LOG = LoggerFactory.getLogger(ResourceManagerImpl.class);
46 private static final String PROPERTY = "property";
47 private static final String JSP = "jsp";
48 private static final String TAG = "tag";
49 private static final String MINIMIZE_SUFFIX = ".min";
50 private boolean production;
51
52 private final Map<String, String> resourceList
53 = new ConcurrentHashMap<String, String>(100, 0.75f, 1);
54
55 private final Map<RendererCacheKey, Renderer> rendererCache
56 = new ConcurrentHashMap<RendererCacheKey, Renderer>(100, 0.75f, 1);
57 private final Map<ImageCacheKey, StringValue> imageCache
58 = new ConcurrentHashMap<ImageCacheKey, StringValue>(100, 0.75f, 1);
59 private final Map<JspCacheKey, String> jspCache
60 = new ConcurrentHashMap<JspCacheKey, String>(100, 0.75f, 1);
61 private final Map<MiscCacheKey, String[]> miscCache
62 = new ConcurrentHashMap<MiscCacheKey, String[]>(100, 0.75f, 1);
63 private final Map<PropertyCacheKey, StringValue> propertyCache
64 = new ConcurrentHashMap<PropertyCacheKey, StringValue>(100, 0.75f, 1);
65 private final Map<ThemeConfigCacheKey, MeasureValue> themeCache
66 = new ConcurrentHashMap<ThemeConfigCacheKey, MeasureValue>(100, 0.75f, 1);
67
68 private TobagoConfigImpl tobagoConfig;
69
70 public ResourceManagerImpl(TobagoConfigImpl tobagoConfig) {
71 this.tobagoConfig = tobagoConfig;
72 this.production = tobagoConfig.getProjectStage() == ProjectStage.Production;
73 }
74
75 public void add(String resourceKey) {
76 if (LOG.isDebugEnabled()) {
77 LOG.debug("adding resourceKey = '{}'", resourceKey);
78 }
79 resourceList.put(resourceKey, "");
80 }
81
82 public void add(String resourceKey, String value) {
83 if (LOG.isDebugEnabled()) {
84 LOG.debug("adding resourceKey = '{}' value= '{}'", resourceKey, value);
85 }
86 resourceList.put(resourceKey, value);
87 }
88
89 @Deprecated
90 public String getJsp(UIViewRoot viewRoot, String name) {
91 String result = null;
92 if (name != null) {
93
94 ClientPropertiesKey clientKey = ClientPropertiesKey.get(FacesContext.getCurrentInstance());
95 JspCacheKey cacheKey = new JspCacheKey(clientKey, name);
96
97 result = jspCache.get(cacheKey);
98 if (result != null) {
99 return result;
100 }
101 try {
102 result = (String) getPaths(clientKey, "",
103 JSP, name, "", false, true, true, null, true, false).get(0);
104 jspCache.put(cacheKey, result);
105 } catch (Exception e) {
106 LOG.error("name = '" + name + "' clientProperties = '" + clientKey.toString() + "'", e);
107 }
108 }
109 return result;
110 }
111
112 @Deprecated
113 public String getProperty(UIViewRoot viewRoot, String bundle, String propertyKey) {
114 return getProperty(FacesContext.getCurrentInstance(), bundle, propertyKey);
115 }
116
117 public String getProperty(FacesContext facesContext, String bundle, String propertyKey) {
118
119 if (bundle != null && propertyKey != null) {
120 ClientPropertiesKey clientKey = ClientPropertiesKey.get(facesContext);
121 PropertyCacheKey cacheKey = new PropertyCacheKey(clientKey, bundle, propertyKey);
122
123 StringValue result = propertyCache.get(cacheKey);
124 if (result == null) {
125 List properties = getPaths(clientKey, "", PROPERTY, bundle, "", false, true, false, propertyKey, true, false);
126 if (properties != null) {
127 result = new StringValue((String) properties.get(0));
128 } else {
129 result = StringValue.NULL;
130 }
131 propertyCache.put(cacheKey, result);
132 }
133 return result.getValue();
134 }
135 return null;
136 }
137
138 @Deprecated
139 public Renderer getRenderer(UIViewRoot viewRoot, String rendererType) {
140 return getRenderer(FacesContext.getCurrentInstance(), rendererType);
141 }
142
143 public Renderer getRenderer(FacesContext facesContext, String rendererType) {
144 Renderer renderer = null;
145
146 if (rendererType != null) {
147 ClientPropertiesKey clientKey = ClientPropertiesKey.get(facesContext);
148 RendererCacheKey cacheKey = new RendererCacheKey(clientKey, rendererType);
149
150 renderer = rendererCache.get(cacheKey);
151 if (renderer != null) {
152 return renderer;
153 }
154 String simpleClassName = null;
155 try {
156 simpleClassName = getRendererClassName(rendererType);
157 List<Class> classes = getPaths(clientKey, "", TAG, simpleClassName, "", false, true, true, null, false, false);
158 if (classes != null && !classes.isEmpty()) {
159 Class clazz = classes.get(0);
160 renderer = (Renderer) clazz.newInstance();
161 rendererCache.put(cacheKey, renderer);
162 } else {
163 LOG.error("Don't find any RendererClass for " + simpleClassName + ". Please check you configuration.");
164 }
165 } catch (InstantiationException e) {
166 LOG.error("name = '" + simpleClassName + "' clientProperties = '" + clientKey.toString() + "'", e);
167 } catch (IllegalAccessException e) {
168 LOG.error("name = '" + simpleClassName + "' clientProperties = '" + clientKey.toString() + "'", e);
169 }
170 }
171 return renderer;
172 }
173
174 @Deprecated
175 public String[] getScripts(UIViewRoot viewRoot, String name) {
176 return getScripts(FacesContext.getCurrentInstance(), name);
177 }
178
179 public String[] getScripts(FacesContext facesContext, String name) {
180 return getStrings(facesContext, name, null);
181 }
182
183 @Deprecated
184 public String[] getStyles(UIViewRoot viewRoot, String name) {
185 return getStyles(FacesContext.getCurrentInstance(), name);
186 }
187
188 public String[] getStyles(FacesContext facesContext, String name) {
189 return getStrings(facesContext, name, null);
190 }
191
192 @Deprecated
193 public String getThemeProperty(UIViewRoot viewRoot, String bundle, String propertyKey) {
194 if (bundle != null && propertyKey != null) {
195
196 ClientPropertiesKey clientKey = ClientPropertiesKey.get(FacesContext.getCurrentInstance());
197 PropertyCacheKey cacheKey = new PropertyCacheKey(clientKey, bundle, propertyKey);
198
199 StringValue result = propertyCache.get(cacheKey);
200 if (result == null) {
201 List properties = getPaths(clientKey, "", PROPERTY, bundle, "", false, true, false, propertyKey, true, true);
202 if (properties != null) {
203 result = new StringValue((String) properties.get(0));
204 } else {
205 result = StringValue.NULL;
206 }
207 propertyCache.put(cacheKey, result);
208 }
209 return result.getValue();
210 }
211 return null;
212 }
213
214 public Measure getThemeMeasure(FacesContext facesContext, Configurable configurable, String name) {
215 return getThemeMeasure(facesContext, configurable.getRendererType(), configurable.getCurrentMarkup(), name);
216 }
217
218 public Measure getThemeMeasure(FacesContext facesContext, String rendererType, Markup markup, String name) {
219
220 ClientPropertiesKey clientKey = ClientPropertiesKey.get(facesContext);
221 ThemeConfigCacheKey cacheKey = new ThemeConfigCacheKey(clientKey, rendererType, markup, name);
222
223 MeasureValue result = themeCache.get(cacheKey);
224
225 if (result == null) {
226 List properties = getPaths(clientKey, "", PROPERTY, "tobago-theme-config", "",
227 false, true, false, rendererType + "." + name, true, true);
228
229 Measure measure = null;
230 if (properties != null) {
231 measure = Measure.valueOf(properties.get(0));
232 }
233
234 if (markup != null) {
235 for (String m : markup) {
236 List mProperties = getPaths(clientKey, "", PROPERTY, "tobago-theme-config", "",
237 false, true, false, rendererType + "[" + m + "]" + "." + name, true, true);
238 if (mProperties != null) {
239 final Measure summand = Measure.valueOf(mProperties.get(0));
240 measure = summand.add(measure);
241 }
242 }
243 }
244
245 if (measure != null) {
246 result = new MeasureValue(measure);
247 } else {
248 result = MeasureValue.NULL;
249 }
250 themeCache.put(cacheKey, result);
251 }
252 return result.getValue();
253 }
254
255 @Deprecated
256 public String getImage(UIViewRoot viewRoot, String name) {
257 return getImage(FacesContext.getCurrentInstance(), name);
258 }
259
260 public String getImage(FacesContext facesContext, String name) {
261 return getImage(facesContext, name, false);
262 }
263
264 @Deprecated
265 public String getImage(UIViewRoot viewRoot, String name, boolean ignoreMissing) {
266 return getImage(FacesContext.getCurrentInstance(), name, ignoreMissing);
267 }
268
269 public String getImage(FacesContext facesContext, String name, boolean ignoreMissing) {
270 if (name != null) {
271 int dot = name.lastIndexOf('.');
272 if (dot == -1) {
273 dot = name.length();
274 }
275
276 ClientPropertiesKey clientKey = ClientPropertiesKey.get(facesContext);
277 ImageCacheKey cacheKey = new ImageCacheKey(clientKey, name);
278
279 StringValue result = imageCache.get(cacheKey);
280 if (result == null) {
281 List paths = getPaths(clientKey, "", null, name.substring(0, dot),
282 name.substring(dot), false, true, true, null, true, ignoreMissing);
283 if (paths != null) {
284 result = new StringValue((String) paths.get(0));
285 } else {
286 result = StringValue.NULL;
287 }
288 imageCache.put(cacheKey, result);
289 }
290 if (LOG.isDebugEnabled()) {
291 if (result.getValue() == null) {
292 LOG.debug("Can't find image for \"{}\"", name);
293 }
294 }
295
296 return result.getValue();
297 }
298
299 return null;
300 }
301
302 private List getPaths(
303 ClientPropertiesKey clientkey, String prefix, String subDir, String name, String suffix,
304 boolean reverseOrder, boolean single, boolean returnKey, String key, boolean returnStrings,
305 boolean ignoreMissing) {
306 List matches = new ArrayList();
307 String contentType = clientkey.getContentType();
308 Theme theme = clientkey.getTheme();
309 UserAgent browser = clientkey.getUserAgent();
310 List<String> locales = LocaleUtils.getLocaleSuffixList(clientkey.getLocale());
311
312
313 for (String localeSuffix : locales) {
314 if (production) {
315 boolean found = checkPath(prefix, reverseOrder, returnKey, returnStrings, matches,
316 name, MINIMIZE_SUFFIX, localeSuffix, suffix, key);
317 if (found && (single || !returnStrings)) {
318 return matches;
319 }
320 if (!found) {
321 found = checkPath(prefix, reverseOrder, returnKey, returnStrings, matches,
322 name, null, localeSuffix, suffix, key);
323 if (found && (single || !returnStrings)) {
324 return matches;
325 }
326 }
327 } else {
328 boolean found = checkPath(prefix, reverseOrder, returnKey, returnStrings, matches,
329 name, null, localeSuffix, suffix, key);
330 if (found && (single || !returnStrings)) {
331 return matches;
332 }
333 }
334 }
335
336
337
338 for (Theme currentTheme : theme.getFallbackList()) {
339 for (String resourceDirectory : tobagoConfig.getResourceDirs()) {
340 for (String browserType : browser.getFallbackList()) {
341 for (String localeSuffix : locales) {
342 if (production) {
343 boolean found = checkPath(prefix, reverseOrder, returnKey, returnStrings, matches,
344 resourceDirectory, contentType, currentTheme, browserType, subDir, name, MINIMIZE_SUFFIX,
345 localeSuffix, suffix, key);
346 if (found && (single || !returnStrings)) {
347 return matches;
348 }
349 if (!found) {
350 found = checkPath(prefix, reverseOrder, returnKey, returnStrings, matches,
351 resourceDirectory, contentType, currentTheme, browserType, subDir, name, null,
352 localeSuffix, suffix, key);
353 if (found && (single || !returnStrings)) {
354 return matches;
355 }
356 }
357 } else {
358 boolean found = checkPath(prefix, reverseOrder, returnKey, returnStrings, matches,
359 resourceDirectory, contentType, currentTheme, browserType, subDir, name, null,
360 localeSuffix, suffix, key);
361 if (found && (single || !returnStrings)) {
362 return matches;
363 }
364 }
365 }
366 }
367 }
368 }
369
370 if (matches.isEmpty()) {
371 if (!ignoreMissing) {
372 LOG.error("Path not found, and no fallback. Using empty string.\n"
373 + "resourceDirs = '" + tobagoConfig.getResourceDirs()
374 + "' contentType = '" + contentType
375 + "' theme = '" + theme.getName()
376 + "' browser = '" + browser
377 + "' subDir = '" + subDir
378 + "' name = '" + name
379 + "' suffix = '" + suffix
380 + "' key = '" + key
381 + "'"
382 }
383 return null;
384 } else {
385 return matches;
386 }
387 }
388
389 private boolean checkPath(
390 String prefix, boolean reverseOrder, boolean returnKey, boolean returnStrings,
391 List matches, String name, String minimizeSuffix, String localeSuffix, String extension, String key) {
392 String path = makePath(name, minimizeSuffix, localeSuffix, extension, key);
393 if (returnStrings && resourceList.containsKey(path)) {
394 String result =
395 returnKey
396 ? prefix + path
397 : prefix + resourceList.get(path);
398
399 if (reverseOrder) {
400 matches.add(0, result);
401 } else {
402 matches.add(result);
403 }
404 if (LOG.isTraceEnabled()) {
405 LOG.trace("testing path: {} *", path);
406 }
407
408 return true;
409 } else if (!returnStrings) {
410 try {
411 path = path.substring(1).replace('/', '.');
412 Class clazz = Class.forName(path);
413 if (LOG.isTraceEnabled()) {
414 LOG.trace("testing path: " + path + " *");
415 }
416 matches.add(clazz);
417 return true;
418 } catch (ClassNotFoundException e) {
419
420 if (LOG.isTraceEnabled()) {
421 LOG.trace("testing path: " + path);
422 }
423 }
424 } else {
425 if (LOG.isTraceEnabled()) {
426 LOG.trace("testing path: " + path);
427 }
428 }
429 return false;
430 }
431
432 private boolean checkPath(
433 String prefix, boolean reverseOrder, boolean returnKey, boolean returnStrings,
434 List matches, String resourceDirectory, String contentType, Theme currentTheme, String browserType,
435 String subDir, String name, String minimizeSuffix, String localeSuffix, String suffix, String key) {
436 String path = makePath(resourceDirectory, contentType, currentTheme, browserType, subDir, name, minimizeSuffix,
437 localeSuffix, suffix, key, null);
438 if (returnStrings && resourceList.containsKey(path)) {
439 String result;
440 if (prefix.length() == 0 && returnKey && resourceDirectory.equals(currentTheme.getResourcePath())) {
441 result = makePath(resourceDirectory, contentType, currentTheme, browserType, subDir, name, minimizeSuffix,
442 localeSuffix, suffix, key, currentTheme.getVersion());
443 } else {
444 result = returnKey
445 ? prefix + path : prefix + resourceList.get(path);
446 }
447 if (reverseOrder) {
448 matches.add(0, result);
449 } else {
450 matches.add(result);
451 }
452 if (LOG.isTraceEnabled()) {
453 LOG.trace("testing path: {} *", path);
454 }
455
456 return true;
457 } else if (!returnStrings) {
458 try {
459 path = path.substring(1).replace('/', '.');
460 Class clazz = Class.forName(path);
461 if (LOG.isTraceEnabled()) {
462 LOG.trace("testing path: " + path + " *");
463 }
464 matches.add(clazz);
465 return true;
466 } catch (ClassNotFoundException e) {
467
468 if (LOG.isTraceEnabled()) {
469 LOG.trace("testing path: " + path);
470 }
471 }
472 } else {
473 if (LOG.isTraceEnabled()) {
474 LOG.trace("testing path: " + path);
475 }
476 }
477 return false;
478 }
479
480 private String makePath(
481 String project, String language, Theme theme, String browser, String subDir,
482 String name, String minimizeSuffix, String localeSuffix, String extension, String key, String version) {
483 StringBuilder searchtext = new StringBuilder(64);
484
485 searchtext.append('/');
486 searchtext.append(project);
487 if (version != null) {
488 searchtext.append('/');
489 searchtext.append(version);
490 }
491 searchtext.append('/');
492 searchtext.append(language);
493 searchtext.append('/');
494 searchtext.append(theme.getName());
495 searchtext.append('/');
496 searchtext.append(browser);
497 if (subDir != null) {
498 searchtext.append('/');
499 searchtext.append(subDir);
500 }
501 searchtext.append('/');
502 searchtext.append(name);
503 if (minimizeSuffix != null) {
504 searchtext.append(minimizeSuffix);
505 }
506 searchtext.append(localeSuffix);
507 searchtext.append(extension);
508 if (key != null) {
509 searchtext.append('/');
510 searchtext.append(key);
511 }
512
513 return searchtext.toString();
514 }
515
516 private String makePath(
517 String name, String minimizeSuffix, String localeSuffix, String extension, String key) {
518 StringBuilder searchtext = new StringBuilder(64);
519
520 searchtext.append('/');
521 searchtext.append(name);
522 if (minimizeSuffix != null) {
523 searchtext.append(minimizeSuffix);
524 }
525 searchtext.append(localeSuffix);
526 searchtext.append(extension);
527 if (key != null) {
528 searchtext.append('/');
529 searchtext.append(key);
530 }
531
532 return searchtext.toString();
533 }
534
535 private String getRendererClassName(String rendererType) {
536 String name;
537 if (LOG.isDebugEnabled()) {
538 LOG.debug("rendererType = '{}'", rendererType);
539 }
540 if ("javax.faces.Text".equals(rendererType)) {
541 name = RendererTypes.OUT;
542 } else {
543 name = rendererType;
544 }
545 name = name + "Renderer";
546 if (name.startsWith("javax.faces.")) {
547 LOG.warn("patching renderer from {}", name);
548 name = name.substring("javax.faces.".length());
549 LOG.warn("patching renderer to {}", name);
550 }
551 return name;
552 }
553
554 private String[] getStrings(FacesContext facesContext, String name, String type) {
555 String[] result = new String[0];
556 if (name != null) {
557 int dot = name.lastIndexOf('.');
558 if (dot == -1) {
559 dot = name.length();
560 }
561
562 ClientPropertiesKey key = ClientPropertiesKey.get(facesContext);
563 MiscCacheKey miscKey = new MiscCacheKey(key, name);
564 String[] cacheResult = miscCache.get(miscKey);
565 if (cacheResult != null) {
566 return cacheResult;
567 }
568 try {
569 List matches = getPaths(key, "", type,
570 name.substring(0, dot), name.substring(dot), true, false, true, null, true, false);
571 if (matches != null) {
572 result = (String[]) matches.toArray(new String[matches.size()]);
573 }
574 miscCache.put(miscKey, result);
575 } catch (Exception e) {
576 LOG.error("name = '" + name + "' clientProperties = '" + key.toString() + "'", e);
577 }
578 }
579 return result;
580 }
581
582 }