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