1   /**
2    * Copyright (c) 2000-2008 Liferay, Inc. All rights reserved.
3    *
4    * Permission is hereby granted, free of charge, to any person obtaining a copy
5    * of this software and associated documentation files (the "Software"), to deal
6    * in the Software without restriction, including without limitation the rights
7    * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8    * copies of the Software, and to permit persons to whom the Software is
9    * furnished to do so, subject to the following conditions:
10   *
11   * The above copyright notice and this permission notice shall be included in
12   * all copies or substantial portions of the Software.
13   *
14   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20   * SOFTWARE.
21   */
22  
23  package com.liferay.portal.tools;
24  
25  import com.liferay.portal.deploy.DeployUtil;
26  import com.liferay.portal.kernel.deploy.auto.AutoDeployException;
27  import com.liferay.portal.kernel.plugin.PluginPackage;
28  import com.liferay.portal.kernel.util.FileUtil;
29  import com.liferay.portal.kernel.util.GetterUtil;
30  import com.liferay.portal.kernel.util.HttpUtil;
31  import com.liferay.portal.kernel.util.PropertiesUtil;
32  import com.liferay.portal.kernel.util.ServerDetector;
33  import com.liferay.portal.kernel.util.StringPool;
34  import com.liferay.portal.kernel.util.StringUtil;
35  import com.liferay.portal.kernel.util.Time;
36  import com.liferay.portal.kernel.util.Validator;
37  import com.liferay.portal.plugin.PluginPackageUtil;
38  import com.liferay.portal.util.DocumentUtil;
39  import com.liferay.portal.util.InitUtil;
40  import com.liferay.portal.util.PortalUtil;
41  import com.liferay.portal.util.PrefsPropsUtil;
42  import com.liferay.portal.util.PropsKeys;
43  import com.liferay.portal.util.PropsUtil;
44  import com.liferay.portal.util.PropsValues;
45  import com.liferay.util.License;
46  import com.liferay.util.SystemProperties;
47  import com.liferay.util.ant.CopyTask;
48  import com.liferay.util.ant.DeleteTask;
49  import com.liferay.util.ant.ExpandTask;
50  import com.liferay.util.ant.UpToDateTask;
51  import com.liferay.util.ant.WarTask;
52  import com.liferay.util.xml.XMLFormatter;
53  
54  import com.sun.portal.portletcontainer.warupdater.PortletWarUpdater;
55  
56  import java.io.File;
57  import java.io.FileInputStream;
58  import java.io.IOException;
59  import java.io.InputStream;
60  
61  import java.util.ArrayList;
62  import java.util.List;
63  import java.util.Map;
64  import java.util.Properties;
65  import java.util.zip.ZipEntry;
66  import java.util.zip.ZipFile;
67  
68  import org.apache.commons.logging.Log;
69  import org.apache.commons.logging.LogFactory;
70  import org.apache.oro.io.GlobFilenameFilter;
71  
72  import org.dom4j.Document;
73  import org.dom4j.Element;
74  
75  /**
76   * <a href="BaseDeployer.java.html"><b><i>View Source</i></b></a>
77   *
78   * @author Brian Wing Shun Chan
79   *
80   */
81  public class BaseDeployer {
82  
83      static {
84          InitUtil.init();
85      }
86  
87      public static final String DEPLOY_TO_PREFIX = "DEPLOY_TO__";
88  
89      public static void main(String[] args) {
90          List<String> wars = new ArrayList<String>();
91          List<String> jars = new ArrayList<String>();
92  
93          for (String arg : args) {
94              String fileName = arg.toLowerCase();
95  
96              if (fileName.endsWith(".war")) {
97                  wars.add(arg);
98              }
99              else if (fileName.endsWith(".jar")) {
100                 jars.add(arg);
101             }
102         }
103 
104         new BaseDeployer(wars, jars);
105     }
106 
107     protected BaseDeployer() {
108     }
109 
110     protected BaseDeployer(List<String> wars, List<String> jars) {
111         baseDir = System.getProperty("deployer.base.dir");
112         destDir = System.getProperty("deployer.dest.dir");
113         appServerType = System.getProperty("deployer.app.server.type");
114         portletTaglibDTD = System.getProperty("deployer.portlet.taglib.dtd");
115         portletExtTaglibDTD = System.getProperty(
116             "deployer.portlet.ext.taglib.dtd");
117         securityTaglibDTD = System.getProperty("deployer.security.taglib.dtd");
118         themeTaglibDTD = System.getProperty("deployer.theme.taglib.dtd");
119         uiTaglibDTD = System.getProperty("deployer.ui.taglib.dtd");
120         utilTaglibDTD = System.getProperty("deployer.util.taglib.dtd");
121         unpackWar = GetterUtil.getBoolean(
122             System.getProperty("deployer.unpack.war"), true);
123         jbossPrefix = GetterUtil.getString(
124             System.getProperty("deployer.jboss.prefix"));
125         tomcatLibDir = System.getProperty("deployer.tomcat.lib.dir");
126         this.wars = wars;
127         this.jars = jars;
128 
129         checkArguments();
130 
131         try {
132             deploy();
133         }
134         catch (Exception e) {
135             e.printStackTrace();
136         }
137     }
138 
139     protected void checkArguments() {
140         if (Validator.isNull(baseDir)) {
141             throw new IllegalArgumentException(
142                 "The system property deployer.base.dir is not set");
143         }
144 
145         if (Validator.isNull(destDir)) {
146             throw new IllegalArgumentException(
147                 "The system property deployer.dest.dir is not set");
148         }
149 
150         if (Validator.isNull(appServerType)) {
151             throw new IllegalArgumentException(
152                 "The system property deployer.app.server.type is not set");
153         }
154 
155         if (!appServerType.startsWith(ServerDetector.GERONIMO_ID) &&
156             !appServerType.startsWith(ServerDetector.GLASSFISH_ID) &&
157             !appServerType.startsWith(ServerDetector.JBOSS_ID) &&
158             !appServerType.startsWith(ServerDetector.JONAS_ID) &&
159             !appServerType.equals(ServerDetector.JETTY_ID) &&
160             !appServerType.equals(ServerDetector.OC4J_ID) &&
161             !appServerType.equals(ServerDetector.ORION_ID) &&
162             !appServerType.equals(ServerDetector.PRAMATI_ID) &&
163             !appServerType.equals(ServerDetector.RESIN_ID) &&
164             !appServerType.equals(ServerDetector.TOMCAT_ID) &&
165             !appServerType.equals(ServerDetector.WEBLOGIC_ID) &&
166             !appServerType.equals(ServerDetector.WEBSPHERE_ID)) {
167 
168             throw new IllegalArgumentException(
169                 appServerType + " is not a valid application server type");
170         }
171 
172         if (appServerType.startsWith(ServerDetector.GLASSFISH_ID) ||
173             appServerType.equals(ServerDetector.PRAMATI_ID) ||
174             appServerType.equals(ServerDetector.WEBLOGIC_ID)) {
175 
176             unpackWar = false;
177         }
178 
179         if (Validator.isNotNull(jbossPrefix) &&
180             !Validator.isNumber(jbossPrefix)) {
181 
182             jbossPrefix = "1";
183         }
184     }
185 
186     protected void copyDependencyXml(String fileName, String targetDir)
187         throws Exception {
188 
189         copyDependencyXml(fileName, targetDir, null);
190     }
191 
192     protected void copyDependencyXml(
193             String fileName, String targetDir, Map<String, String> filterMap)
194         throws Exception {
195 
196         copyDependencyXml(fileName, targetDir, filterMap, false);
197     }
198 
199     protected void copyDependencyXml(
200             String fileName, String targetDir, Map<String, String> filterMap,
201             boolean overwrite)
202         throws Exception {
203 
204         File file = new File(DeployUtil.getResourcePath(fileName));
205         File targetFile = new File(targetDir + "/" + fileName);
206 
207         if (!targetFile.exists()) {
208             CopyTask.copyFile(
209                 file, new File(targetDir), filterMap, overwrite, true);
210         }
211     }
212 
213     protected void copyJars(File srcFile, PluginPackage pluginPackage)
214         throws Exception {
215 
216         for (int i = 0; i < jars.size(); i++) {
217             String jarFullName = jars.get(i);
218             String jarName = jarFullName.substring(
219                 jarFullName.lastIndexOf("/") + 1, jarFullName.length());
220 
221             if ((!appServerType.equals(ServerDetector.TOMCAT_ID)) ||
222                 (appServerType.equals(ServerDetector.TOMCAT_ID) &&
223                     !jarFullName.equals("util-java.jar"))) {
224 
225                 FileUtil.copyFile(
226                     jarFullName, srcFile + "/WEB-INF/lib/" + jarName, true);
227             }
228         }
229 
230         FileUtil.delete(srcFile + "/WEB-INF/lib/util-jsf.jar");
231     }
232 
233     protected void copyPortalDependencies(File srcFile) throws Exception {
234         Properties props = getPluginPackageProperties(srcFile);
235 
236         if (props == null) {
237             return;
238         }
239 
240         // jars
241 
242         String[] portalJars = StringUtil.split(
243             props.getProperty("portal.dependency.jars"));
244 
245         for (int i = 0; i < portalJars.length; i++) {
246             String portalJar = portalJars[i].trim();
247 
248             if (_log.isDebugEnabled()) {
249                 _log.debug("Copy portal JAR " + portalJar);
250             }
251 
252             try {
253                 String portalJarPath = PortalUtil.getPortalLibDir() + portalJar;
254 
255                 FileUtil.copyFile(
256                     portalJarPath, srcFile + "/WEB-INF/lib/" + portalJar, true);
257             }
258             catch (Exception e) {
259                 _log.error("Unable to copy portal JAR " + portalJar, e);
260             }
261         }
262 
263         // tlds
264 
265         String[] portalTlds = StringUtil.split(
266             props.getProperty("portal.dependency.tlds"));
267 
268         for (int i = 0; i < portalTlds.length; i++) {
269             String portalTld = portalTlds[i].trim();
270 
271             if (_log.isDebugEnabled()) {
272                 _log.debug("Copy portal TLD " + portalTld);
273             }
274 
275             try {
276                 String portalTldPath = DeployUtil.getResourcePath(portalTld);
277 
278                 FileUtil.copyFile(
279                     portalTldPath, srcFile + "/WEB-INF/tld/" + portalTld, true);
280             }
281             catch (Exception e) {
282                 _log.error("Unable to copy portal TLD " + portalTld, e);
283             }
284         }
285 
286         // commons-logging*.jar
287 
288         File pluginLibDir = new File(srcFile + "/WEB-INF/lib/");
289 
290         String[] commonsLoggingJars = pluginLibDir.list(
291             new GlobFilenameFilter("commons-logging*.jar"));
292 
293         if ((commonsLoggingJars == null) || (commonsLoggingJars.length == 0)) {
294             String portalJarPath =
295                 PortalUtil.getPortalLibDir() + "commons-logging.jar";
296 
297             FileUtil.copyFile(
298                 portalJarPath, srcFile + "/WEB-INF/lib/commons-logging.jar",
299                 true);
300         }
301 
302         // log4j*.jar
303 
304         String[] log4jJars = pluginLibDir.list(
305             new GlobFilenameFilter("log4j*.jar"));
306 
307         if ((log4jJars == null) || (log4jJars.length == 0)) {
308             String portalJarPath = PortalUtil.getPortalLibDir() + "log4j.jar";
309 
310             FileUtil.copyFile(
311                 portalJarPath, srcFile + "/WEB-INF/lib/log4j.jar", true);
312         }
313     }
314 
315     protected void copyProperties(File srcFile, PluginPackage pluginPackage)
316         throws Exception {
317 
318         copyDependencyXml("log4j.properties", srcFile + "/WEB-INF/classes");
319         copyDependencyXml("logging.properties", srcFile + "/WEB-INF/classes");
320     }
321 
322     protected void copyTlds(File srcFile, PluginPackage pluginPackage)
323         throws Exception {
324 
325         if (Validator.isNotNull(portletTaglibDTD)) {
326             FileUtil.copyFile(
327                 portletTaglibDTD, srcFile + "/WEB-INF/tld/liferay-portlet.tld",
328                 true);
329         }
330 
331         if (Validator.isNotNull(portletExtTaglibDTD)) {
332             FileUtil.copyFile(
333                 portletExtTaglibDTD,
334                 srcFile + "/WEB-INF/tld/liferay-portlet-ext.tld", true);
335         }
336 
337         if (Validator.isNotNull(securityTaglibDTD)) {
338             FileUtil.copyFile(
339                 securityTaglibDTD,
340                 srcFile + "/WEB-INF/tld/liferay-security.tld", true);
341         }
342 
343         if (Validator.isNotNull(themeTaglibDTD)) {
344             FileUtil.copyFile(
345                 themeTaglibDTD, srcFile + "/WEB-INF/tld/liferay-theme.tld",
346                 true);
347         }
348 
349         if (Validator.isNotNull(uiTaglibDTD)) {
350             FileUtil.copyFile(
351                 uiTaglibDTD, srcFile + "/WEB-INF/tld/liferay-ui.tld", true);
352         }
353 
354         if (Validator.isNotNull(utilTaglibDTD)) {
355             FileUtil.copyFile(
356                 utilTaglibDTD, srcFile + "/WEB-INF/tld/liferay-util.tld", true);
357         }
358     }
359 
360     protected void copyXmls(
361             File srcFile, String displayName, PluginPackage pluginPackage)
362         throws Exception {
363 
364         if (appServerType.startsWith(ServerDetector.GERONIMO_ID)) {
365             copyDependencyXml("geronimo-web.xml", srcFile + "/WEB-INF");
366         }
367 
368         copyDependencyXml("web.xml", srcFile + "/WEB-INF");
369     }
370 
371     protected void deploy() throws Exception {
372         try {
373             File baseDirFile = new File(baseDir);
374 
375             File[] files = baseDirFile.listFiles();
376 
377             if (files == null) {
378                 return;
379             }
380 
381             files = FileUtil.sortFiles(files);
382 
383             for (int i = 0; i < files.length; i++) {
384                 File srcFile = files[i];
385 
386                 String fileName = srcFile.getName().toLowerCase();
387 
388                 boolean deploy = false;
389 
390                 if (fileName.endsWith(".war") || fileName.endsWith(".zip")) {
391                     deploy = true;
392 
393                     if ((wars.size() > 0) &&
394                         (!wars.contains(srcFile.getName()))) {
395 
396                         deploy = false;
397                     }
398                 }
399 
400                 if (deploy) {
401                     deployFile(srcFile);
402                 }
403             }
404         }
405         catch (Exception e) {
406             e.printStackTrace();
407         }
408     }
409 
410     protected void deployDirectory(
411             File srcFile, String displayName, boolean override,
412             PluginPackage pluginPackage)
413         throws Exception {
414 
415         deployDirectory(
416             srcFile, null, null, displayName, override, pluginPackage);
417     }
418 
419     protected void deployDirectory(
420             File srcFile, File mergeDir, File deployDir, String displayName,
421             boolean overwrite, PluginPackage pluginPackage)
422         throws Exception {
423 
424         rewriteFiles(srcFile);
425 
426         mergeDirectory(mergeDir, srcFile);
427 
428         processPluginPackageProperties(srcFile, displayName, pluginPackage);
429 
430         copyJars(srcFile, pluginPackage);
431         copyProperties(srcFile, pluginPackage);
432         copyTlds(srcFile, pluginPackage);
433         copyXmls(srcFile, displayName, pluginPackage);
434         copyPortalDependencies(srcFile);
435 
436         updateGeronimoWebXml(srcFile, displayName, pluginPackage);
437 
438         File webXml = new File(srcFile + "/WEB-INF/web.xml");
439 
440         updateWebXml(webXml, srcFile, displayName, pluginPackage);
441 
442         if ((deployDir != null) && !baseDir.equals(destDir)) {
443             updateDeployDirectory(srcFile);
444 
445             String excludes = StringPool.BLANK;
446 
447             if (appServerType.startsWith("jboss")) {
448                 excludes += "**/WEB-INF/lib/log4j.jar,";
449             }
450             else if (appServerType.equals(ServerDetector.TOMCAT_ID)) {
451                 String[] libs = FileUtil.listFiles(tomcatLibDir);
452 
453                 for (int i = 0; i < libs.length; i++) {
454                     excludes += "**/WEB-INF/lib/" + libs[i] + ",";
455                 }
456 
457                 File contextXml = new File(srcFile + "/META-INF/context.xml");
458 
459                 if (contextXml.exists()) {
460                     String content = FileUtil.read(contextXml);
461 
462                     if (content.indexOf(_PORTAL_CLASS_LOADER) != -1) {
463                         excludes += "**/WEB-INF/lib/util-bridges.jar,";
464                         excludes += "**/WEB-INF/lib/util-java.jar,";
465                         excludes += "**/WEB-INF/lib/util-taglib.jar,";
466                     }
467                 }
468 
469                 try {
470 
471                     // LEP-2990
472 
473                     Class.forName("javax.el.ELContext");
474 
475                     excludes += "**/WEB-INF/lib/el-api.jar,";
476                 }
477                 catch (ClassNotFoundException cnfe) {
478                 }
479             }
480 
481             if (!unpackWar || appServerType.equals("websphere")) {
482                 File tempDir = new File(
483                     SystemProperties.get(SystemProperties.TMP_DIR) +
484                         File.separator + Time.getTimestamp());
485 
486                 WarTask.war(srcFile, tempDir, "WEB-INF/web.xml", webXml);
487 
488                 if (!tempDir.renameTo(deployDir)) {
489                     WarTask.war(srcFile, deployDir, "WEB-INF/web.xml", webXml);
490                 }
491 
492                 DeleteTask.deleteDirectory(tempDir);
493             }
494             else {
495 
496                 // The deployer might only copy files that have been modified.
497                 // However, the deployer always copies and overwrites web.xml
498                 // after the other files have been copied because application
499                 // servers usually detect that a WAR has been modified based on
500                 // the web.xml time stamp.
501 
502                 excludes += "**/WEB-INF/web.xml";
503 
504                 CopyTask.copyDirectory(
505                     srcFile, deployDir, StringPool.BLANK, excludes, overwrite,
506                     true);
507 
508                 CopyTask.copyDirectory(
509                     srcFile, deployDir, "**/WEB-INF/web.xml", StringPool.BLANK,
510                     true, false);
511 
512                 if (appServerType.equals(ServerDetector.TOMCAT_ID)) {
513 
514                     // See org.apache.catalina.startup.HostConfig to see how
515                     // Tomcat checks to make sure that web.xml was modified 5
516                     // seconds after WEB-INF
517 
518                     File deployWebXml = new File(
519                         deployDir + "/WEB-INF/web.xml");
520 
521                     deployWebXml.setLastModified(
522                         System.currentTimeMillis() + (Time.SECOND * 6));
523                 }
524             }
525         }
526     }
527 
528     protected void deployFile(File srcFile) throws Exception {
529         PluginPackage pluginPackage = readPluginPackage(srcFile);
530 
531         if (_log.isInfoEnabled()) {
532             _log.info("Deploying " + srcFile.getName());
533         }
534 
535         String deployDir = null;
536         String displayName = null;
537         boolean overwrite = false;
538         String preliminaryContext = null;
539 
540         // File names starting with DEPLOY_TO_PREFIX should use the filename
541         // after the prefix as the deployment context
542 
543         if (srcFile.getName().startsWith(DEPLOY_TO_PREFIX)) {
544             displayName = srcFile.getName().substring(
545                 DEPLOY_TO_PREFIX.length(), srcFile.getName().length() - 4);
546 
547             overwrite = true;
548             preliminaryContext = displayName;
549         }
550 
551         if (preliminaryContext == null) {
552             preliminaryContext = getDisplayName(srcFile);
553         }
554 
555         if (pluginPackage != null) {
556             if (!PluginPackageUtil.isCurrentVersionSupported(
557                     pluginPackage.getLiferayVersions())) {
558 
559                 throw new AutoDeployException(
560                     srcFile.getName() +
561                         " does not support this version of Liferay");
562             }
563 
564             if (displayName == null) {
565                 displayName = pluginPackage.getRecommendedDeploymentContext();
566             }
567 
568             if (Validator.isNull(displayName)) {
569                 displayName = getDisplayName(srcFile);
570             }
571 
572             pluginPackage.setContext(displayName);
573 
574             PluginPackageUtil.updateInstallingPluginPackage(
575                 preliminaryContext, pluginPackage);
576         }
577 
578         if (Validator.isNotNull(displayName)) {
579             deployDir = displayName + ".war";
580         }
581         else {
582             deployDir = srcFile.getName();
583             displayName = getDisplayName(srcFile);
584         }
585 
586         if (appServerType.startsWith(ServerDetector.JBOSS_ID)) {
587             deployDir = jbossPrefix + deployDir;
588         }
589         else if (appServerType.equals(ServerDetector.JETTY_ID) ||
590                  appServerType.equals(ServerDetector.OC4J_ID) ||
591                  appServerType.equals(ServerDetector.ORION_ID) ||
592                  appServerType.equals(ServerDetector.RESIN_ID) ||
593                  appServerType.equals(ServerDetector.TOMCAT_ID)) {
594 
595             if (unpackWar) {
596                 deployDir = deployDir.substring(0, deployDir.length() - 4);
597             }
598         }
599 
600         deployDir = destDir + "/" + deployDir;
601 
602         File deployDirFile = new File(deployDir);
603 
604         try {
605             PluginPackage previousPluginPackage =
606                 readPluginPackage(deployDirFile);
607 
608             if ((pluginPackage != null) && (previousPluginPackage != null)) {
609                 if (_log.isInfoEnabled()) {
610                     String name = pluginPackage.getName();
611                     String previousVersion = previousPluginPackage.getVersion();
612                     String version = pluginPackage.getVersion();
613 
614                     _log.info(
615                         "Updating " + name + " from version " +
616                             previousVersion + " to version " + version);
617                 }
618 
619                 if (pluginPackage.isLaterVersionThan(
620                     previousPluginPackage)) {
621 
622                     overwrite = true;
623                 }
624             }
625 
626             File mergeDirFile = new File(
627                 srcFile.getParent() + "/merge/" + srcFile.getName());
628 
629             if (srcFile.isDirectory()) {
630                 deployDirectory(
631                     srcFile, mergeDirFile, deployDirFile, displayName,
632                     overwrite, pluginPackage);
633             }
634             else {
635                 boolean deployed = deployFile(
636                     srcFile, mergeDirFile, deployDirFile, displayName,
637                     overwrite, pluginPackage);
638 
639                 if (!deployed) {
640                     String context = preliminaryContext;
641 
642                     if (pluginPackage != null) {
643                         context = pluginPackage.getContext();
644                     }
645 
646                     PluginPackageUtil.endPluginPackageInstallation(context);
647                 }
648             }
649         }
650         catch (Exception e) {
651             if (pluginPackage != null) {
652                 PluginPackageUtil.endPluginPackageInstallation(
653                     pluginPackage.getContext());
654             }
655 
656             throw e;
657         }
658     }
659 
660     protected boolean deployFile(
661             File srcFile, File mergeDir, File deployDir, String displayName,
662             boolean overwrite, PluginPackage pluginPackage)
663         throws Exception {
664 
665         boolean undeployOnRedeploy = false;
666 
667         try {
668             undeployOnRedeploy = PrefsPropsUtil.getBoolean(
669                 PropsKeys.HOT_UNDEPLOY_ON_REDEPLOY,
670                 PropsValues.HOT_UNDEPLOY_ON_REDEPLOY);
671         }
672         catch (Exception e) {
673 
674             // This will only happen when running the deploy tool in Ant in the
675             // classical way where the WAR file is actually massaged and
676             // packaged.
677 
678         }
679 
680         if (undeployOnRedeploy) {
681             DeployUtil.undeploy(appServerType, deployDir);
682         }
683 
684         if (!overwrite && UpToDateTask.isUpToDate(srcFile, deployDir)) {
685             if (_log.isInfoEnabled()) {
686                 _log.info(deployDir + " is already up to date");
687             }
688 
689             return false;
690         }
691 
692         File tempDir = new File(
693             SystemProperties.get(SystemProperties.TMP_DIR) + File.separator +
694                 Time.getTimestamp());
695 
696         if ((PropsValues.PORTLET_CONTAINER_IMPL_SUN) &&
697             (this instanceof PortletDeployer)) {
698 
699             File sunTempDir = new File(
700                 SystemProperties.get(SystemProperties.TMP_DIR) +
701                     File.separator + "sun" + File.separator +
702                         Time.getTimestamp());
703 
704             Properties props = new Properties();
705 
706             props.setProperty(PortletWarUpdater.ADD_WEB_XML, "true");
707 
708             PortletWarUpdater warUpdater = new PortletWarUpdater(props);
709 
710             boolean success = warUpdater.preparePortlet(
711                 srcFile, sunTempDir.toString());
712 
713             if (success){
714                 File sunSrcFile = new File(
715                     sunTempDir + File.separator + srcFile.getName());
716 
717                 ExpandTask.expand(sunSrcFile, tempDir);
718             }
719             else {
720                 ExpandTask.expand(srcFile, tempDir);
721             }
722 
723             deployDirectory(
724                 tempDir, mergeDir, deployDir, displayName, overwrite,
725                 pluginPackage);
726 
727             DeleteTask.deleteDirectory(sunTempDir);
728         }
729         else {
730             ExpandTask.expand(srcFile, tempDir);
731 
732             deployDirectory(
733                 tempDir, mergeDir, deployDir, displayName, overwrite,
734                 pluginPackage);
735         }
736 
737         DeleteTask.deleteDirectory(tempDir);
738 
739         return true;
740     }
741 
742     protected String downloadJar(String jar) throws Exception {
743         String tmpDir = SystemProperties.get(SystemProperties.TMP_DIR);
744 
745         File file = new File(
746             tmpDir + "/liferay/com/liferay/portal/deploy/dependencies/" +
747                 jar);
748 
749         if (!file.exists()) {
750             synchronized (this) {
751                 String url = PropsUtil.get(
752                     PropsKeys.LIBRARY_DOWNLOAD_URL + jar);
753 
754                 if (_log.isInfoEnabled()) {
755                     _log.info("Downloading library from " + url);
756                 }
757 
758                 byte[] bytes = HttpUtil.URLtoByteArray(url);
759 
760                 FileUtil.write(file, bytes);
761             }
762         }
763 
764         return FileUtil.getAbsolutePath(file);
765     }
766 
767     protected String getDisplayName(File srcFile) {
768         String displayName = srcFile.getName();
769 
770         displayName = displayName.substring(0, displayName.length() - 4);
771 
772         if (appServerType.startsWith("jboss") &&
773             Validator.isNotNull(jbossPrefix) &&
774             displayName.startsWith(jbossPrefix)) {
775 
776             displayName = displayName.substring(1, displayName.length());
777         }
778 
779         return displayName;
780     }
781 
782     protected String getExtraContent(
783             double webXmlVersion, File srcFile, String displayName)
784         throws Exception {
785 
786         StringBuilder sb = new StringBuilder();
787 
788         sb.append("<display-name>");
789         sb.append(displayName);
790         sb.append("</display-name>");
791 
792         sb.append("<listener>");
793         sb.append("<listener-class>");
794         sb.append("com.liferay.portal.kernel.servlet.PortletContextListener");
795         sb.append("</listener-class>");
796         sb.append("</listener>");
797 
798         boolean hasTaglib = false;
799 
800         if (Validator.isNotNull(portletTaglibDTD) ||
801             Validator.isNotNull(portletExtTaglibDTD) ||
802             Validator.isNotNull(securityTaglibDTD) ||
803             Validator.isNotNull(themeTaglibDTD) ||
804             Validator.isNotNull(uiTaglibDTD) ||
805             Validator.isNotNull(utilTaglibDTD)) {
806 
807             hasTaglib = true;
808         }
809 
810         if (hasTaglib && (webXmlVersion > 2.3)) {
811             sb.append("<jsp-config>");
812         }
813 
814         if (Validator.isNotNull(portletTaglibDTD)) {
815             sb.append("<taglib>");
816             sb.append(
817                 "<taglib-uri>http://java.sun.com/portlet_2_0</taglib-uri>");
818             sb.append("<taglib-location>");
819             sb.append("/WEB-INF/tld/liferay-portlet.tld");
820             sb.append("</taglib-location>");
821             sb.append("</taglib>");
822         }
823 
824         if (Validator.isNotNull(portletExtTaglibDTD)) {
825             sb.append("<taglib>");
826             sb.append("<taglib-uri>");
827             sb.append("http://liferay.com/tld/portlet");
828             sb.append("</taglib-uri>");
829             sb.append("<taglib-location>");
830             sb.append("/WEB-INF/tld/liferay-portlet-ext.tld");
831             sb.append("</taglib-location>");
832             sb.append("</taglib>");
833         }
834 
835         if (Validator.isNotNull(securityTaglibDTD)) {
836             sb.append("<taglib>");
837             sb.append("<taglib-uri>");
838             sb.append("http://liferay.com/tld/security");
839             sb.append("</taglib-uri>");
840             sb.append("<taglib-location>");
841             sb.append("/WEB-INF/tld/liferay-security.tld");
842             sb.append("</taglib-location>");
843             sb.append("</taglib>");
844         }
845 
846         if (Validator.isNotNull(themeTaglibDTD)) {
847             sb.append("<taglib>");
848             sb.append("<taglib-uri>http://liferay.com/tld/theme</taglib-uri>");
849             sb.append("<taglib-location>");
850             sb.append("/WEB-INF/tld/liferay-theme.tld");
851             sb.append("</taglib-location>");
852             sb.append("</taglib>");
853         }
854 
855         if (Validator.isNotNull(uiTaglibDTD)) {
856             sb.append("<taglib>");
857             sb.append("<taglib-uri>http://liferay.com/tld/ui</taglib-uri>");
858             sb.append("<taglib-location>");
859             sb.append("/WEB-INF/tld/liferay-ui.tld");
860             sb.append("</taglib-location>");
861             sb.append("</taglib>");
862         }
863 
864         if (Validator.isNotNull(utilTaglibDTD)) {
865             sb.append("<taglib>");
866             sb.append("<taglib-uri>http://liferay.com/tld/util</taglib-uri>");
867             sb.append("<taglib-location>");
868             sb.append("/WEB-INF/tld/liferay-util.tld");
869             sb.append("</taglib-location>");
870             sb.append("</taglib>");
871         }
872 
873         if (hasTaglib && (webXmlVersion > 2.3)) {
874             sb.append("</jsp-config>");
875         }
876 
877         return sb.toString();
878     }
879 
880     protected String getPluginPackageLicensesXml(List<License> licenses) {
881         StringBuilder sb = new StringBuilder();
882 
883         for (int i = 0; i < licenses.size(); i++) {
884             License license = licenses.get(i);
885 
886             if (i == 0) {
887                 sb.append("\r\n");
888             }
889 
890             sb.append("\t\t<license osi-approved=\"");
891             sb.append(license.isOsiApproved());
892             sb.append("\">");
893             sb.append(license.getName());
894             sb.append("</license>\r\n");
895 
896             if ((i + 1) == licenses.size()) {
897                 sb.append("\t");
898             }
899         }
900 
901         return sb.toString();
902     }
903 
904     protected String getPluginPackageLiferayVersionsXml(
905         List<String> liferayVersions) {
906 
907         StringBuilder sb = new StringBuilder();
908 
909         for (int i = 0; i < liferayVersions.size(); i++) {
910             String liferayVersion = liferayVersions.get(i);
911 
912             if (i == 0) {
913                 sb.append("\r\n");
914             }
915 
916             sb.append("\t\t<liferay-version>");
917             sb.append(liferayVersion);
918             sb.append("</liferay-version>\r\n");
919 
920             if ((i + 1) == liferayVersions.size()) {
921                 sb.append("\t");
922             }
923         }
924 
925         return sb.toString();
926     }
927 
928     protected Properties getPluginPackageProperties(File srcFile)
929         throws Exception {
930 
931         File propsFile = new File(
932             srcFile + "/WEB-INF/liferay-plugin-package.properties");
933 
934         if (!propsFile.exists()) {
935             return null;
936         }
937 
938         String propsString = FileUtil.read(propsFile);
939 
940         return PropertiesUtil.load(propsString);
941     }
942 
943     protected String getPluginPackageTagsXml(List<String> tags) {
944         StringBuilder sb = new StringBuilder();
945 
946         for (int i = 0; i < tags.size(); i++) {
947             String tag = tags.get(i);
948 
949             if (i == 0) {
950                 sb.append("\r\n");
951             }
952 
953             sb.append("\t\t<tag>");
954             sb.append(tag);
955             sb.append("</tag>\r\n");
956 
957             if ((i + 1) == tags.size()) {
958                 sb.append("\t");
959             }
960         }
961 
962         return sb.toString();
963     }
964 
965     protected void mergeDirectory(File mergeDir, File targetDir) {
966         if ((mergeDir == null) || (!mergeDir.exists())) {
967             return;
968         }
969 
970         CopyTask.copyDirectory(mergeDir, targetDir, null, null, true, false);
971     }
972 
973     protected void processPluginPackageProperties(
974             File srcFile, String displayName, PluginPackage pluginPackage)
975         throws Exception {
976     }
977 
978     protected PluginPackage readPluginPackage(File file) {
979         if (!file.exists()) {
980             return null;
981         }
982 
983         InputStream is = null;
984         ZipFile zipFile = null;
985 
986         try {
987             boolean parseProps = false;
988 
989             if (file.isDirectory()) {
990                 String path = file.getPath();
991 
992                 File pluginPackageXmlFile = new File(
993                     file.getParent() + "/merge/" + file.getName() +
994                         "/WEB-INF/liferay-plugin-package.xml");
995 
996                 if (pluginPackageXmlFile.exists()) {
997                     is = new FileInputStream(pluginPackageXmlFile);
998                 }
999                 else {
1000                    pluginPackageXmlFile = new File(
1001                        path + "/WEB-INF/liferay-plugin-package.xml");
1002
1003                    if (pluginPackageXmlFile.exists()) {
1004                        is = new FileInputStream(pluginPackageXmlFile);
1005                    }
1006                }
1007
1008                File pluginPackagePropsFile = new File(
1009                    file.getParent() + "/merge/" + file.getName() +
1010                        "/WEB-INF/liferay-plugin-package.properties");
1011
1012                if (pluginPackagePropsFile.exists()) {
1013                    is = new FileInputStream(pluginPackagePropsFile);
1014
1015                    parseProps = true;
1016                }
1017                else {
1018                    pluginPackagePropsFile = new File(
1019                        path + "/WEB-INF/liferay-plugin-package.properties");
1020
1021                    if (pluginPackagePropsFile.exists()) {
1022                        is = new FileInputStream(pluginPackagePropsFile);
1023
1024                        parseProps = true;
1025                    }
1026                }
1027            }
1028            else {
1029                zipFile = new ZipFile(file);
1030
1031                File pluginPackageXmlFile = new File(
1032                    file.getParent() + "/merge/" + file.getName() +
1033                        "/WEB-INF/liferay-plugin-package.xml");
1034
1035                if (pluginPackageXmlFile.exists()) {
1036                    is = new FileInputStream(pluginPackageXmlFile);
1037                }
1038                else {
1039                    ZipEntry zipEntry = zipFile.getEntry(
1040                        "WEB-INF/liferay-plugin-package.xml");
1041
1042                    if (zipEntry != null) {
1043                        is = zipFile.getInputStream(zipEntry);
1044                    }
1045                }
1046
1047                File pluginPackagePropsFile = new File(
1048                    file.getParent() + "/merge/" + file.getName() +
1049                        "/WEB-INF/liferay-plugin-package.properties");
1050
1051                if (pluginPackagePropsFile.exists()) {
1052                    is = new FileInputStream(pluginPackagePropsFile);
1053
1054                    parseProps = true;
1055                }
1056                else {
1057                    ZipEntry zipEntry = zipFile.getEntry(
1058                        "WEB-INF/liferay-plugin-package.properties");
1059
1060                    if (zipEntry != null) {
1061                        is = zipFile.getInputStream(zipEntry);
1062
1063                        parseProps = true;
1064                    }
1065                }
1066            }
1067
1068            if (is == null) {
1069                if (_log.isInfoEnabled()) {
1070                    _log.info(
1071                        file.getPath() + " does not have a " +
1072                            "WEB-INF/liferay-plugin-package.xml or " +
1073                                "WEB-INF/liferay-plugin-package.properties");
1074                }
1075
1076                return null;
1077            }
1078
1079            if (parseProps) {
1080                String displayName = getDisplayName(file);
1081
1082                String propsString = StringUtil.read(is);
1083
1084                Properties props = PropertiesUtil.load(propsString);
1085
1086                return PluginPackageUtil.readPluginPackageProps(
1087                    displayName, props);
1088            }
1089            else {
1090                String xml = StringUtil.read(is);
1091
1092                xml = XMLFormatter.fixProlog(xml);
1093
1094                return PluginPackageUtil.readPluginPackageXml(xml);
1095            }
1096        }
1097        catch (Exception e) {
1098            _log.error(file.getPath() + ": " + e.toString());
1099        }
1100        finally {
1101            if (is != null) {
1102                try {
1103                    is.close();
1104                }
1105                catch (IOException ioe) {
1106                }
1107            }
1108
1109            if (zipFile != null) {
1110                try {
1111                    zipFile.close();
1112                }
1113                catch (IOException ioe) {
1114                }
1115            }
1116        }
1117
1118        return null;
1119    }
1120
1121    protected void rewriteFiles(File srcDir) throws Exception {
1122        String[] files = FileUtil.listFiles(srcDir + "/WEB-INF/");
1123
1124        for (int i = 0; i < files.length; i++) {
1125            String fileName = GetterUtil.getString(
1126                FileUtil.getShortFileName(files[i]));
1127
1128            // LEP-6415
1129
1130            if (fileName.equalsIgnoreCase("mule-config.xml")) {
1131                continue;
1132            }
1133
1134            String ext = GetterUtil.getString(FileUtil.getExtension(files[i]));
1135
1136            if (!ext.equalsIgnoreCase("xml")) {
1137                continue;
1138            }
1139
1140            // Make sure to rewrite any XML files to include external entities
1141            // into same file. See LEP-3142.
1142
1143            File file = new File(srcDir + "/WEB-INF/" + files[i]);
1144
1145            try {
1146                Document doc = DocumentUtil.readDocumentFromFile(file);
1147
1148                String content = XMLFormatter.toString(
1149                    doc, XMLFormatter.INDENT, true);
1150
1151                FileUtil.write(file, content);
1152            }
1153            catch (Exception e) {
1154                if (_log.isWarnEnabled()) {
1155                    _log.warn(
1156                        "Unable to format " + file + ": " + e.getMessage());
1157                }
1158            }
1159        }
1160    }
1161
1162    protected void updateDeployDirectory(File srcFile) throws Exception {
1163    }
1164
1165    protected void updateGeronimoWebXml(
1166            File srcFile, String displayName, PluginPackage pluginPackage)
1167        throws Exception {
1168
1169        if (!appServerType.startsWith(ServerDetector.GERONIMO_ID)) {
1170            return;
1171        }
1172
1173        File geronimoWebXml = new File(srcFile + "/WEB-INF/geronimo-web.xml");
1174
1175        Document doc = DocumentUtil.readDocumentFromFile(geronimoWebXml);
1176
1177        Element root = doc.getRootElement();
1178
1179        Element environmentEl = root.element("environment");
1180
1181        Element moduleIdEl = environmentEl.element("moduleId");
1182
1183        Element artifactIdEl = moduleIdEl.element("artifactId");
1184
1185        String artifactIdText = GetterUtil.getString(artifactIdEl.getText());
1186
1187        if (!artifactIdText.equals(displayName)) {
1188            artifactIdEl.setText(displayName);
1189
1190            String content = XMLFormatter.toString(doc);
1191
1192            FileUtil.write(geronimoWebXml, content);
1193
1194            if (_log.isInfoEnabled()) {
1195                _log.info("Modifying Geronimo " + geronimoWebXml);
1196            }
1197        }
1198    }
1199
1200    protected void updateWebXml(
1201            File webXml, File srcFile, String displayName,
1202            PluginPackage pluginPackage)
1203        throws Exception {
1204
1205        String content = FileUtil.read(webXml);
1206
1207        int x = content.indexOf("<display-name>");
1208
1209        if (x != -1) {
1210            int y = content.indexOf("</display-name>", x);
1211
1212            y = content.indexOf(">", y) + 1;
1213
1214            content = content.substring(0, x) + content.substring(y);
1215        }
1216
1217        double webXmlVersion = 2.3;
1218
1219        Document webXmlDoc = DocumentUtil.readDocumentFromXML(content);
1220
1221        Element webXmlRoot = webXmlDoc.getRootElement();
1222
1223        webXmlVersion = GetterUtil.getDouble(
1224            webXmlRoot.attributeValue("version"), webXmlVersion);
1225
1226        // Merge extra content
1227
1228        String extraContent = getExtraContent(
1229            webXmlVersion, srcFile, displayName);
1230
1231        int pos = content.indexOf("</web-app>");
1232
1233        String newContent =
1234            content.substring(0, pos) + extraContent +
1235            content.substring(pos, content.length());
1236
1237        // Replace old package names
1238
1239        newContent = StringUtil.replace(
1240            newContent, "com.liferay.portal.shared.",
1241            "com.liferay.portal.kernel.");
1242
1243        newContent = WebXMLBuilder.organizeWebXML(newContent);
1244
1245        FileUtil.write(webXml, newContent, true);
1246
1247        if (_log.isInfoEnabled()) {
1248            _log.info("Modifying Servlet " + webXmlVersion + " " + webXml);
1249        }
1250    }
1251
1252    protected String baseDir;
1253    protected String destDir;
1254    protected String appServerType;
1255    protected String portletTaglibDTD;
1256    protected String portletExtTaglibDTD;
1257    protected String securityTaglibDTD;
1258    protected String themeTaglibDTD;
1259    protected String uiTaglibDTD;
1260    protected String utilTaglibDTD;
1261    protected boolean unpackWar;
1262    protected String jbossPrefix;
1263    protected String tomcatLibDir;
1264    protected List<String> wars;
1265    protected List<String> jars;
1266
1267    private static final String _PORTAL_CLASS_LOADER =
1268        "com.liferay.support.tomcat.loader.PortalClassLoader";
1269
1270    private static Log _log = LogFactory.getLog(BaseDeployer.class);
1271
1272}