001    /**
002     * Copyright (c) 2000-2013 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.tools.deploy;
016    
017    import com.liferay.portal.kernel.plugin.PluginPackage;
018    import com.liferay.portal.kernel.util.FileUtil;
019    import com.liferay.portal.kernel.util.GetterUtil;
020    import com.liferay.portal.kernel.util.PropsKeys;
021    import com.liferay.portal.kernel.util.ServerDetector;
022    import com.liferay.portal.kernel.util.StringBundler;
023    import com.liferay.portal.kernel.util.StringUtil;
024    import com.liferay.portal.kernel.util.Validator;
025    import com.liferay.portal.kernel.xml.Document;
026    import com.liferay.portal.kernel.xml.Element;
027    import com.liferay.portal.kernel.xml.SAXReaderUtil;
028    import com.liferay.portal.model.Plugin;
029    import com.liferay.portal.util.InitUtil;
030    import com.liferay.portal.util.Portal;
031    import com.liferay.portal.util.PortalUtil;
032    import com.liferay.portal.util.PrefsPropsUtil;
033    import com.liferay.portal.util.PropsValues;
034    import com.liferay.portal.xml.DocumentImpl;
035    import com.liferay.util.bridges.alloy.AlloyPortlet;
036    import com.liferay.util.bridges.mvc.MVCPortlet;
037    import com.liferay.util.xml.XMLMerger;
038    import com.liferay.util.xml.descriptor.FacesXMLDescriptor;
039    
040    import java.io.File;
041    
042    import java.util.ArrayList;
043    import java.util.List;
044    
045    /**
046     * @author Brian Wing Shun Chan
047     * @author Brian Myunghun Kim
048     */
049    public class PortletDeployer extends BaseDeployer {
050    
051            public static final String JSF_MYFACES =
052                    "org.apache.myfaces.portlet.MyFacesGenericPortlet";
053    
054            public static final String JSF_STANDARD =
055                    "javax.portlet.faces.GenericFacesPortlet";
056    
057            public static final String JSF_SUN = "com.sun.faces.portlet.FacesPortlet";
058    
059            public static final String LIFERAY_RENDER_KIT_FACTORY =
060                    "com.liferay.util.jsf.sun.faces.renderkit.LiferayRenderKitFactoryImpl";
061    
062            public static final String MYFACES_CONTEXT_FACTORY =
063                    "com.liferay.util.bridges.jsf.myfaces.MyFacesContextFactoryImpl";
064    
065            public static void main(String[] args) {
066                    InitUtil.initWithSpring();
067    
068                    List<String> wars = new ArrayList<String>();
069                    List<String> jars = new ArrayList<String>();
070    
071                    for (String arg : args) {
072                            if (arg.endsWith(".war")) {
073                                    wars.add(arg);
074                            }
075                            else if (arg.endsWith(".jar")) {
076                                    jars.add(arg);
077                            }
078                    }
079    
080                    new PortletDeployer(wars, jars);
081            }
082    
083            public PortletDeployer() {
084            }
085    
086            public PortletDeployer(List<String> wars, List<String> jars) {
087                    super(wars, jars);
088            }
089    
090            @Override
091            public void checkArguments() {
092                    super.checkArguments();
093    
094                    if (Validator.isNull(portletTaglibDTD)) {
095                            throw new IllegalArgumentException(
096                                    "The system property deployer.portlet.taglib.dtd is not set");
097                    }
098            }
099    
100            @Override
101            public void copyXmls(
102                            File srcFile, String displayName, PluginPackage pluginPackage)
103                    throws Exception {
104    
105                    super.copyXmls(srcFile, displayName, pluginPackage);
106    
107                    copyTomcatContextXml(srcFile);
108    
109                    copyDependencyXml(
110                            "_servlet_context_include.jsp", srcFile + "/WEB-INF/jsp");
111            }
112    
113            @Override
114            public String getExtraContent(
115                            double webXmlVersion, File srcFile, String displayName)
116                    throws Exception {
117    
118                    StringBundler sb = new StringBundler();
119    
120                    if (ServerDetector.isWebSphere()) {
121                            sb.append("<context-param>");
122                            sb.append("<param-name>");
123                            sb.append("com.ibm.websphere.portletcontainer.");
124                            sb.append("PortletDeploymentEnabled");
125                            sb.append("</param-name>");
126                            sb.append("<param-value>false</param-value>");
127                            sb.append("</context-param>");
128                    }
129    
130                    File facesXML = new File(srcFile + "/WEB-INF/faces-config.xml");
131                    File portletXML = new File(
132                            srcFile + "/WEB-INF/" + Portal.PORTLET_XML_FILE_NAME_STANDARD);
133                    File webXML = new File(srcFile + "/WEB-INF/web.xml");
134    
135                    updatePortletXML(portletXML);
136    
137                    sb.append(getServletContent(portletXML, webXML));
138    
139                    setupAlloy(srcFile, portletXML);
140    
141                    setupJSF(facesXML, portletXML);
142    
143                    if (_sunFacesPortlet) {
144    
145                            // LiferayConfigureListener
146    
147                            sb.append("<listener>");
148                            sb.append("<listener-class>");
149                            sb.append("com.liferay.util.bridges.jsf.sun.");
150                            sb.append("LiferayConfigureListener");
151                            sb.append("</listener-class>");
152                            sb.append("</listener>");
153                    }
154    
155                    String extraContent = super.getExtraContent(
156                            webXmlVersion, srcFile, displayName);
157    
158                    sb.append(extraContent);
159    
160                    return sb.toString();
161            }
162    
163            @Override
164            public String getExtraFiltersContent(double webXmlVersion, File srcFile)
165                    throws Exception {
166    
167                    StringBundler sb = new StringBundler(4);
168    
169                    String extraFiltersContent = super.getExtraFiltersContent(
170                            webXmlVersion, srcFile);
171    
172                    sb.append(extraFiltersContent);
173    
174                    // Ignore filters
175    
176                    sb.append(getIgnoreFiltersContent(srcFile));
177    
178                    // Speed filters
179    
180                    sb.append(getSpeedFiltersContent(srcFile));
181    
182                    // Servlet context include filters
183    
184                    sb.append(
185                            getServletContextIncludeFiltersContent(webXmlVersion, srcFile));
186    
187                    return sb.toString();
188            }
189    
190            @Override
191            public String getPluginType() {
192                    return Plugin.TYPE_PORTLET;
193            }
194    
195            public String getServletContent(File portletXML, File webXML)
196                    throws Exception {
197    
198                    StringBundler sb = new StringBundler();
199    
200                    Document document = SAXReaderUtil.read(portletXML);
201    
202                    Element rootElement = document.getRootElement();
203    
204                    List<Element> portletElements = rootElement.elements("portlet");
205    
206                    for (Element portletElement : portletElements) {
207                            String portletName = PortalUtil.getJsSafePortletId(
208                                    portletElement.elementText("portlet-name"));
209                            String portletClass = portletElement.elementText("portlet-class");
210    
211                            String servletName = portletName + " Servlet";
212    
213                            sb.append("<servlet>");
214                            sb.append("<servlet-name>");
215                            sb.append(servletName);
216                            sb.append("</servlet-name>");
217                            sb.append("<servlet-class>");
218                            sb.append("com.liferay.portal.kernel.servlet.PortletServlet");
219                            sb.append("</servlet-class>");
220                            sb.append("<init-param>");
221                            sb.append("<param-name>portlet-class</param-name>");
222                            sb.append("<param-value>");
223                            sb.append(portletClass);
224                            sb.append("</param-value>");
225                            sb.append("</init-param>");
226                            sb.append("<load-on-startup>1</load-on-startup>");
227                            sb.append("</servlet>");
228    
229                            sb.append("<servlet-mapping>");
230                            sb.append("<servlet-name>");
231                            sb.append(servletName);
232                            sb.append("</servlet-name>");
233                            sb.append("<url-pattern>/");
234                            sb.append(portletName);
235                            sb.append("/*</url-pattern>");
236                            sb.append("</servlet-mapping>");
237                    }
238    
239                    return sb.toString();
240            }
241    
242            public void setupAlloy(File srcFile, File portletXML) throws Exception {
243                    String content = FileUtil.read(portletXML);
244    
245                    if (!content.contains(AlloyPortlet.class.getName())) {
246                            return;
247                    }
248    
249                    String[] dirNames = FileUtil.listDirs(srcFile + "/WEB-INF/jsp");
250    
251                    for (String dirName : dirNames) {
252                            File dir = new File(srcFile + "/WEB-INF/jsp/" + dirName + "/views");
253    
254                            if (!dir.exists() || !dir.isDirectory()) {
255                                    continue;
256                            }
257    
258                            copyDependencyXml("touch.jsp", dir.toString());
259                    }
260            }
261    
262            public void setupJSF(File facesXML, File portletXML) throws Exception {
263                    _myFacesPortlet = false;
264                    _sunFacesPortlet = false;
265    
266                    if (!facesXML.exists()) {
267                            return;
268                    }
269    
270                    // portlet.xml
271    
272                    Document document = SAXReaderUtil.read(portletXML, true);
273    
274                    Element rootElement = document.getRootElement();
275    
276                    List<Element> portletElements = rootElement.elements("portlet");
277    
278                    for (Element portletElement : portletElements) {
279                            String portletClass = portletElement.elementText("portlet-class");
280    
281                            if (portletClass.equals(JSF_MYFACES)) {
282                                    _myFacesPortlet = true;
283    
284                                    break;
285                            }
286                            else if (portletClass.equals(JSF_SUN)) {
287                                    _sunFacesPortlet = true;
288    
289                                    break;
290                            }
291                    }
292    
293                    // faces-config.xml
294    
295                    document = SAXReaderUtil.read(facesXML, true);
296    
297                    rootElement = document.getRootElement();
298    
299                    Element factoryElement = rootElement.element("factory");
300    
301                    Element renderKitFactoryElement = null;
302                    Element facesContextFactoryElement = null;
303    
304                    if (factoryElement == null) {
305                            factoryElement = rootElement.addElement("factory");
306                    }
307    
308                    renderKitFactoryElement = factoryElement.element("render-kit-factory");
309                    facesContextFactoryElement = factoryElement.element(
310                            "faces-context-factory");
311    
312                    if (appServerType.equals("orion") && _sunFacesPortlet &&
313                            (renderKitFactoryElement == null)) {
314    
315                            renderKitFactoryElement = factoryElement.addElement(
316                                    "render-kit-factory");
317    
318                            renderKitFactoryElement.addText(LIFERAY_RENDER_KIT_FACTORY);
319                    }
320                    else if (_myFacesPortlet && (facesContextFactoryElement == null)) {
321                            facesContextFactoryElement = factoryElement.addElement(
322                                    "faces-context-factory");
323    
324                            facesContextFactoryElement.addText(MYFACES_CONTEXT_FACTORY);
325                    }
326    
327                    if (!appServerType.equals("orion") && _sunFacesPortlet) {
328                            factoryElement.detach();
329                    }
330    
331                    XMLMerger xmlMerger = new XMLMerger(new FacesXMLDescriptor());
332    
333                    DocumentImpl documentImpl = (DocumentImpl)document;
334    
335                    xmlMerger.organizeXML(documentImpl.getWrappedDocument());
336    
337                    FileUtil.write(facesXML, document.formattedString(), true);
338            }
339    
340            @Override
341            public void updateDeployDirectory(File srcFile) throws Exception {
342                    boolean customPortletXML = false;
343    
344                    try {
345                            customPortletXML = PrefsPropsUtil.getBoolean(
346                                    PropsKeys.AUTO_DEPLOY_CUSTOM_PORTLET_XML,
347                                    PropsValues.AUTO_DEPLOY_CUSTOM_PORTLET_XML);
348                    }
349                    catch (Exception e) {
350    
351                            // This will only happen when running the deploy tool in Ant in the
352                            // classical way where the WAR file is actually massaged and
353                            // packaged.
354    
355                            customPortletXML = PropsValues.AUTO_DEPLOY_CUSTOM_PORTLET_XML;
356                    }
357    
358                    customPortletXML = GetterUtil.getBoolean(
359                            System.getProperty("deployer.custom.portlet.xml"),
360                            customPortletXML);
361    
362                    if (!customPortletXML) {
363                            return;
364                    }
365    
366                    File portletXML = new File(
367                            srcFile + "/WEB-INF/" + Portal.PORTLET_XML_FILE_NAME_STANDARD);
368    
369                    if (portletXML.exists()) {
370                            File portletCustomXML = new File(
371                                    srcFile + "/WEB-INF/" + Portal.PORTLET_XML_FILE_NAME_CUSTOM);
372    
373                            if (portletCustomXML.exists()) {
374                                    portletCustomXML.delete();
375                            }
376    
377                            portletXML.renameTo(portletCustomXML);
378                    }
379            }
380    
381            public void updatePortletXML(File portletXML) throws Exception {
382                    if (!portletXML.exists()) {
383                            return;
384                    }
385    
386                    String content = FileUtil.read(portletXML);
387    
388                    content = StringUtil.replace(
389                            content, "com.liferay.util.bridges.jsp.JSPPortlet",
390                            MVCPortlet.class.getName());
391    
392                    FileUtil.write(portletXML, content);
393            }
394    
395            private boolean _myFacesPortlet;
396            private boolean _sunFacesPortlet;
397    
398    }