001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: package org.apache.jetspeed.tools.deploy;
018:
019: import java.io.FileInputStream;
020: import java.io.FileOutputStream;
021: import java.io.IOException;
022: import java.io.InputStream;
023: import java.io.File;
024: import java.nio.channels.FileChannel;
025: import java.util.Enumeration;
026: import java.util.jar.JarFile;
027: import java.util.jar.JarOutputStream;
028: import java.util.zip.ZipEntry;
029:
030: import org.jdom.Document;
031: import org.jdom.input.SAXBuilder;
032: import org.jdom.output.Format;
033: import org.jdom.output.XMLOutputter;
034: import org.xml.sax.EntityResolver;
035: import org.xml.sax.InputSource;
036: import org.xml.sax.SAXException;
037:
038: /**
039: * Makes a web application Deploy-ready for Jetspeed.
040: *
041: * @author <a href="mailto:taylor@apache.org">Dain Sundstrom </a>
042: * @author <a href="mailto:dsundstrom@gluecode.com">David Sean Taylor </a>
043: * @version $Id: JetspeedDeploy.java 545097 2007-06-07 08:11:56Z ate $
044: */
045: public class JetspeedDeploy implements Deploy {
046: public static void main(String[] args) throws Exception {
047: if (args.length < 2) {
048: printUsage();
049: System.exit(1);
050: return;
051: }
052:
053: boolean stripLoggers = false;
054: String version = null;
055: for (int i = 0; i < args.length - 2; i++) {
056: String option = args[i];
057: if (option.equals("-s")) {
058: stripLoggers = true;
059: } else if (option.equals("-v") && i < args.length - 3) {
060: version = args[i + 1];
061: i++;
062: } else {
063: // invalid option
064: printUsage();
065: System.exit(1);
066: return;
067: }
068: }
069:
070: new JetspeedDeploy(args[args.length - 2],
071: args[args.length - 1], stripLoggers, version);
072: }
073:
074: private static void printUsage() {
075: System.out
076: .println("Usage: java -jar jetspeed-deploy-tools-<version>.jar [options] INPUT OUTPUT");
077: System.out.println("Options:");
078: System.out
079: .println(" -s: stripLoggers - remove commons-logging[version].jar and/or log4j[version].jar from war");
080: System.out
081: .println(" (required when targetting application servers like JBoss)");
082: System.out
083: .println(" -v VERSION: force servlet specification version to handle web.xml");
084: System.out
085: .println(" (default will automatically determine version)");
086: }
087:
088: private final byte[] buffer = new byte[4096];
089:
090: public JetspeedDeploy(String inputName, String outputName,
091: boolean stripLoggers) throws Exception {
092: this (inputName, outputName, stripLoggers, null);
093: }
094:
095: public JetspeedDeploy(String inputName, String outputName,
096: boolean stripLoggers, String forcedVersion)
097: throws Exception {
098: File tempFile = null;
099: JarFile jin = null;
100: JarOutputStream jout = null;
101: FileChannel srcChannel = null;
102: FileChannel dstChannel = null;
103:
104: try {
105: String portletApplicationName = getPortletApplicationName(outputName);
106: tempFile = File.createTempFile(portletApplicationName, "");
107: tempFile.deleteOnExit();
108:
109: jin = new JarFile(inputName);
110: jout = new JarOutputStream(new FileOutputStream(tempFile));
111:
112: // copy over all of the files in the input war to the output
113: // war except for web.xml, portlet.xml, and context.xml which
114: // we parse for use later
115: Document webXml = null;
116: Document portletXml = null;
117: Document contextXml = null;
118: boolean taglibFound = false;
119: ZipEntry src;
120: InputStream source;
121: Enumeration zipEntries = jin.entries();
122: while (zipEntries.hasMoreElements()) {
123: src = (ZipEntry) zipEntries.nextElement();
124: source = jin.getInputStream(src);
125: try {
126: String target = src.getName();
127: if ("WEB-INF/web.xml".equals(target)) {
128: System.out.println("Found web.xml");
129: webXml = parseXml(source);
130: } else if ("WEB-INF/portlet.xml".equals(target)) {
131: System.out.println("Found WEB-INF/portlet.xml");
132: portletXml = parseXml(source);
133: } else if ("META-INF/context.xml".equals(target)) {
134: System.out
135: .println("Found META-INF/context.xml");
136: contextXml = parseXml(source);
137: } else {
138: if (stripLoggers
139: && target.endsWith(".jar")
140: && (target
141: .startsWith("WEB-INF/lib/commons-logging") || target
142: .startsWith("WEB-INF/lib/log4j"))) {
143: System.out.println("Stripping logger "
144: + target);
145: continue;
146: } else if ("WEB-INF/tld/portlet.tld"
147: .equals(target)) {
148: System.out
149: .println("Warning: WEB-INF/tld/portlet.tld already provided, won't be replaced.");
150: taglibFound = true;
151: }
152: addFile(target, source, jout);
153: }
154: } finally {
155: source.close();
156: }
157: }
158:
159: if (webXml == null) {
160: throw new IllegalArgumentException("WEB-INF/web.xml");
161: }
162: if (portletXml == null) {
163: throw new IllegalArgumentException(
164: "WEB-INF/portlet.xml");
165: }
166:
167: JetspeedWebApplicationRewriterFactory webRewriterFactory = new JetspeedWebApplicationRewriterFactory();
168: JetspeedWebApplicationRewriter webRewriter = webRewriterFactory
169: .getInstance(webXml, portletApplicationName,
170: forcedVersion);
171: webRewriter.processWebXML();
172: JetspeedContextRewriter contextRewriter = new JetspeedContextRewriter(
173: contextXml, portletApplicationName);
174: contextRewriter.processContextXML();
175:
176: // write the web.xml, portlet.xml, and context.xml files
177: addFile("WEB-INF/web.xml", webXml, jout);
178: addFile("WEB-INF/portlet.xml", portletXml, jout);
179: addFile("META-INF/context.xml", contextXml, jout);
180:
181: if (!taglibFound) {
182: System.out
183: .println("Attempting to add portlet.tld to war...");
184: InputStream is = this
185: .getClass()
186: .getResourceAsStream(
187: "/org/apache/jetspeed/tools/deploy/portlet.tld");
188: if (is == null) {
189: System.out
190: .println("Failed to find portlet.tld in classpath");
191: } else {
192: System.out.println("Adding portlet.tld to war...");
193:
194: try {
195: addFile("WEB-INF/tld/portlet.tld", is, jout);
196: } finally {
197: is.close();
198: }
199: }
200: }
201:
202: jout.close();
203: jin.close();
204: jin = null;
205: jout = null;
206:
207: System.out.println("Creating war " + outputName + " ...");
208: System.out.flush();
209: // Now copy the new war to its destination
210: srcChannel = new FileInputStream(tempFile).getChannel();
211: dstChannel = new FileOutputStream(outputName).getChannel();
212: dstChannel.transferFrom(srcChannel, 0, srcChannel.size());
213: srcChannel.close();
214: srcChannel = null;
215: dstChannel.close();
216: dstChannel = null;
217: tempFile.delete();
218: tempFile = null;
219: System.out.println("War " + outputName + " created");
220: System.out.flush();
221: } finally {
222: if (srcChannel != null && srcChannel.isOpen()) {
223: try {
224: srcChannel.close();
225: } catch (IOException e1) {
226: // ignore
227: }
228: }
229: if (dstChannel != null && dstChannel.isOpen()) {
230: try {
231: dstChannel.close();
232: } catch (IOException e1) {
233: // ignore
234: }
235: }
236: if (jin != null) {
237: try {
238: jin.close();
239: jin = null;
240: } catch (IOException e1) {
241: // ignore
242: }
243: }
244: if (jout != null) {
245: try {
246: jout.close();
247: jout = null;
248: } catch (IOException e1) {
249: // ignore
250: }
251: }
252: if (tempFile != null && tempFile.exists()) {
253: tempFile.delete();
254: }
255: }
256: }
257:
258: protected Document parseXml(InputStream source) throws Exception {
259: // Parse using the local dtds instead of remote dtds. This
260: // allows to deploy the application offline
261: SAXBuilder saxBuilder = new SAXBuilder();
262: saxBuilder.setEntityResolver(new EntityResolver() {
263: public InputSource resolveEntity(java.lang.String publicId,
264: java.lang.String systemId) throws SAXException,
265: java.io.IOException {
266: if (systemId
267: .equals("http://java.sun.com/dtd/web-app_2_3.dtd")) {
268: return new InputSource(getClass()
269: .getResourceAsStream("web-app_2_3.dtd"));
270: }
271: return null;
272: }
273: });
274: Document document = saxBuilder.build(source);
275: return document;
276: }
277:
278: protected void addFile(String path, InputStream source,
279: JarOutputStream jos) throws IOException {
280: jos.putNextEntry(new ZipEntry(path));
281: try {
282: int count;
283: while ((count = source.read(buffer)) > 0) {
284: jos.write(buffer, 0, count);
285: }
286: } finally {
287: jos.closeEntry();
288: }
289: }
290:
291: protected void addFile(String path, Document source,
292: JarOutputStream jos) throws IOException {
293: if (source != null) {
294: jos.putNextEntry(new ZipEntry(path));
295: XMLOutputter xmlOutputter = new XMLOutputter(Format
296: .getPrettyFormat());
297: try {
298: xmlOutputter.output(source, jos);
299: } finally {
300: jos.closeEntry();
301: }
302: }
303: }
304:
305: protected String getPortletApplicationName(String path) {
306: File file = new File(path);
307: String name = file.getName();
308: String portletApplicationName = name;
309:
310: int index = name.lastIndexOf("-infused.war");
311: if (index > -1) {
312: portletApplicationName = name.substring(0, index);
313: } else {
314: index = name.lastIndexOf(".");
315: if (index > -1) {
316: portletApplicationName = name.substring(0, index);
317: }
318: }
319: return portletApplicationName;
320: }
321: }
|