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         boolean hasTaglib = false;
793 
794         if (Validator.isNotNull(portletTaglibDTD) ||
795             Validator.isNotNull(portletExtTaglibDTD) ||
796             Validator.isNotNull(securityTaglibDTD) ||
797             Validator.isNotNull(themeTaglibDTD) ||
798             Validator.isNotNull(uiTaglibDTD) ||
799             Validator.isNotNull(utilTaglibDTD)) {
800 
801             hasTaglib = true;
802         }
803 
804         if (hasTaglib && (webXmlVersion > 2.3)) {
805             sb.append("<jsp-config>");
806         }
807 
808         if (Validator.isNotNull(portletTaglibDTD)) {
809             sb.append("<taglib>");
810             sb.append(
811                 "<taglib-uri>http://java.sun.com/portlet_2_0</taglib-uri>");
812             sb.append("<taglib-location>");
813             sb.append("/WEB-INF/tld/liferay-portlet.tld");
814             sb.append("</taglib-location>");
815             sb.append("</taglib>");
816         }
817 
818         if (Validator.isNotNull(portletExtTaglibDTD)) {
819             sb.append("<taglib>");
820             sb.append("<taglib-uri>");
821             sb.append("http://liferay.com/tld/portlet");
822             sb.append("</taglib-uri>");
823             sb.append("<taglib-location>");
824             sb.append("/WEB-INF/tld/liferay-portlet-ext.tld");
825             sb.append("</taglib-location>");
826             sb.append("</taglib>");
827         }
828 
829         if (Validator.isNotNull(securityTaglibDTD)) {
830             sb.append("<taglib>");
831             sb.append("<taglib-uri>");
832             sb.append("http://liferay.com/tld/security");
833             sb.append("</taglib-uri>");
834             sb.append("<taglib-location>");
835             sb.append("/WEB-INF/tld/liferay-security.tld");
836             sb.append("</taglib-location>");
837             sb.append("</taglib>");
838         }
839 
840         if (Validator.isNotNull(themeTaglibDTD)) {
841             sb.append("<taglib>");
842             sb.append("<taglib-uri>http://liferay.com/tld/theme</taglib-uri>");
843             sb.append("<taglib-location>");
844             sb.append("/WEB-INF/tld/liferay-theme.tld");
845             sb.append("</taglib-location>");
846             sb.append("</taglib>");
847         }
848 
849         if (Validator.isNotNull(uiTaglibDTD)) {
850             sb.append("<taglib>");
851             sb.append("<taglib-uri>http://liferay.com/tld/ui</taglib-uri>");
852             sb.append("<taglib-location>");
853             sb.append("/WEB-INF/tld/liferay-ui.tld");
854             sb.append("</taglib-location>");
855             sb.append("</taglib>");
856         }
857 
858         if (Validator.isNotNull(utilTaglibDTD)) {
859             sb.append("<taglib>");
860             sb.append("<taglib-uri>http://liferay.com/tld/util</taglib-uri>");
861             sb.append("<taglib-location>");
862             sb.append("/WEB-INF/tld/liferay-util.tld");
863             sb.append("</taglib-location>");
864             sb.append("</taglib>");
865         }
866 
867         if (hasTaglib && (webXmlVersion > 2.3)) {
868             sb.append("</jsp-config>");
869         }
870 
871         return sb.toString();
872     }
873 
874     protected String getPluginPackageLicensesXml(List<License> licenses) {
875         StringBuilder sb = new StringBuilder();
876 
877         for (int i = 0; i < licenses.size(); i++) {
878             License license = licenses.get(i);
879 
880             if (i == 0) {
881                 sb.append("\r\n");
882             }
883 
884             sb.append("\t\t<license osi-approved=\"");
885             sb.append(license.isOsiApproved());
886             sb.append("\">");
887             sb.append(license.getName());
888             sb.append("</license>\r\n");
889 
890             if ((i + 1) == licenses.size()) {
891                 sb.append("\t");
892             }
893         }
894 
895         return sb.toString();
896     }
897 
898     protected String getPluginPackageLiferayVersionsXml(
899         List<String> liferayVersions) {
900 
901         StringBuilder sb = new StringBuilder();
902 
903         for (int i = 0; i < liferayVersions.size(); i++) {
904             String liferayVersion = liferayVersions.get(i);
905 
906             if (i == 0) {
907                 sb.append("\r\n");
908             }
909 
910             sb.append("\t\t<liferay-version>");
911             sb.append(liferayVersion);
912             sb.append("</liferay-version>\r\n");
913 
914             if ((i + 1) == liferayVersions.size()) {
915                 sb.append("\t");
916             }
917         }
918 
919         return sb.toString();
920     }
921 
922     protected Properties getPluginPackageProperties(File srcFile)
923         throws Exception {
924 
925         File propsFile = new File(
926             srcFile + "/WEB-INF/liferay-plugin-package.properties");
927 
928         if (!propsFile.exists()) {
929             return null;
930         }
931 
932         String propsString = FileUtil.read(propsFile);
933 
934         return PropertiesUtil.load(propsString);
935     }
936 
937     protected String getPluginPackageTagsXml(List<String> tags) {
938         StringBuilder sb = new StringBuilder();
939 
940         for (int i = 0; i < tags.size(); i++) {
941             String tag = tags.get(i);
942 
943             if (i == 0) {
944                 sb.append("\r\n");
945             }
946 
947             sb.append("\t\t<tag>");
948             sb.append(tag);
949             sb.append("</tag>\r\n");
950 
951             if ((i + 1) == tags.size()) {
952                 sb.append("\t");
953             }
954         }
955 
956         return sb.toString();
957     }
958 
959     protected void mergeDirectory(File mergeDir, File targetDir) {
960         if ((mergeDir == null) || (!mergeDir.exists())) {
961             return;
962         }
963 
964         CopyTask.copyDirectory(mergeDir, targetDir, null, null, true, false);
965     }
966 
967     protected void processPluginPackageProperties(
968             File srcFile, String displayName, PluginPackage pluginPackage)
969         throws Exception {
970     }
971 
972     protected PluginPackage readPluginPackage(File file) {
973         if (!file.exists()) {
974             return null;
975         }
976 
977         InputStream is = null;
978         ZipFile zipFile = null;
979 
980         try {
981             boolean parseProps = false;
982 
983             if (file.isDirectory()) {
984                 String path = file.getPath();
985 
986                 File pluginPackageXmlFile = new File(
987                     file.getParent() + "/merge/" + file.getName() +
988                         "/WEB-INF/liferay-plugin-package.xml");
989 
990                 if (pluginPackageXmlFile.exists()) {
991                     is = new FileInputStream(pluginPackageXmlFile);
992                 }
993                 else {
994                     pluginPackageXmlFile = new File(
995                         path + "/WEB-INF/liferay-plugin-package.xml");
996 
997                     if (pluginPackageXmlFile.exists()) {
998                         is = new FileInputStream(pluginPackageXmlFile);
999                     }
1000                }
1001
1002                File pluginPackagePropsFile = new File(
1003                    file.getParent() + "/merge/" + file.getName() +
1004                        "/WEB-INF/liferay-plugin-package.properties");
1005
1006                if (pluginPackagePropsFile.exists()) {
1007                    is = new FileInputStream(pluginPackagePropsFile);
1008
1009                    parseProps = true;
1010                }
1011                else {
1012                    pluginPackagePropsFile = new File(
1013                        path + "/WEB-INF/liferay-plugin-package.properties");
1014
1015                    if (pluginPackagePropsFile.exists()) {
1016                        is = new FileInputStream(pluginPackagePropsFile);
1017
1018                        parseProps = true;
1019                    }
1020                }
1021            }
1022            else {
1023                zipFile = new ZipFile(file);
1024
1025                File pluginPackageXmlFile = new File(
1026                    file.getParent() + "/merge/" + file.getName() +
1027                        "/WEB-INF/liferay-plugin-package.xml");
1028
1029                if (pluginPackageXmlFile.exists()) {
1030                    is = new FileInputStream(pluginPackageXmlFile);
1031                }
1032                else {
1033                    ZipEntry zipEntry = zipFile.getEntry(
1034                        "WEB-INF/liferay-plugin-package.xml");
1035
1036                    if (zipEntry != null) {
1037                        is = zipFile.getInputStream(zipEntry);
1038                    }
1039                }
1040
1041                File pluginPackagePropsFile = new File(
1042                    file.getParent() + "/merge/" + file.getName() +
1043                        "/WEB-INF/liferay-plugin-package.properties");
1044
1045                if (pluginPackagePropsFile.exists()) {
1046                    is = new FileInputStream(pluginPackagePropsFile);
1047
1048                    parseProps = true;
1049                }
1050                else {
1051                    ZipEntry zipEntry = zipFile.getEntry(
1052                        "WEB-INF/liferay-plugin-package.properties");
1053
1054                    if (zipEntry != null) {
1055                        is = zipFile.getInputStream(zipEntry);
1056
1057                        parseProps = true;
1058                    }
1059                }
1060            }
1061
1062            if (is == null) {
1063                if (_log.isInfoEnabled()) {
1064                    _log.info(
1065                        file.getPath() + " does not have a " +
1066                            "WEB-INF/liferay-plugin-package.xml or " +
1067                                "WEB-INF/liferay-plugin-package.properties");
1068                }
1069
1070                return null;
1071            }
1072
1073            if (parseProps) {
1074                String displayName = getDisplayName(file);
1075
1076                String propsString = StringUtil.read(is);
1077
1078                Properties props = PropertiesUtil.load(propsString);
1079
1080                return PluginPackageUtil.readPluginPackageProps(
1081                    displayName, props);
1082            }
1083            else {
1084                String xml = StringUtil.read(is);
1085
1086                xml = XMLFormatter.fixProlog(xml);
1087
1088                return PluginPackageUtil.readPluginPackageXml(xml);
1089            }
1090        }
1091        catch (Exception e) {
1092            _log.error(file.getPath() + ": " + e.toString());
1093        }
1094        finally {
1095            if (is != null) {
1096                try {
1097                    is.close();
1098                }
1099                catch (IOException ioe) {
1100                }
1101            }
1102
1103            if (zipFile != null) {
1104                try {
1105                    zipFile.close();
1106                }
1107                catch (IOException ioe) {
1108                }
1109            }
1110        }
1111
1112        return null;
1113    }
1114
1115    protected void rewriteFiles(File srcDir) throws Exception {
1116        String[] files = FileUtil.listFiles(srcDir + "/WEB-INF/");
1117
1118        for (int i = 0; i < files.length; i++) {
1119            String fileName = GetterUtil.getString(
1120                FileUtil.getShortFileName(files[i]));
1121
1122            // LEP-6415
1123
1124            if (fileName.equalsIgnoreCase("mule-config.xml")) {
1125                continue;
1126            }
1127
1128            String ext = GetterUtil.getString(FileUtil.getExtension(files[i]));
1129
1130            if (!ext.equalsIgnoreCase("xml")) {
1131                continue;
1132            }
1133
1134            // Make sure to rewrite any XML files to include external entities
1135            // into same file. See LEP-3142.
1136
1137            File file = new File(srcDir + "/WEB-INF/" + files[i]);
1138
1139            try {
1140                Document doc = DocumentUtil.readDocumentFromFile(file);
1141
1142                String content = XMLFormatter.toString(
1143                    doc, XMLFormatter.INDENT, true);
1144
1145                FileUtil.write(file, content);
1146            }
1147            catch (Exception e) {
1148                if (_log.isWarnEnabled()) {
1149                    _log.warn(
1150                        "Unable to format " + file + ": " + e.getMessage());
1151                }
1152            }
1153        }
1154    }
1155
1156    protected void updateDeployDirectory(File srcFile) throws Exception {
1157    }
1158
1159    protected void updateGeronimoWebXml(
1160            File srcFile, String displayName, PluginPackage pluginPackage)
1161        throws Exception {
1162
1163        if (!appServerType.startsWith(ServerDetector.GERONIMO_ID)) {
1164            return;
1165        }
1166
1167        File geronimoWebXml = new File(srcFile + "/WEB-INF/geronimo-web.xml");
1168
1169        Document doc = DocumentUtil.readDocumentFromFile(geronimoWebXml);
1170
1171        Element root = doc.getRootElement();
1172
1173        Element environmentEl = root.element("environment");
1174
1175        Element moduleIdEl = environmentEl.element("moduleId");
1176
1177        Element artifactIdEl = moduleIdEl.element("artifactId");
1178
1179        String artifactIdText = GetterUtil.getString(artifactIdEl.getText());
1180
1181        if (!artifactIdText.equals(displayName)) {
1182            artifactIdEl.setText(displayName);
1183
1184            String content = XMLFormatter.toString(doc);
1185
1186            FileUtil.write(geronimoWebXml, content);
1187
1188            if (_log.isInfoEnabled()) {
1189                _log.info("Modifying Geronimo " + geronimoWebXml);
1190            }
1191        }
1192    }
1193
1194    protected void updateWebXml(
1195            File webXml, File srcFile, String displayName,
1196            PluginPackage pluginPackage)
1197        throws Exception {
1198
1199        String content = FileUtil.read(webXml);
1200
1201        int x = content.indexOf("<display-name>");
1202
1203        if (x != -1) {
1204            int y = content.indexOf("</display-name>", x);
1205
1206            y = content.indexOf(">", y) + 1;
1207
1208            content = content.substring(0, x) + content.substring(y);
1209        }
1210
1211        double webXmlVersion = 2.3;
1212
1213        Document webXmlDoc = DocumentUtil.readDocumentFromXML(content);
1214
1215        Element webXmlRoot = webXmlDoc.getRootElement();
1216
1217        webXmlVersion = GetterUtil.getDouble(
1218            webXmlRoot.attributeValue("version"), webXmlVersion);
1219
1220        // Merge extra content
1221
1222        String extraContent = getExtraContent(
1223            webXmlVersion, srcFile, displayName);
1224
1225        int pos = content.indexOf("</web-app>");
1226
1227        String newContent =
1228            content.substring(0, pos) + extraContent +
1229            content.substring(pos, content.length());
1230
1231        // Replace old package names
1232
1233        newContent = StringUtil.replace(
1234            newContent, "com.liferay.portal.shared.",
1235            "com.liferay.portal.kernel.");
1236
1237        newContent = WebXMLBuilder.organizeWebXML(newContent);
1238
1239        FileUtil.write(webXml, newContent, true);
1240
1241        if (_log.isInfoEnabled()) {
1242            _log.info("Modifying Servlet " + webXmlVersion + " " + webXml);
1243        }
1244    }
1245
1246    protected String baseDir;
1247    protected String destDir;
1248    protected String appServerType;
1249    protected String portletTaglibDTD;
1250    protected String portletExtTaglibDTD;
1251    protected String securityTaglibDTD;
1252    protected String themeTaglibDTD;
1253    protected String uiTaglibDTD;
1254    protected String utilTaglibDTD;
1255    protected boolean unpackWar;
1256    protected String jbossPrefix;
1257    protected String tomcatLibDir;
1258    protected List<String> wars;
1259    protected List<String> jars;
1260
1261    private static final String _PORTAL_CLASS_LOADER =
1262        "com.liferay.support.tomcat.loader.PortalClassLoader";
1263
1264    private static Log _log = LogFactory.getLog(BaseDeployer.class);
1265
1266}