org.bonitasoft.engine.io.IOUtil.java Source code

Java tutorial

Introduction

Here is the source code for org.bonitasoft.engine.io.IOUtil.java

Source

/**
 * Copyright (C) 2015 BonitaSoft S.A.
 * BonitaSoft, 32 rue Gustave Eiffel - 38000 Grenoble
 * This library is free software; you can redistribute it and/or modify it under the terms
 * of the GNU Lesser General Public License as published by the Free Software Foundation
 * version 2.1 of the License.
 * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Lesser General Public License for more details.
 * You should have received a copy of the GNU Lesser General Public License along with this
 * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
 * Floor, Boston, MA 02110-1301, USA.
 **/
package org.bonitasoft.engine.io;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.lang.management.ManagementFactory;
import java.net.URI;
import java.net.URL;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileChannel.MapMode;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Scanner;
import java.util.jar.JarEntry;
import java.util.jar.JarOutputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;

import javax.xml.XMLConstants;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;

import org.apache.commons.io.FileUtils;
import org.bonitasoft.engine.exception.BonitaRuntimeException;
import org.xml.sax.SAXException;

/**
 * @author Elias Ricken de Medeiros
 * @author Celine Souchet
 */
public class IOUtil {

    public static final String TMP_DIRECTORY = System.getProperty("java.io.tmpdir");
    public static final String FILE_ENCODING = "UTF-8";
    private static final String LINE_SEPARATOR = System.getProperty("line.separator");
    private static final int BUFFER_SIZE = 100000;
    private static final String JVM_NAME = ManagementFactory.getRuntimeMXBean().getName();

    public static byte[] generateJar(final Class<?>... classes) throws IOException {
        return generateJar(getResources(classes));
    }

    public static Map<String, byte[]> getResources(final Class<?>... classes) throws IOException {
        if (classes == null || classes.length == 0) {
            final String message = "No classes available";
            throw new IOException(message);
        }
        final Map<String, byte[]> resources = new HashMap<>();
        for (final Class<?> clazz : classes) {
            resources.put(clazz.getName().replace(".", "/") + ".class", getClassData(clazz));
            for (final Class<?> internalClass : clazz.getDeclaredClasses()) {
                resources.put(internalClass.getName().replace(".", "/") + ".class", getClassData(internalClass));
            }
        }
        return resources;
    }

    public static byte[] getClassData(final Class<?> clazz) throws IOException {
        if (clazz == null) {
            final String message = "Class is null";
            throw new IOException(message);
        }
        final String resource = clazz.getName().replace('.', '/') + ".class";
        byte[] data;
        try (InputStream inputStream = clazz.getClassLoader().getResourceAsStream(resource)) {
            if (inputStream == null) {
                throw new IOException(
                        "Impossible to get stream from class: " + clazz.getName() + ", className= " + resource);
            }
            data = IOUtil.getAllContentFrom(inputStream);
        }
        return data;
    }

    public static byte[] generateJar(final Map<String, byte[]> resources) throws IOException {
        if (resources == null || resources.isEmpty()) {
            final String message = "No resources available";
            throw new IOException(message);
        }

        ByteArrayOutputStream baos = null;
        JarOutputStream jarOutStream = null;
        try {
            baos = new ByteArrayOutputStream();
            jarOutStream = new JarOutputStream(new BufferedOutputStream(baos));
            for (final Map.Entry<String, byte[]> resource : resources.entrySet()) {
                jarOutStream.putNextEntry(new JarEntry(resource.getKey()));
                jarOutStream.write(resource.getValue());
            }
            jarOutStream.flush();
            baos.flush();
        } finally {
            if (jarOutStream != null) {
                jarOutStream.close();
            }
            if (baos != null) {
                baos.close();
            }
        }

        return baos.toByteArray();
    }

    /**
     * Return the whole underlying stream content into a single String.
     * Warning: the whole content of stream will be kept in memory!! Use with
     * care!
     *
     * @param in the stream to read
     * @return the whole content of the stream in a single String.
     * @throws IOException if an I/O exception occurs
     */
    public static byte[] getAllContentFrom(final InputStream in) throws IOException {
        if (in == null) {
            throw new IOException("The InputStream is null!");
        }
        final byte[] buffer = new byte[BUFFER_SIZE];
        final byte[] resultArray;

        try (BufferedInputStream bis = new BufferedInputStream(in);
                ByteArrayOutputStream result = new ByteArrayOutputStream()) {
            int amountRead;
            while ((amountRead = bis.read(buffer)) > 0) {
                result.write(buffer, 0, amountRead);
            }
            resultArray = result.toByteArray();
            result.flush();
        }
        return resultArray;
    }

    /**
     * Equivalent to {@link #getAllContentFrom(InputStream) getAllContentFrom(new
     * FileInputStream(file))};
     *
     * @param file the file to read
     * @return the whole content of the file in a single String.
     * @throws IOException If an I/O exception occurs
     */
    public static byte[] getAllContentFrom(final File file) throws IOException {
        try (InputStream in = new FileInputStream(file)) {
            return getAllContentFrom(in);
        }
    }

    /**
     * Return the whole underlying stream content into a single String.
     * Warning: the whole content of stream will be kept in memory!! Use with
     * care!
     *
     * @param url the URL to read
     * @return the whole content of the stream in a single String.
     * @throws IOException if an I/O exception occurs
     */
    public static byte[] getAllContentFrom(final URL url) throws IOException {
        try (InputStream in = url.openStream()) {
            return getAllContentFrom(in);
        }
    }

    public static File createTempDirectory(final URI directoryPath) {
        final File tmpDir = new File(directoryPath);
        tmpDir.setReadable(true);
        tmpDir.setWritable(true);

        mkdirs(tmpDir);

        try {
            // to initialize internal class FilenameUtils. Otherwise it cannot load the class as the shutdown is in progress:
            FileUtils.isSymlink(tmpDir);
        } catch (IOException ignored) {
        }

        try {

            Runtime.getRuntime().addShutdownHook(new Thread(() -> {
                try {
                    final boolean deleted = deleteDir(tmpDir);
                    if (!deleted) {
                        System.err.println("Unable to delete directory: " + tmpDir
                                + ". Trying with an alternative force delete.");
                        FileUtils.forceDelete(tmpDir);
                    }
                } catch (final IOException e) {
                    throw new BonitaRuntimeException(e);
                }
            }));
        } catch (IllegalStateException ignored) {
            // happen in case of hook already registered and when shutting down
        }
        return tmpDir;
    }

    public static boolean deleteDir(final File dir) throws IOException {
        return deleteDir(dir, 1, 0);
    }

    public static boolean deleteDir(final File dir, final int attempts, final long sleepTime) throws IOException {
        if (dir != null) {
            boolean result = true;
            if (!dir.exists()) {
                return true; //already deleted
            }
            if (!dir.isDirectory()) {
                throw new IOException("Unable to delete directory: " + dir + ", it is not a directory");
            }
            for (final File file : dir.listFiles()) {
                if (file.isDirectory()) {
                    result &= deleteDir(file, attempts, sleepTime);
                } else {
                    result &= deleteFile(file, attempts, sleepTime);
                }
            }
            return result && deleteFile(dir, attempts, sleepTime);
        }
        return false;
    }

    public static boolean deleteFile(final File f, final int attempts, final long sleepTime) {
        int retries = attempts;
        while (retries > 0) {
            if (f.delete()) {
                break;
            }
            retries--;
            try {
                Thread.sleep(sleepTime);
            } catch (final InterruptedException ignored) {
            }
        }
        return retries > 0;
    }

    public static byte[] zip(final Map<String, byte[]> files) throws IOException {
        final ByteArrayOutputStream baos = new ByteArrayOutputStream();
        try (ZipOutputStream zos = new ZipOutputStream(baos)) {
            for (final Entry<String, byte[]> file : files.entrySet()) {
                zos.putNextEntry(new ZipEntry(file.getKey()));
                zos.write(file.getValue());
                zos.closeEntry();
            }
            return baos.toByteArray();
        }
    }

    /**
     * Create a structured zip archive recursively.
     * The string must be OS specific String to represent path.
     *
     */
    public static void zipDir(final String dir2zip, final ZipOutputStream zos, final String root)
            throws IOException {
        final File zipDir = new File(dir2zip);
        final byte[] readBuffer = new byte[BUFFER_SIZE];

        for (final String pathName : zipDir.list()) {
            final File file = new File(zipDir, pathName);
            final String path = file.getPath();
            if (file.isDirectory()) {
                zipDir(path, zos, root);
                continue;
            }
            try {
                final ZipEntry anEntry = new ZipEntry(path.substring(root.length() + 1, path.length())
                        .replace(String.valueOf(File.separatorChar), "/"));
                zos.putNextEntry(anEntry);
                copyFileToZip(zos, readBuffer, file);
                zos.flush();
            } finally {
                zos.closeEntry();
            }
        }
    }

    private static void copyFileToZip(final ZipOutputStream zos, final byte[] readBuffer, final File file)
            throws IOException {
        int bytesIn;
        try (FileInputStream fis = new FileInputStream(file)) {
            while ((bytesIn = fis.read(readBuffer)) != -1) {
                zos.write(readBuffer, 0, bytesIn);
            }
        }
    }

    /**
     * Read the contents from the given FileInputStream. Return the result as a String.
     *
     * @param inputStream the stream to read from
     * @return the content read from the inputStream, as a String
     */
    public static String read(final InputStream inputStream) {
        if (inputStream == null) {
            throw new IllegalArgumentException("Input stream is null");
        }
        Scanner scanner = null;
        try {
            scanner = new Scanner(inputStream, FILE_ENCODING);
            return read(scanner);
        } finally {
            if (scanner != null) {
                scanner.close();
            }
        }
    }

    private static String read(final Scanner scanner) {
        final StringBuilder text = new StringBuilder();
        boolean isFirst = true;
        while (scanner.hasNextLine()) {
            if (isFirst) {
                text.append(scanner.nextLine());
            } else {
                text.append(LINE_SEPARATOR).append(scanner.nextLine());
            }
            isFirst = false;
        }
        return text.toString();
    }

    /**
     * Read the contents of the given file.
     *
     * @param file the file to read
     */
    public static String read(final File file) throws IOException {
        try (FileInputStream fileInputStream = new FileInputStream(file)) {
            return read(fileInputStream);
        }
    }

    public static void unzipToFolder(final InputStream inputStream, final File outputFolder) throws IOException {
        try (ZipInputStream zipInputstream = new ZipInputStream(inputStream)) {
            extractZipEntries(zipInputstream, outputFolder);
        }
    }

    private static void mkdirs(final File file) {
        if (!file.exists()) {
            file.mkdirs();
        }
    }

    private static void extractZipEntries(final ZipInputStream zipInputstream, final File outputFolder)
            throws IOException {
        ZipEntry zipEntry;
        while ((zipEntry = zipInputstream.getNextEntry()) != null) {
            try {
                // For each entry, a file is created in the output directory "folder"
                final File outputFile = new File(outputFolder.getAbsolutePath(), zipEntry.getName());
                // If the entry is a directory, it creates in the output folder, and we go to the next entry (continue).
                if (zipEntry.isDirectory()) {
                    mkdirs(outputFile);
                    continue;
                }
                writeZipInputToFile(zipInputstream, outputFile);
            } finally {
                zipInputstream.closeEntry();
            }
        }
    }

    private static void writeZipInputToFile(final ZipInputStream zipInputstream, final File outputFile)
            throws FileNotFoundException, IOException {
        // The input is a file. An FileOutputStream is created to write the content of the new file.
        mkdirs(outputFile.getParentFile());
        try (FileOutputStream fileOutputStream = new FileOutputStream(outputFile)) {
            // The contents of the new file, that is read from the ZipInputStream using a buffer (byte []), is written.
            int bytesRead;
            final byte[] buffer = new byte[BUFFER_SIZE];
            while ((bytesRead = zipInputstream.read(buffer)) > -1) {
                fileOutputStream.write(buffer, 0, bytesRead);
            }
            fileOutputStream.flush();
        } catch (final IOException ioe) {
            // In case of error, the file is deleted
            outputFile.delete();
            throw ioe;
        }
    }

    public static void writeContentToFile(final String content, final File outputFile) throws IOException {
        final FileOutputStream fileOutput = new FileOutputStream(outputFile);
        writeContentToFileOutputStream(content, fileOutput);
    }

    public static void writeContentToFileOutputStream(final String content, final FileOutputStream fileOutput)
            throws IOException {
        try (OutputStreamWriter out = new OutputStreamWriter(fileOutput, FILE_ENCODING)) {
            out.write(content);
            out.flush();
        } finally {
            fileOutput.close();
        }
    }

    public static void write(final File file, final byte[] fileContent) throws IOException {
        try (FileOutputStream fos = new FileOutputStream(file);
                BufferedOutputStream bos = new BufferedOutputStream(fos)) {
            bos.write(fileContent);
            bos.flush();
        }
    }

    public static byte[] getContent(final File file) throws IOException {
        try (FileInputStream fin = new FileInputStream(file); FileChannel ch = fin.getChannel()) {
            final int size = (int) ch.size();
            final MappedByteBuffer buf = ch.map(MapMode.READ_ONLY, 0, size);
            final byte[] bytes = new byte[size];
            buf.get(bytes);
            return bytes;
        }
    }

    public static byte[] marshallObjectToXML(final Object jaxbModel, final URL schemaURL)
            throws JAXBException, IOException, SAXException {
        if (jaxbModel == null) {
            return null;
        }
        if (schemaURL == null) {
            throw new IllegalArgumentException("schemaURL is null");
        }
        final SchemaFactory sf = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
        final Schema schema = sf.newSchema(schemaURL);
        try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
            final JAXBContext contextObj = JAXBContext.newInstance(jaxbModel.getClass());
            final Marshaller m = contextObj.createMarshaller();
            m.setSchema(schema);
            m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
            m.marshal(jaxbModel, baos);
            return baos.toByteArray();
        }
    }

    public static <T> T unmarshallXMLtoObject(final byte[] xmlObject, final Class<T> objectClass,
            final URL schemaURL) throws JAXBException, IOException, SAXException {
        if (xmlObject == null) {
            return null;
        }
        if (schemaURL == null) {
            throw new IllegalArgumentException("schemaURL is null");
        }
        final SchemaFactory sf = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
        final Schema schema = sf.newSchema(schemaURL);
        final JAXBContext contextObj = JAXBContext.newInstance(objectClass);
        final Unmarshaller um = contextObj.createUnmarshaller();
        um.setSchema(schema);
        try (ByteArrayInputStream bais = new ByteArrayInputStream(xmlObject)) {
            final StreamSource ss = new StreamSource(bais);
            final JAXBElement<T> jaxbElement = um.unmarshal(ss, objectClass);
            return jaxbElement.getValue();
        }
    }

}