001    /**
002     * Copyright (c) 2000-present Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portal.deploy.hot;
016    
017    import com.liferay.portal.kernel.log.Log;
018    import com.liferay.portal.kernel.log.LogFactoryUtil;
019    import com.liferay.portal.kernel.log.SanitizerLogWrapper;
020    import com.liferay.portal.kernel.url.URLContainer;
021    import com.liferay.portal.kernel.util.CharPool;
022    import com.liferay.portal.kernel.util.FileUtil;
023    import com.liferay.portal.kernel.util.GetterUtil;
024    import com.liferay.portal.kernel.util.StringBundler;
025    import com.liferay.portal.kernel.util.StringPool;
026    import com.liferay.portal.kernel.util.StringUtil;
027    import com.liferay.portal.kernel.util.Validator;
028    import com.liferay.portal.util.CustomJspRegistryUtil;
029    import com.liferay.portal.util.PortalUtil;
030    import com.liferay.registry.Filter;
031    import com.liferay.registry.Registry;
032    import com.liferay.registry.RegistryUtil;
033    import com.liferay.registry.ServiceReference;
034    import com.liferay.registry.ServiceTracker;
035    import com.liferay.registry.ServiceTrackerCustomizer;
036    
037    import java.io.File;
038    import java.io.IOException;
039    import java.io.InputStream;
040    
041    import java.net.URL;
042    
043    import java.util.Collections;
044    import java.util.HashMap;
045    import java.util.HashSet;
046    import java.util.List;
047    import java.util.Map;
048    import java.util.Set;
049    import java.util.concurrent.ConcurrentHashMap;
050    
051    /**
052     * @author Peter Fellwock
053     * @author Raymond Aug??
054     */
055    public class CustomJspBagRegistryUtil {
056    
057            public static Map<ServiceReference<CustomJspBag>, CustomJspBag>
058                    getCustomJspBags() {
059    
060                    return Collections.unmodifiableMap(_instance._customJspBagsMap);
061            }
062    
063            protected InputStream getCustomJspInputStream(
064                            URLContainer urlContainer, String customJsp)
065                    throws IOException {
066    
067                    URL url = urlContainer.getResource(customJsp);
068    
069                    return url.openStream();
070            }
071    
072            protected void getCustomJsps(
073                    URLContainer urlContainer, String resourcePath,
074                    List<String> customJsps) {
075    
076                    Set<String> resourcePaths = urlContainer.getResources(resourcePath);
077    
078                    if ((resourcePaths == null) || resourcePaths.isEmpty()) {
079                            return;
080                    }
081    
082                    for (String curResourcePath : resourcePaths) {
083                            if (curResourcePath.endsWith(StringPool.SLASH)) {
084                                    getCustomJsps(urlContainer, curResourcePath, customJsps);
085                            }
086                            else {
087                                    String customJsp = curResourcePath;
088    
089                                    customJsp = StringUtil.replace(
090                                            customJsp, StringPool.DOUBLE_SLASH, StringPool.SLASH);
091    
092                                    customJsps.add(customJsp);
093                            }
094                    }
095            }
096    
097            protected String getPortalJsp(String customJsp, String customJspDir) {
098                    if (Validator.isNull(customJsp) || Validator.isNull(customJspDir)) {
099                            return null;
100                    }
101    
102                    int pos = customJsp.indexOf(customJspDir);
103    
104                    return customJsp.substring(pos + customJspDir.length());
105            }
106    
107            protected File getPortalJspBackupFile(File portalJspFile) {
108                    String fileName = portalJspFile.getName();
109                    String filePath = portalJspFile.toString();
110    
111                    int fileNameIndex = fileName.lastIndexOf(CharPool.PERIOD);
112    
113                    if (fileNameIndex > 0) {
114                            int filePathIndex = filePath.lastIndexOf(fileName);
115    
116                            fileName =
117                                    fileName.substring(0, fileNameIndex) + ".portal" +
118                                            fileName.substring(fileNameIndex);
119    
120                            filePath = filePath.substring(0, filePathIndex) + fileName;
121                    }
122                    else {
123                            filePath += ".portal";
124                    }
125    
126                    return new File(filePath);
127            }
128    
129            protected void initCustomJspBag(
130                            String contextId, String contextName, CustomJspBag customJspBag)
131                    throws Exception {
132    
133                    String customJspDir = customJspBag.getCustomJspDir();
134                    boolean customJspGlobal = customJspBag.isCustomJspGlobal();
135                    List<String> customJsps = customJspBag.getCustomJsps();
136    
137                    String portalWebDir = PortalUtil.getPortalWebDir();
138    
139                    for (String customJsp : customJsps) {
140                            String portalJsp = getPortalJsp(customJsp, customJspDir);
141    
142                            if (customJspGlobal) {
143                                    File portalJspFile = new File(portalWebDir + portalJsp);
144                                    File portalJspBackupFile = getPortalJspBackupFile(
145                                            portalJspFile);
146    
147                                    if (portalJspFile.exists() && !portalJspBackupFile.exists()) {
148                                            FileUtil.copyFile(portalJspFile, portalJspBackupFile);
149                                    }
150                            }
151                            else {
152                                    portalJsp = CustomJspRegistryUtil.getCustomJspFileName(
153                                            contextId, portalJsp);
154                            }
155    
156                            FileUtil.write(
157                                    portalWebDir + portalJsp,
158                                    getCustomJspInputStream(
159                                            customJspBag.getURLContainer(), customJsp));
160                    }
161    
162                    if (!customJspGlobal) {
163                            CustomJspRegistryUtil.registerServletContextName(
164                                    contextId, contextName);
165                    }
166            }
167    
168            protected void verifyCustomJsps(String contextId, CustomJspBag customJspBag)
169                    throws DuplicateCustomJspException {
170    
171                    Set<String> customJsps = new HashSet<>();
172    
173                    for (String customJsp : customJspBag.getCustomJsps()) {
174                            String portalJsp = getPortalJsp(
175                                    customJsp, customJspBag.getCustomJspDir());
176    
177                            customJsps.add(portalJsp);
178                    }
179    
180                    Map<String, String> conflictingCustomJsps = new HashMap<>();
181    
182                    for (Map.Entry<ServiceReference<CustomJspBag>, CustomJspBag> entry :
183                                    _customJspBagsMap.entrySet()) {
184    
185                            CustomJspBag currentCustomJspBag = entry.getValue();
186    
187                            if (!currentCustomJspBag.isCustomJspGlobal()) {
188                                    continue;
189                            }
190    
191                            ServiceReference<CustomJspBag> serviceReference = entry.getKey();
192    
193                            String contextName = GetterUtil.getString(
194                                    serviceReference.getProperty("context.name"));
195    
196                            List<String> currentCustomJsps =
197                                    currentCustomJspBag.getCustomJsps();
198    
199                            for (String currentCustomJsp : currentCustomJsps) {
200                                    String currentPortalJsp = getPortalJsp(
201                                            currentCustomJsp, currentCustomJspBag.getCustomJspDir());
202    
203                                    if (customJsps.contains(currentPortalJsp)) {
204                                            conflictingCustomJsps.put(currentPortalJsp, contextName);
205                                    }
206                            }
207                    }
208    
209                    if (conflictingCustomJsps.isEmpty()) {
210                            return;
211                    }
212    
213                    _log.error(contextId + " conflicts with the installed hooks");
214    
215                    if (_log.isDebugEnabled()) {
216                            Log log = SanitizerLogWrapper.allowCRLF(_log);
217    
218                            StringBundler sb = new StringBundler(
219                                    conflictingCustomJsps.size() * 4 + 2);
220    
221                            sb.append("Colliding JSP files in ");
222                            sb.append(contextId);
223                            sb.append(StringPool.NEW_LINE);
224    
225                            int i = 0;
226    
227                            for (Map.Entry<String, String> entry :
228                                            conflictingCustomJsps.entrySet()) {
229    
230                                    sb.append(entry.getKey());
231                                    sb.append(" with ");
232                                    sb.append(entry.getValue());
233    
234                                    if ((i + 1) < conflictingCustomJsps.size()) {
235                                            sb.append(StringPool.NEW_LINE);
236                                    }
237    
238                                    i++;
239                            }
240    
241                            log.debug(sb.toString());
242                    }
243    
244                    throw new DuplicateCustomJspException();
245            }
246    
247            private CustomJspBagRegistryUtil() {
248                    Registry registry = RegistryUtil.getRegistry();
249    
250                    Filter filter = registry.getFilter(
251                            "(&(context.id=*)(context.name=*)(objectClass=" +
252                                    CustomJspBag.class.getName() + "))");
253    
254                    _serviceTracker = registry.trackServices(
255                            filter, new CustomJspBagRegistryUtilServiceTrackerCustomizer());
256    
257                    _serviceTracker.open();
258            }
259    
260            private static final Log _log = LogFactoryUtil.getLog(
261                    CustomJspBagRegistryUtil.class);
262    
263            private static final CustomJspBagRegistryUtil _instance =
264                    new CustomJspBagRegistryUtil();
265    
266            private final Map<ServiceReference<CustomJspBag>, CustomJspBag>
267                    _customJspBagsMap = new ConcurrentHashMap<>();
268            private final ServiceTracker<CustomJspBag, CustomJspBag> _serviceTracker;
269    
270            private class CustomJspBagRegistryUtilServiceTrackerCustomizer
271                    implements ServiceTrackerCustomizer<CustomJspBag, CustomJspBag> {
272    
273                    @Override
274                    public CustomJspBag addingService(
275                            ServiceReference<CustomJspBag> serviceReference) {
276    
277                            Registry registry = RegistryUtil.getRegistry();
278    
279                            CustomJspBag customJspBag = registry.getService(serviceReference);
280    
281                            List<String> customJsps = customJspBag.getCustomJsps();
282    
283                            if (customJsps.isEmpty()) {
284                                    getCustomJsps(
285                                            customJspBag.getURLContainer(),
286                                            customJspBag.getCustomJspDir(),
287                                            customJspBag.getCustomJsps());
288    
289                                    customJsps = customJspBag.getCustomJsps();
290    
291                                    if (customJsps.isEmpty()) {
292                                            return null;
293                                    }
294                            }
295    
296                            if (_log.isDebugEnabled()) {
297                                    StringBundler sb = new StringBundler(customJsps.size() * 2);
298    
299                                    sb.append("Custom JSP files:\n");
300    
301                                    for (int i = 0; i < customJsps.size(); i++) {
302                                            String customJsp = customJsps.get(i);
303    
304                                            sb.append(customJsp);
305    
306                                            if ((i + 1) < customJsps.size()) {
307                                                    sb.append(StringPool.NEW_LINE);
308                                            }
309                                    }
310    
311                                    Log log = SanitizerLogWrapper.allowCRLF(_log);
312    
313                                    log.debug(sb.toString());
314                            }
315    
316                            String contextId = GetterUtil.getString(
317                                    serviceReference.getProperty("context.id"));
318    
319                            if (customJspBag.isCustomJspGlobal() &&
320                                    !_customJspBagsMap.isEmpty()) {
321    
322                                    try {
323                                            verifyCustomJsps(contextId, customJspBag);
324                                    }
325                                    catch (DuplicateCustomJspException e) {
326                                            return null;
327                                    }
328                            }
329    
330                            _customJspBagsMap.put(serviceReference, customJspBag);
331    
332                            String contextName = GetterUtil.getString(
333                                    serviceReference.getProperty("context.name"));
334    
335                            try {
336                                    initCustomJspBag(contextId, contextName, customJspBag);
337                            }
338                            catch (Exception e) {
339                                    return null;
340                            }
341    
342                            return customJspBag;
343                    }
344    
345                    @Override
346                    public void modifiedService(
347                            ServiceReference<CustomJspBag> serviceReference,
348                            CustomJspBag customJspBag) {
349    
350                            removedService(serviceReference, customJspBag);
351    
352                            addingService(serviceReference);
353                    }
354    
355                    @Override
356                    public void removedService(
357                            ServiceReference<CustomJspBag> serviceReference,
358                            CustomJspBag customJspBag) {
359    
360                            Registry registry = RegistryUtil.getRegistry();
361    
362                            registry.ungetService(serviceReference);
363    
364                            String contextId = GetterUtil.getString(
365                                    serviceReference.getProperty("context.id"));
366    
367                            for (String customJsp : customJspBag.getCustomJsps()) {
368                                    String customJspDir = customJspBag.getCustomJspDir();
369    
370                                    int pos = customJsp.indexOf(customJspDir);
371    
372                                    String portalJsp = customJsp.substring(
373                                            pos + customJspDir.length());
374    
375                                    if (customJspBag.isCustomJspGlobal()) {
376                                            File portalJspFile = new File(
377                                                    PortalUtil.getPortalWebDir() + portalJsp);
378                                            File portalJspBackupFile = getPortalJspBackupFile(
379                                                    portalJspFile);
380    
381                                            if (portalJspBackupFile.exists()) {
382                                                    try {
383                                                            FileUtil.copyFile(
384                                                                    portalJspBackupFile, portalJspFile);
385                                                    }
386                                                    catch (IOException e) {
387                                                            return;
388                                                    }
389    
390                                                    portalJspBackupFile.delete();
391                                            }
392                                            else if (portalJspFile.exists()) {
393                                                    portalJspFile.delete();
394                                            }
395                                    }
396                                    else {
397                                            portalJsp = CustomJspRegistryUtil.getCustomJspFileName(
398                                                    contextId, portalJsp);
399    
400                                            File portalJspFile = new File(
401                                                    PortalUtil.getPortalWebDir() + portalJsp);
402    
403                                            if (portalJspFile.exists()) {
404                                                    portalJspFile.delete();
405                                            }
406                                    }
407                            }
408    
409                            if (!customJspBag.isCustomJspGlobal()) {
410                                    CustomJspRegistryUtil.unregisterServletContextName(contextId);
411                            }
412    
413                            _customJspBagsMap.remove(serviceReference);
414                    }
415    
416            }
417    
418    }