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.ear;
018:
019: import java.io.BufferedOutputStream;
020: import java.io.ByteArrayOutputStream;
021: import java.io.File;
022: import java.io.FileInputStream;
023: import java.io.FileOutputStream;
024: import java.io.IOException;
025: import java.util.jar.JarEntry;
026: import java.util.jar.JarInputStream;
027: import java.util.jar.JarOutputStream;
028:
029: import org.apache.commons.io.IOUtils;
030: import org.apache.commons.logging.Log;
031: import org.apache.commons.logging.LogFactory;
032: import org.apache.pluto.util.UtilityException;
033: import org.apache.pluto.util.assemble.AbstractArchiveAssembler;
034: import org.apache.pluto.util.assemble.AssemblerConfig;
035: import org.apache.pluto.util.assemble.io.JarStreamingAssembly;
036:
037: /**
038: * Assembles war files contained inside of an EAR. War files that do
039: * not contain a portlet.xml are not assembled. All files are copied
040: * into the destination archive.
041: */
042: public class EarAssembler extends AbstractArchiveAssembler {
043:
044: private static final Log LOG = LogFactory
045: .getLog(EarAssembler.class);
046: private static final int BUFLEN = 1024 * 8; // 8kb
047:
048: public void assembleInternal(AssemblerConfig config)
049: throws UtilityException, IOException {
050:
051: File source = config.getSource();
052: File dest = config.getDestination();
053:
054: JarInputStream earIn = new JarInputStream(new FileInputStream(
055: source));
056: JarOutputStream earOut = new JarOutputStream(
057: new BufferedOutputStream(new FileOutputStream(dest),
058: BUFLEN));
059:
060: try {
061:
062: JarEntry entry;
063:
064: // Iterate over entries in the EAR archive
065: while ((entry = earIn.getNextJarEntry()) != null) {
066:
067: // If a war file is encountered, assemble it into a
068: // ByteArrayOutputStream and write the assembled bytes
069: // back to the EAR archive.
070: if (entry.getName().toLowerCase().endsWith(".war")) {
071:
072: if (LOG.isDebugEnabled()) {
073: LOG.debug("Assembling war file "
074: + entry.getName());
075: }
076:
077: // keep a handle to the AssemblySink so we can write out
078: // JarEntry metadata and the bytes later.
079: AssemblySink warBytesOut = getAssemblySink(config,
080: entry);
081: JarOutputStream warOut = new JarOutputStream(
082: warBytesOut);
083:
084: JarStreamingAssembly.assembleStream(
085: new JarInputStream(earIn), warOut, config
086: .getDispatchServletClass());
087:
088: JarEntry warEntry = new JarEntry(entry);
089:
090: // Write out the assembled JarEntry metadata
091: warEntry.setSize(warBytesOut.getByteCount());
092: warEntry.setCrc(warBytesOut.getCrc());
093: warEntry.setCompressedSize(-1);
094: earOut.putNextEntry(warEntry);
095:
096: // Write out the assembled WAR file to the EAR
097: warBytesOut.writeTo(earOut);
098:
099: earOut.flush();
100: earOut.closeEntry();
101: earIn.closeEntry();
102:
103: } else {
104:
105: earOut.putNextEntry(entry);
106: IOUtils.copy(earIn, earOut);
107:
108: earOut.flush();
109: earOut.closeEntry();
110: earIn.closeEntry();
111:
112: }
113: }
114:
115: } finally {
116:
117: earOut.close();
118: earIn.close();
119:
120: }
121: }
122:
123: /**
124: * Obtain a sink used as a temporary container for assembled war bytes. By default a
125: * filesystem based sink is used.
126: *
127: * @param config the AssemblerConfig
128: * @param entry the JarEntry
129: * @return the AssemblySink
130: * @throws IOException
131: */
132: protected AssemblySink getAssemblySink(AssemblerConfig config,
133: JarEntry entry) throws IOException {
134: File f = File.createTempFile("earAssemblySink", "tmp");
135: f.deleteOnExit();
136: return getFileAssemblySink(entry, f);
137: }
138:
139: private AssemblySink getByteArrayAssemblySink(JarEntry entry) {
140: // Create a buffer the size of the warfile, plus a little extra, to
141: // account for the additional bytes added to web.xml as a result of
142: // assembly.
143:
144: ByteArrayAssemblySink warBytesOut = null;
145: int defaultBuflen = 1024 * 1024 * 10; // 10Mb
146: int assemblyBuflen = 1024 * 32; // 32kb additional bytes for assembly
147:
148: // ByteArrayOutputStream grows the buffer by a left bitshift each time
149: // its internal buffer would overflow. The goal is to prevent the
150: // buffer from overflowing, otherwise the exponential growth of
151: // the internal buffer can cause OOM errors.
152:
153: // note that we can only optimize the buffer for file sizes less than
154: // Integer.MAX_VALUE - assemblyBuf
155: if (entry.getSize() > (Integer.MAX_VALUE - assemblyBuflen)
156: || entry.getSize() < 1) {
157: warBytesOut = new ByteArrayAssemblySink(
158: new ByteArrayOutputStream(defaultBuflen));
159: } else {
160: int buflen = (int) entry.getSize() + assemblyBuflen;
161: warBytesOut = new ByteArrayAssemblySink(
162: new ByteArrayOutputStream(buflen));
163: }
164:
165: return warBytesOut;
166: }
167:
168: private AssemblySink getFileAssemblySink(JarEntry e, File f) {
169: AssemblySink sink = null;
170: try {
171: sink = new FileAssemblySink(f);
172: } catch (Exception ex) {
173: throw new RuntimeException(ex);
174: }
175: return sink;
176: }
177:
178: }
|