View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    * 
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   * 
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.jetspeed.tools.deploy;
18  
19  import java.io.FileInputStream;
20  import java.io.FileOutputStream;
21  import java.io.IOException;
22  import java.io.InputStream;
23  import java.io.File;
24  import java.nio.channels.FileChannel;
25  import java.util.Enumeration;
26  import java.util.jar.JarFile;
27  import java.util.jar.JarOutputStream;
28  import java.util.zip.ZipEntry;
29  
30  import org.jdom.Document;
31  import org.jdom.input.SAXBuilder;
32  import org.jdom.output.Format;
33  import org.jdom.output.XMLOutputter;
34  import org.xml.sax.EntityResolver;
35  import org.xml.sax.InputSource;
36  import org.xml.sax.SAXException;
37  
38  /***
39   * Makes a web application Deploy-ready for Jetspeed.
40   * 
41   * @author <a href="mailto:taylor@apache.org">Dain Sundstrom </a>
42   * @author <a href="mailto:dsundstrom@gluecode.com">David Sean Taylor </a>
43   * @version $Id: JetspeedDeploy.java 545097 2007-06-07 08:11:56Z ate $
44   */
45  public class JetspeedDeploy implements Deploy
46  {
47      public static void main(String[] args) throws Exception
48      {
49          if (args.length < 2)
50          {
51              printUsage();
52              System.exit(1);
53              return;
54          }
55          
56          boolean stripLoggers = false;
57          String version = null;
58          for(int i = 0; i < args.length-2; i++) {
59              String option = args[i];
60              if(option.equals("-s")) {
61                  stripLoggers = true;
62              } else if(option.equals("-v") && i < args.length-3) {
63                  version = args[i+1];
64                  i++;
65              } else {
66                  // invalid option
67                  printUsage();
68                  System.exit(1);
69                  return;
70              }
71          }
72          
73          new JetspeedDeploy(args[args.length-2], args[args.length-1], stripLoggers, version);
74      }
75      
76      private static void printUsage() {
77          System.out.println("Usage: java -jar jetspeed-deploy-tools-<version>.jar [options] INPUT OUTPUT");
78          System.out.println("Options:");
79          System.out.println("  -s: stripLoggers - remove commons-logging[version].jar and/or log4j[version].jar from war");
80          System.out.println("                     (required when targetting application servers like JBoss)");
81          System.out.println("  -v VERSION: force servlet specification version to handle web.xml");
82          System.out.println("                     (default will automatically determine version)");
83      }
84  
85      private final byte[] buffer = new byte[4096];
86  
87      public JetspeedDeploy(String inputName, String outputName, boolean stripLoggers) throws Exception {
88          this(inputName, outputName, stripLoggers, null);
89      }
90      
91      public JetspeedDeploy(String inputName, String outputName, boolean stripLoggers, String forcedVersion) throws Exception
92      {
93          File tempFile = null;
94          JarFile jin = null;
95          JarOutputStream jout = null;
96          FileChannel srcChannel = null;
97          FileChannel dstChannel = null;
98  
99          try
100         {
101             String portletApplicationName = getPortletApplicationName(outputName);
102             tempFile = File.createTempFile(portletApplicationName, "");
103             tempFile.deleteOnExit();
104 
105             jin = new JarFile(inputName);
106             jout = new JarOutputStream(new FileOutputStream(tempFile));
107 
108             // copy over all of the files in the input war to the output
109             // war except for web.xml, portlet.xml, and context.xml which
110             // we parse for use later
111             Document webXml = null;
112             Document portletXml = null;
113             Document contextXml = null;
114             boolean taglibFound = false;
115             ZipEntry src;
116             InputStream source;
117             Enumeration zipEntries = jin.entries();
118             while (zipEntries.hasMoreElements())
119             {
120                 src = (ZipEntry) zipEntries.nextElement();
121                 source = jin.getInputStream(src);
122                 try
123                 {
124                     String target = src.getName();
125                     if ("WEB-INF/web.xml".equals(target))
126                     {
127                         System.out.println("Found web.xml");
128                         webXml = parseXml(source);
129                     }
130                     else if ("WEB-INF/portlet.xml".equals(target))
131                     {
132                         System.out.println("Found WEB-INF/portlet.xml");
133                         portletXml = parseXml(source);
134                     }
135                     else if ("META-INF/context.xml".equals(target))
136                     {
137                         System.out.println("Found META-INF/context.xml");
138                         contextXml = parseXml(source);
139                     }
140                     else
141                     {
142                         if ( stripLoggers && target.endsWith(".jar") &&
143                              (target.startsWith("WEB-INF/lib/commons-logging") || target.startsWith("WEB-INF/lib/log4j")))
144                         {
145                             System.out.println("Stripping logger "+target);
146                             continue;
147                         }
148                         else if ("WEB-INF/tld/portlet.tld".equals(target))
149                         {
150                             System.out.println("Warning: WEB-INF/tld/portlet.tld already provided, won't be replaced.");
151                             taglibFound = true;
152                         }
153                         addFile(target, source, jout);
154                     }
155                 }
156                 finally
157                 {
158                     source.close();
159                 }
160             }
161 
162             if (webXml == null)
163             {
164                 throw new IllegalArgumentException("WEB-INF/web.xml");
165             }
166             if (portletXml == null)
167             {
168                 throw new IllegalArgumentException("WEB-INF/portlet.xml");
169             }
170             
171             JetspeedWebApplicationRewriterFactory webRewriterFactory = new JetspeedWebApplicationRewriterFactory();
172             JetspeedWebApplicationRewriter webRewriter = webRewriterFactory.getInstance(
173                     webXml,
174                     portletApplicationName,
175                     forcedVersion);
176             webRewriter.processWebXML();
177             JetspeedContextRewriter contextRewriter = new JetspeedContextRewriter(contextXml, portletApplicationName);
178             contextRewriter.processContextXML();
179 
180             // write the web.xml, portlet.xml, and context.xml files
181             addFile("WEB-INF/web.xml", webXml, jout);
182             addFile("WEB-INF/portlet.xml", portletXml, jout);
183             addFile("META-INF/context.xml", contextXml, jout);
184 
185             if (!taglibFound)
186             {
187                 System.out.println("Attempting to add portlet.tld to war...");
188                 InputStream is = this.getClass().getResourceAsStream("/org/apache/jetspeed/tools/deploy/portlet.tld");
189                 if (is == null)
190                 {
191                     System.out.println("Failed to find portlet.tld in classpath");
192                 }
193                 else
194                 {
195                     System.out.println("Adding portlet.tld to war...");
196 
197                     try
198                     {
199                         addFile("WEB-INF/tld/portlet.tld", is, jout);
200                     }
201                     finally
202                     {
203                         is.close();
204                     }
205                 }
206             }
207 
208             jout.close();
209             jin.close();
210             jin = null;
211             jout = null;
212 
213             System.out.println("Creating war " + outputName + " ...");
214             System.out.flush();
215             // Now copy the new war to its destination
216             srcChannel = new FileInputStream(tempFile).getChannel();
217             dstChannel = new FileOutputStream(outputName).getChannel();
218             dstChannel.transferFrom(srcChannel, 0, srcChannel.size());
219             srcChannel.close();
220             srcChannel = null;
221             dstChannel.close();
222             dstChannel = null;
223             tempFile.delete();
224             tempFile = null;
225             System.out.println("War " + outputName + " created");
226             System.out.flush();
227         }
228         finally
229         {
230             if (srcChannel != null && srcChannel.isOpen())
231             {
232                 try
233                 {
234                     srcChannel.close();
235                 }
236                 catch (IOException e1)
237                 {
238                     // ignore
239                 }
240             }
241             if (dstChannel != null && dstChannel.isOpen())
242             {
243                 try
244                 {
245                     dstChannel.close();
246                 }
247                 catch (IOException e1)
248                 {
249                     // ignore
250                 }
251             }
252             if (jin != null)
253             {
254                 try
255                 {
256                     jin.close();
257                     jin = null;
258                 }
259                 catch (IOException e1)
260                 {
261                     // ignore
262                 }
263             }
264             if (jout != null)
265             {
266                 try
267                 {
268                     jout.close();
269                     jout = null;
270                 }
271                 catch (IOException e1)
272                 {
273                     // ignore
274                 }
275             }
276             if (tempFile != null && tempFile.exists())
277             {
278                 tempFile.delete();
279             }
280         }
281     }
282 
283     protected Document parseXml(InputStream source) throws Exception
284     {
285         // Parse using the local dtds instead of remote dtds. This
286         // allows to deploy the application offline
287         SAXBuilder saxBuilder = new SAXBuilder();
288         saxBuilder.setEntityResolver(new EntityResolver()
289         {
290             public InputSource resolveEntity(java.lang.String publicId, java.lang.String systemId) throws SAXException,
291                             java.io.IOException
292             {
293                 if (systemId.equals("http://java.sun.com/dtd/web-app_2_3.dtd"))
294                 {
295                     return new InputSource(getClass().getResourceAsStream("web-app_2_3.dtd"));
296                 }
297                 return null;
298             }
299         });
300         Document document = saxBuilder.build(source);
301         return document;
302     }
303 
304     protected void addFile(String path, InputStream source, JarOutputStream jos) throws IOException
305     {
306         jos.putNextEntry(new ZipEntry(path));
307         try
308         {
309             int count;
310             while ((count = source.read(buffer)) > 0)
311             {
312                 jos.write(buffer, 0, count);
313             }
314         }
315         finally
316         {
317             jos.closeEntry();
318         }
319     }
320 
321     protected void addFile(String path, Document source, JarOutputStream jos) throws IOException
322     {
323         if (source != null)
324         {
325             jos.putNextEntry(new ZipEntry(path));
326             XMLOutputter xmlOutputter = new XMLOutputter(Format.getPrettyFormat());
327             try
328             {
329                 xmlOutputter.output(source, jos);
330             }
331             finally
332             {
333                 jos.closeEntry();
334             }
335         }
336     }
337 
338     protected String getPortletApplicationName(String path)
339     {
340         File file = new File(path);
341         String name = file.getName();
342         String portletApplicationName = name;
343 
344         int index = name.lastIndexOf("-infused.war");
345         if (index > -1)
346         {
347             portletApplicationName = name.substring(0, index);            
348         }
349         else
350         {
351             index = name.lastIndexOf(".");
352             if (index > -1)
353             {
354                 portletApplicationName = name.substring(0, index);
355             }            
356         }
357         return portletApplicationName;
358     }
359 }