1   /**
2    * Copyright (c) 2000-2009 Liferay, Inc. All rights reserved.
3    *
4    * Permission is hereby granted, free of charge, to any person obtaining a copy
5    * of this software and associated documentation files (the "Software"), to deal
6    * in the Software without restriction, including without limitation the rights
7    * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8    * copies of the Software, and to permit persons to whom the Software is
9    * furnished to do so, subject to the following conditions:
10   *
11   * The above copyright notice and this permission notice shall be included in
12   * all copies or substantial portions of the Software.
13   *
14   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20   * SOFTWARE.
21   */
22  
23  package com.liferay.portal.service.impl;
24  
25  import com.liferay.portal.PortalException;
26  import com.liferay.portal.SystemException;
27  import com.liferay.portal.kernel.image.SpriteProcessorUtil;
28  import com.liferay.portal.kernel.log.Log;
29  import com.liferay.portal.kernel.log.LogFactoryUtil;
30  import com.liferay.portal.kernel.plugin.PluginPackage;
31  import com.liferay.portal.kernel.servlet.ServletContextUtil;
32  import com.liferay.portal.kernel.util.GetterUtil;
33  import com.liferay.portal.kernel.util.ListUtil;
34  import com.liferay.portal.kernel.util.ReleaseInfo;
35  import com.liferay.portal.kernel.util.StringPool;
36  import com.liferay.portal.kernel.util.StringUtil;
37  import com.liferay.portal.kernel.util.Validator;
38  import com.liferay.portal.kernel.xml.Document;
39  import com.liferay.portal.kernel.xml.Element;
40  import com.liferay.portal.kernel.xml.SAXReaderUtil;
41  import com.liferay.portal.model.ColorScheme;
42  import com.liferay.portal.model.PluginSetting;
43  import com.liferay.portal.model.PortletConstants;
44  import com.liferay.portal.model.Theme;
45  import com.liferay.portal.model.impl.ColorSchemeImpl;
46  import com.liferay.portal.model.impl.ThemeImpl;
47  import com.liferay.portal.plugin.PluginUtil;
48  import com.liferay.portal.service.LayoutTemplateLocalServiceUtil;
49  import com.liferay.portal.service.PluginSettingLocalServiceUtil;
50  import com.liferay.portal.service.base.ThemeLocalServiceBaseImpl;
51  import com.liferay.portal.theme.ThemeCompanyId;
52  import com.liferay.portal.theme.ThemeCompanyLimit;
53  import com.liferay.portal.theme.ThemeGroupId;
54  import com.liferay.portal.theme.ThemeGroupLimit;
55  import com.liferay.portal.util.PortalUtil;
56  import com.liferay.util.ContextReplace;
57  import com.liferay.util.Version;
58  
59  import java.io.File;
60  
61  import java.util.ArrayList;
62  import java.util.HashSet;
63  import java.util.Iterator;
64  import java.util.List;
65  import java.util.Map;
66  import java.util.Properties;
67  import java.util.Set;
68  import java.util.concurrent.ConcurrentHashMap;
69  
70  import javax.servlet.ServletContext;
71  
72  /**
73   * <a href="ThemeLocalServiceImpl.java.html"><b><i>View Source</i></b></a>
74   *
75   * @author Brian Wing Shun Chan
76   * @author Jorge Ferrer
77   *
78   */
79  public class ThemeLocalServiceImpl extends ThemeLocalServiceBaseImpl {
80  
81      public ColorScheme getColorScheme(
82          long companyId, String themeId, String colorSchemeId,
83          boolean wapTheme) {
84  
85          colorSchemeId = GetterUtil.getString(colorSchemeId);
86  
87          Theme theme = getTheme(companyId, themeId, wapTheme);
88  
89          Map<String, ColorScheme> colorSchemesMap = theme.getColorSchemesMap();
90  
91          ColorScheme colorScheme = colorSchemesMap.get(colorSchemeId);
92  
93          if (colorScheme == null) {
94              List<ColorScheme> colorSchemes = theme.getColorSchemes();
95  
96              if (colorSchemes.size() > 0) {
97                  for (int i = (colorSchemes.size() - 1); i >= 0; i--) {
98                      colorScheme = colorSchemes.get(i);
99  
100                     if (colorScheme.isDefaultCs()) {
101                         break;
102                     }
103                 }
104             }
105         }
106 
107         if (colorScheme == null) {
108             if (wapTheme) {
109                 colorSchemeId = ColorSchemeImpl.getDefaultWapColorSchemeId();
110             }
111             else {
112                 colorSchemeId =
113                     ColorSchemeImpl.getDefaultRegularColorSchemeId();
114             }
115         }
116 
117         if (colorScheme == null) {
118             colorScheme = ColorSchemeImpl.getNullColorScheme();
119         }
120 
121         return colorScheme;
122     }
123 
124     public Theme getTheme(long companyId, String themeId, boolean wapTheme) {
125         themeId = GetterUtil.getString(themeId);
126 
127         Theme theme = _getThemes(companyId).get(themeId);
128 
129         if (theme == null) {
130             if (_log.isWarnEnabled()) {
131                 _log.warn(
132                     "No theme found for specified theme id " + themeId +
133                         ". Returning the default theme.");
134             }
135 
136             if (wapTheme) {
137                 themeId = ThemeImpl.getDefaultWapThemeId();
138             }
139             else {
140                 themeId = ThemeImpl.getDefaultRegularThemeId();
141             }
142 
143             theme = _themes.get(themeId);
144         }
145 
146         if (theme == null) {
147             if (_themes.isEmpty()) {
148                 if (_log.isDebugEnabled()) {
149                     _log.debug("No themes are installed");
150                 }
151 
152                 return null;
153             }
154 
155             _log.error(
156                 "No theme found for default theme id " + themeId +
157                     ". Returning a random theme.");
158 
159             Iterator<Map.Entry<String, Theme>> itr =
160                 _themes.entrySet().iterator();
161 
162             while (itr.hasNext()) {
163                 Map.Entry<String, Theme> entry = itr.next();
164 
165                 theme = entry.getValue();
166             }
167         }
168 
169         return theme;
170     }
171 
172     public List<Theme> getThemes(long companyId) {
173         List<Theme> themes = ListUtil.fromCollection(
174             _getThemes(companyId).values());
175 
176         return ListUtil.sort(themes);
177     }
178 
179     public List<Theme> getThemes(
180             long companyId, long groupId, long userId, boolean wapTheme)
181         throws PortalException, SystemException {
182 
183         List<Theme> themes = getThemes(companyId);
184 
185         themes = PluginUtil.restrictPlugins(themes, companyId, userId);
186 
187         Iterator<Theme> itr = themes.iterator();
188 
189         while (itr.hasNext()) {
190             Theme theme = itr.next();
191 
192             if ((theme.getThemeId().equals("controlpanel")) ||
193                 (!theme.isGroupAvailable(groupId)) ||
194                 (theme.isWapTheme() != wapTheme)) {
195 
196                 itr.remove();
197             }
198         }
199 
200         return themes;
201     }
202 
203     public List<String> init(
204         ServletContext servletContext, String themesPath,
205         boolean loadFromServletContext, String[] xmls,
206         PluginPackage pluginPackage) {
207 
208         return init(
209             null, servletContext, themesPath, loadFromServletContext, xmls,
210             pluginPackage);
211     }
212 
213     public List<String> init(
214         String servletContextName, ServletContext servletContext,
215         String themesPath, boolean loadFromServletContext, String[] xmls,
216         PluginPackage pluginPackage) {
217 
218         List<String> themeIds = new ArrayList<String>();
219 
220         try {
221             for (int i = 0; i < xmls.length; i++) {
222                 Set<String> themes = _readThemes(
223                     servletContextName, servletContext, themesPath,
224                     loadFromServletContext, xmls[i], pluginPackage);
225 
226                 Iterator<String> itr = themes.iterator();
227 
228                 while (itr.hasNext()) {
229                     String themeId = itr.next();
230 
231                     if (!themeIds.contains(themeId)) {
232                         themeIds.add(themeId);
233                     }
234                 }
235             }
236         }
237         catch (Exception e) {
238             e.printStackTrace();
239         }
240 
241         _themesPool.clear();
242 
243         return themeIds;
244     }
245 
246     public void uninstallThemes(List<String> themeIds) {
247         for (int i = 0; i < themeIds.size(); i++) {
248             String themeId = themeIds.get(i);
249 
250             _themes.remove(themeId);
251 
252             LayoutTemplateLocalServiceUtil.uninstallLayoutTemplates(themeId);
253         }
254 
255         _themesPool.clear();
256     }
257 
258     private List<ThemeCompanyId> _getCompanyLimitExcludes(Element el) {
259         List<ThemeCompanyId> includes = new ArrayList<ThemeCompanyId>();
260 
261         if (el != null) {
262             List<Element> companyIds = el.elements("company-id");
263 
264             for (int i = 0; i < companyIds.size(); i++) {
265                 Element companyIdEl = companyIds.get(i);
266 
267                 String name = companyIdEl.attributeValue("name");
268                 String pattern = companyIdEl.attributeValue("pattern");
269 
270                 ThemeCompanyId themeCompanyId = null;
271 
272                 if (Validator.isNotNull(name)) {
273                     themeCompanyId = new ThemeCompanyId(name, false);
274                 }
275                 else if (Validator.isNotNull(pattern)) {
276                     themeCompanyId = new ThemeCompanyId(pattern, true);
277                 }
278 
279                 if (themeCompanyId != null) {
280                     includes.add(themeCompanyId);
281                 }
282             }
283         }
284 
285         return includes;
286     }
287 
288     private List<ThemeCompanyId> _getCompanyLimitIncludes(Element el) {
289         return _getCompanyLimitExcludes(el);
290     }
291 
292     private List<ThemeGroupId> _getGroupLimitExcludes(Element el) {
293         List<ThemeGroupId> includes = new ArrayList<ThemeGroupId>();
294 
295         if (el != null) {
296             List<Element> groupIds = el.elements("group-id");
297 
298             for (int i = 0; i < groupIds.size(); i++) {
299                 Element groupIdEl = groupIds.get(i);
300 
301                 String name = groupIdEl.attributeValue("name");
302                 String pattern = groupIdEl.attributeValue("pattern");
303 
304                 ThemeGroupId themeGroupId = null;
305 
306                 if (Validator.isNotNull(name)) {
307                     themeGroupId = new ThemeGroupId(name, false);
308                 }
309                 else if (Validator.isNotNull(pattern)) {
310                     themeGroupId = new ThemeGroupId(pattern, true);
311                 }
312 
313                 if (themeGroupId != null) {
314                     includes.add(themeGroupId);
315                 }
316             }
317         }
318 
319         return includes;
320     }
321 
322     private List<ThemeGroupId> _getGroupLimitIncludes(Element el) {
323         return _getGroupLimitExcludes(el);
324     }
325 
326     private Map<String, Theme> _getThemes(long companyId) {
327         Map<String, Theme> themes = _themesPool.get(companyId);
328 
329         if (themes == null) {
330             themes = new ConcurrentHashMap<String, Theme>();
331 
332             Iterator<Map.Entry<String, Theme>> itr =
333                 _themes.entrySet().iterator();
334 
335             while (itr.hasNext()) {
336                 Map.Entry<String, Theme> entry = itr.next();
337 
338                 String themeId = entry.getKey();
339                 Theme theme = entry.getValue();
340 
341                 if (theme.isCompanyAvailable(companyId)) {
342                     themes.put(themeId, theme);
343                 }
344             }
345 
346             _themesPool.put(companyId, themes);
347         }
348 
349         return themes;
350     }
351 
352     private Version _getVersion(String version) {
353         if (version.equals("${current-version}")) {
354             version = ReleaseInfo.getVersion();
355         }
356 
357         return Version.getInstance(version);
358     }
359 
360     private void _readColorSchemes(
361         Element theme, Map<String, ColorScheme> colorSchemes,
362         ContextReplace themeContextReplace) {
363 
364         Iterator<Element> itr = theme.elements("color-scheme").iterator();
365 
366         while (itr.hasNext()) {
367             Element colorScheme = itr.next();
368 
369             ContextReplace colorSchemeContextReplace =
370                 (ContextReplace)themeContextReplace.clone();
371 
372             String id = colorScheme.attributeValue("id");
373 
374             colorSchemeContextReplace.addValue("color-scheme-id", id);
375 
376             ColorScheme colorSchemeModel = colorSchemes.get(id);
377 
378             if (colorSchemeModel == null) {
379                 colorSchemeModel = new ColorSchemeImpl(id);
380             }
381 
382             String name = GetterUtil.getString(
383                 colorScheme.attributeValue("name"), colorSchemeModel.getName());
384 
385             name = colorSchemeContextReplace.replace(name);
386 
387             boolean defaultCs = GetterUtil.getBoolean(
388                 colorScheme.elementText("default-cs"),
389                 colorSchemeModel.isDefaultCs());
390 
391             String cssClass = GetterUtil.getString(
392                 colorScheme.elementText("css-class"),
393                 colorSchemeModel.getCssClass());
394 
395             cssClass = colorSchemeContextReplace.replace(cssClass);
396 
397             colorSchemeContextReplace.addValue("css-class", cssClass);
398 
399             String colorSchemeImagesPath = GetterUtil.getString(
400                 colorScheme.elementText("color-scheme-images-path"),
401                 colorSchemeModel.getColorSchemeImagesPath());
402 
403             colorSchemeImagesPath = colorSchemeContextReplace.replace(
404                 colorSchemeImagesPath);
405 
406             colorSchemeContextReplace.addValue(
407                 "color-scheme-images-path", colorSchemeImagesPath);
408 
409             colorSchemeModel.setName(name);
410             colorSchemeModel.setDefaultCs(defaultCs);
411             colorSchemeModel.setCssClass(cssClass);
412             colorSchemeModel.setColorSchemeImagesPath(colorSchemeImagesPath);
413 
414             colorSchemes.put(id, colorSchemeModel);
415         }
416     }
417 
418     private Set<String> _readThemes(
419             String servletContextName, ServletContext servletContext,
420             String themesPath, boolean loadFromServletContext, String xml,
421             PluginPackage pluginPackage)
422         throws Exception {
423 
424         Set<String> themeIds = new HashSet<String>();
425 
426         if (xml == null) {
427             return themeIds;
428         }
429 
430         Document doc = SAXReaderUtil.read(xml, true);
431 
432         Element root = doc.getRootElement();
433 
434         Version portalVersion = _getVersion(ReleaseInfo.getVersion());
435 
436         boolean compatible = false;
437 
438         Element compatibilityEl = root.element("compatibility");
439 
440         if (compatibilityEl != null) {
441             Iterator<Element> itr = compatibilityEl.elements(
442                 "version").iterator();
443 
444             while (itr.hasNext()) {
445                 Element versionEl = itr.next();
446 
447                 Version version = _getVersion(versionEl.getTextTrim());
448 
449                 if (version.includes(portalVersion)) {
450                     compatible = true;
451 
452                     break;
453                 }
454             }
455         }
456 
457         if (!compatible) {
458             _log.error(
459                 "Themes in this WAR are not compatible with " +
460                     ReleaseInfo.getServerInfo());
461 
462             return themeIds;
463         }
464 
465         ThemeCompanyLimit companyLimit = null;
466 
467         Element companyLimitEl = root.element("company-limit");
468 
469         if (companyLimitEl != null) {
470             companyLimit = new ThemeCompanyLimit();
471 
472             Element companyIncludesEl =
473                 companyLimitEl.element("company-includes");
474 
475             if (companyIncludesEl != null) {
476                 companyLimit.setIncludes(
477                     _getCompanyLimitIncludes(companyIncludesEl));
478             }
479 
480             Element companyExcludesEl =
481                 companyLimitEl.element("company-excludes");
482 
483             if (companyExcludesEl != null) {
484                 companyLimit.setExcludes(
485                     _getCompanyLimitExcludes(companyExcludesEl));
486             }
487         }
488 
489         ThemeGroupLimit groupLimit = null;
490 
491         Element groupLimitEl = root.element("group-limit");
492 
493         if (groupLimitEl != null) {
494             groupLimit = new ThemeGroupLimit();
495 
496             Element groupIncludesEl = groupLimitEl.element("group-includes");
497 
498             if (groupIncludesEl != null) {
499                 groupLimit.setIncludes(_getGroupLimitIncludes(groupIncludesEl));
500             }
501 
502             Element groupExcludesEl =
503                 groupLimitEl.element("group-excludes");
504 
505             if (groupExcludesEl != null) {
506                 groupLimit.setExcludes(_getGroupLimitExcludes(groupExcludesEl));
507             }
508         }
509 
510         long timestamp = ServletContextUtil.getLastModified(servletContext);
511 
512         Iterator<Element> itr1 = root.elements("theme").iterator();
513 
514         while (itr1.hasNext()) {
515             Element theme = itr1.next();
516 
517             ContextReplace themeContextReplace = new ContextReplace();
518 
519             themeContextReplace.addValue("themes-path", themesPath);
520 
521             String themeId = theme.attributeValue("id");
522 
523             if (servletContextName != null) {
524                 themeId =
525                     themeId + PortletConstants.WAR_SEPARATOR +
526                         servletContextName;
527             }
528 
529             themeId = PortalUtil.getJsSafePortletId(themeId);
530 
531             themeContextReplace.addValue("theme-id", themeId);
532 
533             themeIds.add(themeId);
534 
535             Theme themeModel = _themes.get(themeId);
536 
537             if (themeModel == null) {
538                 themeModel = new ThemeImpl(themeId);
539 
540                 _themes.put(themeId, themeModel);
541             }
542 
543             themeModel.setTimestamp(timestamp);
544 
545             PluginSetting pluginSetting =
546                 PluginSettingLocalServiceUtil.getDefaultPluginSetting();
547 
548             themeModel.setPluginPackage(pluginPackage);
549             themeModel.setDefaultPluginSetting(pluginSetting);
550 
551             themeModel.setThemeCompanyLimit(companyLimit);
552             themeModel.setThemeGroupLimit(groupLimit);
553 
554             if (servletContextName != null) {
555                 themeModel.setServletContextName(servletContextName);
556             }
557 
558             themeModel.setLoadFromServletContext(loadFromServletContext);
559 
560             String name = GetterUtil.getString(
561                 theme.attributeValue("name"), themeModel.getName());
562 
563             String rootPath = GetterUtil.getString(
564                 theme.elementText("root-path"), themeModel.getRootPath());
565 
566             rootPath = themeContextReplace.replace(rootPath);
567 
568             themeContextReplace.addValue("root-path", rootPath);
569 
570             String templatesPath = GetterUtil.getString(
571                 theme.elementText("templates-path"),
572                 themeModel.getTemplatesPath());
573 
574             templatesPath = themeContextReplace.replace(templatesPath);
575             templatesPath = StringUtil.safePath(templatesPath);
576 
577             themeContextReplace.addValue("templates-path", templatesPath);
578 
579             String cssPath = GetterUtil.getString(
580                 theme.elementText("css-path"), themeModel.getCssPath());
581 
582             cssPath = themeContextReplace.replace(cssPath);
583             cssPath = StringUtil.safePath(cssPath);
584 
585             themeContextReplace.addValue("css-path", cssPath);
586 
587             String imagesPath = GetterUtil.getString(
588                 theme.elementText("images-path"),
589                 themeModel.getImagesPath());
590 
591             imagesPath = themeContextReplace.replace(imagesPath);
592             imagesPath = StringUtil.safePath(imagesPath);
593 
594             themeContextReplace.addValue("images-path", imagesPath);
595 
596             String javaScriptPath = GetterUtil.getString(
597                 theme.elementText("javascript-path"),
598                 themeModel.getJavaScriptPath());
599 
600             javaScriptPath = themeContextReplace.replace(javaScriptPath);
601             javaScriptPath = StringUtil.safePath(javaScriptPath);
602 
603             themeContextReplace.addValue("javascript-path", javaScriptPath);
604 
605             String virtualPath = GetterUtil.getString(
606                 theme.elementText("virtual-path"), themeModel.getVirtualPath());
607 
608             String templateExtension = GetterUtil.getString(
609                 theme.elementText("template-extension"),
610                 themeModel.getTemplateExtension());
611 
612             themeModel.setName(name);
613             themeModel.setRootPath(rootPath);
614             themeModel.setTemplatesPath(templatesPath);
615             themeModel.setCssPath(cssPath);
616             themeModel.setImagesPath(imagesPath);
617             themeModel.setJavaScriptPath(javaScriptPath);
618             themeModel.setVirtualPath(virtualPath);
619             themeModel.setTemplateExtension(templateExtension);
620 
621             Element settingsEl = theme.element("settings");
622 
623             if (settingsEl != null) {
624                 Iterator<Element> itr2 = settingsEl.elements(
625                     "setting").iterator();
626 
627                 while (itr2.hasNext()) {
628                     Element settingEl = itr2.next();
629 
630                     String key = settingEl.attributeValue("key");
631                     String value = settingEl.attributeValue("value");
632 
633                     themeModel.setSetting(key, value);
634                 }
635             }
636 
637             themeModel.setWapTheme(GetterUtil.getBoolean(
638                 theme.elementText("wap-theme"), themeModel.isWapTheme()));
639 
640             Element rolesEl = theme.element("roles");
641 
642             if (rolesEl != null) {
643                 Iterator<Element> itr2 = rolesEl.elements(
644                     "role-name").iterator();
645 
646                 while (itr2.hasNext()) {
647                     Element roleNameEl = itr2.next();
648 
649                     pluginSetting.addRole(roleNameEl.getText());
650                 }
651             }
652 
653             _readColorSchemes(
654                 theme, themeModel.getColorSchemesMap(), themeContextReplace);
655             _readColorSchemes(
656                 theme, themeModel.getColorSchemesMap(), themeContextReplace);
657 
658             Element layoutTemplatesEl = theme.element("layout-templates");
659 
660             if (layoutTemplatesEl != null) {
661                 Element standardEl = layoutTemplatesEl.element("standard");
662 
663                 if (standardEl != null) {
664                     LayoutTemplateLocalServiceUtil.readLayoutTemplate(
665                         servletContextName, servletContext, null,
666                         standardEl, true, themeId, pluginPackage);
667                 }
668 
669                 Element customEl = layoutTemplatesEl.element("custom");
670 
671                 if (customEl != null) {
672                     LayoutTemplateLocalServiceUtil.readLayoutTemplate(
673                         servletContextName, servletContext, null,
674                         customEl, false, themeId, pluginPackage);
675                 }
676             }
677 
678             if (!themeModel.isWapTheme()) {
679                 _setSpriteImages(servletContext, themeModel, imagesPath);
680             }
681         }
682 
683         return themeIds;
684     }
685 
686     private void _setSpriteImages(
687             ServletContext servletContext, Theme theme, String resourcePath)
688         throws Exception {
689 
690         Set<String> resourcePaths = servletContext.getResourcePaths(
691             resourcePath);
692 
693         if (resourcePaths == null) {
694             return;
695         }
696 
697         List<File> images = new ArrayList<File>(resourcePaths.size());
698 
699         for (String curResourcePath : resourcePaths) {
700             if (curResourcePath.endsWith(StringPool.SLASH)) {
701                 _setSpriteImages(servletContext, theme, curResourcePath);
702             }
703             else if (curResourcePath.endsWith(".png")) {
704                 images.add(
705                     new File(servletContext.getRealPath(curResourcePath)));
706             }
707         }
708 
709         String spriteFileName = ".sprite.png";
710         String spritePropertiesFileName = ".sprite.properties";
711         String spritePropertiesRootPath = servletContext.getRealPath(
712             theme.getImagesPath());
713 
714         Properties spriteProperties = SpriteProcessorUtil.generate(
715             images, spriteFileName, spritePropertiesFileName,
716             spritePropertiesRootPath, 16, 16, 10 * 1024);
717 
718         if (spriteProperties == null) {
719             return;
720         }
721 
722         spriteFileName =
723             resourcePath.substring(
724                 theme.getImagesPath().length(), resourcePath.length()) +
725             spriteFileName;
726 
727         theme.setSpriteImages(spriteFileName, spriteProperties);
728     }
729 
730     private static Log _log =
731          LogFactoryUtil.getLog(ThemeLocalServiceImpl.class);
732 
733     private static Map<String, Theme> _themes =
734         new ConcurrentHashMap<String, Theme>();
735     private static Map<Long, Map<String, Theme>> _themesPool =
736         new ConcurrentHashMap<Long, Map<String, Theme>>();
737 
738 }