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