001    /**
002     * Copyright (c) 2000-2011 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.service.impl;
016    
017    import com.liferay.portal.kernel.exception.PortalException;
018    import com.liferay.portal.kernel.exception.SystemException;
019    import com.liferay.portal.kernel.lar.PortletDataHandlerKeys;
020    import com.liferay.portal.kernel.lar.UserIdStrategy;
021    import com.liferay.portal.kernel.log.Log;
022    import com.liferay.portal.kernel.log.LogFactoryUtil;
023    import com.liferay.portal.kernel.util.FileUtil;
024    import com.liferay.portal.kernel.util.GetterUtil;
025    import com.liferay.portal.kernel.util.ListUtil;
026    import com.liferay.portal.kernel.util.SystemProperties;
027    import com.liferay.portal.kernel.util.UnicodeProperties;
028    import com.liferay.portal.kernel.util.Validator;
029    import com.liferay.portal.kernel.uuid.PortalUUIDUtil;
030    import com.liferay.portal.model.Group;
031    import com.liferay.portal.model.Layout;
032    import com.liferay.portal.model.LayoutConstants;
033    import com.liferay.portal.model.LayoutPrototype;
034    import com.liferay.portal.model.LayoutSet;
035    import com.liferay.portal.model.LayoutSetPrototype;
036    import com.liferay.portal.model.Lock;
037    import com.liferay.portal.model.UserGroup;
038    import com.liferay.portal.model.impl.VirtualLayout;
039    import com.liferay.portal.security.permission.PermissionChecker;
040    import com.liferay.portal.security.permission.PermissionThreadLocal;
041    import com.liferay.portal.service.GroupLocalServiceUtil;
042    import com.liferay.portal.service.LayoutLocalServiceUtil;
043    import com.liferay.portal.service.LayoutPrototypeLocalServiceUtil;
044    import com.liferay.portal.service.LayoutSetLocalServiceUtil;
045    import com.liferay.portal.service.LayoutSetPrototypeLocalServiceUtil;
046    import com.liferay.portal.service.LockLocalServiceUtil;
047    import com.liferay.portal.service.UserGroupLocalServiceUtil;
048    import com.liferay.portal.service.UserLocalServiceUtil;
049    import com.liferay.portal.service.persistence.LayoutSetUtil;
050    import com.liferay.portal.service.persistence.LayoutUtil;
051    import com.liferay.portal.util.PropsValues;
052    import com.liferay.portlet.sites.util.SitesUtil;
053    
054    import java.io.File;
055    
056    import java.lang.reflect.Method;
057    
058    import java.util.Arrays;
059    import java.util.Date;
060    import java.util.LinkedHashMap;
061    import java.util.List;
062    import java.util.Map;
063    
064    import org.aopalliance.intercept.MethodInterceptor;
065    import org.aopalliance.intercept.MethodInvocation;
066    
067    import org.springframework.core.annotation.Order;
068    
069    /**
070     * @author Raymond Augé
071     * @author Jorge Ferrer
072     */
073    @Order(2)
074    public class LayoutLocalServiceVirtualLayoutsAdvice
075            implements MethodInterceptor {
076    
077            public Object invoke(MethodInvocation methodInvocation) throws Throwable {
078                    PermissionChecker permissionChecker =
079                            PermissionThreadLocal.getPermissionChecker();
080    
081                    if (permissionChecker == null) {
082                            return methodInvocation.proceed();
083                    }
084    
085                    Method method = methodInvocation.getMethod();
086    
087                    String methodName = method.getName();
088    
089                    Object[] arguments = methodInvocation.getArguments();
090    
091                    Class<?>[] parameterTypes = method.getParameterTypes();
092    
093                    if (methodName.equals("getLayout") &&
094                            (Arrays.equals(parameterTypes, _TYPES_L) ||
095                             Arrays.equals(parameterTypes, _TYPES_L_B_L))) {
096    
097                            Layout layout = (Layout)methodInvocation.proceed();
098    
099                            if (Validator.isNull(layout.getSourcePrototypeLayoutUuid())) {
100                                    return layout;
101                            }
102    
103                            Group group = layout.getGroup();
104                            LayoutSet layoutSet = layout.getLayoutSet();
105    
106                            mergeLayoutProtypeLayout(group, layout);
107                            mergeLayoutSetProtypeLayouts(group, layoutSet);
108                    }
109                    else if (methodName.equals("getLayouts") &&
110                                     (Arrays.equals(parameterTypes, _TYPES_L_B_L) ||
111                                      Arrays.equals(parameterTypes, _TYPES_L_B_L_B_I_I))) {
112    
113                            long groupId = (Long)arguments[0];
114                            boolean privateLayout = (Boolean)arguments[1];
115                            long parentLayoutId = (Long)arguments[2];
116    
117                            try {
118                                    Group group = GroupLocalServiceUtil.getGroup(groupId);
119    
120                                    LayoutSet layoutSet = LayoutSetLocalServiceUtil.getLayoutSet(
121                                            groupId, privateLayout);
122    
123                                    mergeLayoutSetProtypeLayouts(group, layoutSet);
124    
125                                    if (!PropsValues.
126                                                    USER_GROUPS_COPY_LAYOUTS_TO_USER_PERSONAL_SITE &&
127                                            group.isUser() &&
128                                            (parentLayoutId ==
129                                                    LayoutConstants.DEFAULT_PARENT_LAYOUT_ID)) {
130    
131                                            Object returnValue = methodInvocation.proceed();
132    
133                                            return addUserGroupLayouts(
134                                                    group, layoutSet, (List<Layout>)returnValue);
135                                    }
136                            }
137                            catch (Exception e) {
138                                    _log.error(e, e);
139    
140                                    throw e;
141                            }
142                    }
143    
144                    return methodInvocation.proceed();
145            }
146    
147            protected List<Layout> addUserGroupLayouts(
148                            Group group, LayoutSet layoutSet, List<Layout> layouts)
149                    throws Exception {
150    
151                    layouts = ListUtil.copy(layouts);
152    
153                    List<UserGroup> userUserGroups =
154                            UserGroupLocalServiceUtil.getUserUserGroups(group.getClassPK());
155    
156                    for (UserGroup userGroup : userUserGroups) {
157                            Group userGroupGroup = userGroup.getGroup();
158    
159                            List<Layout> userGroupLayouts = LayoutLocalServiceUtil.getLayouts(
160                                    userGroupGroup.getGroupId(), layoutSet.isPrivateLayout());
161    
162                            for (Layout userGroupLayout : userGroupLayouts) {
163                                    Layout virtualLayout = new VirtualLayout(
164                                            userGroupLayout, group);
165    
166                                    layouts.add(virtualLayout);
167                            }
168                    }
169    
170                    return layouts;
171            }
172    
173            protected Map<String, String[]> getLayoutTemplatesParameters(
174                    boolean firstTime) {
175    
176                    Map<String, String[]> parameterMap =
177                            new LinkedHashMap<String, String[]>();
178    
179                    parameterMap.put(
180                            PortletDataHandlerKeys.CATEGORIES,
181                            new String[] {Boolean.TRUE.toString()});
182                    parameterMap.put(
183                            PortletDataHandlerKeys.DELETE_MISSING_LAYOUTS,
184                            new String[] {Boolean.FALSE.toString()});
185                    parameterMap.put(
186                            PortletDataHandlerKeys.DELETE_PORTLET_DATA,
187                            new String[] {Boolean.FALSE.toString()});
188                    parameterMap.put(
189                            PortletDataHandlerKeys.IGNORE_LAST_PUBLISH_DATE,
190                            new String[] {Boolean.TRUE.toString()});
191                    parameterMap.put(
192                            PortletDataHandlerKeys.LAYOUT_SET_PROTOTYPE_LINK_ENABLED,
193                            new String[] {Boolean.TRUE.toString()});
194                    parameterMap.put(
195                            PortletDataHandlerKeys.PERMISSIONS,
196                            new String[] {Boolean.TRUE.toString()});
197                    parameterMap.put(
198                            PortletDataHandlerKeys.PORTLET_ARCHIVED_SETUPS,
199                            new String[] {Boolean.TRUE.toString()});
200                    parameterMap.put(
201                            PortletDataHandlerKeys.PORTLET_SETUP,
202                            new String[] {Boolean.TRUE.toString()});
203                    parameterMap.put(
204                            PortletDataHandlerKeys.THEME,
205                            new String[] {Boolean.FALSE.toString()});
206                    parameterMap.put(
207                            PortletDataHandlerKeys.THEME_REFERENCE,
208                            new String[] {Boolean.TRUE.toString()});
209                    parameterMap.put(
210                            PortletDataHandlerKeys.UPDATE_LAST_PUBLISH_DATE,
211                            new String[] {Boolean.FALSE.toString()});
212                    parameterMap.put(
213                            PortletDataHandlerKeys.USER_ID_STRATEGY,
214                            new String[] {UserIdStrategy.CURRENT_USER_ID});
215    
216                    if (firstTime) {
217                            parameterMap.put(
218                                    PortletDataHandlerKeys.DATA_STRATEGY,
219                                    new String[] {PortletDataHandlerKeys.DATA_STRATEGY_MIRROR});
220                            parameterMap.put(
221                                    PortletDataHandlerKeys.PORTLET_DATA,
222                                    new String[] {Boolean.TRUE.toString()});
223                            parameterMap.put(
224                                    PortletDataHandlerKeys.PORTLET_DATA_ALL,
225                                    new String[] {Boolean.TRUE.toString()});
226                    }
227                    else {
228                            parameterMap.put(
229                                    PortletDataHandlerKeys.PORTLET_DATA,
230                                    new String[] {Boolean.FALSE.toString()});
231                            parameterMap.put(
232                                    PortletDataHandlerKeys.PORTLET_DATA_ALL,
233                                    new String[] {Boolean.FALSE.toString()});
234                    }
235    
236                    return parameterMap;
237            }
238    
239            protected void importLayoutSetPrototype(
240                            LayoutSetPrototype layoutSetPrototype, long groupId,
241                            boolean privateLayout, Map<String, String[]> parameterMap)
242                    throws PortalException, SystemException {
243    
244                    File file = null;
245    
246                    File cacheFile = new File(
247                            _TEMP_DIR.concat(layoutSetPrototype.getUuid()).concat(".lar"));
248    
249                    if (cacheFile.exists()) {
250                            Date modifiedDate = layoutSetPrototype.getModifiedDate();
251    
252                            if (cacheFile.lastModified() >= modifiedDate.getTime()) {
253                                    if (_log.isDebugEnabled()) {
254                                            _log.debug(
255                                                    "Using cached layout set prototype LAR file " +
256                                                            cacheFile.getAbsolutePath());
257                                    }
258    
259                                    file = cacheFile;
260                            }
261                    }
262    
263                    boolean newFile = false;
264    
265                    if (file == null) {
266                            Group layoutSetPrototypeGroup = layoutSetPrototype.getGroup();
267    
268                            file = LayoutLocalServiceUtil.exportLayoutsAsFile(
269                                    layoutSetPrototypeGroup.getGroupId(), true, null,
270                                    parameterMap, null, null);
271    
272                            newFile = true;
273                    }
274    
275                    long userId = UserLocalServiceUtil.getDefaultUserId(
276                            layoutSetPrototype.getCompanyId());
277    
278                    LayoutLocalServiceUtil.importLayouts(
279                            userId, groupId, privateLayout, parameterMap, file);
280    
281                    if (newFile) {
282                            try {
283                                    FileUtil.copyFile(file, cacheFile);
284    
285                                    if (_log.isDebugEnabled()) {
286                                            _log.debug(
287                                                    "Copied " + file.getAbsolutePath() + " to " +
288                                                            cacheFile.getAbsolutePath());
289                                    }
290                            }
291                            catch (Exception e) {
292                                    _log.error(
293                                            "Unable to copy file " + file.getAbsolutePath() + " to " +
294                                                    cacheFile.getAbsolutePath(),
295                                            e);
296                            }
297                    }
298            }
299    
300            protected void mergeLayoutProtypeLayout(Group group, Layout layout)
301                    throws Exception {
302    
303                    if (!layout.getLayoutPrototypeLinkEnabled() ||
304                            Validator.isNull(layout.getLayoutPrototypeUuid()) ||
305                            group.isLayoutPrototype() || group.isLayoutSetPrototype()) {
306    
307                            return;
308                    }
309    
310                    UnicodeProperties typeSettingsProperties =
311                            layout.getTypeSettingsProperties();
312    
313                    long lastMergeTime = GetterUtil.getLong(
314                            typeSettingsProperties.getProperty("last-merge-time"));
315    
316                    LayoutPrototype layoutPrototype =
317                            LayoutPrototypeLocalServiceUtil.getLayoutPrototypeByUuid(
318                                    layout.getLayoutPrototypeUuid());
319    
320                    Layout layoutPrototypeLayout = layoutPrototype.getLayout();
321    
322                    Date modifiedDate = layoutPrototypeLayout.getModifiedDate();
323    
324                    if (lastMergeTime >= modifiedDate.getTime()) {
325                            return;
326                    }
327    
328                    UnicodeProperties prototypeTypeSettingsProperties =
329                            layoutPrototypeLayout.getTypeSettingsProperties();
330    
331                    int mergeFailCount = GetterUtil.getInteger(
332                            prototypeTypeSettingsProperties.getProperty("merge-fail-count"));
333    
334                    if (mergeFailCount >
335                            PropsValues.LAYOUT_PROTOTYPE_MERGE_FAIL_THRESHOLD) {
336    
337                            return;
338                    }
339    
340                    String owner = PortalUUIDUtil.generate();
341    
342                    try {
343                            Lock lock = LockLocalServiceUtil.lock(
344                                    LayoutLocalServiceVirtualLayoutsAdvice.class.getName(),
345                                    String.valueOf(layout.getPlid()), owner, false);
346    
347                            // Double deep check
348    
349                            if (!owner.equals(lock.getOwner())) {
350                                    Date createDate = lock.getCreateDate();
351    
352                                    if (((System.currentTimeMillis() - createDate.getTime()) >=
353                                            PropsValues.LAYOUT_PROTOTYPE_MERGE_LOCK_MAX_TIME)) {
354    
355                                            // Acquire lock if the lock is older than the lock max time
356    
357                                            lock = LockLocalServiceUtil.lock(
358                                                    LayoutLocalServiceVirtualLayoutsAdvice.class.getName(),
359                                                    String.valueOf(layout.getPlid()), lock.getOwner(),
360                                                    owner, false);
361    
362                                            // Check if acquiring the lock succeeded or if another
363                                            // process has the lock
364    
365                                            if (!owner.equals(lock.getOwner())) {
366                                                    return;
367                                            }
368                                    }
369                                    else {
370                                            return;
371                                    }
372                            }
373                    }
374                    catch (Exception e) {
375                            return;
376                    }
377    
378                    try {
379                            SitesUtil.applyLayoutPrototype(layoutPrototype, layout, true);
380    
381                            layout = LayoutLocalServiceUtil.getLayout(layout.getPlid());
382    
383                            typeSettingsProperties = layout.getTypeSettingsProperties();
384    
385                            typeSettingsProperties.setProperty(
386                                    "last-merge-time", String.valueOf(modifiedDate.getTime()));
387    
388                            LayoutLocalServiceUtil.updateLayout(layout, false);
389                    }
390                    catch (Exception e) {
391                            _log.error(e, e);
392    
393                            prototypeTypeSettingsProperties.setProperty(
394                                    "merge-fail-count", String.valueOf(++mergeFailCount));
395    
396                            // Invoke updateImpl so that we do not trigger the listeners
397    
398                            LayoutUtil.updateImpl(layoutPrototypeLayout, false);
399                    }
400                    finally {
401                            LockLocalServiceUtil.unlock(
402                                    LayoutLocalServiceVirtualLayoutsAdvice.class.getName(),
403                                    String.valueOf(layout.getPlid()), owner, false);
404                    }
405            }
406    
407            protected void mergeLayoutSetProtypeLayouts(
408                            Group group, LayoutSet layoutSet)
409                    throws Exception {
410    
411                    if (!layoutSet.getLayoutSetPrototypeLinkEnabled() ||
412                            Validator.isNull(layoutSet.getLayoutSetPrototypeUuid()) ||
413                            group.isLayoutPrototype() || group.isLayoutSetPrototype()) {
414    
415                            return;
416                    }
417    
418                    UnicodeProperties settingsProperties =
419                            layoutSet.getSettingsProperties();
420    
421                    long lastMergeTime = GetterUtil.getLong(
422                            settingsProperties.getProperty("last-merge-time"));
423    
424                    LayoutSetPrototype layoutSetPrototype =
425                            LayoutSetPrototypeLocalServiceUtil.getLayoutSetPrototypeByUuid(
426                                    layoutSet.getLayoutSetPrototypeUuid());
427    
428                    Date modifiedDate = layoutSetPrototype.getModifiedDate();
429    
430                    if (lastMergeTime >= modifiedDate.getTime()) {
431                            return;
432                    }
433    
434                    LayoutSet layoutSetPrototypeLayoutSet =
435                            layoutSetPrototype.getLayoutSet();
436    
437                    UnicodeProperties layoutSetPrototypeSettingsProperties =
438                            layoutSetPrototypeLayoutSet.getSettingsProperties();
439    
440                    int mergeFailCount = GetterUtil.getInteger(
441                            layoutSetPrototypeSettingsProperties.getProperty(
442                                    "merge-fail-count"));
443    
444                    if (mergeFailCount >
445                                    PropsValues.LAYOUT_SET_PROTOTYPE_MERGE_FAIL_THRESHOLD) {
446    
447                            return;
448                    }
449    
450                    String owner = PortalUUIDUtil.generate();
451    
452                    try {
453                            Lock lock = LockLocalServiceUtil.lock(
454                                    LayoutLocalServiceVirtualLayoutsAdvice.class.getName(),
455                                    String.valueOf(layoutSet.getLayoutSetId()), owner, false);
456    
457                            // Double deep check
458    
459                            if (!owner.equals(lock.getOwner())) {
460                                    Date createDate = lock.getCreateDate();
461    
462                                    if (((System.currentTimeMillis() - createDate.getTime()) >=
463                                                    PropsValues.LAYOUT_SET_PROTOTYPE_MERGE_LOCK_MAX_TIME)) {
464    
465                                            // Acquire lock if the lock is older than the lock max time
466    
467                                            lock = LockLocalServiceUtil.lock(
468                                                    LayoutLocalServiceVirtualLayoutsAdvice.class.getName(),
469                                                    String.valueOf(layoutSet.getLayoutSetId()),
470                                                    lock.getOwner(), owner, false);
471    
472                                            // Check if acquiring the lock succeeded or if another
473                                            // process has the lock
474    
475                                            if (!owner.equals(lock.getOwner())) {
476                                                    return;
477                                            }
478                                    }
479                                    else {
480                                            return;
481                                    }
482                            }
483                    }
484                    catch (Exception e) {
485                            return;
486                    }
487    
488                    try {
489                            Map<String, String[]> parameterMap = null;
490    
491                            if (lastMergeTime > 0) {
492                                    parameterMap = getLayoutTemplatesParameters(false);
493                            }
494                            else {
495                                    parameterMap = getLayoutTemplatesParameters(true);
496                            }
497    
498                            importLayoutSetPrototype(
499                                    layoutSetPrototype, layoutSet.getGroupId(),
500                                    layoutSet.isPrivateLayout(), parameterMap);
501    
502                            settingsProperties.setProperty(
503                                    "last-merge-time", String.valueOf(modifiedDate.getTime()));
504    
505                            LayoutSetLocalServiceUtil.updateLayoutSet(layoutSet, false);
506                    }
507                    catch (Exception e) {
508                            _log.error(e, e);
509    
510                            layoutSetPrototypeSettingsProperties.setProperty(
511                                    "merge-fail-count", String.valueOf(++mergeFailCount));
512    
513                            // Invoke updateImpl so that we do not trigger the listeners
514    
515                            LayoutSetUtil.updateImpl(layoutSetPrototypeLayoutSet, false);
516                    }
517                    finally {
518                            LockLocalServiceUtil.unlock(
519                                    LayoutLocalServiceVirtualLayoutsAdvice.class.getName(),
520                                    String.valueOf(layoutSet.getLayoutSetId()), owner, false);
521                    }
522            }
523    
524            private static final String _TEMP_DIR =
525                    SystemProperties.get(SystemProperties.TMP_DIR) +
526                            "/liferay/layout_set_prototype/";
527    
528            private static final Class<?>[] _TYPES_L = {Long.TYPE};
529    
530            private static final Class<?>[] _TYPES_L_B_L = {
531                    Long.TYPE, Boolean.TYPE, Long.TYPE
532            };
533    
534            private static final Class<?>[] _TYPES_L_B_L_B_I_I = {
535                    Long.TYPE, Boolean.TYPE, Long.TYPE, Boolean.TYPE, Integer.TYPE,
536                    Integer.TYPE
537            };
538    
539            private static Log _log = LogFactoryUtil.getLog(
540                    LayoutLocalServiceVirtualLayoutsAdvice.class);
541    
542    }