Migrate Zip Extension as a core module
This commit is contained in:
20
build.gradle
20
build.gradle
@@ -87,8 +87,8 @@ ext {
|
||||
lettuceVersion = '6.2.3.RELEASE'
|
||||
log4jVersion = '2.19.0'
|
||||
mailVersion = '1.0.0'
|
||||
micrometerPropagationVersion = '1.0.1-SNAPSHOT'
|
||||
micrometerTracingVersion = '1.0.2-SNAPSHOT'
|
||||
micrometerPropagationVersion = '1.0.2'
|
||||
micrometerTracingVersion = '1.0.2'
|
||||
micrometerVersion = '1.11.0-SNAPSHOT'
|
||||
mockitoVersion = '5.1.1'
|
||||
mongoDriverVersion = '4.8.2'
|
||||
@@ -96,7 +96,7 @@ ext {
|
||||
pahoMqttClientVersion = '1.2.5'
|
||||
postgresVersion = '42.5.2'
|
||||
r2dbch2Version = '1.0.0.RELEASE'
|
||||
reactorVersion = '2022.0.3-SNAPSHOT'
|
||||
reactorVersion = '2022.0.3'
|
||||
resilience4jVersion = '2.0.2'
|
||||
romeToolsVersion = '1.18.0'
|
||||
rsocketVersion = '1.1.3'
|
||||
@@ -104,16 +104,17 @@ ext {
|
||||
smackVersion = '4.4.6'
|
||||
springAmqpVersion = '3.0.2-SNAPSHOT'
|
||||
springDataVersion = '2023.0.0-SNAPSHOT'
|
||||
springGraphqlVersion = '1.2.0-SNAPSHOT'
|
||||
springGraphqlVersion = '1.1.2-SNAPSHOT'
|
||||
springKafkaVersion = '3.0.3-SNAPSHOT'
|
||||
springRetryVersion = '2.0.0'
|
||||
springSecurityVersion = '6.1.0-SNAPSHOT'
|
||||
springVersion = '6.0.5-SNAPSHOT'
|
||||
springVersion = '6.0.5'
|
||||
springWsVersion = '4.0.1'
|
||||
testcontainersVersion = '1.17.6'
|
||||
tomcatVersion = '11.0.0-M1'
|
||||
xmlUnitVersion = '2.9.1'
|
||||
xstreamVersion = '1.4.20'
|
||||
ztZipVersion = '1.15'
|
||||
|
||||
javaProjects = subprojects - project(':spring-integration-bom')
|
||||
}
|
||||
@@ -1094,6 +1095,15 @@ project('spring-integration-zeromq') {
|
||||
}
|
||||
}
|
||||
|
||||
project('spring-integration-zip') {
|
||||
description = 'Spring Integration Zip Support'
|
||||
dependencies {
|
||||
api project(':spring-integration-file')
|
||||
api "org.zeroturnaround:zt-zip:$ztZipVersion"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
project('spring-integration-zookeeper') {
|
||||
description = 'Spring Integration Zookeeper Support'
|
||||
dependencies {
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright 2015-2023 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;
|
||||
|
||||
/**
|
||||
* Zip adapter specific message headers.
|
||||
*
|
||||
* @author Gunnar Hillert
|
||||
*
|
||||
* @since 6.1
|
||||
*/
|
||||
public abstract class ZipHeaders {
|
||||
|
||||
public static final String PREFIX = "zip_";
|
||||
|
||||
public static final String ZIP_ENTRY_FILE_NAME = PREFIX + "entryFilename";
|
||||
|
||||
public static final String ZIP_ENTRY_PATH = PREFIX + "entryPath";
|
||||
|
||||
public static final String ZIP_ENTRY_LAST_MODIFIED_DATE = PREFIX + "entryLastModifiedDate";
|
||||
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright 2015-2023 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.config.xml;
|
||||
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.integration.config.xml.AbstractTransformerParser;
|
||||
import org.springframework.integration.config.xml.IntegrationNamespaceUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Base class for Zip transformer parsers.
|
||||
*
|
||||
* @author Gunnar Hillert
|
||||
* @author Artem Bilan
|
||||
*
|
||||
* @since 6.1
|
||||
*/
|
||||
public abstract class AbstractZipTransformerParser extends AbstractTransformerParser {
|
||||
|
||||
/**
|
||||
* @param element The XML Element to process
|
||||
* @param parserContext The Spring ParserContext
|
||||
* @param builder BeanDefinitionBuilder for constructing Bean Definitions
|
||||
*/
|
||||
@Override
|
||||
protected final void parseTransformer(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
|
||||
String deleteFiles = element.getAttribute("delete-files");
|
||||
if (StringUtils.hasText(deleteFiles)) {
|
||||
builder.addPropertyValue("deleteFiles", deleteFiles);
|
||||
}
|
||||
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "charset");
|
||||
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "result-type", "zipResultType");
|
||||
postProcessTransformer(element, parserContext, builder);
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses may override this method to provide additional configuration.
|
||||
*
|
||||
* @param element The XML Element to process
|
||||
* @param parserContext The Spring ParserContext
|
||||
* @param builder BeanDefinitionBuilder for constructing Bean Definitions
|
||||
*/
|
||||
protected void postProcessTransformer(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright 2015-2023 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.config.xml;
|
||||
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.integration.config.xml.IntegrationNamespaceUtils;
|
||||
import org.springframework.integration.zip.transformer.UnZipTransformer;
|
||||
|
||||
/**
|
||||
* Parser for the 'unzip-transformer' element.
|
||||
*
|
||||
* @author Gunnar Hillert
|
||||
* @author Artem Bilan
|
||||
*
|
||||
* @since 6.1
|
||||
*/
|
||||
public class UnZipTransformerParser extends AbstractZipTransformerParser {
|
||||
|
||||
@Override
|
||||
protected String getTransformerClassName() {
|
||||
return UnZipTransformer.class.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void postProcessTransformer(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
|
||||
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "expect-single-result");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright 2015-2023 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.config.xml;
|
||||
|
||||
import org.springframework.integration.config.xml.AbstractIntegrationNamespaceHandler;
|
||||
|
||||
/**
|
||||
* The namespace handler for the Zip namespace
|
||||
*
|
||||
* @author Gunnar Hillert
|
||||
*
|
||||
* @since 6.1
|
||||
*
|
||||
*/
|
||||
public class ZipNamespaceHandler extends AbstractIntegrationNamespaceHandler {
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
this.registerBeanDefinitionParser("zip-transformer", new ZipTransformerParser());
|
||||
this.registerBeanDefinitionParser("unzip-transformer", new UnZipTransformerParser());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright 2015-2023 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.config.xml;
|
||||
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.integration.config.xml.IntegrationNamespaceUtils;
|
||||
import org.springframework.integration.zip.transformer.ZipTransformer;
|
||||
|
||||
/**
|
||||
* Parser for the 'zip-transformer' element.
|
||||
*
|
||||
* @author Gunnar Hillert
|
||||
* @author Artem Bilan
|
||||
*
|
||||
* @since 6.1
|
||||
*/
|
||||
public class ZipTransformerParser extends AbstractZipTransformerParser {
|
||||
|
||||
@Override
|
||||
protected String getTransformerClassName() {
|
||||
return ZipTransformer.class.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void postProcessTransformer(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
|
||||
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "compression-level");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* Provides parser classes to provide Xml namespace support for the Zip components.
|
||||
*/
|
||||
package org.springframework.integration.zip.config.xml;
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* Root package of the Zip Module.
|
||||
*/
|
||||
package org.springframework.integration.zip;
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright 2015-2023 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.splitter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.io.FilenameUtils;
|
||||
|
||||
import org.springframework.integration.file.FileHeaders;
|
||||
import org.springframework.integration.splitter.AbstractMessageSplitter;
|
||||
import org.springframework.integration.support.MessageBuilder;
|
||||
import org.springframework.integration.zip.ZipHeaders;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageHeaders;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Gunnar Hillert
|
||||
* @author Andriy Kryvtsun
|
||||
* @author Artem Bilan
|
||||
*
|
||||
* @since 6.1
|
||||
*/
|
||||
public class UnZipResultSplitter extends AbstractMessageSplitter {
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
protected Object splitMessage(Message<?> message) {
|
||||
Assert.state(message.getPayload() instanceof Map,
|
||||
"The UnZipResultSplitter supports only Map<String, Object> payload");
|
||||
Map<String, Object> unzippedEntries = (Map<String, Object>) message.getPayload();
|
||||
MessageHeaders headers = message.getHeaders();
|
||||
|
||||
List<MessageBuilder<Object>> messageBuilders = new ArrayList<>(unzippedEntries.size());
|
||||
|
||||
for (Map.Entry<String, Object> entry : unzippedEntries.entrySet()) {
|
||||
String path = FilenameUtils.getPath(entry.getKey());
|
||||
String filename = FilenameUtils.getName(entry.getKey());
|
||||
MessageBuilder<Object> messageBuilder = MessageBuilder.withPayload(entry.getValue())
|
||||
.setHeader(FileHeaders.FILENAME, filename)
|
||||
.setHeader(ZipHeaders.ZIP_ENTRY_PATH, path)
|
||||
.copyHeadersIfAbsent(headers);
|
||||
messageBuilders.add(messageBuilder);
|
||||
}
|
||||
|
||||
return messageBuilders;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* Classes to support Splitter pattern for Zip.
|
||||
*/
|
||||
package org.springframework.integration.zip.splitter;
|
||||
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Copyright 2015-2023 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.File;
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
import org.springframework.integration.file.DefaultFileNameGenerator;
|
||||
import org.springframework.integration.file.FileNameGenerator;
|
||||
import org.springframework.integration.transformer.AbstractTransformer;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Base class for transformers that provide Zip compression.
|
||||
*
|
||||
* @author Gunnar Hillert
|
||||
* @author Artem Bilan
|
||||
*
|
||||
* @since 6.1
|
||||
*/
|
||||
public abstract class AbstractZipTransformer extends AbstractTransformer {
|
||||
|
||||
protected Charset charset = Charset.defaultCharset();
|
||||
|
||||
protected FileNameGenerator fileNameGenerator;
|
||||
|
||||
protected ZipResultType zipResultType = ZipResultType.FILE;
|
||||
|
||||
protected File workDirectory =
|
||||
new File(System.getProperty("java.io.tmpdir") + File.separator + "ziptransformer");
|
||||
|
||||
protected boolean deleteFiles;
|
||||
|
||||
/**
|
||||
* If the payload is an instance of {@link File}, this property specifies
|
||||
* whether to delete the {@link File} after transformation.
|
||||
* Default is <em>false</em>.
|
||||
* @param deleteFiles Defaults to <em>false</em> if not set
|
||||
*/
|
||||
public void setDeleteFiles(boolean deleteFiles) {
|
||||
this.deleteFiles = deleteFiles;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the work-directory. The work directory is used when the {@link ZipResultType}
|
||||
* is set to {@link ZipResultType#FILE}. By default, this property is set to
|
||||
* the System temporary directory containing a subdirectory "ziptransformer".
|
||||
* @param workDirectory Must not be null and must not represent a file.
|
||||
*/
|
||||
public void setWorkDirectory(File workDirectory) {
|
||||
Assert.notNull(workDirectory, "workDirectory must not be null.");
|
||||
Assert.isTrue(workDirectory.isDirectory(), "The workDirectory specified must be a directory.");
|
||||
this.workDirectory = workDirectory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the format of the data returned after transformation. Available
|
||||
* options are:
|
||||
* <ul>
|
||||
* <li>File</li>
|
||||
* <li>Byte Array</li>
|
||||
* </ul>
|
||||
* Defaults to {@link ZipResultType#FILE}.
|
||||
* @param zipResultType Must not be null
|
||||
*/
|
||||
public void setZipResultType(ZipResultType zipResultType) {
|
||||
Assert.notNull(zipResultType, "The zipResultType must not be empty.");
|
||||
this.zipResultType = zipResultType;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onInit() {
|
||||
super.onInit();
|
||||
|
||||
if (!this.workDirectory.exists()) {
|
||||
logger.info(() -> "Creating work directory: " + this.workDirectory);
|
||||
Assert.isTrue(this.workDirectory.mkdirs(), () -> "Can't create the 'workDirectory': " + this.workDirectory);
|
||||
}
|
||||
DefaultFileNameGenerator defaultFileNameGenerator = new DefaultFileNameGenerator();
|
||||
defaultFileNameGenerator.setBeanFactory(getBeanFactory());
|
||||
defaultFileNameGenerator.setConversionService(getConversionService());
|
||||
this.fileNameGenerator = defaultFileNameGenerator;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param message the message and its payload must not be null.
|
||||
*/
|
||||
@Override
|
||||
protected Object doTransform(Message<?> message) {
|
||||
return doZipTransform(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses must implement this method to provide the Zip transformation
|
||||
* logic.
|
||||
* @param message The message will never be null.
|
||||
* @return The result of the Zip transformation.
|
||||
*/
|
||||
protected abstract Object doZipTransform(Message<?> message);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
* Copyright 2015-2023 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.BufferedOutputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Collection;
|
||||
import java.util.zip.ZipFile;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.zeroturnaround.zip.ZipEntrySource;
|
||||
import org.zeroturnaround.zip.ZipException;
|
||||
|
||||
import org.springframework.core.log.LogAccessor;
|
||||
|
||||
/**
|
||||
* Once the Spring Integration Zip support matures, we need to contribute the
|
||||
* methods in this utility class back to the ZT Zip project.
|
||||
*
|
||||
* @author Gunnar Hillert
|
||||
*
|
||||
* @since 6.1
|
||||
*/
|
||||
public class SpringZipUtils {
|
||||
|
||||
private static final LogAccessor logger = new LogAccessor(SpringZipUtils.class);
|
||||
|
||||
public static byte[] pack(Collection<ZipEntrySource> entries, int compressionLevel) {
|
||||
logger.debug(() -> "Creating byte array from: " + entries);
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
pack(entries, outputStream, compressionLevel);
|
||||
return outputStream.toByteArray();
|
||||
}
|
||||
|
||||
public static void pack(Collection<ZipEntrySource> entries, File zip, int compressionLevel) {
|
||||
logger.debug(() -> "Creating '" + zip + "' from " + entries + ".");
|
||||
|
||||
FileOutputStream outputStream;
|
||||
try {
|
||||
outputStream = new FileOutputStream(zip);
|
||||
}
|
||||
catch (FileNotFoundException e) {
|
||||
throw new IllegalStateException(String.format("File '%s' not found.", zip.getAbsolutePath()), e);
|
||||
}
|
||||
pack(entries, outputStream, compressionLevel);
|
||||
|
||||
}
|
||||
|
||||
private static void pack(Collection<ZipEntrySource> entries, OutputStream outputStream, int compressionLevel) {
|
||||
|
||||
ZipOutputStream out = null;
|
||||
final BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(outputStream);
|
||||
|
||||
try {
|
||||
out = new ZipOutputStream(bufferedOutputStream);
|
||||
out.setLevel(compressionLevel);
|
||||
for (ZipEntrySource entry : entries) {
|
||||
addEntry(entry, out);
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw rethrow(e);
|
||||
}
|
||||
finally {
|
||||
IOUtils.closeQuietly(out);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static void addEntry(ZipEntrySource entry, ZipOutputStream out) throws IOException {
|
||||
out.putNextEntry(entry.getEntry());
|
||||
InputStream in = entry.getInputStream();
|
||||
if (in != null) {
|
||||
try {
|
||||
IOUtils.copy(in, out);
|
||||
}
|
||||
finally {
|
||||
IOUtils.closeQuietly(in);
|
||||
}
|
||||
}
|
||||
out.closeEntry();
|
||||
}
|
||||
|
||||
private static ZipException rethrow(IOException e) {
|
||||
throw new ZipException(e);
|
||||
}
|
||||
|
||||
public static void copy(InputStream in, File file) throws IOException {
|
||||
OutputStream out = new BufferedOutputStream(new FileOutputStream(file));
|
||||
try {
|
||||
IOUtils.copy(in, out);
|
||||
}
|
||||
finally {
|
||||
IOUtils.closeQuietly(out);
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] copy(InputStream in) throws IOException {
|
||||
return IOUtils.toByteArray(in);
|
||||
}
|
||||
|
||||
static boolean isValid(final File file) {
|
||||
ZipFile zipfile = null;
|
||||
try {
|
||||
zipfile = new ZipFile(file);
|
||||
return true;
|
||||
}
|
||||
catch (IOException e) {
|
||||
return false;
|
||||
}
|
||||
finally {
|
||||
IOUtils.closeQuietly(zipfile);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,205 @@
|
||||
/*
|
||||
* Copyright 2015-2023 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.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
|
||||
* @author Ingo Dueppe
|
||||
*
|
||||
* @since 6.1
|
||||
*/
|
||||
public class UnZipTransformer extends AbstractZipTransformer {
|
||||
|
||||
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) {
|
||||
try {
|
||||
Object payload = message.getPayload();
|
||||
Object unzippedData;
|
||||
|
||||
InputStream inputStream = null;
|
||||
|
||||
try {
|
||||
if (payload instanceof final File filePayload) {
|
||||
if (filePayload.isDirectory()) {
|
||||
throw new UnsupportedOperationException("Cannot unzip a directory: " +
|
||||
filePayload.getAbsolutePath());
|
||||
}
|
||||
|
||||
if (!SpringZipUtils.isValid(filePayload)) {
|
||||
throw new IllegalStateException("Not a zip file: " + 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("Unsupported payload type '" + payload.getClass().getSimpleName()
|
||||
+ "'. The only supported payload types are java.io.File, byte[] and java.io.InputStream");
|
||||
}
|
||||
|
||||
final SortedMap<String, Object> uncompressedData = new TreeMap<>();
|
||||
|
||||
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";
|
||||
|
||||
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 {
|
||||
mkDirOfAncestorDirectories(destinationFile);
|
||||
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(tempDir.getCanonicalPath() + File.separator)) {
|
||||
|
||||
throw new ZipException("The file " + zipEntryName +
|
||||
" is trying to leave the target output directory of " + workDirectory);
|
||||
}
|
||||
return destinationFile;
|
||||
}
|
||||
});
|
||||
|
||||
if (uncompressedData.isEmpty()) {
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
IOUtils.closeQuietly(inputStream);
|
||||
if (payload instanceof final File filePayload && this.deleteFiles) {
|
||||
if (!filePayload.delete() && logger.isWarnEnabled()) {
|
||||
logger.warn(() -> "failed to delete File '" + filePayload + "'");
|
||||
}
|
||||
}
|
||||
}
|
||||
finally {
|
||||
IOUtils.closeQuietly(inputStream);
|
||||
}
|
||||
return unzippedData;
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new MessageHandlingException(message, "Failed to apply Zip transformation.", e);
|
||||
}
|
||||
}
|
||||
|
||||
private static void mkDirOfAncestorDirectories(File destinationFile) {
|
||||
File parentDirectory = destinationFile.getParentFile();
|
||||
if (parentDirectory != null) {
|
||||
parentDirectory.mkdirs();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright 2015-2023 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;
|
||||
|
||||
/**
|
||||
* @author Gunnar Hillert
|
||||
*
|
||||
* @since 6.1
|
||||
*/
|
||||
public enum ZipResultType {
|
||||
|
||||
FILE, BYTE_ARRAY
|
||||
|
||||
}
|
||||
@@ -0,0 +1,223 @@
|
||||
/*
|
||||
* Copyright 2015-2023 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.File;
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.zip.Deflater;
|
||||
|
||||
import org.apache.commons.io.FilenameUtils;
|
||||
import org.zeroturnaround.zip.ByteSource;
|
||||
import org.zeroturnaround.zip.FileSource;
|
||||
import org.zeroturnaround.zip.ZipEntrySource;
|
||||
|
||||
import org.springframework.integration.file.FileHeaders;
|
||||
import org.springframework.integration.transformer.Transformer;
|
||||
import org.springframework.integration.zip.ZipHeaders;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.FileCopyUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* {@link Transformer} implementation that applies a Zip transformation to the
|
||||
* message payload. Keep in mind that Zip entry timestamps are recorded only to
|
||||
* two 2 second precision:
|
||||
* <p>
|
||||
* See also: <a href="http://mindprod.com/jgloss/zip.html"/>
|
||||
* <p>
|
||||
* If you want to generate Zip files larger than {@code 4GB}, you must use Java 7:
|
||||
* <p>
|
||||
* See also: <a href="https://blogs.oracle.com/xuemingshen/entry/zip64_support_for_4g_zipfile"/>
|
||||
*
|
||||
* @author Gunnar Hillert
|
||||
* @author Artem Bilan
|
||||
*
|
||||
* @since 6.1
|
||||
*/
|
||||
public class ZipTransformer extends AbstractZipTransformer {
|
||||
|
||||
private static final String ZIP_EXTENSION = ".zip";
|
||||
|
||||
private volatile int compressionLevel = Deflater.DEFAULT_COMPRESSION;
|
||||
|
||||
private volatile boolean useFileAttributes = true;
|
||||
|
||||
/**
|
||||
* Set the compression level. Default is {@link Deflater#DEFAULT_COMPRESSION}.
|
||||
* @param compressionLevel Must be an integer value from 0-9.
|
||||
*/
|
||||
public void setCompressionLevel(int compressionLevel) {
|
||||
Assert.isTrue(compressionLevel >= 0 && compressionLevel <= 9, "Acceptable levels are 0-9");
|
||||
this.compressionLevel = compressionLevel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify whether the name of the file shall be used for the zip entry.
|
||||
* @param useFileAttributes Defaults to true if not set explicitly
|
||||
*/
|
||||
public void setUseFileAttributes(boolean useFileAttributes) {
|
||||
this.useFileAttributes = useFileAttributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* The payload may encompass the following types:
|
||||
* <ul>
|
||||
* <li>{@link File}
|
||||
*...<li>{@link String}
|
||||
*...<li>byte[]
|
||||
*...<li>{@link Iterable}
|
||||
* </ul>
|
||||
* When providing an {@link Iterable}, nested Iterables are not supported. However,
|
||||
* payloads can be any of the other supported types.
|
||||
*/
|
||||
@Override
|
||||
protected Object doZipTransform(Message<?> message) {
|
||||
final Object payload = message.getPayload();
|
||||
final Object zippedData;
|
||||
final String baseFileName = this.fileNameGenerator.generateFileName(message);
|
||||
|
||||
final String zipEntryName;
|
||||
final String zipFileName;
|
||||
|
||||
if (message.getHeaders().containsKey(ZipHeaders.ZIP_ENTRY_FILE_NAME)) {
|
||||
zipEntryName = (String) message.getHeaders().get(ZipHeaders.ZIP_ENTRY_FILE_NAME);
|
||||
}
|
||||
else {
|
||||
zipEntryName = baseFileName;
|
||||
}
|
||||
|
||||
if (message.getHeaders().containsKey(FileHeaders.FILENAME)) {
|
||||
zipFileName = (String) message.getHeaders().get(FileHeaders.FILENAME);
|
||||
}
|
||||
else {
|
||||
zipFileName = baseFileName + ZIP_EXTENSION;
|
||||
}
|
||||
|
||||
final Date lastModifiedDate;
|
||||
|
||||
if (message.getHeaders().containsKey(ZipHeaders.ZIP_ENTRY_LAST_MODIFIED_DATE)) {
|
||||
lastModifiedDate = (Date) message.getHeaders().get(ZipHeaders.ZIP_ENTRY_LAST_MODIFIED_DATE);
|
||||
}
|
||||
else {
|
||||
lastModifiedDate = new Date();
|
||||
}
|
||||
|
||||
java.util.List<ZipEntrySource> entries = new ArrayList<>();
|
||||
|
||||
if (payload instanceof Iterable<?>) {
|
||||
int counter = 1;
|
||||
|
||||
String baseName = FilenameUtils.getBaseName(zipEntryName);
|
||||
String fileExtension = FilenameUtils.getExtension(zipEntryName);
|
||||
|
||||
if (StringUtils.hasText(fileExtension)) {
|
||||
fileExtension = FilenameUtils.EXTENSION_SEPARATOR_STR + fileExtension;
|
||||
}
|
||||
|
||||
for (Object item : (Iterable<?>) payload) {
|
||||
|
||||
final ZipEntrySource zipEntrySource = createZipEntrySource(item, lastModifiedDate, baseName + "_"
|
||||
+ counter + fileExtension, this.useFileAttributes);
|
||||
logger.debug(() -> "ZipEntrySource path: '" + zipEntrySource.getPath() + "'");
|
||||
entries.add(zipEntrySource);
|
||||
counter++;
|
||||
}
|
||||
}
|
||||
else {
|
||||
final ZipEntrySource zipEntrySource =
|
||||
createZipEntrySource(payload, lastModifiedDate, zipEntryName, this.useFileAttributes);
|
||||
entries.add(zipEntrySource);
|
||||
}
|
||||
|
||||
final byte[] zippedBytes = SpringZipUtils.pack(entries, this.compressionLevel);
|
||||
|
||||
if (ZipResultType.FILE.equals(this.zipResultType)) {
|
||||
final File zippedFile = new File(this.workDirectory, zipFileName);
|
||||
try {
|
||||
FileCopyUtils.copy(zippedBytes, zippedFile);
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new UncheckedIOException(ex);
|
||||
}
|
||||
zippedData = zippedFile;
|
||||
}
|
||||
else if (ZipResultType.BYTE_ARRAY.equals(this.zipResultType)) {
|
||||
zippedData = zippedBytes;
|
||||
}
|
||||
else {
|
||||
throw new IllegalStateException("Unsupported zipResultType " + this.zipResultType);
|
||||
}
|
||||
|
||||
if (this.deleteFiles) {
|
||||
if (payload instanceof Iterable<?>) {
|
||||
for (Object item : (Iterable<?>) payload) {
|
||||
deleteFile(item);
|
||||
}
|
||||
}
|
||||
else {
|
||||
deleteFile(payload);
|
||||
}
|
||||
}
|
||||
return getMessageBuilderFactory()
|
||||
.withPayload(zippedData)
|
||||
.copyHeaders(message.getHeaders())
|
||||
.setHeader(FileHeaders.FILENAME, zipFileName)
|
||||
.build();
|
||||
}
|
||||
|
||||
private void deleteFile(Object fileToDelete) {
|
||||
if (fileToDelete instanceof File && !((File) fileToDelete).delete()) {
|
||||
logger.warn(() -> "Failed to delete File '" + fileToDelete + "'");
|
||||
}
|
||||
}
|
||||
|
||||
private ZipEntrySource createZipEntrySource(Object item,
|
||||
Date lastModifiedDate, String zipEntryName, boolean useFileAttributes) {
|
||||
|
||||
if (item instanceof final File filePayload) {
|
||||
String fileName = useFileAttributes ? filePayload.getName() : zipEntryName;
|
||||
|
||||
if (((File) item).isDirectory()) {
|
||||
throw new UnsupportedOperationException("Zipping of directories is not supported.");
|
||||
}
|
||||
|
||||
return new FileSource(fileName, filePayload);
|
||||
|
||||
}
|
||||
else if (item instanceof byte[] || item instanceof String) {
|
||||
byte[] bytesToCompress;
|
||||
|
||||
if (item instanceof String) {
|
||||
bytesToCompress = ((String) item).getBytes(this.charset);
|
||||
}
|
||||
else {
|
||||
bytesToCompress = (byte[]) item;
|
||||
}
|
||||
|
||||
return new ByteSource(zipEntryName, bytesToCompress, lastModifiedDate.getTime());
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException("Unsupported payload type. The only supported payloads are " +
|
||||
"java.io.File, java.lang.String, and byte[]");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* Classes to support Transformer pattern for Zip.
|
||||
*/
|
||||
package org.springframework.integration.zip.transformer;
|
||||
@@ -0,0 +1 @@
|
||||
http\://www.springframework.org/schema/integration/zip=org.springframework.integration.zip.config.xml.ZipNamespaceHandler
|
||||
@@ -0,0 +1,4 @@
|
||||
http\://www.springframework.org/schema/integration/zip/spring-integration-zip-1.0.xsd=org/springframework/integration/zip/config/spring-integration-zip.xsd
|
||||
http\://www.springframework.org/schema/integration/zip/spring-integration-zip.xsd=org/springframework/integration/zip/config/spring-integration-zip.xsd
|
||||
https\://www.springframework.org/schema/integration/zip/spring-integration-zip-1.0.xsd=org/springframework/integration/zip/config/spring-integration-zip.xsd
|
||||
https\://www.springframework.org/schema/integration/zip/spring-integration-zip.xsd=org/springframework/integration/zip/config/spring-integration-zip.xsd
|
||||
@@ -0,0 +1,4 @@
|
||||
# Tooling related information for the integration Zip namespace
|
||||
http\://www.springframework.org/schema/integration/zip@name=integration Zip Namespace
|
||||
http\://www.springframework.org/schema/integration/zip@prefix=int-zip
|
||||
http\://www.springframework.org/schema/integration/zip@icon=org/springframework/integration/zip/config/spring-integration-zip.gif
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 539 B |
@@ -0,0 +1,126 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<xsd:schema xmlns="http://www.springframework.org/schema/integration/zip"
|
||||
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
|
||||
xmlns:integration="http://www.springframework.org/schema/integration"
|
||||
targetNamespace="http://www.springframework.org/schema/integration/zip"
|
||||
elementFormDefault="qualified">
|
||||
|
||||
<xsd:import namespace="http://www.springframework.org/schema/beans" />
|
||||
<xsd:import namespace="http://www.springframework.org/schema/tool" />
|
||||
<xsd:import namespace="http://www.springframework.org/schema/integration"
|
||||
schemaLocation="https://www.springframework.org/schema/integration/spring-integration.xsd" />
|
||||
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Defines the configuration elements for the Spring Integration
|
||||
Zip Adapter.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
|
||||
<xsd:element name="zip-transformer">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Creates a Transformer that compresses message
|
||||
payloads using Zip compressions. The following payload types are
|
||||
supported:
|
||||
|
||||
- java.io.File
|
||||
- byte[]
|
||||
- String
|
||||
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:complexType>
|
||||
<xsd:complexContent>
|
||||
<xsd:extension base="transformerType">
|
||||
<xsd:attribute name="compression-level" type="xsd:integer"
|
||||
use="optional">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Sets the compression level. Default is
|
||||
java.util.zip.Deflater.DEFAULT_COMPRESSION
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:extension>
|
||||
</xsd:complexContent>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
<xsd:element name="unzip-transformer">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Creates a Transformer that decompresses message
|
||||
payloads using Zip compressions. The following payload types are
|
||||
supported:
|
||||
|
||||
- java.io.File
|
||||
- byte[]
|
||||
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:complexType>
|
||||
<xsd:complexContent>
|
||||
<xsd:extension base="transformerType">
|
||||
</xsd:extension>
|
||||
</xsd:complexContent>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
<xsd:complexType name="transformerType">
|
||||
<xsd:sequence>
|
||||
<xsd:element ref="integration:poller" minOccurs="0" maxOccurs="1"/>
|
||||
<xsd:element name="request-handler-advice-chain" type="integration:handlerAdviceChainType" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="id" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Identifies the underlying Spring bean definition (EventDrivenConsumer)
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attributeGroup ref="integration:inputOutputChannelGroup"/>
|
||||
<xsd:attribute name="delete-files" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
If the payload is an instance of {@link File}, this
|
||||
attribute specifies whether to delete the {@link File} after
|
||||
transformation. The default is 'false'.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="expect-single-result" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
If set to 'true', this property indicates
|
||||
that only one result object shall be returned as a result
|
||||
from the executed Unzip operation. Defaults to 'false'.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="result-type">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Defines the format of the data returned after
|
||||
transformation. Available options are:
|
||||
- File
|
||||
- Byte Array
|
||||
|
||||
Depending on the used input format, not all
|
||||
options may be applicable.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:simpleType>
|
||||
<xsd:union memberTypes="resultType xsd:string" />
|
||||
</xsd:simpleType>
|
||||
</xsd:attribute>
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:simpleType name="resultType">
|
||||
<xsd:restriction base="xsd:token">
|
||||
<xsd:enumeration value="BYTE_ARRAY" />
|
||||
<xsd:enumeration value="FILE" />
|
||||
</xsd:restriction>
|
||||
</xsd:simpleType>
|
||||
|
||||
</xsd:schema>
|
||||
@@ -0,0 +1,35 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:int="http://www.springframework.org/schema/integration"
|
||||
xmlns:int-zip="http://www.springframework.org/schema/integration/zip"
|
||||
xmlns:int-file="http://www.springframework.org/schema/integration/file"
|
||||
xmlns:context="http://www.springframework.org/schema/context"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd
|
||||
http://www.springframework.org/schema/integration/file https://www.springframework.org/schema/integration/file/spring-integration-file.xsd
|
||||
http://www.springframework.org/schema/integration/zip https://www.springframework.org/schema/integration/zip/spring-integration-zip.xsd
|
||||
http://www.springframework.org/schema/integration https://www.springframework.org/schema/integration/spring-integration.xsd">
|
||||
|
||||
<context:property-placeholder/>
|
||||
|
||||
<int:channel id="input"/>
|
||||
|
||||
<int:chain input-channel="input" output-channel="out">
|
||||
<int-zip:unzip-transformer result-type="BYTE_ARRAY"/>
|
||||
<int:splitter>
|
||||
<bean class="org.springframework.integration.zip.splitter.UnZipResultSplitter"/>
|
||||
</int:splitter>
|
||||
</int:chain>
|
||||
|
||||
<int:channel id="out">
|
||||
<int:interceptors>
|
||||
<int:wire-tap channel="logger"/>
|
||||
</int:interceptors>
|
||||
</int:channel>
|
||||
|
||||
<int:logging-channel-adapter id="logger" log-full-message="true" level="INFO"/>
|
||||
|
||||
<int-file:outbound-channel-adapter id="write-file" channel="out" directory-expression="'${workDir}/' + headers.zip_entryPath"/>
|
||||
|
||||
</beans>
|
||||
@@ -0,0 +1,160 @@
|
||||
/*
|
||||
* Copyright 2015-2023 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;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
import org.zeroturnaround.zip.ZipException;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.integration.support.MessageBuilder;
|
||||
import org.springframework.integration.transformer.MessageTransformationException;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Gunnar Hillert
|
||||
* @author Artem Bilan
|
||||
*
|
||||
* @since 6.1
|
||||
*/
|
||||
@SpringJUnitConfig
|
||||
@DirtiesContext
|
||||
public class UnZip2FileTests {
|
||||
|
||||
@Autowired
|
||||
private ApplicationContext context;
|
||||
|
||||
@Autowired
|
||||
private MessageChannel input;
|
||||
|
||||
@TempDir
|
||||
public static File workDir;
|
||||
|
||||
@BeforeAll
|
||||
public static void setup() {
|
||||
System.setProperty("workDir", workDir.getAbsolutePath());
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
public void cleanUp() {
|
||||
cleanupDirectory(workDir);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void unZipWithOneEntry() throws Exception {
|
||||
final Resource resource = this.context.getResource("classpath:testzipdata/single.zip");
|
||||
final InputStream is = resource.getInputStream();
|
||||
|
||||
byte[] zipdata = IOUtils.toByteArray(is);
|
||||
is.close();
|
||||
|
||||
final Message<byte[]> message = MessageBuilder.withPayload(zipdata).build();
|
||||
|
||||
input.send(message);
|
||||
|
||||
assertThat(workDir.list()).hasSize(1);
|
||||
|
||||
File fileInWorkDir = workDir.listFiles()[0];
|
||||
|
||||
assertThat(fileInWorkDir).isFile();
|
||||
assertThat(fileInWorkDir).hasName("single.txt");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void unZipWithMultipleEntries() throws Exception {
|
||||
final Resource resource = this.context.getResource("classpath:testzipdata/countries.zip");
|
||||
final InputStream is = resource.getInputStream();
|
||||
|
||||
byte[] zipdata = IOUtils.toByteArray(is);
|
||||
is.close();
|
||||
|
||||
final Message<byte[]> message = MessageBuilder.withPayload(zipdata).build();
|
||||
|
||||
input.send(message);
|
||||
|
||||
assertThat(workDir.list()).hasSize(4);
|
||||
|
||||
File[] files = workDir.listFiles();
|
||||
|
||||
boolean continents = false;
|
||||
boolean de = false;
|
||||
boolean fr = false;
|
||||
boolean pl = false;
|
||||
|
||||
for (File file : files) {
|
||||
if (file.getName().equals("continents")) {
|
||||
continents = true;
|
||||
assertThat(file).isDirectory();
|
||||
assertThat(file.list()).hasSize(2);
|
||||
}
|
||||
if (file.getName().equals("de.txt")) {
|
||||
de = true;
|
||||
assertThat(file).isFile();
|
||||
}
|
||||
if (file.getName().equals("fr.txt")) {
|
||||
fr = true;
|
||||
assertThat(file).isFile();
|
||||
}
|
||||
if (file.getName().equals("pl.txt")) {
|
||||
pl = true;
|
||||
assertThat(file).isFile();
|
||||
}
|
||||
}
|
||||
|
||||
assertThat(continents).isTrue();
|
||||
assertThat(de).isTrue();
|
||||
assertThat(fr).isTrue();
|
||||
assertThat(pl).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void unZipTraversal() throws Exception {
|
||||
final Resource resource = this.context.getResource("classpath:testzipdata/zip-malicious-traversal.zip");
|
||||
final InputStream is = resource.getInputStream();
|
||||
byte[] zipdata = IOUtils.toByteArray(is);
|
||||
final Message<byte[]> message = MessageBuilder.withPayload(zipdata).build();
|
||||
assertThatExceptionOfType(MessageTransformationException.class)
|
||||
.isThrownBy(() -> input.send(message))
|
||||
.withRootCauseInstanceOf(ZipException.class)
|
||||
.withStackTraceContaining("is trying to leave the target output directory");
|
||||
}
|
||||
|
||||
private static void cleanupDirectory(File dir) {
|
||||
for (File file: dir.listFiles()) {
|
||||
if (file.isDirectory()) {
|
||||
cleanupDirectory(file);
|
||||
}
|
||||
file.delete();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:int="http://www.springframework.org/schema/integration"
|
||||
xmlns:int-zip="http://www.springframework.org/schema/integration/zip"
|
||||
xmlns:int-file="http://www.springframework.org/schema/integration/file"
|
||||
xmlns:context="http://www.springframework.org/schema/context"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd
|
||||
http://www.springframework.org/schema/integration/file https://www.springframework.org/schema/integration/file/spring-integration-file.xsd
|
||||
http://www.springframework.org/schema/integration/zip https://www.springframework.org/schema/integration/zip/spring-integration-zip.xsd
|
||||
http://www.springframework.org/schema/integration https://www.springframework.org/schema/integration/spring-integration.xsd">
|
||||
|
||||
<context:property-placeholder/>
|
||||
|
||||
<int:channel id="input"/>
|
||||
|
||||
<int-zip:zip-transformer input-channel="input" output-channel="write-file" result-type="BYTE_ARRAY">
|
||||
<int-zip:request-handler-advice-chain>
|
||||
<int:retry-advice/>
|
||||
</int-zip:request-handler-advice-chain>
|
||||
</int-zip:zip-transformer>
|
||||
|
||||
<int-file:outbound-channel-adapter id="write-file" directory="${workDir}" auto-create-directory="true"/>
|
||||
|
||||
</beans>
|
||||
@@ -0,0 +1,158 @@
|
||||
/*
|
||||
* Copyright 2015-2023 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;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.integration.file.FileHeaders;
|
||||
import org.springframework.integration.support.MessageBuilder;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Gunnar Hillert
|
||||
* @author Artem Bilan
|
||||
*
|
||||
* @since 6.1
|
||||
*/
|
||||
@SpringJUnitConfig
|
||||
@DirtiesContext
|
||||
public class Zip2FileTests {
|
||||
|
||||
@Autowired
|
||||
private MessageChannel input;
|
||||
|
||||
@TempDir
|
||||
public static File workDir;
|
||||
|
||||
@BeforeAll
|
||||
public static void setup() {
|
||||
System.setProperty("workDir", workDir.getAbsolutePath());
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
public void cleanUp() {
|
||||
for (File file : workDir.listFiles()) {
|
||||
file.delete();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void zipStringWithDefaultFileName() {
|
||||
final Message<String> message = MessageBuilder.withPayload("Zip me up.").build();
|
||||
input.send(message);
|
||||
assertThat(workDir.list()).hasSize(1);
|
||||
|
||||
File fileInWorkDir = workDir.listFiles()[0];
|
||||
|
||||
assertThat(fileInWorkDir.isFile()).isTrue();
|
||||
assertThat(fileInWorkDir.getName()).contains(message.getHeaders().getId().toString());
|
||||
assertThat(fileInWorkDir.getName()).endsWith(".zip");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void zipStringWithExplicitFileName() {
|
||||
input.send(MessageBuilder.withPayload("Zip me up.")
|
||||
.setHeader(FileHeaders.FILENAME, "zipString.zip")
|
||||
.build());
|
||||
|
||||
assertThat(workDir.list()).hasSize(1);
|
||||
assertThat(workDir.list()[0]).isEqualTo("zipString.zip");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void zipBytesWithExplicitFileName() {
|
||||
input.send(MessageBuilder.withPayload("Zip me up.".getBytes())
|
||||
.setHeader(FileHeaders.FILENAME, "zipString.zip")
|
||||
.build());
|
||||
|
||||
assertThat(workDir.list()).hasSize(1);
|
||||
assertThat(workDir.list()[0]).isEqualTo("zipString.zip");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void zipFile() throws IOException {
|
||||
File fileToCompress = File.createTempFile("test1", "tmp");
|
||||
FileUtils.writeStringToFile(fileToCompress, "hello world", Charset.defaultCharset());
|
||||
|
||||
input.send(MessageBuilder.withPayload(fileToCompress).build());
|
||||
|
||||
assertThat(workDir.list()).hasSize(1);
|
||||
assertThat(workDir.list()[0]).isEqualTo(fileToCompress.getName() + ".zip");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void zipIterableWithMultipleStrings() {
|
||||
String stringToCompress1 = "String1";
|
||||
String stringToCompress2 = "String2";
|
||||
String stringToCompress3 = "String3";
|
||||
String stringToCompress4 = "String4";
|
||||
|
||||
List<String> stringsToCompress = new ArrayList<>(4);
|
||||
|
||||
stringsToCompress.add(stringToCompress1);
|
||||
stringsToCompress.add(stringToCompress2);
|
||||
stringsToCompress.add(stringToCompress3);
|
||||
stringsToCompress.add(stringToCompress4);
|
||||
|
||||
input.send(MessageBuilder.withPayload(stringsToCompress)
|
||||
.setHeader(FileHeaders.FILENAME, "zipWith4Strings.zip")
|
||||
.build());
|
||||
|
||||
assertThat(workDir.list()).hasSize(1);
|
||||
assertThat(workDir.list()[0]).isEqualTo("zipWith4Strings.zip");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void zipIterableWithDifferentTypes() throws IOException {
|
||||
|
||||
String stringToCompress = "String1";
|
||||
byte[] bytesToCompress = "String2".getBytes();
|
||||
File fileToCompress = File.createTempFile("test2", "tmp");
|
||||
FileUtils.writeStringToFile(fileToCompress, "hello world", Charset.defaultCharset());
|
||||
|
||||
final List<Object> objectsToCompress = new ArrayList<>(3);
|
||||
|
||||
objectsToCompress.add(stringToCompress);
|
||||
objectsToCompress.add(bytesToCompress);
|
||||
objectsToCompress.add(fileToCompress);
|
||||
|
||||
input.send(MessageBuilder.withPayload(objectsToCompress)
|
||||
.setHeader(FileHeaders.FILENAME, "objects-to-compress.zip")
|
||||
.build());
|
||||
|
||||
assertThat(workDir.list()).hasSize(1);
|
||||
assertThat(workDir.list()[0]).isEqualTo("objects-to-compress.zip");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:int="http://www.springframework.org/schema/integration"
|
||||
xmlns:int-zip="http://www.springframework.org/schema/integration/zip"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/integration https://www.springframework.org/schema/integration/spring-integration.xsd
|
||||
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://www.springframework.org/schema/integration/zip https://www.springframework.org/schema/integration/zip/spring-integration-zip.xsd">
|
||||
|
||||
<int:channel id="input"/>
|
||||
<int:channel id="output"/>
|
||||
|
||||
<int-zip:unzip-transformer id="unzipTransformer"
|
||||
delete-files="true" input-channel="input" output-channel="output"
|
||||
result-type="FILE" expect-single-result="true"/>
|
||||
|
||||
<int-zip:unzip-transformer id="unzipTransformerWithDefaults"
|
||||
input-channel="input" output-channel="output" />
|
||||
</beans>
|
||||
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* Copyright 2015-2023 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.config.xml;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.integration.channel.AbstractMessageChannel;
|
||||
import org.springframework.integration.endpoint.EventDrivenConsumer;
|
||||
import org.springframework.integration.file.DefaultFileNameGenerator;
|
||||
import org.springframework.integration.file.FileNameGenerator;
|
||||
import org.springframework.integration.test.util.TestUtils;
|
||||
import org.springframework.integration.transformer.MessageTransformingHandler;
|
||||
import org.springframework.integration.zip.transformer.UnZipTransformer;
|
||||
import org.springframework.integration.zip.transformer.ZipResultType;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Gunnar Hillert
|
||||
* @author Artem Bilan
|
||||
*
|
||||
* @since 6.1
|
||||
*/
|
||||
@SpringJUnitConfig
|
||||
@DirtiesContext
|
||||
public class UnZipTransformerParserTests {
|
||||
|
||||
@Autowired
|
||||
private ConfigurableApplicationContext context;
|
||||
|
||||
@Test
|
||||
public void testUnZipTransformerParserWithDefaults() {
|
||||
EventDrivenConsumer consumer = this.context.getBean("unzipTransformerWithDefaults", EventDrivenConsumer.class);
|
||||
|
||||
final AbstractMessageChannel inputChannel = TestUtils.getPropertyValue(consumer, "inputChannel", AbstractMessageChannel.class);
|
||||
assertThat(inputChannel.getComponentName()).isEqualTo("input");
|
||||
|
||||
final MessageTransformingHandler handler = TestUtils.getPropertyValue(consumer, "handler", MessageTransformingHandler.class);
|
||||
|
||||
assertThat(TestUtils.getPropertyValue(handler, "outputChannelName")).isEqualTo("output");
|
||||
|
||||
final UnZipTransformer unZipTransformer = TestUtils.getPropertyValue(handler, "transformer", UnZipTransformer.class);
|
||||
|
||||
final Charset charset = TestUtils.getPropertyValue(unZipTransformer, "charset", Charset.class);
|
||||
final FileNameGenerator fileNameGenerator = TestUtils.getPropertyValue(unZipTransformer, "fileNameGenerator", FileNameGenerator.class);
|
||||
final ZipResultType zipResultType = TestUtils.getPropertyValue(unZipTransformer, "zipResultType", ZipResultType.class);
|
||||
final File workDirectory = TestUtils.getPropertyValue(unZipTransformer, "workDirectory", File.class);
|
||||
final Boolean deleteFiles = TestUtils.getPropertyValue(unZipTransformer, "deleteFiles", Boolean.class);
|
||||
final Boolean expectSingleResult = TestUtils.getPropertyValue(unZipTransformer, "expectSingleResult", Boolean.class);
|
||||
|
||||
assertThat(charset).isNotNull();
|
||||
assertThat(fileNameGenerator).isNotNull();
|
||||
assertThat(zipResultType).isNotNull();
|
||||
assertThat(workDirectory).isNotNull();
|
||||
assertThat(deleteFiles).isNotNull();
|
||||
assertThat(expectSingleResult).isNotNull();
|
||||
|
||||
assertThat(charset).isEqualTo(Charset.defaultCharset());
|
||||
assertThat(fileNameGenerator).isInstanceOf(DefaultFileNameGenerator.class);
|
||||
assertThat(zipResultType).isEqualTo(ZipResultType.FILE);
|
||||
assertThat(workDirectory)
|
||||
.isEqualTo(new File(System.getProperty("java.io.tmpdir") + File.separator + "ziptransformer"));
|
||||
assertThat(workDirectory.exists()).isTrue();
|
||||
assertThat(workDirectory.isDirectory()).isTrue();
|
||||
assertThat(deleteFiles).isFalse();
|
||||
assertThat(expectSingleResult).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUnZipTransformerParserWithExplicitSettings() {
|
||||
EventDrivenConsumer consumer = this.context.getBean("unzipTransformer", EventDrivenConsumer.class);
|
||||
|
||||
final AbstractMessageChannel inputChannel = TestUtils.getPropertyValue(consumer, "inputChannel", AbstractMessageChannel.class);
|
||||
assertThat(inputChannel.getComponentName()).isEqualTo("input");
|
||||
|
||||
final MessageTransformingHandler handler = TestUtils.getPropertyValue(consumer, "handler", MessageTransformingHandler.class);
|
||||
|
||||
assertThat(TestUtils.getPropertyValue(handler, "outputChannelName")).isEqualTo("output");
|
||||
|
||||
final UnZipTransformer unZipTransformer = TestUtils.getPropertyValue(handler, "transformer", UnZipTransformer.class);
|
||||
|
||||
final Charset charset = TestUtils.getPropertyValue(unZipTransformer, "charset", Charset.class);
|
||||
final FileNameGenerator fileNameGenerator = TestUtils.getPropertyValue(unZipTransformer, "fileNameGenerator", FileNameGenerator.class);
|
||||
final ZipResultType zipResultType = TestUtils.getPropertyValue(unZipTransformer, "zipResultType", ZipResultType.class);
|
||||
final File workDirectory = TestUtils.getPropertyValue(unZipTransformer, "workDirectory", File.class);
|
||||
final Boolean deleteFiles = TestUtils.getPropertyValue(unZipTransformer, "deleteFiles", Boolean.class);
|
||||
final Boolean expectSingleResult = TestUtils.getPropertyValue(unZipTransformer, "expectSingleResult", Boolean.class);
|
||||
|
||||
assertThat(charset).isNotNull();
|
||||
assertThat(fileNameGenerator).isNotNull();
|
||||
assertThat(zipResultType).isNotNull();
|
||||
assertThat(workDirectory).isNotNull();
|
||||
assertThat(deleteFiles).isNotNull();
|
||||
assertThat(expectSingleResult).isNotNull();
|
||||
|
||||
assertThat(charset).isEqualTo(Charset.defaultCharset());
|
||||
assertThat(fileNameGenerator).isInstanceOf(DefaultFileNameGenerator.class);
|
||||
assertThat(zipResultType).isEqualTo(ZipResultType.FILE);
|
||||
assertThat(workDirectory)
|
||||
.isEqualTo(new File(System.getProperty("java.io.tmpdir") + File.separator + "ziptransformer"));
|
||||
assertThat(workDirectory.exists()).isTrue();
|
||||
assertThat(workDirectory.isDirectory()).isTrue();
|
||||
assertThat(deleteFiles).isTrue();
|
||||
assertThat(expectSingleResult).isTrue();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:int="http://www.springframework.org/schema/integration"
|
||||
xmlns:int-zip="http://www.springframework.org/schema/integration/zip"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/integration https://www.springframework.org/schema/integration/spring-integration.xsd
|
||||
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://www.springframework.org/schema/integration/zip https://www.springframework.org/schema/integration/zip/spring-integration-zip.xsd">
|
||||
|
||||
<int:channel id="input"/>
|
||||
<int:channel id="output"/>
|
||||
|
||||
<int-zip:zip-transformer id="zipTransformer" compression-level="2"
|
||||
delete-files="true" input-channel="input" output-channel="output"
|
||||
result-type="BYTE_ARRAY"/>
|
||||
|
||||
<int-zip:zip-transformer id="zipTransformerWithDefaults"
|
||||
input-channel="input" output-channel="output" />
|
||||
</beans>
|
||||
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
* Copyright 2015-2023 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.config.xml;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.zip.Deflater;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.BeanCreationException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.support.ClassPathXmlApplicationContext;
|
||||
import org.springframework.integration.channel.AbstractMessageChannel;
|
||||
import org.springframework.integration.endpoint.EventDrivenConsumer;
|
||||
import org.springframework.integration.file.DefaultFileNameGenerator;
|
||||
import org.springframework.integration.file.FileNameGenerator;
|
||||
import org.springframework.integration.test.util.TestUtils;
|
||||
import org.springframework.integration.transformer.MessageTransformingHandler;
|
||||
import org.springframework.integration.zip.transformer.ZipResultType;
|
||||
import org.springframework.integration.zip.transformer.ZipTransformer;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
|
||||
/**
|
||||
* @author Gunnar Hillert
|
||||
* @author Artem Bilan
|
||||
*
|
||||
* @since 6.1
|
||||
*/
|
||||
@SpringJUnitConfig
|
||||
@DirtiesContext
|
||||
public class ZipTransformerParserTests {
|
||||
|
||||
@Autowired
|
||||
private ConfigurableApplicationContext context;
|
||||
|
||||
@Test
|
||||
public void testZipTransformerParserWithDefaults() {
|
||||
EventDrivenConsumer consumer = this.context.getBean("zipTransformerWithDefaults", EventDrivenConsumer.class);
|
||||
|
||||
final AbstractMessageChannel inputChannel = TestUtils.getPropertyValue(consumer, "inputChannel", AbstractMessageChannel.class);
|
||||
assertThat(inputChannel.getComponentName()).isEqualTo("input");
|
||||
|
||||
final MessageTransformingHandler handler = TestUtils.getPropertyValue(consumer, "handler", MessageTransformingHandler.class);
|
||||
|
||||
assertThat(TestUtils.getPropertyValue(handler, "outputChannelName")).isEqualTo("output");
|
||||
|
||||
final ZipTransformer zipTransformer = TestUtils.getPropertyValue(handler, "transformer", ZipTransformer.class);
|
||||
|
||||
final Charset charset = TestUtils.getPropertyValue(zipTransformer, "charset", Charset.class);
|
||||
final FileNameGenerator fileNameGenerator = TestUtils.getPropertyValue(zipTransformer, "fileNameGenerator", FileNameGenerator.class);
|
||||
final ZipResultType zipResultType = TestUtils.getPropertyValue(zipTransformer, "zipResultType", ZipResultType.class);
|
||||
final File workDirectory = TestUtils.getPropertyValue(zipTransformer, "workDirectory", File.class);
|
||||
final Integer compressionLevel = TestUtils.getPropertyValue(zipTransformer, "compressionLevel", Integer.class);
|
||||
final Boolean deleteFiles = TestUtils.getPropertyValue(zipTransformer, "deleteFiles", Boolean.class);
|
||||
|
||||
assertThat(charset).isNotNull();
|
||||
assertThat(fileNameGenerator).isNotNull();
|
||||
assertThat(zipResultType).isNotNull();
|
||||
assertThat(workDirectory).isNotNull();
|
||||
assertThat(deleteFiles).isNotNull();
|
||||
assertThat(compressionLevel).isNotNull();
|
||||
|
||||
assertThat(charset).isEqualTo(Charset.defaultCharset());
|
||||
assertThat(fileNameGenerator).isInstanceOf(DefaultFileNameGenerator.class);
|
||||
assertThat(zipResultType).isEqualTo(ZipResultType.FILE);
|
||||
assertThat(workDirectory)
|
||||
.isEqualTo(new File(System.getProperty("java.io.tmpdir") + File.separator + "ziptransformer"));
|
||||
assertThat(workDirectory.exists()).isTrue();
|
||||
assertThat(workDirectory.isDirectory()).isTrue();
|
||||
assertThat(deleteFiles).isFalse();
|
||||
assertThat(compressionLevel).isEqualTo(Deflater.DEFAULT_COMPRESSION);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testZipTransformerParserWithExplicitSettings() {
|
||||
EventDrivenConsumer consumer = this.context.getBean("zipTransformer", EventDrivenConsumer.class);
|
||||
|
||||
final AbstractMessageChannel inputChannel = TestUtils.getPropertyValue(consumer, "inputChannel", AbstractMessageChannel.class);
|
||||
assertThat(inputChannel.getComponentName()).isEqualTo("input");
|
||||
|
||||
final MessageTransformingHandler handler = TestUtils.getPropertyValue(consumer, "handler", MessageTransformingHandler.class);
|
||||
|
||||
assertThat(TestUtils.getPropertyValue(handler, "outputChannelName")).isEqualTo("output");
|
||||
|
||||
final ZipTransformer zipTransformer = TestUtils.getPropertyValue(handler, "transformer", ZipTransformer.class);
|
||||
|
||||
final Charset charset = TestUtils.getPropertyValue(zipTransformer, "charset", Charset.class);
|
||||
final FileNameGenerator fileNameGenerator = TestUtils.getPropertyValue(zipTransformer, "fileNameGenerator", FileNameGenerator.class);
|
||||
final ZipResultType zipResultType = TestUtils.getPropertyValue(zipTransformer, "zipResultType", ZipResultType.class);
|
||||
final File workDirectory = TestUtils.getPropertyValue(zipTransformer, "workDirectory", File.class);
|
||||
final Integer compressionLevel = TestUtils.getPropertyValue(zipTransformer, "compressionLevel", Integer.class);
|
||||
final Boolean deleteFiles = TestUtils.getPropertyValue(zipTransformer, "deleteFiles", Boolean.class);
|
||||
|
||||
assertThat(charset).isNotNull();
|
||||
assertThat(fileNameGenerator).isNotNull();
|
||||
assertThat(zipResultType).isNotNull();
|
||||
assertThat(workDirectory).isNotNull();
|
||||
assertThat(deleteFiles).isNotNull();
|
||||
assertThat(compressionLevel).isNotNull();
|
||||
|
||||
assertThat(charset).isEqualTo(Charset.defaultCharset());
|
||||
assertThat(fileNameGenerator).isInstanceOf(DefaultFileNameGenerator.class);
|
||||
assertThat(zipResultType).isEqualTo(ZipResultType.BYTE_ARRAY);
|
||||
assertThat(workDirectory)
|
||||
.isEqualTo(new File(System.getProperty("java.io.tmpdir") + File.separator + "ziptransformer"));
|
||||
assertThat(workDirectory.exists()).isTrue();
|
||||
assertThat(workDirectory.isDirectory()).isTrue();
|
||||
assertThat(compressionLevel).isEqualTo(2);
|
||||
assertThat(deleteFiles).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testZipTransformerParserWithIncorrectResultType() {
|
||||
|
||||
assertThatExceptionOfType(BeanCreationException.class)
|
||||
.isThrownBy(() ->
|
||||
new ClassPathXmlApplicationContext("ZipTransformerParserTestsWithIncorrectResultType.xml",
|
||||
getClass()))
|
||||
.withMessageContaining("Failed to convert property value of type 'java.lang.String' " +
|
||||
"to required type 'org.springframework.integration.zip.transformer.ZipResultType'");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:int="http://www.springframework.org/schema/integration"
|
||||
xmlns:int-zip="http://www.springframework.org/schema/integration/zip"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/integration https://www.springframework.org/schema/integration/spring-integration.xsd
|
||||
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://www.springframework.org/schema/integration/zip https://www.springframework.org/schema/integration/zip/spring-integration-zip.xsd">
|
||||
|
||||
<int:channel id="input"/>
|
||||
<int:channel id="output"/>
|
||||
|
||||
<int-zip:zip-transformer id="zipTransformer" compression-level="2"
|
||||
delete-files="true" input-channel="input" output-channel="output"
|
||||
result-type="INCORRECT"/>
|
||||
|
||||
</beans>
|
||||
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:int="http://www.springframework.org/schema/integration"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans
|
||||
https://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://www.springframework.org/schema/integration
|
||||
https://www.springframework.org/schema/integration/spring-integration.xsd">
|
||||
|
||||
<int:channel id="input"/>
|
||||
|
||||
<int:splitter input-channel="input" output-channel="output">
|
||||
<bean class="org.springframework.integration.zip.splitter.UnZipResultSplitter"/>
|
||||
</int:splitter>
|
||||
|
||||
<int:channel id="output">
|
||||
<int:queue/>
|
||||
</int:channel>
|
||||
|
||||
</beans>
|
||||
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
* Copyright 2016-2023 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.splitter;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.integration.channel.QueueChannel;
|
||||
import org.springframework.integration.file.FileHeaders;
|
||||
import org.springframework.integration.support.MessageBuilder;
|
||||
import org.springframework.integration.zip.ZipHeaders;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Andriy Kryvtsun
|
||||
* @author Artem Bilan
|
||||
*
|
||||
* @since 6.1
|
||||
*/
|
||||
@SpringJUnitConfig
|
||||
@DirtiesContext
|
||||
public class UnZipResultSplitterTests {
|
||||
|
||||
private static final String DIR_1 = "dir1/";
|
||||
|
||||
private static final String DIR_2 = "dir2/";
|
||||
|
||||
private static final String FILE_1 = "file1";
|
||||
|
||||
private static final String FILE_2 = "file2";
|
||||
|
||||
private static final String DATA_1 = "data1";
|
||||
|
||||
private static final String DATA_2 = "data2";
|
||||
|
||||
private static final int TIMEOUT = 10000;
|
||||
|
||||
@Autowired
|
||||
private MessageChannel input;
|
||||
|
||||
@Autowired
|
||||
private QueueChannel output;
|
||||
|
||||
@Test
|
||||
public void splitPreservingSourceMessageHeaderValues() {
|
||||
|
||||
final String headerName = "headerName";
|
||||
final String headerValue = "headerValue";
|
||||
|
||||
Message<?> inMessage = MessageBuilder.withPayload(createPayload())
|
||||
.setHeader(headerName, headerValue)
|
||||
.build();
|
||||
|
||||
input.send(inMessage);
|
||||
|
||||
Message<?> message1 = output.receive(TIMEOUT);
|
||||
checkMessageWithHeaderValue(message1, headerName, headerValue, DATA_1);
|
||||
|
||||
Message<?> message2 = output.receive(TIMEOUT);
|
||||
checkMessageWithHeaderValue(message2, headerName, headerValue, DATA_2);
|
||||
}
|
||||
|
||||
private static void checkMessageWithHeaderValue(Message<?> message, String headerName, String headerValue,
|
||||
String payload) {
|
||||
|
||||
assertThat(message).isNotNull();
|
||||
checkHeaderValue(message, headerName, headerValue);
|
||||
checkPayload(message, payload);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void splitPreservingServiceHeaderValues() {
|
||||
Message<?> inMessage = MessageBuilder.withPayload(createPayload())
|
||||
.setHeader(ZipHeaders.ZIP_ENTRY_PATH, "dir")
|
||||
.setHeader(FileHeaders.FILENAME, "filename")
|
||||
.build();
|
||||
|
||||
input.send(inMessage);
|
||||
|
||||
Message<?> message1 = output.receive(TIMEOUT);
|
||||
checkMessageWithServiceHeaderValues(message1, DIR_1, FILE_1, DATA_1);
|
||||
|
||||
Message<?> message2 = output.receive(TIMEOUT);
|
||||
checkMessageWithServiceHeaderValues(message2, DIR_2, FILE_2, DATA_2);
|
||||
}
|
||||
|
||||
private static void checkMessageWithServiceHeaderValues(Message<?> message, String path, String filename,
|
||||
String payload) {
|
||||
|
||||
assertThat(message).isNotNull();
|
||||
checkHeaderValue(message, ZipHeaders.ZIP_ENTRY_PATH, path);
|
||||
checkHeaderValue(message, FileHeaders.FILENAME, filename);
|
||||
checkPayload(message, payload);
|
||||
}
|
||||
|
||||
private static Map<String, Object> createPayload() {
|
||||
Map<String, Object> payload = new LinkedHashMap<>();
|
||||
payload.put(DIR_1 + FILE_1, DATA_1);
|
||||
payload.put(DIR_2 + FILE_2, DATA_2);
|
||||
return payload;
|
||||
}
|
||||
|
||||
private static void checkPayload(Message<?> message, String payload) {
|
||||
assertThat(message.getPayload()).isEqualTo(payload);
|
||||
}
|
||||
|
||||
private static void checkHeaderValue(Message<?> message, String headerName, String headerValue) {
|
||||
assertThat(message.getHeaders().get(headerName)).isEqualTo(headerValue);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,247 @@
|
||||
/*
|
||||
* Copyright 2015-2023 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.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
import org.zeroturnaround.zip.ZipException;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.integration.support.MessageBuilder;
|
||||
import org.springframework.integration.transformer.MessageTransformationException;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessagingException;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Gunnar Hillert
|
||||
* @author Artem Bilan
|
||||
* @author Ingo Dueppe
|
||||
*
|
||||
* @since 6.1
|
||||
*/
|
||||
@SpringJUnitConfig
|
||||
@DirtiesContext
|
||||
public class UnZipTransformerTests {
|
||||
|
||||
@TempDir
|
||||
public File workDir;
|
||||
|
||||
@Autowired
|
||||
private ResourceLoader resourceLoader;
|
||||
|
||||
@Test
|
||||
public void unzipFlatFileEntryZip() throws IOException {
|
||||
final Resource zipResource = this.resourceLoader.getResource("classpath:testzipdata/flatfileentry.zip");
|
||||
final InputStream is = zipResource.getInputStream();
|
||||
|
||||
final Message<InputStream> message = MessageBuilder.withPayload(is).build();
|
||||
|
||||
final UnZipTransformer unZipTransformer = new UnZipTransformer();
|
||||
unZipTransformer.setZipResultType(ZipResultType.FILE);
|
||||
unZipTransformer.afterPropertiesSet();
|
||||
|
||||
final Message<?> resultMessage = unZipTransformer.transform(message);
|
||||
|
||||
assertThat(resultMessage).isNotNull();
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, byte[]> unzippedData = (Map<String, byte[]>) resultMessage.getPayload();
|
||||
|
||||
assertThat(unzippedData).isNotNull().hasSize(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void unzipSingleFileAsInputStreamToByteArray() throws IOException {
|
||||
final Resource resource = this.resourceLoader.getResource("classpath:testzipdata/single.zip");
|
||||
final InputStream is = resource.getInputStream();
|
||||
|
||||
final Message<InputStream> message = MessageBuilder.withPayload(is).build();
|
||||
|
||||
final UnZipTransformer unZipTransformer = new UnZipTransformer();
|
||||
unZipTransformer.setZipResultType(ZipResultType.BYTE_ARRAY);
|
||||
unZipTransformer.afterPropertiesSet();
|
||||
|
||||
final Message<?> resultMessage = unZipTransformer.transform(message);
|
||||
|
||||
assertThat(resultMessage).isNotNull();
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, byte[]> unzippedData = (Map<String, byte[]>) resultMessage.getPayload();
|
||||
|
||||
assertThat(unzippedData).isNotNull().hasSize(1);
|
||||
assertThat(new String(unzippedData.values().iterator().next())).isEqualTo("Spring Integration Rocks!");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void unzipSingleFileToByteArray() throws IOException {
|
||||
final Resource resource = this.resourceLoader.getResource("classpath:testzipdata/single.zip");
|
||||
final InputStream is = resource.getInputStream();
|
||||
|
||||
final File inputFile = new File(this.workDir, "unzipSingleFileToByteArray");
|
||||
|
||||
FileOutputStream out = new FileOutputStream(inputFile);
|
||||
IOUtils.copy(is, out);
|
||||
is.close();
|
||||
out.close();
|
||||
|
||||
final Message<File> message = MessageBuilder.withPayload(inputFile).build();
|
||||
|
||||
final UnZipTransformer unZipTransformer = new UnZipTransformer();
|
||||
unZipTransformer.setZipResultType(ZipResultType.BYTE_ARRAY);
|
||||
unZipTransformer.afterPropertiesSet();
|
||||
|
||||
final Message<?> resultMessage = unZipTransformer.transform(message);
|
||||
|
||||
assertThat(resultMessage).isNotNull();
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, byte[]> unzippedData = (Map<String, byte[]>) resultMessage.getPayload();
|
||||
|
||||
assertThat(unzippedData).isNotNull().hasSize(1);
|
||||
assertThat(inputFile).exists();
|
||||
assertThat(new String(unzippedData.values().iterator().next())).isEqualTo("Spring Integration Rocks!");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void unzipSingleFileToByteArrayWithDeleteFilesTrue() throws IOException {
|
||||
final Resource resource = this.resourceLoader.getResource("classpath:testzipdata/single.zip");
|
||||
final InputStream is = resource.getInputStream();
|
||||
|
||||
final File inputFile = new File(this.workDir, "unzipSingleFileToByteArray");
|
||||
|
||||
FileOutputStream output = new FileOutputStream(inputFile);
|
||||
IOUtils.copy(is, output);
|
||||
is.close();
|
||||
output.close();
|
||||
|
||||
final Message<File> message = MessageBuilder.withPayload(inputFile).build();
|
||||
|
||||
final UnZipTransformer unZipTransformer = new UnZipTransformer();
|
||||
unZipTransformer.setZipResultType(ZipResultType.BYTE_ARRAY);
|
||||
unZipTransformer.setDeleteFiles(true);
|
||||
unZipTransformer.afterPropertiesSet();
|
||||
|
||||
final Message<?> resultMessage = unZipTransformer.transform(message);
|
||||
|
||||
assertThat(resultMessage).isNotNull();
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, byte[]> unzippedData = (Map<String, byte[]>) resultMessage.getPayload();
|
||||
|
||||
assertThat(unzippedData).isNotNull().hasSize(1);
|
||||
assertThat(inputFile).doesNotExist();
|
||||
assertThat(new String(unzippedData.values().iterator().next())).isEqualTo("Spring Integration Rocks!");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void unzipMultipleFilesAsInputStreamToByteArray() throws IOException {
|
||||
final Resource resource = this.resourceLoader.getResource("classpath:testzipdata/countries.zip");
|
||||
final InputStream is = resource.getInputStream();
|
||||
|
||||
final Message<InputStream> message = MessageBuilder.withPayload(is).build();
|
||||
|
||||
final UnZipTransformer unZipTransformer = new UnZipTransformer();
|
||||
unZipTransformer.setZipResultType(ZipResultType.BYTE_ARRAY);
|
||||
unZipTransformer.afterPropertiesSet();
|
||||
|
||||
final Message<?> resultMessage = unZipTransformer.transform(message);
|
||||
|
||||
assertThat(resultMessage).isNotNull();
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, byte[]> unzippedData = (Map<String, byte[]>) resultMessage.getPayload();
|
||||
|
||||
assertThat(unzippedData).isNotNull().hasSize(5);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void unzipMultipleFilesAsInputStreamWithExpectSingleResultTrue() throws IOException {
|
||||
final Resource resource = this.resourceLoader.getResource("classpath:testzipdata/countries.zip");
|
||||
final InputStream is = resource.getInputStream();
|
||||
|
||||
final Message<InputStream> message = MessageBuilder.withPayload(is).build();
|
||||
|
||||
final UnZipTransformer unZipTransformer = new UnZipTransformer();
|
||||
unZipTransformer.setZipResultType(ZipResultType.BYTE_ARRAY);
|
||||
unZipTransformer.setExpectSingleResult(true);
|
||||
unZipTransformer.afterPropertiesSet();
|
||||
|
||||
|
||||
assertThatExceptionOfType(MessagingException.class)
|
||||
.isThrownBy(() -> unZipTransformer.transform(message))
|
||||
.withStackTraceContaining("The UnZip operation extracted 5 result objects " +
|
||||
"but expectSingleResult was 'true'.");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void unzipInvalidZipFile() throws IOException {
|
||||
File fileToUnzip = File.createTempFile("test1", "tmp");
|
||||
FileUtils.writeStringToFile(fileToUnzip, "hello world", Charset.defaultCharset());
|
||||
|
||||
UnZipTransformer unZipTransformer = new UnZipTransformer();
|
||||
unZipTransformer.setZipResultType(ZipResultType.BYTE_ARRAY);
|
||||
unZipTransformer.setExpectSingleResult(true);
|
||||
unZipTransformer.afterPropertiesSet();
|
||||
|
||||
Message<File> message = MessageBuilder.withPayload(fileToUnzip).build();
|
||||
|
||||
assertThatExceptionOfType(MessagingException.class)
|
||||
.isThrownBy(() -> unZipTransformer.transform(message))
|
||||
.withStackTraceContaining(String.format("Not a zip file: %s", fileToUnzip.getAbsolutePath()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUnzipMaliciousTraversalZipFile() throws IOException {
|
||||
final Resource resource = this.resourceLoader.getResource("classpath:testzipdata/zip-malicious-traversal.zip");
|
||||
final InputStream is = resource.getInputStream();
|
||||
|
||||
final Message<InputStream> message = MessageBuilder.withPayload(is).build();
|
||||
|
||||
final UnZipTransformer unZipTransformer = new UnZipTransformer();
|
||||
unZipTransformer.afterPropertiesSet();
|
||||
|
||||
|
||||
assertThatExceptionOfType(MessageTransformationException.class)
|
||||
.isThrownBy(() -> unZipTransformer.transform(message))
|
||||
.withRootCauseInstanceOf(ZipException.class)
|
||||
.withStackTraceContaining("is trying to leave the target output directory");
|
||||
}
|
||||
|
||||
@Configuration
|
||||
public static class TestConfiguration {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,246 @@
|
||||
/*
|
||||
* Copyright 2015-2023 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.RandomAccessFile;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
import org.zeroturnaround.zip.ZipUtil;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.integration.support.MessageBuilder;
|
||||
import org.springframework.integration.zip.ZipHeaders;
|
||||
import org.springframework.messaging.Message;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Gunnar Hillert
|
||||
* @author Artem Bilan
|
||||
*
|
||||
* @since 6.1
|
||||
*/
|
||||
public class ZipTransformerTests {
|
||||
|
||||
@TempDir
|
||||
public File workDir;
|
||||
|
||||
@Test
|
||||
public void zipString() {
|
||||
final ZipTransformer zipTransformer = new ZipTransformer();
|
||||
zipTransformer.setBeanFactory(mock(BeanFactory.class));
|
||||
zipTransformer.setZipResultType(ZipResultType.BYTE_ARRAY);
|
||||
zipTransformer.afterPropertiesSet();
|
||||
|
||||
final String stringToCompress = "Hello World";
|
||||
|
||||
final Date fileDate = new Date();
|
||||
|
||||
final Message<String> message = MessageBuilder.withPayload(stringToCompress)
|
||||
.setHeader(ZipHeaders.ZIP_ENTRY_FILE_NAME, "test.txt")
|
||||
.setHeader(ZipHeaders.ZIP_ENTRY_LAST_MODIFIED_DATE, fileDate)
|
||||
.build();
|
||||
|
||||
final Message<?> result = zipTransformer.transform(message);
|
||||
|
||||
Object resultPayload = result.getPayload();
|
||||
|
||||
assertThat(resultPayload).isInstanceOf(byte[].class);
|
||||
|
||||
ZipUtil.unpack(new ByteArrayInputStream((byte[]) resultPayload), this.workDir);
|
||||
|
||||
final File unzippedEntry = new File(this.workDir, "test.txt");
|
||||
assertThat(unzippedEntry).exists().isFile();
|
||||
|
||||
//See https://stackoverflow.com/questions/3725662/what-is-the-earliest-timestamp-value-that-is-supported-in-zip-file-format
|
||||
assertThat(unzippedEntry.lastModified())
|
||||
.isGreaterThan(fileDate.getTime() - 3000)
|
||||
.isLessThan(fileDate.getTime() + 3000);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void zipStringCollection() {
|
||||
final ZipTransformer zipTransformer = new ZipTransformer();
|
||||
zipTransformer.setBeanFactory(mock(BeanFactory.class));
|
||||
zipTransformer.setZipResultType(ZipResultType.BYTE_ARRAY);
|
||||
zipTransformer.afterPropertiesSet();
|
||||
|
||||
final String string1ToCompress = "Cartman";
|
||||
final String string2ToCompress = "Kenny";
|
||||
final String string3ToCompress = "Butters";
|
||||
|
||||
final List<String> strings = new ArrayList<>(3);
|
||||
|
||||
strings.add(string1ToCompress);
|
||||
strings.add(string2ToCompress);
|
||||
strings.add(string3ToCompress);
|
||||
|
||||
final Date fileDate = new Date();
|
||||
|
||||
final Message<List<String>> message = MessageBuilder.withPayload(strings)
|
||||
.setHeader(ZipHeaders.ZIP_ENTRY_FILE_NAME, "test.txt")
|
||||
.setHeader(ZipHeaders.ZIP_ENTRY_LAST_MODIFIED_DATE, fileDate)
|
||||
.build();
|
||||
|
||||
final Message<?> result = zipTransformer.transform(message);
|
||||
|
||||
Object resultPayload = result.getPayload();
|
||||
|
||||
assertThat(resultPayload).isInstanceOf(byte[].class);
|
||||
|
||||
ZipUtil.unpack(new ByteArrayInputStream((byte[]) resultPayload), this.workDir);
|
||||
|
||||
File[] files = this.workDir.listFiles();
|
||||
|
||||
assertThat(files).hasSizeGreaterThanOrEqualTo(3);
|
||||
|
||||
final Set<String> expectedFileNames = new HashSet<>();
|
||||
|
||||
expectedFileNames.add("test_1.txt");
|
||||
expectedFileNames.add("test_2.txt");
|
||||
expectedFileNames.add("test_3.txt");
|
||||
|
||||
for (File file : files) {
|
||||
if (file.getName().startsWith("test")) {
|
||||
assertThat(file).exists().isFile();
|
||||
|
||||
//See https://stackoverflow.com/questions/3725662/what-is-the-earliest-timestamp-value-that-is-supported-in-zip-file-format
|
||||
assertThat(file.lastModified())
|
||||
.isLessThan(fileDate.getTime() + 4000)
|
||||
.isGreaterThan(fileDate.getTime() - 4000);
|
||||
|
||||
assertThat(file).hasExtension("txt");
|
||||
|
||||
assertThat(expectedFileNames).contains(file.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void zipStringToFile() {
|
||||
final ZipTransformer zipTransformer = new ZipTransformer();
|
||||
zipTransformer.setBeanFactory(mock(BeanFactory.class));
|
||||
zipTransformer.afterPropertiesSet();
|
||||
|
||||
final String stringToCompress = "Hello World";
|
||||
|
||||
final String zipEntryFileName = "test.txt";
|
||||
final Message<String> message = MessageBuilder.withPayload(stringToCompress)
|
||||
.setHeader(ZipHeaders.ZIP_ENTRY_FILE_NAME, zipEntryFileName)
|
||||
.build();
|
||||
|
||||
final Message<?> result = zipTransformer.transform(message);
|
||||
|
||||
assertThat(result.getPayload()).isInstanceOf(File.class);
|
||||
|
||||
final File payload = (File) result.getPayload();
|
||||
|
||||
assertThat(payload).hasName(message.getHeaders().getId().toString() + ".msg.zip");
|
||||
assertThat((SpringZipUtils.isValid(payload))).isTrue();
|
||||
|
||||
final byte[] zipEntryData = ZipUtil.unpackEntry(payload, "test.txt");
|
||||
|
||||
assertThat(zipEntryData).isNotNull();
|
||||
assertThat(new String(zipEntryData)).isEqualTo("Hello World");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void zipFile() {
|
||||
|
||||
ZipTransformer zipTransformer = new ZipTransformer();
|
||||
zipTransformer.setBeanFactory(mock(BeanFactory.class));
|
||||
zipTransformer.setDeleteFiles(true);
|
||||
zipTransformer.afterPropertiesSet();
|
||||
|
||||
final File testFile = createTestFile(10);
|
||||
|
||||
assertThat(testFile).exists();
|
||||
|
||||
final Message<File> message = MessageBuilder.withPayload(testFile).build();
|
||||
|
||||
final Message<?> result = zipTransformer.transform(message);
|
||||
|
||||
assertThat(result.getPayload()).isInstanceOf(File.class);
|
||||
|
||||
final File payload = (File) result.getPayload();
|
||||
|
||||
assertThat(payload).hasName(testFile.getName() + ".zip");
|
||||
assertThat(SpringZipUtils.isValid(payload)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void zipCollection() {
|
||||
|
||||
final File testFile1 = createTestFile(1);
|
||||
final File testFile2 = createTestFile(2);
|
||||
final File testFile3 = createTestFile(3);
|
||||
final File testFile4 = createTestFile(4);
|
||||
|
||||
assertThat(testFile1).exists();
|
||||
assertThat(testFile2).exists();
|
||||
assertThat(testFile3).exists();
|
||||
assertThat(testFile4).exists();
|
||||
|
||||
final Collection<File> files = new ArrayList<>();
|
||||
|
||||
files.add(testFile1);
|
||||
files.add(testFile2);
|
||||
files.add(testFile3);
|
||||
files.add(testFile4);
|
||||
|
||||
final ZipTransformer zipTransformer = new ZipTransformer();
|
||||
zipTransformer.setBeanFactory(mock(BeanFactory.class));
|
||||
zipTransformer.afterPropertiesSet();
|
||||
|
||||
final Message<Collection<File>> message = MessageBuilder.withPayload(files).build();
|
||||
|
||||
final Message<?> result = zipTransformer.transform(message);
|
||||
|
||||
assertThat(result.getPayload()).isInstanceOf(File.class);
|
||||
|
||||
final File outputZipFile = (File) result.getPayload();
|
||||
|
||||
assertThat(outputZipFile).exists().isFile().hasExtension("zip");
|
||||
assertThat(SpringZipUtils.isValid(outputZipFile)).isTrue();
|
||||
}
|
||||
|
||||
private File createTestFile(int size) {
|
||||
final File testFile = new File(this.workDir, "testdata" + UUID.randomUUID().toString() + ".data");
|
||||
|
||||
try (RandomAccessFile f = new RandomAccessFile(testFile, "rw")) {
|
||||
f.setLength((long) size * 1024 * 1024);
|
||||
}
|
||||
catch (Exception e) {
|
||||
// Ignore
|
||||
}
|
||||
return testFile;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
15
spring-integration-zip/src/test/resources/log4j2-test.xml
Normal file
15
spring-integration-zip/src/test/resources/log4j2-test.xml
Normal file
@@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Configuration status="WARN">
|
||||
<Appenders>
|
||||
<Console name="STDOUT" target="SYSTEM_OUT">
|
||||
<PatternLayout pattern="%d %p [%t] [%c] - %m%n" />
|
||||
</Console>
|
||||
</Appenders>
|
||||
<Loggers>
|
||||
<Logger name="org.springframework.integration" level="warn"/>
|
||||
<Logger name="org.springframework.integration.zip" level="warn"/>
|
||||
<Root level="warn">
|
||||
<AppenderRef ref="STDOUT" />
|
||||
</Root>
|
||||
</Loggers>
|
||||
</Configuration>
|
||||
Binary file not shown.
@@ -0,0 +1 @@
|
||||
Asia
|
||||
@@ -0,0 +1 @@
|
||||
Europe
|
||||
@@ -0,0 +1 @@
|
||||
Germany
|
||||
@@ -0,0 +1 @@
|
||||
France
|
||||
@@ -0,0 +1 @@
|
||||
Poland
|
||||
@@ -0,0 +1 @@
|
||||
Spring Integration Rocks!
|
||||
Binary file not shown.
@@ -0,0 +1 @@
|
||||
Spring Integration Rocks!
|
||||
BIN
spring-integration-zip/src/test/resources/testzipdata/single.zip
Normal file
BIN
spring-integration-zip/src/test/resources/testzipdata/single.zip
Normal file
Binary file not shown.
Binary file not shown.
@@ -17,6 +17,11 @@ In general the project has been moved to the latest dependency versions.
|
||||
[[x6.1-new-components]]
|
||||
=== New Components
|
||||
|
||||
[[x6.1-zip]]
|
||||
==== Zip Support
|
||||
|
||||
The Zip Spring Integration Extension project has been migrated as the `spring-integration-zip` module.
|
||||
See <<./zip.adoc#zip,Zip Support>> for more information.
|
||||
|
||||
|
||||
[[x6.1-general]]
|
||||
|
||||
272
src/reference/asciidoc/zip.adoc
Normal file
272
src/reference/asciidoc/zip.adoc
Normal file
@@ -0,0 +1,272 @@
|
||||
[[zip]]
|
||||
== Zip Support
|
||||
|
||||
This Spring Integration module provides https://en.wikipedia.org/wiki/ZIP_(file_format)[Zip] (un-)compression support.
|
||||
A zipping algorithm implementation is based on a https://github.com/zeroturnaround/zt-zip[ZeroTurnaround ZIP Library].
|
||||
The following components are provided:
|
||||
|
||||
* <<zip-transformer,Zip Transformer>>
|
||||
* <<zip-transformer,UnZip Transformer>>
|
||||
* <<unzip-splitter,UnZip Result Splitter>>
|
||||
|
||||
You need to include this dependency into your project:
|
||||
|
||||
====
|
||||
[source, xml, subs="normal", role="primary"]
|
||||
.Maven
|
||||
----
|
||||
<dependency>
|
||||
<groupId>org.springframework.integration</groupId>
|
||||
<artifactId>spring-integration-zip</artifactId>
|
||||
<version>{project-version}</version>
|
||||
</dependency>
|
||||
----
|
||||
|
||||
[source, groovy, subs="normal", role="secondary"]
|
||||
.Gradle
|
||||
----
|
||||
compile "org.springframework.integration:spring-integration-zip:{project-version}"
|
||||
----
|
||||
====
|
||||
|
||||
[[xpath-namespace-support]]
|
||||
=== Namespace Support
|
||||
|
||||
All components within the Spring Integration Zip module provide namespace support.
|
||||
In order to enable namespace support, you need to import the schema for the Spring Integration Zip Module.
|
||||
The following example shows a typical setup:
|
||||
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:int="http://www.springframework.org/schema/integration"
|
||||
xmlns:int-zip="http://www.springframework.org/schema/integration/zip"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans
|
||||
https://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://www.springframework.org/schema/integration
|
||||
https://www.springframework.org/schema/integration/spring-integration.xsd
|
||||
http://www.springframework.org/schema/integration/zip
|
||||
https://www.springframework.org/schema/integration/zip/spring-integration-zip.xsd">
|
||||
</beans>
|
||||
----
|
||||
====
|
||||
|
||||
[[zip-transformer]]
|
||||
=== (Un)Zip Transformer
|
||||
|
||||
The `ZipTransformer` implements a zipping functionality for these types of input `payload`: `File`, `String`, `byte[]` and `Iterable`.
|
||||
In input data types can be mixed as part of an `Iterable`.
|
||||
For example, it should be easily to compress a collection containing Strings, byte arrays and Files.
|
||||
It is important to note that nested Iterables are *NOT SUPPORTED* at present time.
|
||||
|
||||
The `ZipTransformer` can be customized by setting several properties:
|
||||
|
||||
* `compressionLevel` - sets the compression level.
|
||||
Default is `Deflater#DEFAULT_COMPRESSION`.
|
||||
|
||||
* `useFileAttributes` - specifies whether the name of the file shall be used for the zip entry.
|
||||
|
||||
For example to zip a simple `test.txt` file into a `test.txt.zip`, only this configuration is enough:
|
||||
|
||||
====
|
||||
[source, java, role="primary"]
|
||||
.Java DSL
|
||||
----
|
||||
@Bean
|
||||
public IntegrationFlow zipFlow() {
|
||||
return IntegrationFlow
|
||||
.from("zipChannel")
|
||||
.transform(new ZipTransformer())
|
||||
.get();
|
||||
}
|
||||
----
|
||||
[source, kotlin, role="secondary"]
|
||||
.Kotlin DSL
|
||||
----
|
||||
@Bean
|
||||
fun zipFlow() =
|
||||
integrationFlow("zipChannel") {
|
||||
transform(ZipTransformer())
|
||||
}
|
||||
----
|
||||
[source, groovy, role="secondary"]
|
||||
.Groovy DSL
|
||||
----
|
||||
@Bean
|
||||
zipFlow() {
|
||||
integrationFlow 'zipChannel',
|
||||
{
|
||||
transform new ZipTransformer()
|
||||
}
|
||||
}
|
||||
----
|
||||
[source, java, role="secondary"]
|
||||
.Java
|
||||
----
|
||||
@Transfomer(inputChannel = "zipChannel")
|
||||
@Bean
|
||||
ZipTransformer zipTransformer() {
|
||||
return new ZipTransformer();
|
||||
}
|
||||
----
|
||||
[source, xml, role="secondary"]
|
||||
.XML
|
||||
----
|
||||
<int-zip:zip-transformer input-channel="zipChannel"/>
|
||||
----
|
||||
====
|
||||
|
||||
See `ZipTransformer` Javadocs for more information.
|
||||
|
||||
An `UnZipTransformer` supports these of input `payload`: `File`, `byte[]` and `InputStream`.
|
||||
When unzipping data, an `expectSingleResult` property can be specified.
|
||||
If set to `true` and more than `1` zip entry were detected, a `MessagingException` will be raised.
|
||||
This property also influences the return type of the payload.
|
||||
If set to `false` (default), then the payload will be of type `SortedMap`, if `true`, however, the actual zip entry will be returned.
|
||||
|
||||
Other properties that can be set on the `UnZipTransformer`:
|
||||
|
||||
* `deleteFiles` - if the payload is an instance of `File`, this property specifies whether to delete the File after transformation.
|
||||
Default is `false`.
|
||||
|
||||
* `ZipResultType` - defines the format of the data returned after transformation.
|
||||
Available options are: `File`, `byte[]`.
|
||||
|
||||
* `workDirectory` - the work directory is used when a `ZipResultType` is set to `ZipResultType.FILE`.
|
||||
By default, this property is set to the System temporary directory containing a subdirectory `ziptransformer`.
|
||||
|
||||
For example to zip a simple `test.zip` file into a map of its entries, only this configuration is enough:
|
||||
|
||||
====
|
||||
[source, java, role="primary"]
|
||||
.Java DSL
|
||||
----
|
||||
@Bean
|
||||
public IntegrationFlow unzipFlow() {
|
||||
return IntegrationFlow
|
||||
.from("unzipChannel")
|
||||
.transform(new UnZipTransformer())
|
||||
.get();
|
||||
}
|
||||
----
|
||||
[source, kotlin, role="secondary"]
|
||||
.Kotlin DSL
|
||||
----
|
||||
@Bean
|
||||
fun unzipFlow() =
|
||||
integrationFlow("unzipChannel") {
|
||||
transform(UnZipTransformer())
|
||||
}
|
||||
----
|
||||
[source, groovy, role="secondary"]
|
||||
.Groovy DSL
|
||||
----
|
||||
@Bean
|
||||
unzipFlow() {
|
||||
integrationFlow 'unzipChannel',
|
||||
{
|
||||
transform new UnZipTransformer()
|
||||
}
|
||||
}
|
||||
----
|
||||
[source, java, role="secondary"]
|
||||
.Java
|
||||
----
|
||||
@Transfomer(inputChannel = "unzipChannel")
|
||||
@Bean
|
||||
UnZipTransformer unzipTransformer() {
|
||||
return new UnZipTransformer();
|
||||
}
|
||||
----
|
||||
[source, xml, role="secondary"]
|
||||
.XML
|
||||
----
|
||||
<int-zip:unzip-transformer input-channel="unzipChannel"/>
|
||||
----
|
||||
====
|
||||
|
||||
[[unzip-splitter]]
|
||||
=== Unzipped Splitter
|
||||
|
||||
The `UnZipResultSplitter` is useful in cases where zip files contain more than `1` entry.
|
||||
Essentially it has to be used as the next step in the integration flow after the mentioned above `UnZipTransformer`.
|
||||
It supports only a `Map` as an input data and emits every entry into an `outputChannel` with `FileHeaders.FILENAME` and `ZipHeaders.ZIP_ENTRY_PATH` headers.
|
||||
|
||||
The following example demonstrates a simple configuration for splitting unzipped result:
|
||||
|
||||
====
|
||||
[source, java, role="primary"]
|
||||
.Java DSL
|
||||
----
|
||||
@Bean
|
||||
public IntegrationFlow unzipSplitFlow(Executor executor) {
|
||||
return IntegrationFlow
|
||||
.from("unzipChannel")
|
||||
.transform(new UnZipTransformer())
|
||||
.split(new UnZipResultSplitter())
|
||||
.channel(c -> c.executor("entriesChannel", executor))
|
||||
.get();
|
||||
}
|
||||
----
|
||||
[source, kotlin, role="secondary"]
|
||||
.Kotlin DSL
|
||||
----
|
||||
@Bean
|
||||
fun unzipFlow(executor: Executor) =
|
||||
integrationFlow("unzipChannel") {
|
||||
transform(UnZipTransformer())
|
||||
split(UnZipResultSplitter())
|
||||
channel { executor("entriesChannel", executor) }
|
||||
}
|
||||
----
|
||||
[source, groovy, role="secondary"]
|
||||
.Groovy DSL
|
||||
----
|
||||
@Bean
|
||||
unzipFlow(Executor executor) {
|
||||
integrationFlow 'unzipChannel',
|
||||
{
|
||||
transform new UnZipTransformer()
|
||||
split new UnZipResultSplitter()
|
||||
channel { executor 'entriesChannel', executor }
|
||||
}
|
||||
}
|
||||
----
|
||||
[source, java, role="secondary"]
|
||||
.Java
|
||||
----
|
||||
@Transfomer(inputChannel = "unzipChannel", outputChannel = "splitChannel")
|
||||
@Bean
|
||||
UnZipTransformer unzipTransformer() {
|
||||
return new UnZipTransformer();
|
||||
}
|
||||
|
||||
@Spitter(inputChannel = "splitChannel", outputChannel = "entriesChannel")
|
||||
@Bean
|
||||
UnZipResultSplitter unZipSplitter() {
|
||||
return new UnZipResultSplitter();
|
||||
}
|
||||
|
||||
@Bean
|
||||
ExecutorChannel entriesChannel(Executor executor) {
|
||||
return new ExecutorChannel(executor);
|
||||
}
|
||||
----
|
||||
[source, xml, role="secondary"]
|
||||
.XML
|
||||
----
|
||||
<int:chain input-channel="unzipChannel" output-channel="entriesChannel">
|
||||
<int-zip:unzip-transformer/>
|
||||
<int:splitter>
|
||||
<bean class="org.springframework.integration.zip.splitter.UnZipResultSplitter"/>
|
||||
</int:splitter>
|
||||
</int:chain>
|
||||
|
||||
<int:channel id="entriesChannel">
|
||||
<int:dispatcher task-executor="executor"/>
|
||||
</int:channel>
|
||||
----
|
||||
====
|
||||
Reference in New Issue
Block a user