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.pluto.util.assemble.io;
018:
019: import java.io.ByteArrayInputStream;
020: import java.io.ByteArrayOutputStream;
021: import java.io.FileNotFoundException;
022: import java.io.IOException;
023: import java.io.InputStream;
024: import java.util.jar.JarEntry;
025: import java.util.jar.JarInputStream;
026: import java.util.jar.JarOutputStream;
027: import java.util.zip.CRC32;
028: import java.util.zip.ZipEntry;
029:
030: import org.apache.commons.io.IOUtils;
031: import org.apache.commons.logging.Log;
032: import org.apache.commons.logging.LogFactory;
033: import org.apache.pluto.util.assemble.Assembler;
034:
035: /**
036: * Utility class responsible for accepting a JarInputStream representing a web application archive,
037: * iterating over each JarEntry in the input stream and assembling the WAR web.xml for portlet
038: * deployment.
039: */
040: public class JarStreamingAssembly {
041:
042: private static final Log LOG = LogFactory
043: .getLog(JarStreamingAssembly.class);
044:
045: /**
046: * Reads the source JarInputStream, copying entries to the destination JarOutputStream.
047: * The web.xml and portlet.xml are cached, and after the entire archive is copied
048: * (minus the web.xml) a re-written web.xml is generated and written to the
049: * destination JAR.
050: *
051: * @param source the WAR source input stream
052: * @param dest the WAR destination output stream
053: * @param dispatchServletClass the name of the dispatch class
054: * @throws IOException
055: */
056: public static void assembleStream(JarInputStream source,
057: JarOutputStream dest, String dispatchServletClass)
058: throws IOException {
059:
060: try {
061: //Need to buffer the web.xml and portlet.xml files for the rewritting
062: JarEntry servletXmlEntry = null;
063: byte[] servletXmlBuffer = null;
064: byte[] portletXmlBuffer = null;
065:
066: JarEntry originalJarEntry;
067:
068: //Read the source archive entry by entry
069: while ((originalJarEntry = source.getNextJarEntry()) != null) {
070:
071: final JarEntry newJarEntry = smartClone(originalJarEntry);
072: originalJarEntry = null;
073:
074: //Capture the web.xml JarEntry and contents as a byte[], don't write it out now or
075: //update the CRC or length of the destEntry.
076: if (Assembler.SERVLET_XML.equals(newJarEntry.getName())) {
077: servletXmlEntry = newJarEntry;
078: servletXmlBuffer = IOUtils.toByteArray(source);
079: }
080:
081: //Capture the portlet.xml contents as a byte[]
082: else if (Assembler.PORTLET_XML.equals(newJarEntry
083: .getName())) {
084: portletXmlBuffer = IOUtils.toByteArray(source);
085: dest.putNextEntry(newJarEntry);
086: IOUtils.write(portletXmlBuffer, dest);
087: }
088:
089: //Copy all other entries directly to the output archive
090: else {
091: dest.putNextEntry(newJarEntry);
092: IOUtils.copy(source, dest);
093: }
094:
095: dest.closeEntry();
096: dest.flush();
097:
098: }
099:
100: // If no portlet.xml was found in the archive, skip the assembly step.
101: if (portletXmlBuffer != null) {
102: // container for assembled web.xml bytes
103: final byte[] webXmlBytes;
104:
105: // Checks to make sure the web.xml was found in the archive
106: if (servletXmlBuffer == null) {
107: throw new FileNotFoundException(
108: "File '"
109: + Assembler.SERVLET_XML
110: + "' could not be found in the source input stream.");
111: }
112:
113: //Create streams of the byte[] data for the updater method
114: final InputStream webXmlIn = new ByteArrayInputStream(
115: servletXmlBuffer);
116: final InputStream portletXmlIn = new ByteArrayInputStream(
117: portletXmlBuffer);
118: final ByteArrayOutputStream webXmlOut = new ByteArrayOutputStream(
119: servletXmlBuffer.length);
120:
121: //Update the web.xml
122: WebXmlStreamingAssembly.assembleStream(webXmlIn,
123: portletXmlIn, webXmlOut, dispatchServletClass);
124: IOUtils.copy(webXmlIn, webXmlOut);
125: webXmlBytes = webXmlOut.toByteArray();
126:
127: //If no compression is being used (STORED) we have to manually update the size and crc
128: if (servletXmlEntry.getMethod() == ZipEntry.STORED) {
129: servletXmlEntry.setSize(webXmlBytes.length);
130: final CRC32 webXmlCrc = new CRC32();
131: webXmlCrc.update(webXmlBytes);
132: servletXmlEntry.setCrc(webXmlCrc.getValue());
133: }
134:
135: //write out the assembled web.xml entry and contents
136: dest.putNextEntry(servletXmlEntry);
137: IOUtils.write(webXmlBytes, dest);
138:
139: if (LOG.isDebugEnabled()) {
140: LOG.debug("Jar stream " + source
141: + " successfully assembled.");
142: }
143: } else {
144: if (LOG.isDebugEnabled()) {
145: LOG
146: .debug("No portlet XML file was found, assembly was not required.");
147: }
148:
149: //copy the original, unmodified web.xml entry to the destination
150: dest.putNextEntry(servletXmlEntry);
151: IOUtils.write(servletXmlBuffer, dest);
152:
153: if (LOG.isDebugEnabled()) {
154: LOG.debug("Jar stream " + source
155: + " successfully assembled.");
156: }
157: }
158:
159: } finally {
160:
161: dest.flush();
162: dest.close();
163:
164: }
165: }
166:
167: private static JarEntry smartClone(JarEntry originalJarEntry) {
168: final JarEntry newJarEntry = new JarEntry(originalJarEntry
169: .getName());
170: newJarEntry.setComment(originalJarEntry.getComment());
171: newJarEntry.setExtra(originalJarEntry.getExtra());
172: newJarEntry.setMethod(originalJarEntry.getMethod());
173: newJarEntry.setTime(originalJarEntry.getTime());
174:
175: //Must set size and CRC for STORED entries
176: if (newJarEntry.getMethod() == ZipEntry.STORED) {
177: newJarEntry.setSize(originalJarEntry.getSize());
178: newJarEntry.setCrc(originalJarEntry.getCrc());
179: }
180:
181: return newJarEntry;
182: }
183:
184: }
|