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