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