org.springframework.integration.zip.transformer.UnZipTransformer.java Source code

Java tutorial

Introduction

Here is the source code for org.springframework.integration.zip.transformer.UnZipTransformer.java

Source

/*
 * Copyright 2015-2018 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.integration.zip.transformer;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.zip.ZipEntry;

import org.apache.commons.io.IOUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.zeroturnaround.zip.ZipEntryCallback;
import org.zeroturnaround.zip.ZipException;
import org.zeroturnaround.zip.ZipUtil;

import org.springframework.messaging.Message;
import org.springframework.messaging.MessageHandlingException;
import org.springframework.messaging.MessagingException;

/**
 * Transformer implementation that applies an UnZip transformation to the message
 * payload.
 *
 * @author Gunnar Hillert
 * @author Artem Bilan
 *
 * @since 1.0
 *
 */
public class UnZipTransformer extends AbstractZipTransformer {

    private static final Log logger = LogFactory.getLog(UnZipTransformer.class);

    private volatile boolean expectSingleResult = false;

    /**
     *
     * This parameter indicates that only one result object shall be returned as
     * a result from the executed Unzip operation. If set to <code>true</code> and
     * more than 1 element is returned, then that
     * 1 element is extracted and returned as payload.
     *
     * If the result map contains more than 1 element and
     * {@link #expectSingleResult} is <code>true</code>, then a
     * {@link MessagingException} is thrown.
     *
     * If set to <code>false</code>, the complete result list is returned as the
     * payload. This is the {@code default}.
     *
     * @param expectSingleResult If not set explicitly, will default to false
     *
     */
    public void setExpectSingleResult(boolean expectSingleResult) {
        this.expectSingleResult = expectSingleResult;
    }

    @Override
    protected Object doZipTransform(final Message<?> message) throws Exception {

        try {
            final Object payload = message.getPayload();
            final Object unzippedData;

            InputStream inputStream = null;

            try {
                if (payload instanceof File) {
                    final File filePayload = (File) payload;

                    if (filePayload.isDirectory()) {
                        throw new UnsupportedOperationException(
                                String.format("Cannot unzip a directory: '%s'", filePayload.getAbsolutePath()));
                    }

                    if (!SpringZipUtils.isValid(filePayload)) {
                        throw new IllegalStateException(
                                String.format("Not a zip file: '%s'.", filePayload.getAbsolutePath()));
                    }

                    inputStream = new FileInputStream(filePayload);
                } else if (payload instanceof InputStream) {
                    inputStream = (InputStream) payload;
                } else if (payload instanceof byte[]) {
                    inputStream = new ByteArrayInputStream((byte[]) payload);
                } else {
                    throw new IllegalArgumentException(String.format("Unsupported payload type '%s'. "
                            + "The only supported payload types are java.io.File, byte[] and java.io.InputStream",
                            payload.getClass().getSimpleName()));
                }

                final SortedMap<String, Object> uncompressedData = new TreeMap<String, Object>();

                ZipUtil.iterate(inputStream, new ZipEntryCallback() {

                    @Override
                    public void process(InputStream zipEntryInputStream, ZipEntry zipEntry) throws IOException {

                        final String zipEntryName = zipEntry.getName();
                        final long zipEntryTime = zipEntry.getTime();
                        final long zipEntryCompressedSize = zipEntry.getCompressedSize();
                        final String type = zipEntry.isDirectory() ? "directory" : "file";

                        if (logger.isInfoEnabled()) {
                            logger.info(String.format(
                                    "Unpacking Zip Entry - Name: '%s',Time: '%s', "
                                            + "Compressed Size: '%s', Type: '%s'",
                                    zipEntryName, zipEntryTime, zipEntryCompressedSize, type));
                        }

                        if (ZipResultType.FILE.equals(zipResultType)) {
                            final File destinationFile = checkPath(message, zipEntryName);

                            if (zipEntry.isDirectory()) {
                                destinationFile.mkdirs(); //NOSONAR false positive
                            } else {
                                SpringZipUtils.copy(zipEntryInputStream, destinationFile);
                                uncompressedData.put(zipEntryName, destinationFile);
                            }
                        } else if (ZipResultType.BYTE_ARRAY.equals(zipResultType)) {
                            if (!zipEntry.isDirectory()) {
                                checkPath(message, zipEntryName);
                                byte[] data = IOUtils.toByteArray(zipEntryInputStream);
                                uncompressedData.put(zipEntryName, data);
                            }
                        } else {
                            throw new IllegalStateException("Unsupported zipResultType " + zipResultType);
                        }
                    }

                    public File checkPath(final Message<?> message, final String zipEntryName) throws IOException {
                        final File tempDir = new File(workDirectory, message.getHeaders().getId().toString());
                        tempDir.mkdirs(); //NOSONAR false positive
                        final File destinationFile = new File(tempDir, zipEntryName);

                        /* If we see the relative traversal string of ".." we need to make sure
                         * that the outputdir + name doesn't leave the outputdir.
                         */
                        if (!destinationFile.getCanonicalPath().startsWith(workDirectory.getCanonicalPath())) {
                            throw new ZipException("The file " + zipEntryName
                                    + " is trying to leave the target output directory of " + workDirectory);
                        }
                        return destinationFile;
                    }
                });

                if (uncompressedData.isEmpty()) {
                    if (logger.isWarnEnabled()) {
                        logger.warn(
                                "No data unzipped from payload with message Id " + message.getHeaders().getId());
                    }
                    unzippedData = null;
                } else {

                    if (this.expectSingleResult) {
                        if (uncompressedData.size() == 1) {
                            unzippedData = uncompressedData.values().iterator().next();
                        } else {
                            throw new MessagingException(message,
                                    String.format(
                                            "The UnZip operation extracted %s "
                                                    + "result objects but expectSingleResult was 'true'.",
                                            uncompressedData.size()));
                        }
                    } else {
                        unzippedData = uncompressedData;
                    }

                }
            } finally {
                IOUtils.closeQuietly(inputStream);
                if (payload instanceof File && this.deleteFiles) {
                    final File filePayload = (File) payload;
                    if (!filePayload.delete() && logger.isWarnEnabled()) {
                        if (logger.isWarnEnabled()) {
                            logger.warn("failed to delete File '" + filePayload + "'");
                        }
                    }
                }
            }
            return unzippedData;
        } catch (Exception e) {
            throw new MessageHandlingException(message, "Failed to apply Zip transformation.", e);
        }
    }

}