001/*
002 * (C) Copyright 2006-2011 Nuxeo SA (http://nuxeo.com/) and contributors.
003 *
004 * All rights reserved. This program and the accompanying materials
005 * are made available under the terms of the GNU Lesser General Public License
006 * (LGPL) version 2.1 which accompanies this distribution, and is available at
007 * http://www.gnu.org/licenses/lgpl.html
008 *
009 * This library is distributed in the hope that it will be useful,
010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012 * Lesser General Public License for more details.
013 *
014 * Contributors:
015 *     bstefanescu, jcarsique
016 */
017package org.nuxeo.runtime.deployment.preprocessor;
018
019import java.io.File;
020import java.io.FileInputStream;
021import java.io.IOException;
022import java.util.ArrayList;
023import java.util.Collection;
024
025import javax.xml.parsers.DocumentBuilder;
026import javax.xml.parsers.DocumentBuilderFactory;
027import javax.xml.parsers.ParserConfigurationException;
028
029import org.apache.commons.io.FileUtils;
030import org.apache.commons.lang.StringUtils;
031import org.apache.commons.logging.Log;
032import org.apache.commons.logging.LogFactory;
033import org.nuxeo.common.utils.ZipUtils;
034import org.nuxeo.launcher.config.ConfigurationException;
035import org.nuxeo.launcher.config.ConfigurationGenerator;
036import org.w3c.dom.Document;
037import org.w3c.dom.Element;
038import org.w3c.dom.Node;
039import org.w3c.dom.NodeList;
040import org.xml.sax.SAXException;
041
042/**
043 * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a>
044 */
045public class PackZip {
046
047    private static Log log = LogFactory.getLog(PackZip.class);
048
049    public static final String ORDER_PREPROCESSING = "preprocessing";
050
051    public static final String ORDER_PACKAGING = "packaging";
052
053    protected File nuxeoEar;
054
055    protected File deployerJar;
056
057    protected File deployDir;
058
059    protected File jbossLib;
060
061    protected File dsFile;
062
063    protected File target;
064
065    public PackZip(File nuxeoEar, File target) {
066        if (!nuxeoEar.isDirectory()) {
067            throw new IllegalArgumentException("Invalid build - no exploded nuxeo.ear found at "
068                    + nuxeoEar.getAbsolutePath());
069        }
070        if (!target.isDirectory()) {
071            throw new IllegalArgumentException("Invalid configuration - no target directory found at "
072                    + nuxeoEar.getAbsolutePath());
073        }
074        this.nuxeoEar = nuxeoEar;
075        this.target = target;
076        this.deployDir = nuxeoEar.getParentFile();
077        this.jbossLib = new File(deployDir.getParentFile(), "lib");
078        this.dsFile = new File(deployDir, "nuxeo-ds.xml");
079        File deployers = new File(deployDir.getParentFile(), "deployers");
080        String[] names = deployers.list();
081        if (names == null) {
082            throw new IllegalArgumentException(
083                    "Invalid nuxeo.ear location - no nuxeo jboss deployer JAR found in deployers directory");
084        }
085        for (String name : names) {
086            if (name.startsWith("nuxeo-jboss-deployer") && name.endsWith(".jar")) {
087                deployerJar = new File(deployers, name);
088            }
089        }
090        if (deployerJar == null) {
091            throw new IllegalArgumentException(
092                    "Invalid build - no nuxeo jboss deployer JAR found in deployers directory");
093        }
094    }
095
096    protected void executePreprocessing() throws ConfigurationException, IOException {
097        // configure from templates
098        new ConfigurationGenerator().run();
099        // run preprocessor
100        runPreprocessor();
101    }
102
103    protected void executePackaging() throws IOException, SAXException, ParserConfigurationException {
104        // move non ejb jars to nuxeo.ear/lib
105        moveNonEjbsToLib(nuxeoEar);
106        // replace nuxeo-structure.xml with nuxeo-structure-zip.xml
107        replaceStructureFile();
108        // move libs in jboss/lib to nuxeo.ear/lib
109        moveJarsFromJbossLib();
110        // move nuxeo jboss deployer to nuxeo.ear/lib
111        FileUtils.moveFile(deployerJar, new File(nuxeoEar, "lib" + File.separator + deployerJar.getName()));
112        // zip the ear into target directory
113        ZipUtils.zip(nuxeoEar.listFiles(), new File(target, "nuxeo.ear"));
114        // copy nuxeo-ds.xml to target dir
115        FileUtils.copyFileToDirectory(dsFile, target);
116    }
117
118    public void execute(String order) throws ConfigurationException, IOException, ParserConfigurationException,
119            SAXException {
120        if (ORDER_PREPROCESSING.equals(order) || StringUtils.isBlank(order)) {
121            executePreprocessing();
122        }
123        if (ORDER_PACKAGING.equals(order) || StringUtils.isBlank(order)) {
124            executePackaging();
125        }
126        if (!(ORDER_PREPROCESSING.equals(order) || StringUtils.isBlank(order) || ORDER_PACKAGING.equals(order))) {
127            fail("Order param should be " + ORDER_PREPROCESSING + " or " + ORDER_PACKAGING);
128        }
129    }
130
131    protected void runPreprocessor() throws IOException {
132        DeploymentPreprocessor.main(new String[] { nuxeoEar.getAbsolutePath() });
133    }
134
135    protected void replaceStructureFile() throws IOException {
136        File oldf = new File(nuxeoEar, "META-INF" + File.separator + "nuxeo-structure.xml");
137        File newf = new File(nuxeoEar, "META-INF" + File.separator + "nuxeo-structure-zip.xml");
138        if (oldf.exists() && !FileUtils.deleteQuietly(oldf)) {
139            log.warn("Cannot delete " + oldf.getName() + ", it may not replace it with the new file.");
140        }
141        FileUtils.moveFile(newf, oldf);
142    }
143
144    protected void moveJarsFromJbossLib() {
145    }
146
147    protected void moveNonEjbsToLib(File wd) throws ParserConfigurationException, SAXException, IOException {
148        File file = new File(wd, "META-INF" + File.separator + "application.xml");
149        if (!file.isFile()) {
150            log.error("You should run this tool from a preprocessed nuxeo.ear folder");
151        }
152        DocumentBuilder docBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
153        FileInputStream in = new FileInputStream(file);
154        Document doc = docBuilder.parse(in);
155        Element root = doc.getDocumentElement();
156        NodeList list = root.getElementsByTagName("module");
157        Collection<String> paths = new ArrayList<String>();
158        for (int i = 0; i < list.getLength(); i++) {
159            Element el = (Element) list.item(i);
160            Node n = el.getFirstChild();
161            while (n != null) {
162                if (n.getNodeType() == Node.ELEMENT_NODE) {
163                    Element mtype = ((Element) n);
164                    String type = n.getNodeName().toLowerCase();
165                    if (!"web".equals(type)) {
166                        String path = mtype.getTextContent().trim();
167                        paths.add(path);
168                    }
169                }
170                n = n.getNextSibling();
171            }
172        }
173
174        File ejbs = new File(wd, "tmp-ejbs");
175        ejbs.mkdirs();
176        for (String path : paths) {
177            log.info("Move EAR module " + path + " to " + ejbs.getName());
178            File f = new File(wd, path);
179            if (f.getName().endsWith(".txt")) {
180                continue;
181            }
182            FileUtils.moveToDirectory(f, ejbs, false);
183        }
184        File lib = new File(wd, "lib");
185        File[] files = new File(wd, "bundles").listFiles();
186        if (files != null) {
187            for (File f : files) {
188                if (f.getName().endsWith(".txt")) {
189                    continue;
190                }
191                log.info("Move POJO bundle " + f.getName() + " to lib");
192                FileUtils.moveToDirectory(f, lib, false);
193            }
194        }
195        File bundles = new File(wd, "bundles");
196        files = ejbs.listFiles();
197        if (files != null) {
198            for (File f : files) {
199                if (f.getName().endsWith(".txt")) {
200                    continue;
201                }
202                log.info("Move back EAR module " + f.getName() + " to bundles");
203                FileUtils.moveToDirectory(f, bundles, false);
204            }
205        }
206    }
207
208    protected static void fail(String message) {
209        log.error(message);
210        System.exit(1);
211    }
212
213    public static void main(String[] args) throws IOException, ConfigurationException, ParserConfigurationException,
214            SAXException {
215        if (args.length < 2) {
216            fail("Usage: PackZip nuxeo_ear_directory target_directory [order]");
217        }
218        String v = args[0];
219        File ear = new File(v);
220        if (!ear.isDirectory()) {
221            fail("Invalid build - no exploded nuxeo.ear found at " + ear.getAbsolutePath());
222        }
223        v = args[1];
224        File target = new File(v);
225        ear = ear.getCanonicalFile();
226        target = target.getCanonicalFile();
227        if (target.exists()) {
228            FileUtils.deleteDirectory(target);
229        }
230        target.mkdirs();
231        if (!target.isDirectory()) {
232            fail("Invalid target directory: " + v + ". Not a directory or directory could not be created");
233        }
234
235        log.info("Packing nuxeo.ear at " + ear.getAbsolutePath() + " into " + target.getAbsolutePath());
236
237        PackZip pack = new PackZip(ear, target);
238        if (args.length >= 3) {
239            pack.execute(args[2]);
240        } else {
241            pack.execute(null);
242        }
243    }
244}