diff --git a/.gitignore b/.gitignore index 6c4962e..6a5b018 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,5 @@ target spring-integration-aws/src/test/resources/awscredentials.properties *.orig /spring-integration-java-dsl/hostkey.ser +.springBeans + diff --git a/README.md b/README.md index 865987f..cc1c907 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ The Spring Integration Extensions project provides extension modules for [Spring * [Splunk][] Support * [Voldemort][] Support * [XQuery][] Support - +* Zip Support (Compression and Uncompression) ## Samples diff --git a/samples/zip/README.md b/samples/zip/README.md new file mode 100644 index 0000000..cf5fd47 --- /dev/null +++ b/samples/zip/README.md @@ -0,0 +1,56 @@ +Spring Integration Zip Sample +=================== + +This sample illustrates the usage of the Spring Integration Zip Extension. It uses the following components: + +* zip-transformer +* unzip-transformer + +You can run the application by either + +* running the "Main" class from within STS (Right-click on Main class --> Run As --> Java Application) +* or from the command line: + - mvn package + - mvn exec:java + +You should see a screen as the following: + +``` +========================================================= + + Welcome to the Spring Integration Zip Sample + + For more information please visit: + http://www.springsource.org/spring-integration + +========================================================= +17:08:41.883 INFO [org.springframework.integration.samples.zip.Main.main()][org.springframework.integration.samples.zip.SpringIntegrationUtils] +========================================================= + + Intput directory is: '/dev/spring-integration-extensions/samples/zip/input-zip' + Intput directory is: '/dev/spring-integration-extensions/samples/zip/input-uncompressed' + Output directory is: 'target/output/decompressedFilesOut' + Output directory is: 'target/output/zipFilesOut' + +========================================================= +17:08:41.887 INFO [org.springframework.integration.samples.zip.Main.main()][org.springframework.integration.samples.zip.Main] +========================================================= + + Please press 'q + Enter' to quit the application. + +========================================================= +``` +## Compressing Files + +Drop an uncompressed file into the **input-uncompressed** directory. The file will be compressed and stored under **target/output/zipFilesOut**. + +## Uncompressing Files + +Drop a compressed file into the **input-zip** directory. The file will be decompressed and stored under **target/output/decompressedFilesOut**. + +-------------------------------------------------------------------------------- + +For help please take a look at the Spring Integration documentation: + +http://www.springsource.org/spring-integration + diff --git a/samples/zip/pom.xml b/samples/zip/pom.xml new file mode 100644 index 0000000..b396dec --- /dev/null +++ b/samples/zip/pom.xml @@ -0,0 +1,119 @@ + + 4.0.0 + + org.springframework.integration.samples + zip-sample + 1.0.0.BUILD-SNAPSHOT + jar + + zip-sample + http://projects.spring.io/spring-integration/ + + + 2.2.1 + + + + UTF-8 + 1.0.0.BUILD-SNAPSHOT + 1.2.17 + 4.11 + + + + + repo.spring.io.milestone + Spring Framework Maven Milestone Repository + https://repo.spring.io/libs-milestone + + + + + + + maven-eclipse-plugin + 2.9 + + + org.springframework.ide.eclipse.core.springnature + + + org.springframework.ide.eclipse.core.springbuilder + + true + true + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.0 + + 1.6 + 1.6 + -Xlint:all + true + true + + + + org.codehaus.mojo + exec-maven-plugin + 1.2.1 + + org.springframework.integration.samples.zip.Main + + + + + + + + + + + junit + junit + ${junit.version} + test + + + + + + org.springframework.integration + spring-integration-zip + ${spring.integration.zip.version} + + + + org.springframework + spring-core + 4.2.0.RC1 + + + + + + log4j + log4j + ${log4j.version} + + + + + + commons-io + commons-io + 2.4 + + + + org.slf4j + slf4j-jcl + 1.7.12 + + + + diff --git a/samples/zip/src/main/java/org/springframework/integration/samples/zip/Main.java b/samples/zip/src/main/java/org/springframework/integration/samples/zip/Main.java new file mode 100644 index 0000000..cbd3e69 --- /dev/null +++ b/samples/zip/src/main/java/org/springframework/integration/samples/zip/Main.java @@ -0,0 +1,84 @@ +/* + * Copyright 2015 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 + * + * http://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.samples.zip; + +import java.util.Scanner; + +import org.apache.log4j.Logger; +import org.springframework.context.support.AbstractApplicationContext; +import org.springframework.context.support.ClassPathXmlApplicationContext; + + +/** + * Starts the Spring Context and will initialize the Spring Integration routes. + * + * @author Gunnar Hillert + * @since 1.0 + * + */ +public final class Main { + + private static final Logger LOGGER = Logger.getLogger(Main.class); + + private Main() { } + + /** + * Load the Spring Integration Application Context + * + * @param args - command line arguments + */ + public static void main(final String... args) { + + if (LOGGER.isInfoEnabled()) { + LOGGER.info("\n=========================================================" + + "\n " + + "\n Welcome to the Spring Integration Zip Sample " + + "\n " + + "\n For more information please visit: " + + "\n http://www.springsource.org/spring-integration " + + "\n " + + "\n=========================================================" ); + } + + final AbstractApplicationContext context = + new ClassPathXmlApplicationContext("classpath:META-INF/spring/integration/*-context.xml"); + + context.registerShutdownHook(); + + SpringIntegrationUtils.displayDirectories(context); + + final Scanner scanner = new Scanner(System.in); + + if (LOGGER.isInfoEnabled()) { + LOGGER.info("\n=========================================================" + + "\n " + + "\n Please press 'q + Enter' to quit the application. " + + "\n " + + "\n=========================================================" ); + } + + while (!scanner.hasNext("q")) { + //Do nothing unless user presses 'q' to quit. + } + + if (LOGGER.isInfoEnabled()) { + LOGGER.info("Exiting application...bye."); + } + + System.exit(0); + + } +} diff --git a/samples/zip/src/main/java/org/springframework/integration/samples/zip/SpringIntegrationUtils.java b/samples/zip/src/main/java/org/springframework/integration/samples/zip/SpringIntegrationUtils.java new file mode 100644 index 0000000..30b93e4 --- /dev/null +++ b/samples/zip/src/main/java/org/springframework/integration/samples/zip/SpringIntegrationUtils.java @@ -0,0 +1,91 @@ +/* + * Copyright 2015 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 + * + * http://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.samples.zip; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.beans.DirectFieldAccessor; +import org.springframework.context.ApplicationContext; +import org.springframework.expression.Expression; +import org.springframework.integration.file.FileReadingMessageSource; +import org.springframework.integration.file.FileWritingMessageHandler; + +/** + * Displays the names of the input and output directories. + * + * @author Gunnar Hillert + * @since 1.0 + * + */ +public final class SpringIntegrationUtils { + + private static final Log logger = LogFactory.getLog(SpringIntegrationUtils.class); + + private SpringIntegrationUtils() { } + + /** + * Helper Method to dynamically determine and display input and output + * directories as defined in the Spring Integration context. + * + * @param context Spring Application Context + */ + public static void displayDirectories(final ApplicationContext context) { + + final Map fileReadingMessageSources = context.getBeansOfType(FileReadingMessageSource.class); + + final List inputDirectories = new ArrayList(); + + for (FileReadingMessageSource source : fileReadingMessageSources.values()) { + final File inDir = (File) new DirectFieldAccessor(source).getPropertyValue("directory"); + inputDirectories.add(inDir.getAbsolutePath()); + } + + + final Map fileWritingMessageHandlers = context.getBeansOfType(FileWritingMessageHandler.class); + + final List outputDirectories = new ArrayList(); + + for (final FileWritingMessageHandler messageHandler : fileWritingMessageHandlers.values()) { + final Expression outDir = (Expression) new DirectFieldAccessor(messageHandler).getPropertyValue("destinationDirectoryExpression"); + outputDirectories.add(outDir.getExpressionString()); + } + + final StringBuilder stringBuilder = new StringBuilder(); + + stringBuilder.append("\n========================================================="); + stringBuilder.append("\n"); + + for (final String inputDirectory : inputDirectories) { + stringBuilder.append("\n Intput directory is: '" + inputDirectory + "'"); + } + + for (final String outputDirectory : outputDirectories) { + stringBuilder.append("\n Output directory is: '" + outputDirectory + "'"); + } + + stringBuilder.append("\n\n========================================================="); + + logger.info(stringBuilder.toString()); + + } + +} diff --git a/samples/zip/src/main/java/org/springframework/integration/samples/zip/TransformationHandler.java b/samples/zip/src/main/java/org/springframework/integration/samples/zip/TransformationHandler.java new file mode 100644 index 0000000..fca42e5 --- /dev/null +++ b/samples/zip/src/main/java/org/springframework/integration/samples/zip/TransformationHandler.java @@ -0,0 +1,70 @@ +/* + * Copyright 2015 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 + * + * http://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.samples.zip; + +import java.io.File; +import java.io.IOException; +import java.util.Locale; + +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.FilenameUtils; +import org.springframework.integration.annotation.Transformer; +import org.springframework.integration.file.FileHeaders; +import org.springframework.integration.support.MessageBuilder; +import org.springframework.messaging.Message; + +/** + * This Spring Integration transformation handler takes the input file, converts + * the file into a string, converts the file contents into an upper-case string + * and then sets a few Spring Integration message headers. + * + * @author Gunnar Hillert + * @since 1.0 + */ +public class TransformationHandler { + + /** + * Actual Spring Integration transformation handler. + * + * @param inputMessage Spring Integration input message + * @return New Spring Integration message with updated headers + */ + @Transformer + public Message handleFile(final Message inputMessage) { + + final File inputFile = inputMessage.getPayload(); + final String filename = inputFile.getName(); + final String fileExtension = FilenameUtils.getExtension(filename); + + final String inputAsString; + + try { + inputAsString = FileUtils.readFileToString(inputFile); + } catch (IOException e) { + throw new IllegalStateException(e); + } + + final Message message = MessageBuilder.withPayload(inputAsString.toUpperCase(Locale.ENGLISH)) + .setHeader(FileHeaders.FILENAME, filename) + .setHeader(FileHeaders.ORIGINAL_FILE, inputFile) + .setHeader("file_size", inputFile.length()) + .setHeader("file_extension", fileExtension) + .build(); + + return message; + } +} diff --git a/samples/zip/src/main/resources/META-INF/spring/integration/spring-integration-context.xml b/samples/zip/src/main/resources/META-INF/spring/integration/spring-integration-context.xml new file mode 100644 index 0000000..5617c03 --- /dev/null +++ b/samples/zip/src/main/resources/META-INF/spring/integration/spring-integration-context.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/zip/src/main/resources/log4j.xml b/samples/zip/src/main/resources/log4j.xml new file mode 100644 index 0000000..f4d8379 --- /dev/null +++ b/samples/zip/src/main/resources/log4j.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/zip/src/test/java/org/springframework/integration/samples/zip/SpringIntegrationProjectStartupTest.java b/samples/zip/src/test/java/org/springframework/integration/samples/zip/SpringIntegrationProjectStartupTest.java new file mode 100644 index 0000000..a93fa73 --- /dev/null +++ b/samples/zip/src/test/java/org/springframework/integration/samples/zip/SpringIntegrationProjectStartupTest.java @@ -0,0 +1,43 @@ +/* + * Copyright 2015 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 + * + * http://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.samples.zip; + +import org.junit.Test; +import org.springframework.context.ApplicationContext; +import org.springframework.context.support.ClassPathXmlApplicationContext; + +import org.springframework.integration.samples.zip.SpringIntegrationUtils; + +/** + * Verify that the Spring Integration Application Context starts successfully. + * + * @author Gunnar Hillert + * @since 1.0 + * + */ +public class SpringIntegrationProjectStartupTest { + + @Test + public void testStartupOfSpringInegrationContext() throws Exception{ + final ApplicationContext context + = new ClassPathXmlApplicationContext("/META-INF/spring/integration/spring-integration-context.xml", + SpringIntegrationProjectStartupTest.class); + SpringIntegrationUtils.displayDirectories(context); + Thread.sleep(2000); + } + +} diff --git a/spring-integration-kafka/src/test/java/org/springframework/integration/kafka/support/ConsumerConfigurationTests.java b/spring-integration-kafka/src/test/java/org/springframework/integration/kafka/support/ConsumerConfigurationTests.java new file mode 100644 index 0000000..86692fc --- /dev/null +++ b/spring-integration-kafka/src/test/java/org/springframework/integration/kafka/support/ConsumerConfigurationTests.java @@ -0,0 +1,610 @@ +/* + * Copyright 2002-2013 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 + * + * http://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.kafka.support; + +import static org.junit.Assert.assertNull; +import static org.mockito.Mockito.*; + +import java.util.*; + +import kafka.consumer.ConsumerIterator; +import kafka.consumer.KafkaStream; +import kafka.javaapi.consumer.ConsumerConnector; +import kafka.message.MessageAndMetadata; +import kafka.serializer.Decoder; + +import org.junit.Assert; +import org.junit.Test; + +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; + +/** + * @author Soby Chacko + * @author Rajasekar Elango + * @since 0.5 + */ +public class ConsumerConfigurationTests { + + @Test + @SuppressWarnings("unchecked") + public void testReceiveMessageForSingleTopicFromSingleStream() { + final ConsumerMetadata consumerMetadata = mock(ConsumerMetadata.class); + final ConsumerConnectionProvider consumerConnectionProvider = + mock(ConsumerConnectionProvider.class); + final MessageLeftOverTracker messageLeftOverTracker = mock(MessageLeftOverTracker.class); + final ConsumerConnector consumerConnector = mock(ConsumerConnector.class); + + Map topicStreamMap = new HashMap(); + topicStreamMap.put("topic1", 1); + when(consumerMetadata.getTopicStreamMap()).thenReturn(topicStreamMap); + + when(consumerConnectionProvider.getConsumerConnector()).thenReturn(consumerConnector); + + final ConsumerConfiguration consumerConfiguration = new ConsumerConfiguration(consumerMetadata, + consumerConnectionProvider, messageLeftOverTracker); + consumerConfiguration.setMaxMessages(1); + + final KafkaStream stream = mock(KafkaStream.class); + final List> streams = new ArrayList>(); + streams.add(stream); + final Map>> messageStreams = new HashMap>>(); + messageStreams.put("topic", streams); + + when(consumerConfiguration.createMessageStreamsForTopic()).thenReturn(messageStreams); + final ConsumerIterator iterator = mock(ConsumerIterator.class); + when(stream.iterator()).thenReturn(iterator); + final MessageAndMetadata messageAndMetadata = mock(MessageAndMetadata.class); + when(iterator.next()).thenReturn(messageAndMetadata); + when(messageAndMetadata.message()).thenReturn((V) "got message"); + when(messageAndMetadata.topic()).thenReturn("topic"); + when(messageAndMetadata.partition()).thenReturn(1); + + final Map>> messages = consumerConfiguration.receive(); + Assert.assertEquals(1, messages.size()); + Assert.assertEquals(1, messages.get("topic").size()); + Assert.assertEquals("got message", messages.get("topic").get(1).get(0)); + + verify(stream, times(1)).iterator(); + verify(iterator, times(1)).next(); + verify(messageAndMetadata, times(1)).message(); + verify(messageAndMetadata, times(1)).topic(); + } + + @Test + @SuppressWarnings("unchecked") + public void testReceiveMessageForSingleTopicFromMultipleStreams() { +<<<<<<< HEAD + final ConsumerMetadata consumerMetadata = mock(ConsumerMetadata.class); +======= + final ConsumerMetadata consumerMetadata = Mockito.mock(ConsumerMetadata.class); +>>>>>>> INTEXT-40 - Add ZIP Transformer + final ConsumerConnectionProvider consumerConnectionProvider = + mock(ConsumerConnectionProvider.class); + final MessageLeftOverTracker messageLeftOverTracker = mock(MessageLeftOverTracker.class); + + Map topicStreamMap = new HashMap(); + topicStreamMap.put("topic1", 1); + when(consumerMetadata.getTopicStreamMap()).thenReturn(topicStreamMap); + + final ConsumerConnector consumerConnector = mock(ConsumerConnector.class); + + when(consumerConnectionProvider.getConsumerConnector()).thenReturn(consumerConnector); + + final ConsumerConfiguration consumerConfiguration = new ConsumerConfiguration(consumerMetadata, + consumerConnectionProvider, messageLeftOverTracker); + consumerConfiguration.setMaxMessages(3); + + final KafkaStream stream1 = mock(KafkaStream.class); + final KafkaStream stream2 = mock(KafkaStream.class); + final KafkaStream stream3 = mock(KafkaStream.class); + final List> streams = new ArrayList>(); + streams.add(stream1); + streams.add(stream2); + streams.add(stream3); + final Map>> messageStreams = new HashMap>>(); + messageStreams.put("topic", streams); + + when(consumerConfiguration.createMessageStreamsForTopic()).thenReturn(messageStreams); + final ConsumerIterator iterator1 = mock(ConsumerIterator.class); + final ConsumerIterator iterator2 = mock(ConsumerIterator.class); + final ConsumerIterator iterator3 = mock(ConsumerIterator.class); + + when(stream1.iterator()).thenReturn(iterator1); + when(stream2.iterator()).thenReturn(iterator2); + when(stream3.iterator()).thenReturn(iterator3); + final MessageAndMetadata messageAndMetadata1 = mock(MessageAndMetadata.class); + final MessageAndMetadata messageAndMetadata2 = mock(MessageAndMetadata.class); + final MessageAndMetadata messageAndMetadata3 = mock(MessageAndMetadata.class); + + when(iterator1.next()).thenReturn(messageAndMetadata1); + when(iterator2.next()).thenReturn(messageAndMetadata2); + when(iterator3.next()).thenReturn(messageAndMetadata3); + + when(messageAndMetadata1.message()).thenReturn((V)"got message"); + when(messageAndMetadata1.topic()).thenReturn("topic"); + when(messageAndMetadata1.partition()).thenReturn(1); + + when(messageAndMetadata2.message()).thenReturn((V)"got message"); + when(messageAndMetadata2.topic()).thenReturn("topic"); + when(messageAndMetadata2.partition()).thenReturn(2); + + when(messageAndMetadata3.message()).thenReturn((V)"got message"); + when(messageAndMetadata3.topic()).thenReturn("topic"); + when(messageAndMetadata3.partition()).thenReturn(3); + + final Map>> messages = consumerConfiguration.receive(); + Assert.assertEquals(messages.size(), 1); + int sum = 0; + + final Map> values = messages.get("topic"); + + for (final List l : values.values()) { + sum += l.size(); + } + + Assert.assertEquals(3, sum); + } + + @Test + @SuppressWarnings("unchecked") + public void testReceiveMessageForMultipleTopicsFromMultipleStreams() { +<<<<<<< HEAD + final ConsumerMetadata consumerMetadata = mock(ConsumerMetadata.class); +======= + final ConsumerMetadata consumerMetadata = Mockito.mock(ConsumerMetadata.class); +>>>>>>> INTEXT-40 - Add ZIP Transformer + final ConsumerConnectionProvider consumerConnectionProvider = + mock(ConsumerConnectionProvider.class); + final MessageLeftOverTracker messageLeftOverTracker = mock(MessageLeftOverTracker.class); + + Map topicStreamMap = new HashMap(); + topicStreamMap.put("topic1", 1); + when(consumerMetadata.getTopicStreamMap()).thenReturn(topicStreamMap); + + + final ConsumerConnector consumerConnector = mock(ConsumerConnector.class); + + when(consumerConnectionProvider.getConsumerConnector()).thenReturn(consumerConnector); + + final ConsumerConfiguration consumerConfiguration = new ConsumerConfiguration(consumerMetadata, + consumerConnectionProvider, messageLeftOverTracker); + consumerConfiguration.setMaxMessages(9); + + final KafkaStream stream1 = mock(KafkaStream.class); + final KafkaStream stream2 = mock(KafkaStream.class); + final KafkaStream stream3 = mock(KafkaStream.class); + final List> streams = new ArrayList>(); + streams.add(stream1); + streams.add(stream2); + streams.add(stream3); + final Map>> messageStreams = new HashMap>>(); + messageStreams.put("topic1", streams); + messageStreams.put("topic2", streams); + messageStreams.put("topic3", streams); + + when(consumerConfiguration.createMessageStreamsForTopic()).thenReturn(messageStreams); + final ConsumerIterator iterator1 = mock(ConsumerIterator.class); + final ConsumerIterator iterator2 = mock(ConsumerIterator.class); + final ConsumerIterator iterator3 = mock(ConsumerIterator.class); + + when(stream1.iterator()).thenReturn(iterator1); + when(stream2.iterator()).thenReturn(iterator2); + when(stream3.iterator()).thenReturn(iterator3); + final MessageAndMetadata messageAndMetadata1 = mock(MessageAndMetadata.class); + final MessageAndMetadata messageAndMetadata2 = mock(MessageAndMetadata.class); + final MessageAndMetadata messageAndMetadata3 = mock(MessageAndMetadata.class); + + when(iterator1.next()).thenReturn(messageAndMetadata1); + when(iterator2.next()).thenReturn(messageAndMetadata2); + when(iterator3.next()).thenReturn(messageAndMetadata3); + + when(messageAndMetadata1.message()).thenReturn((V)"got message1"); + when(messageAndMetadata1.topic()).thenReturn("topic1"); + when(messageAndMetadata1.partition()).thenAnswer(getAnswer()); + + when(messageAndMetadata2.message()).thenReturn((V)"got message2"); + when(messageAndMetadata2.topic()).thenReturn("topic2"); + when(messageAndMetadata1.partition()).thenAnswer(getAnswer()); + + when(messageAndMetadata3.message()).thenReturn((V)"got message3"); + when(messageAndMetadata3.topic()).thenReturn("topic3"); + when(messageAndMetadata1.partition()).thenAnswer(getAnswer()); + + final Map>> messages = consumerConfiguration.receive(); + + int sum = 0; + + final Collection>> values = messages.values(); + + for (final Map> m : values) { + for (final List l : m.values()) { + sum += l.size(); + } + } + + Assert.assertEquals(9, sum); + } + + + + private Answer getAnswer() { + return new Answer() { + private int count = 0; + + @Override + public Object answer(final InvocationOnMock invocation) throws Throwable { + if (count++ == 1) { + return 1; + } else if (count++ == 2) { + return 2; + } + + return 3; + } + }; + } + + @Test + @SuppressWarnings("unchecked") + public void testReceiveMessageAndVerifyMessageLeftoverFromPreviousPollAreTakenFirst() { +<<<<<<< HEAD + final ConsumerMetadata consumerMetadata = mock(ConsumerMetadata.class); +======= + final ConsumerMetadata consumerMetadata = Mockito.mock(ConsumerMetadata.class); +>>>>>>> INTEXT-40 - Add ZIP Transformer + final ConsumerConnectionProvider consumerConnectionProvider = + mock(ConsumerConnectionProvider.class); + final MessageLeftOverTracker messageLeftOverTracker = mock(MessageLeftOverTracker.class); + final ConsumerConnector consumerConnector = mock(ConsumerConnector.class); + + Map topicStreamMap = new HashMap(); + topicStreamMap.put("topic1", 1); + when(consumerMetadata.getTopicStreamMap()).thenReturn(topicStreamMap); + when(messageLeftOverTracker.getCurrentCount()).thenReturn(3); + + final MessageAndMetadata m1 = mock(MessageAndMetadata.class); + final MessageAndMetadata m2 = mock(MessageAndMetadata.class); + final MessageAndMetadata m3 = mock(MessageAndMetadata.class); + + when(m1.key()).thenReturn("key1"); + when(m1.message()).thenReturn("value1"); + when(m1.topic()).thenReturn("topic1"); + when(m1.partition()).thenReturn(1); + + when(m2.key()).thenReturn("key2"); + when(m2.message()).thenReturn("value2"); + when(m2.topic()).thenReturn("topic2"); + when(m2.partition()).thenReturn(1); + + when(m3.key()).thenReturn("key1"); + when(m3.message()).thenReturn("value3"); + when(m3.topic()).thenReturn("topic3"); + when(m3.partition()).thenReturn(1); + + final List> mList = new ArrayList>(); + mList.add(m1); + mList.add(m2); + mList.add(m3); + + when((List>) (Object) messageLeftOverTracker.getMessageLeftOverFromPreviousPoll()).thenReturn(mList); + + when(consumerConnectionProvider.getConsumerConnector()).thenReturn(consumerConnector); + + final ConsumerConfiguration consumerConfiguration = new ConsumerConfiguration(consumerMetadata, + consumerConnectionProvider, messageLeftOverTracker); + consumerConfiguration.setMaxMessages(5); + + final KafkaStream stream = mock(KafkaStream.class); + final List> streams = new ArrayList>(); + streams.add(stream); + final Map>> messageStreams = new HashMap>>(); + messageStreams.put("topic1", streams); + when(consumerConfiguration.createMessageStreamsForTopic()).thenReturn(messageStreams); + final ConsumerIterator iterator = mock(ConsumerIterator.class); + when(stream.iterator()).thenReturn(iterator); + final MessageAndMetadata messageAndMetadata = mock(MessageAndMetadata.class); + when(iterator.next()).thenReturn(messageAndMetadata); + when(messageAndMetadata.message()).thenReturn((V) "got message"); + when(messageAndMetadata.topic()).thenReturn("topic1"); + when(messageAndMetadata.partition()).thenReturn(1); + + final Map>> messages = consumerConfiguration.receive(); + int sum = 0; + + final Collection>> values = messages.values(); + + for (final Map> m : values) { + for (final List l : m.values()) { + sum += l.size(); + } + + } + Assert.assertEquals(5, sum); + + Assert.assertTrue(messages.containsKey("topic1")); + Assert.assertTrue(messages.containsKey("topic2")); + Assert.assertTrue(messages.containsKey("topic3")); + + Assert.assertTrue(valueFound(messages.get("topic1").get(1), "value1")); + Assert.assertTrue(valueFound(messages.get("topic2").get(1), "value2")); + Assert.assertTrue(valueFound(messages.get("topic3").get(1), "value3")); + } + + @Test +<<<<<<< HEAD + @SuppressWarnings("unchecked") + public void testGetConsumerMapWithMessageStreamsWithNullDecoders() { + + final ConsumerMetadata mockedConsumerMetadata = mock(ConsumerMetadata.class); + + assertNull(mockedConsumerMetadata.getKeyDecoder()); + assertNull(mockedConsumerMetadata.getValueDecoder()); + + final Map topicsStreamMap = new HashMap(); + when(mockedConsumerMetadata.getTopicStreamMap()).thenReturn(topicsStreamMap); + + final ConsumerConnectionProvider mockedConsumerConnectionProvider = mock(ConsumerConnectionProvider.class); + final MessageLeftOverTracker mockedMessageLeftOverTracker = mock(MessageLeftOverTracker.class); + final ConsumerConnector mockedConsumerConnector = mock(ConsumerConnector.class); + + when(mockedConsumerConnectionProvider.getConsumerConnector()).thenReturn(mockedConsumerConnector); + + final Map>> messageStreams = new HashMap>>(); + when((Map>>) + (Object) mockedConsumerConnector.createMessageStreams(topicsStreamMap)).thenReturn(messageStreams); + + final ConsumerConfiguration consumerConfiguration = new ConsumerConfiguration(mockedConsumerMetadata, + mockedConsumerConnectionProvider, mockedMessageLeftOverTracker); + + consumerConfiguration.createMessageStreamsForTopic(); + + verify(mockedConsumerMetadata, atLeast(1)).getTopicStreamMap(); + verify(mockedConsumerConnector, atLeast(1)).createMessageStreams(topicsStreamMap, null, null); + //verify(mockedConsumerConnector, atMost(0)).createMessageStreams(topicsStreamMap, null, null); + } + + @Test + @SuppressWarnings("unchecked") + public void testGetConsumerMapWithMessageStreamsWithDecoders() { + + @SuppressWarnings("unchecked") + final ConsumerMetadata mockedConsumerMetadata = mock(ConsumerMetadata.class); + + final Map topicsStreamMap = new HashMap(); + when(mockedConsumerMetadata.getTopicStreamMap()).thenReturn(topicsStreamMap); + + @SuppressWarnings("unchecked") + final Decoder mockedKeyDecoder = mock(Decoder.class); + + @SuppressWarnings("unchecked") + final Decoder mockedValueDecoder = mock(Decoder.class); + + when(mockedConsumerMetadata.getKeyDecoder()).thenReturn(mockedKeyDecoder); + when(mockedConsumerMetadata.getValueDecoder()).thenReturn(mockedValueDecoder); + + final ConsumerConnectionProvider mockedConsumerConnectionProvider = + mock(ConsumerConnectionProvider.class); + final MessageLeftOverTracker mockedMessageLeftOverTracker = mock(MessageLeftOverTracker.class); + final ConsumerConnector mockedConsumerConnector = mock(ConsumerConnector.class); + + when(mockedConsumerConnectionProvider.getConsumerConnector()).thenReturn(mockedConsumerConnector); + + final Map>> messageStreams = new HashMap>>(); + when(mockedConsumerConnector.createMessageStreams(topicsStreamMap)).thenReturn(messageStreams); + + final ConsumerConfiguration consumerConfiguration = + new ConsumerConfiguration(mockedConsumerMetadata, mockedConsumerConnectionProvider, + mockedMessageLeftOverTracker); + + consumerConfiguration.createMessageStreamsForTopic(); + + verify(mockedConsumerMetadata, atLeast(1)).getTopicStreamMap(); + verify(mockedConsumerConnector, atMost(0)).createMessageStreams(topicsStreamMap); + verify(mockedConsumerConnector, atLeast(1)) + .createMessageStreams(topicsStreamMap, mockedKeyDecoder, mockedValueDecoder); + } + + + @Test + @SuppressWarnings("unchecked") + public void testReceiveMessageForTopicFilterFromSingleStream() { + final ConsumerMetadata consumerMetadata = mock(ConsumerMetadata.class); + final ConsumerConnectionProvider consumerConnectionProvider = + mock(ConsumerConnectionProvider.class); + final MessageLeftOverTracker messageLeftOverTracker = mock(MessageLeftOverTracker.class); + final ConsumerConnector consumerConnector = mock(ConsumerConnector.class); + + when(consumerMetadata.getTopicFilterConfiguration()).thenReturn(new TopicFilterConfiguration(".*", 1, false)); + + when(consumerConnectionProvider.getConsumerConnector()).thenReturn(consumerConnector); + + final ConsumerConfiguration consumerConfiguration = + new ConsumerConfiguration(consumerMetadata, consumerConnectionProvider, + messageLeftOverTracker); + consumerConfiguration.setMaxMessages(1); + + final KafkaStream stream = mock(KafkaStream.class); + final List> streams = new ArrayList>(); + streams.add(stream); + + when(consumerConfiguration.createMessageStreamsForTopicFilter()).thenReturn(streams); + final ConsumerIterator iterator = mock(ConsumerIterator.class); + when(stream.iterator()).thenReturn(iterator); + final MessageAndMetadata messageAndMetadata = mock(MessageAndMetadata.class); + when(iterator.next()).thenReturn(messageAndMetadata); + when(messageAndMetadata.message()).thenReturn("got message"); + when(messageAndMetadata.topic()).thenReturn("topic"); + when(messageAndMetadata.partition()).thenReturn(1); + + final Map>> messages = consumerConfiguration.receive(); + Assert.assertEquals(1, messages.size()); + Assert.assertEquals(1, messages.get("topic").size()); + Assert.assertEquals("got message", messages.get("topic").get(1).get(0)); + + verify(stream, times(1)).iterator(); + verify(iterator, times(1)).next(); + verify(messageAndMetadata, times(1)).message(); + verify(messageAndMetadata, times(1)).topic(); + } + + @Test + @SuppressWarnings("unchecked") + public void testReceiveMessageForTopicFilterFromMultipleStreams() { + final ConsumerMetadata consumerMetadata = mock(ConsumerMetadata.class); + final ConsumerConnectionProvider consumerConnectionProvider = + mock(ConsumerConnectionProvider.class); + final MessageLeftOverTracker messageLeftOverTracker = mock(MessageLeftOverTracker.class); + + when(consumerMetadata.getTopicFilterConfiguration()).thenReturn(new TopicFilterConfiguration(".*", 1, false)); + + final ConsumerConnector consumerConnector = mock(ConsumerConnector.class); + + when(consumerConnectionProvider.getConsumerConnector()).thenReturn(consumerConnector); + + final ConsumerConfiguration consumerConfiguration = + new ConsumerConfiguration(consumerMetadata, consumerConnectionProvider, + messageLeftOverTracker); + consumerConfiguration.setMaxMessages(3); + + final KafkaStream stream1 = mock(KafkaStream.class); + final KafkaStream stream2 = mock(KafkaStream.class); + final KafkaStream stream3 = mock(KafkaStream.class); + final List> streams = new ArrayList>(); + streams.add(stream1); + streams.add(stream2); + streams.add(stream3); + + when(consumerConfiguration.createMessageStreamsForTopicFilter()).thenReturn(streams); + final ConsumerIterator iterator1 = mock(ConsumerIterator.class); + final ConsumerIterator iterator2 = mock(ConsumerIterator.class); + final ConsumerIterator iterator3 = mock(ConsumerIterator.class); + + when(stream1.iterator()).thenReturn(iterator1); + when(stream2.iterator()).thenReturn(iterator2); + when(stream3.iterator()).thenReturn(iterator3); + final MessageAndMetadata messageAndMetadata1 = mock(MessageAndMetadata.class); + final MessageAndMetadata messageAndMetadata2 = mock(MessageAndMetadata.class); + final MessageAndMetadata messageAndMetadata3 = mock(MessageAndMetadata.class); + + when(iterator1.next()).thenReturn(messageAndMetadata1); + when(iterator2.next()).thenReturn(messageAndMetadata2); + when(iterator3.next()).thenReturn(messageAndMetadata3); + + when(messageAndMetadata1.message()).thenReturn("got message".getBytes()); + when(messageAndMetadata1.topic()).thenReturn("topic"); + when(messageAndMetadata1.partition()).thenReturn(1); + + when(messageAndMetadata2.message()).thenReturn("got message".getBytes()); + when(messageAndMetadata2.topic()).thenReturn("topic"); + when(messageAndMetadata2.partition()).thenReturn(2); + + when(messageAndMetadata3.message()).thenReturn("got message".getBytes()); + when(messageAndMetadata3.topic()).thenReturn("topic"); + when(messageAndMetadata3.partition()).thenReturn(3); + + final Map>> messages = consumerConfiguration.receive(); + Assert.assertEquals(1, messages.size()); + int sum = 0; + + final Map> values = messages.get("topic"); + + for (final List l : values.values()) { + sum += l.size(); + } + + Assert.assertEquals(3, sum); +======= + public void testGetConsumerMapWithMessageStreamsWithNullDecoders() { + + final ConsumerMetadata consumerMetadata = Mockito.mock(ConsumerMetadata.class); + + final Map topicsStreamMap = new HashMap(); + when(consumerMetadata.getTopicStreamMap()).thenReturn(topicsStreamMap); + + final ConsumerConnectionProvider consumerConnectionProvider = + Mockito.mock(ConsumerConnectionProvider.class); + final MessageLeftOverTracker messageLeftOverTracker = Mockito.mock(MessageLeftOverTracker.class); + final ConsumerConnector consumerConnector = Mockito.mock(ConsumerConnector.class); + + Mockito.when(consumerConnectionProvider.getConsumerConnector()).thenReturn(consumerConnector); + + final Map>> messageStreams = new HashMap>>(); + Mockito.when(consumerConnector.createMessageStreams(topicsStreamMap)).thenReturn(messageStreams); + Mockito.when(consumerConnector.createMessageStreams(topicsStreamMap)).thenReturn(messageStreams); + + final ConsumerConfiguration consumerConfiguration = new ConsumerConfiguration(consumerMetadata, + consumerConnectionProvider, messageLeftOverTracker); + + consumerConfiguration.getConsumerMapWithMessageStreams(); + + verify(consumerMetadata, atLeast(1)).getTopicStreamMap(); + verify(consumerConnector, atLeast(1)).createMessageStreams(topicsStreamMap); + verify(consumerConnector, atMost(0)).createMessageStreams(topicsStreamMap, null, null); + + } + + @Test + public void testGetConsumerMapWithMessageStreamsWithDecoders() { + + @SuppressWarnings("unchecked") + final ConsumerMetadata consumerMetadata = mock(ConsumerMetadata.class); + + final Map topicsStreamMap = new HashMap(); + when(consumerMetadata.getTopicStreamMap()).thenReturn(topicsStreamMap); + + @SuppressWarnings("unchecked") + final Decoder mockedKeyDecoder = (Decoder) mock(Decoder.class); + + @SuppressWarnings("unchecked") + final Decoder mockedValueDecoder = (Decoder) mock(Decoder.class); + + when(consumerMetadata.getKeyDecoder()).thenReturn(mockedKeyDecoder); + when(consumerMetadata.getValueDecoder()).thenReturn(mockedValueDecoder); + + final ConsumerConnectionProvider consumerConnectionProvider = + Mockito.mock(ConsumerConnectionProvider.class); + final MessageLeftOverTracker messageLeftOverTracker = Mockito.mock(MessageLeftOverTracker.class); + final ConsumerConnector consumerConnector = Mockito.mock(ConsumerConnector.class); + + Mockito.when(consumerConnectionProvider.getConsumerConnector()).thenReturn(consumerConnector); + + final Map>> messageStreams = new HashMap>>(); + Mockito.when(consumerConnector.createMessageStreams(topicsStreamMap)).thenReturn(messageStreams); + Mockito.when(consumerConnector.createMessageStreams(topicsStreamMap)).thenReturn(messageStreams); + + final ConsumerConfiguration consumerConfiguration = new ConsumerConfiguration(consumerMetadata, + consumerConnectionProvider, messageLeftOverTracker); + + consumerConfiguration.getConsumerMapWithMessageStreams(); + + verify(consumerMetadata, atLeast(1)).getTopicStreamMap(); + verify(consumerConnector, atMost(0)).createMessageStreams(topicsStreamMap); + verify(consumerConnector, atLeast(1)).createMessageStreams(topicsStreamMap, mockedKeyDecoder, mockedValueDecoder); + +>>>>>>> INTEXT-40 - Add ZIP Transformer + } + + private boolean valueFound(final List l, final String value){ + for (final Object o : l){ + if (value.equals(o)){ + return true; + } + } + + return false; + } +} diff --git a/spring-integration-zip/README.md b/spring-integration-zip/README.md new file mode 100644 index 0000000..10fc814 --- /dev/null +++ b/spring-integration-zip/README.md @@ -0,0 +1,171 @@ +Spring Integration Zip Support +============================== + +| | Build Status | +| ------------- | :-------------: | +| Linux | [![Build Status](http://build.spring.io/plugins/servlet/buildStatusImage/INTEXT-ZIP)](https://build.spring.io/browse/INTEXT-ZIP) | +| Windows | [![Build Status](http://build.spring.io/plugins/servlet/buildStatusImage/INTEXT-ZIPWIN)](https://build.spring.io/browse/INTEXT-ZIPWIN) | + +## Introduction + +This *Spring Integration Extension* provides [Zip][] (un-) compression support. The following components are provided: + +* Zip transformer +* Unzip transformer +* UnZipResultSplitter + +**Important!** This module is currently under active development and not all functionality is provided or stable, yet. + +## ZIP Compression Support + +The following input data types can be **compressed**: + +* File +* String +* byte[] +* Iterable + +In input data types can be mixed as part of an Iterable. E.g. you should be +easily be able 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 zip transformer 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. + +## ZIP Un-compression Support + +The following input data types can be **decompressed**: + +* File +* InputStream +* byte[] + +When unzipping data, you can also specify a property **expectSingleResult**. 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* (the *default*), +then the payload will be of type *SortedMap*, if *true*, however, the actual zip +entry will be returned. + +Othe 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*. + +### workDirectory + +Set the work-directory. The work directory is used when the ZipResultType is set to ZipResultType.FILE. By default this property is set to the System temporary directory containing a sub-directory "ziptransformer". + +### ZipResultType + +Defines the format of the data returned after transformation. Available options are: + +* File +* byte[] + +## UnZipResultSplitter + +The `UnZipResultSplitter` is useful in cases where Zip files contain more than *1* +zip entry. + +## Zipping and Unzipping Large Files + +TBD + +## Java Package Structure + +### Base package + +The base package `org.springframework.integration.zip` contains the *ZipHeaders* class which defines the *Spring Integration* message headers that are specific to the Zip module. + +### config.xml + +This package contains the parser classes for the XML Namespace support. + +### transformer + +Contain the classes responsible for the actual (un-) zip operation: + +* ZipTransformer +* UnZipTransformer + +## Namespace Support + +Full XML namespace support is provided. + +## Building the Project + +To build and install jars into your local Maven cache, please execute: + + ./gradlew install + +If you encounter out of memory errors during the build, increase available heap and permgen for Gradle: + + GRADLE_OPTS='-XX:MaxPermSize=1024m -Xmx1024m' + +To build api Javadoc (results will be in `build/api`): + + ./gradlew api + +To build complete distribution including `-dist` and `-schema` zip files (results will be in `build/distributions`) + + ./gradlew dist + +# IDE Support + +While your custom Spring Integration Adapter is initially created with SpringSource Tool Suite, you in fact end up with a Gradle-based project. As such, the created project can be imported into other IDEs as well. + +## Using Spring Tool Suite + +Gradle projects can be directly imported into STS. But please make sure that you have the Gradle support installed. + +## Using Plain Eclipse + +To generate Eclipse metadata (*.classpath* and *.project* files), do the following: + + ./gradlew eclipse + +Once complete, you may then import the project into Eclipse as usual: + + *File -> Import -> Existing projects into workspace* + +Browse to the root directory of the project and it should import free of errors. + +## Using IntelliJ IDEA + +To generate IDEA metadata (.iml and .ipr files), do the following: + + ./gradlew idea + +# Further Resources + +## Getting support + +Check out the [spring-integration][spring-integration tag] tag on [Stack Overflow][]. + +## Related GitHub projects + +* [Spring Integration][] +* [Spring Integration Samples][] +* [Spring Integration Templates][] +* [Spring Integration Dsl Groovy][] +* [Spring Integration Dsl Scala][] + +For more information, please also don't forget to visit the [Spring Integration][] website. + +[Spring Integration]: https://github.com/SpringSource/spring-integration +[spring-integration tag]: http://stackoverflow.com/questions/tagged/spring-integration +[Spring Integration Samples]: https://github.com/SpringSource/spring-integration-samples +[Spring Integration Templates]: https://github.com/SpringSource/spring-integration-templates/tree/master/si-sts-templates +[Spring Integration Dsl Groovy]: https://github.com/SpringSource/spring-integration-dsl-groovy +[Spring Integration Dsl Scala]: https://github.com/SpringSource/spring-integration-dsl-scala +[Stack Overflow]: http://stackoverflow.com/faq + +[Zip]: http://en.wikipedia.org/wiki/Zip_%28file_format%29 \ No newline at end of file diff --git a/spring-integration-zip/build.gradle b/spring-integration-zip/build.gradle new file mode 100644 index 0000000..4bbfbc0 --- /dev/null +++ b/spring-integration-zip/build.gradle @@ -0,0 +1,243 @@ +description = 'Spring Integration Zip Adapter' + +buildscript { + repositories { + maven { url 'https://repo.springsource.org/plugins-snapshot' } + } +} + +apply plugin: 'java' +apply from: "${rootProject.projectDir}/publish-maven.gradle" +apply plugin: 'eclipse' +apply plugin: 'idea' + +group = 'org.springframework.integration' + +repositories { + maven { url 'http://repo.springsource.org/libs-milestone' } + maven { url 'http://repo.springsource.org/plugins-release' } +} + +sourceCompatibility=1.6 +targetCompatibility=1.6 + +ext { + + linkHomepage = 'https://github.com/spring-projects/spring-integration-extensions' + linkCi = 'https://build.spring.io/browse/INTEXT-ZIP' + linkIssue = 'https://jira.spring.io/browse/INTEXT' + linkScmUrl = 'https://github.com/spring-projects/spring-integration-extensions' + linkScmConnection = 'https://github.com/spring-projects/spring-integration-extensions.git' + linkScmDevConnection = 'git@github.com:spring-projects/spring-integration-extensions.git' + + junitVersion = '4.11' + log4jVersion = '1.2.17' + mockitoVersion = '1.9.5' + springVersion = '4.0.7.RELEASE' + springIntegrationVersion = '4.2.0.M1' + ztZipVersion = '1.8' + + idPrefix = 'zip' +} + +eclipse { + project { + natures += 'org.springframework.ide.eclipse.core.springnature' + } +} + +sourceSets { + test { + resources { + srcDirs = ['src/test/resources', 'src/test/java'] + } + } +} + +// See http://www.gradle.org/docs/current/userguide/dependency_management.html#sub:configurations +// and http://www.gradle.org/docs/current/dsl/org.gradle.api.artifacts.ConfigurationContainer.html +configurations { + jacoco //Configuration Group used by Sonar to provide Code Coverage using JaCoCo +} + +dependencies { + compile "org.springframework.integration:spring-integration-core:$springIntegrationVersion" + compile "org.springframework.integration:spring-integration-file:$springIntegrationVersion" + compile "org.zeroturnaround:zt-zip:$ztZipVersion" + testCompile "org.springframework.integration:spring-integration-test:$springIntegrationVersion" + testCompile "junit:junit:$junitVersion" + testCompile "log4j:log4j:$log4jVersion" + testCompile "org.mockito:mockito-all:$mockitoVersion" + testCompile "org.springframework:spring-test:$springVersion" + jacoco group: "org.jacoco", name: "org.jacoco.agent", version: "0.7.4.201502262128", classifier: "runtime" +} + + +// enable all compiler warnings; individual projects may customize further +ext.xLintArg = '-Xlint:all' +[compileJava, compileTestJava]*.options*.compilerArgs = [xLintArg] + +test { + // suppress all console output during testing unless running `gradle -i` + logging.captureStandardOutput(LogLevel.INFO) + jvmArgs "-javaagent:${configurations.jacoco.asPath}=destfile=${buildDir}/jacoco.exec,includes=*" +} + +task sourcesJar(type: Jar) { + classifier = 'sources' + from sourceSets.main.allJava +} + +task javadocJar(type: Jar) { + classifier = 'javadoc' + from javadoc +} + +artifacts { + archives sourcesJar + archives javadocJar +} + +apply plugin: 'sonar-runner' + +sonarRunner { + sonarProperties { + property "sonar.jacoco.reportPath", "${buildDir.name}/jacoco.exec" + property "sonar.links.homepage", linkHomepage + property "sonar.links.ci", linkCi + property "sonar.links.issue", linkIssue + property "sonar.links.scm", linkScmUrl + property "sonar.links.scm_dev", linkScmDevConnection + property "sonar.java.coveragePlugin", "jacoco" + } +} + +task api(type: Javadoc) { + group = 'Documentation' + description = 'Generates the Javadoc API documentation.' + title = "${rootProject.description} ${version} API" + options.memberLevel = org.gradle.external.javadoc.JavadocMemberLevel.PROTECTED + options.author = true + options.header = rootProject.description + options.overview = 'src/api/overview.html' + + source = sourceSets.main.allJava + classpath = project.sourceSets.main.compileClasspath + destinationDir = new File(buildDir, "api") +} + +task schemaZip(type: Zip) { + group = 'Distribution' + classifier = 'schema' + description = "Builds -${classifier} archive containing all " + + "XSDs for deployment at static.springframework.org/schema." + + def Properties schemas = new Properties(); + def shortName = idPrefix.replaceFirst("${idPrefix}-", '') + + project.sourceSets.main.resources.find { + it.path.endsWith('META-INF/spring.schemas') + }?.withInputStream { schemas.load(it) } + + for (def key : schemas.keySet()) { + File xsdFile = project.sourceSets.main.resources.find { + it.path.endsWith(schemas.get(key)) + } + assert xsdFile != null + into ("integration/${shortName}") { + from xsdFile.path + } + } + +} + +task docsZip(type: Zip) { + group = 'Distribution' + classifier = 'docs' + description = "Builds -${classifier} archive containing api docs " + + "for deployment at static.springframework.org/spring-integration/docs." + + from('src/dist') { + include 'changelog.txt' + } + + from (api) { + into 'api' + } + +} + +task distZip(type: Zip, dependsOn: [docsZip, schemaZip]) { + group = 'Distribution' + classifier = 'dist' + description = "Builds -${classifier} archive, containing all jars and docs, " + + "suitable for community download page." + + ext.baseDir = "${project.name}-${project.version}"; + + from('src/dist') { + include 'readme.txt' + include 'license.txt' + include 'notice.txt' + into "${baseDir}" + } + + from(zipTree(docsZip.archivePath)) { + into "${baseDir}/docs" + } + + from(zipTree(schemaZip.archivePath)) { + into "${baseDir}/schema" + } + + into ("${baseDir}/libs") { + from project.jar + from project.sourcesJar + from project.javadocJar + } +} + +// Create an optional "with dependencies" distribution. +// Not published by default; only for use when building from source. +task depsZip(type: Zip, dependsOn: distZip) { zipTask -> + group = 'Distribution' + classifier = 'dist-with-deps' + description = "Builds -${classifier} archive, containing everything " + + "in the -${distZip.classifier} archive plus all dependencies." + + from zipTree(distZip.archivePath) + + gradle.taskGraph.whenReady { taskGraph -> + if (taskGraph.hasTask(":${zipTask.name}")) { + def projectName = rootProject.name + def artifacts = new HashSet() + + rootProject.configurations.runtime.resolvedConfiguration.resolvedArtifacts.each { artifact -> + def dependency = artifact.moduleVersion.id + if (!projectName.equals(dependency.name)) { + artifacts << artifact.file + } + } + + zipTask.from(artifacts) { + into "${distZip.baseDir}/deps" + } + } + } +} + +artifacts { + archives distZip + archives docsZip + archives schemaZip +} + +task dist(dependsOn: assemble) { + group = 'Distribution' + description = 'Builds -dist, -docs and -schema distribution archives.' +} + +task wrapper(type: Wrapper) { + description = 'Generates gradlew[.bat] scripts' + gradleVersion = '2.4' +} diff --git a/spring-integration-zip/gradle.properties b/spring-integration-zip/gradle.properties new file mode 100644 index 0000000..bebfcbc --- /dev/null +++ b/spring-integration-zip/gradle.properties @@ -0,0 +1 @@ +version=1.0.0.BUILD-SNAPSHOT diff --git a/spring-integration-zip/gradle/wrapper/gradle-wrapper.jar b/spring-integration-zip/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..3d0dee6 Binary files /dev/null and b/spring-integration-zip/gradle/wrapper/gradle-wrapper.jar differ diff --git a/spring-integration-zip/gradle/wrapper/gradle-wrapper.properties b/spring-integration-zip/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..1330889 --- /dev/null +++ b/spring-integration-zip/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Tue May 12 22:21:08 EDT 2015 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-2.3-bin.zip diff --git a/spring-integration-zip/gradlew b/spring-integration-zip/gradlew new file mode 100755 index 0000000..91a7e26 --- /dev/null +++ b/spring-integration-zip/gradlew @@ -0,0 +1,164 @@ +#!/usr/bin/env bash + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn ( ) { + echo "$*" +} + +die ( ) { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; +esac + +# For Cygwin, ensure paths are in UNIX format before anything is touched. +if $cygwin ; then + [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` +fi + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >&- +APP_HOME="`pwd -P`" +cd "$SAVED" >&- + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/spring-integration-zip/gradlew.bat b/spring-integration-zip/gradlew.bat new file mode 100644 index 0000000..aec9973 --- /dev/null +++ b/spring-integration-zip/gradlew.bat @@ -0,0 +1,90 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windowz variants + +if not "%OS%" == "Windows_NT" goto win9xME_args +if "%@eval[2+2]" == "4" goto 4NT_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* +goto execute + +:4NT_args +@rem Get arguments from the 4NT Shell from JP Software +set CMD_LINE_ARGS=%$ + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/spring-integration-zip/publish-maven.gradle b/spring-integration-zip/publish-maven.gradle new file mode 100644 index 0000000..6294f70 --- /dev/null +++ b/spring-integration-zip/publish-maven.gradle @@ -0,0 +1,61 @@ +apply plugin: 'maven' + +ext.optionalDeps = [] +ext.providedDeps = [] + +ext.optional = { optionalDeps << it } +ext.provided = { providedDeps << it } + +install { + repositories.mavenInstaller { + customizePom(pom, project) + } +} + +def customizePom(pom, gradleProject) { + pom.whenConfigured { generatedPom -> + // respect 'optional' and 'provided' dependencies + gradleProject.optionalDeps.each { dep -> + generatedPom.dependencies.find { it.artifactId == dep.name }?.optional = true + } + gradleProject.providedDeps.each { dep -> + generatedPom.dependencies.find { it.artifactId == dep.name }?.scope = 'provided' + } + + // eliminate test-scoped dependencies (no need in maven central poms) + generatedPom.dependencies.removeAll { dep -> + dep.scope == 'test' + } + + // add all items necessary for maven central publication + generatedPom.project { + name = gradleProject.description + description = gradleProject.description + url = 'https://github.com/SpringSource/spring-integration-extensions' + organization { + name = 'SpringSource' + url = 'http://springsource.org' + } + licenses { + license { + name 'The Apache Software License, Version 2.0' + url 'http://www.apache.org/licenses/LICENSE-2.0.txt' + distribution 'repo' + } + } + scm { + url = 'https://github.com/SpringSource/spring-integration-extensions' + connection = 'scm:git:git://github.com/SpringSource/spring-integration-extensions' + developerConnection = 'scm:git:git://github.com/SpringSource/spring-integration-extensions' + } + + developers { + developer { + id = 'not specified' + name = 'Gunnar Hillert' + email = 'not specified' + } + } + } + } +} diff --git a/spring-integration-zip/src/api/overview.html b/spring-integration-zip/src/api/overview.html new file mode 100644 index 0000000..fb0198b --- /dev/null +++ b/spring-integration-zip/src/api/overview.html @@ -0,0 +1,22 @@ + + +This document is the API specification for Spring Integration +
+
+

+ For further API reference and developer documentation, see the + Spring + Integration reference documentation. + That documentation contains more detailed, developer-targeted + descriptions, with conceptual overviews, definitions of terms, + workarounds, and working code examples. +

+ +

+ If you are interested in commercial training, consultancy, and + support for Spring Integration, please visit + http://www.springsource.com +

+
+ + diff --git a/spring-integration-zip/src/dist/changelog.txt b/spring-integration-zip/src/dist/changelog.txt new file mode 100644 index 0000000..d263567 --- /dev/null +++ b/spring-integration-zip/src/dist/changelog.txt @@ -0,0 +1,15 @@ +Spring Integration Zip Adapter CHANGELOG +========================================= + +For the full detailed changelog, see: +https://.... + + +Changes in version 1.0 GA (insert date here) +https://.... + + +*** GENERAL *** + +Upgraded Spring Framework dependency to ... +... diff --git a/spring-integration-zip/src/dist/license.txt b/spring-integration-zip/src/dist/license.txt new file mode 100644 index 0000000..261eeb9 --- /dev/null +++ b/spring-integration-zip/src/dist/license.txt @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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 + + http://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. diff --git a/spring-integration-zip/src/dist/notice.txt b/spring-integration-zip/src/dist/notice.txt new file mode 100644 index 0000000..f62045a --- /dev/null +++ b/spring-integration-zip/src/dist/notice.txt @@ -0,0 +1,21 @@ + ======================================================================== + == NOTICE file corresponding to section 4 d of the Apache License, == + == Version 2.0, in this case for the Spring Integration distribution. == + ======================================================================== + + This product includes software developed by + the Apache Software Foundation (http://www.apache.org). + + The end-user documentation included with a redistribution, if any, + must include the following acknowledgement: + + "This product includes software developed by the Spring Framework + Project (http://www.springframework.org)." + + Alternatively, this acknowledgement may appear in the software itself, + if and wherever such third-party acknowledgements normally appear. + + The names "Spring", "Spring Framework", and "Spring Integration" must + not be used to endorse or promote products derived from this software + without prior written permission. For written permission, please contact + enquiries@springsource.com. diff --git a/spring-integration-zip/src/dist/readme.txt b/spring-integration-zip/src/dist/readme.txt new file mode 100644 index 0000000..c863ba6 --- /dev/null +++ b/spring-integration-zip/src/dist/readme.txt @@ -0,0 +1,13 @@ +Spring Integration Zip Adapter +----------------------------------- + +To find out what has changed since any earlier releases, see 'changelog.txt'. + +Please consult the documentation located within the 'docs/reference' directory +of this release and also visit the official Spring Integration home at +http://www.springsource.org/spring-integration + +There you will find links to the forum, issue tracker, and several other resources. + +See https://github.com/SpringSource/spring-integration#readme for additional +information including instructions on building from source. diff --git a/spring-integration-zip/src/main/java/org/springframework/integration/zip/ZipHeaders.java b/spring-integration-zip/src/main/java/org/springframework/integration/zip/ZipHeaders.java new file mode 100644 index 0000000..1b1fe01 --- /dev/null +++ b/spring-integration-zip/src/main/java/org/springframework/integration/zip/ZipHeaders.java @@ -0,0 +1,37 @@ +/* + * Copyright 2015 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 + * + * http://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 1.0 + */ +public final class ZipHeaders { + + private 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"; + + /** Noninstantiable utility class */ + private ZipHeaders() { + throw new AssertionError(); + } + +} diff --git a/spring-integration-zip/src/main/java/org/springframework/integration/zip/config/xml/AbstractZipTransformerParser.java b/spring-integration-zip/src/main/java/org/springframework/integration/zip/config/xml/AbstractZipTransformerParser.java new file mode 100644 index 0000000..578c71b --- /dev/null +++ b/spring-integration-zip/src/main/java/org/springframework/integration/zip/config/xml/AbstractZipTransformerParser.java @@ -0,0 +1,58 @@ +/* + * Copyright 2015 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 + * + * http://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.util.StringUtils; + +/** + * Base class for Zip transformer parsers. + * + * @author Gunnar Hillert + * @since 1.0 + */ +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); + } + this.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) { + } + +} diff --git a/spring-integration-zip/src/main/java/org/springframework/integration/zip/config/xml/UnZipTransformerParser.java b/spring-integration-zip/src/main/java/org/springframework/integration/zip/config/xml/UnZipTransformerParser.java new file mode 100644 index 0000000..d7ef757 --- /dev/null +++ b/spring-integration-zip/src/main/java/org/springframework/integration/zip/config/xml/UnZipTransformerParser.java @@ -0,0 +1,65 @@ +/* + * Copyright 2015 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 + * + * http://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.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; +import org.springframework.integration.zip.transformer.ZipResultType; +import org.springframework.util.StringUtils; +import org.w3c.dom.Element; + +/** + * Parser for the 'unzip-transformer' element. + * + * @author Gunnar Hillert + * @since 1.0 + */ +public class UnZipTransformerParser extends AbstractZipTransformerParser { + + @Override + protected String getTransformerClassName() { + return UnZipTransformer.class.getName(); + } + + @Override + protected void postProcessTransformer(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) { + Object source = parserContext.extractSource(element); + + IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "charset"); + IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "expect-single-result"); + + final String resultType = element.getAttribute("result-type"); + + if (StringUtils.hasText(resultType)) { + + final ZipResultType zipResultType = ZipResultType.convertToZipResultType(resultType); + + if (zipResultType != null) { + builder.addPropertyValue("zipResultType", zipResultType); + } + else { + parserContext.getReaderContext().error( + String.format("Unable to convert the provided result-type '%s' " + + "to the respective ZipResultType enum.", resultType), source); + } + + } + } + +} diff --git a/spring-integration-zip/src/main/java/org/springframework/integration/zip/config/xml/ZipNamespaceHandler.java b/spring-integration-zip/src/main/java/org/springframework/integration/zip/config/xml/ZipNamespaceHandler.java new file mode 100644 index 0000000..6ef92e5 --- /dev/null +++ b/spring-integration-zip/src/main/java/org/springframework/integration/zip/config/xml/ZipNamespaceHandler.java @@ -0,0 +1,38 @@ +/* + * Copyright 2015 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 + * + * http://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 1.0 + * + */ +public class ZipNamespaceHandler extends AbstractIntegrationNamespaceHandler { + + /* (non-Javadoc) + * @see org.springframework.beans.factory.xml.NamespaceHandler#init() + */ + @Override + public void init() { + this.registerBeanDefinitionParser("zip-transformer", new ZipTransformerParser()); + this.registerBeanDefinitionParser("unzip-transformer", new UnZipTransformerParser()); + } + +} diff --git a/spring-integration-zip/src/main/java/org/springframework/integration/zip/config/xml/ZipTransformerParser.java b/spring-integration-zip/src/main/java/org/springframework/integration/zip/config/xml/ZipTransformerParser.java new file mode 100644 index 0000000..45d9b96 --- /dev/null +++ b/spring-integration-zip/src/main/java/org/springframework/integration/zip/config/xml/ZipTransformerParser.java @@ -0,0 +1,65 @@ +/* + * Copyright 2015 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 + * + * http://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.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.xml.ParserContext; +import org.springframework.integration.config.xml.IntegrationNamespaceUtils; +import org.springframework.integration.zip.transformer.ZipResultType; +import org.springframework.integration.zip.transformer.ZipTransformer; +import org.springframework.util.StringUtils; +import org.w3c.dom.Element; + +/** + * Parser for the 'zip-transformer' element. + * + * @author Gunnar Hillert + * @since 1.0 + */ +public class ZipTransformerParser extends AbstractZipTransformerParser { + + @Override + protected String getTransformerClassName() { + return ZipTransformer.class.getName(); + } + + @Override + protected void postProcessTransformer(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) { + Object source = parserContext.extractSource(element); + + IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "charset"); + IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "compression-level"); + + final String resultType = element.getAttribute("result-type"); + + if (StringUtils.hasText(resultType)) { + + final ZipResultType zipResultType = ZipResultType.convertToZipResultType(resultType); + + if (zipResultType != null) { + builder.addPropertyValue("zipResultType", zipResultType); + } + else { + parserContext.getReaderContext().error( + String.format("Unable to convert the provided result-type '%s' " + + "to the respective ZipResultType enum.", resultType), source); + } + + } + } + +} diff --git a/spring-integration-zip/src/main/java/org/springframework/integration/zip/config/xml/package-info.java b/spring-integration-zip/src/main/java/org/springframework/integration/zip/config/xml/package-info.java new file mode 100644 index 0000000..e23dd6f --- /dev/null +++ b/spring-integration-zip/src/main/java/org/springframework/integration/zip/config/xml/package-info.java @@ -0,0 +1,4 @@ +/** + * Provides parser classes to provide Xml namespace support for the Zip components. + */ +package org.springframework.integration.zip.config.xml; diff --git a/spring-integration-zip/src/main/java/org/springframework/integration/zip/package-info.java b/spring-integration-zip/src/main/java/org/springframework/integration/zip/package-info.java new file mode 100644 index 0000000..96d4adb --- /dev/null +++ b/spring-integration-zip/src/main/java/org/springframework/integration/zip/package-info.java @@ -0,0 +1,4 @@ +/** + * Root package of the Zip Module. + */ +package org.springframework.integration.zip; diff --git a/spring-integration-zip/src/main/java/org/springframework/integration/zip/transformer/AbstractZipTransformer.java b/spring-integration-zip/src/main/java/org/springframework/integration/zip/transformer/AbstractZipTransformer.java new file mode 100644 index 0000000..c32f411 --- /dev/null +++ b/spring-integration-zip/src/main/java/org/springframework/integration/zip/transformer/AbstractZipTransformer.java @@ -0,0 +1,131 @@ +/* + * Copyright 2015 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 + * + * http://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.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +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 + * @since 1.0 + */ +public abstract class AbstractZipTransformer extends AbstractTransformer { + + private static final Log logger = LogFactory.getLog(ZipTransformer.class); + + protected volatile Charset charset = Charset.defaultCharset(); + + protected volatile FileNameGenerator fileNameGenerator; + + protected volatile ZipResultType zipResultType = ZipResultType.FILE; + + protected volatile File workDirectory = new File(System.getProperty("java.io.tmpdir") + File.separator + "ziptransformer"); + + protected volatile boolean deleteFiles; + + /** + * If the payload is an instance of {@link File}, this property specifies + * whether to delete the {@link File} after transformation. + * Default is false. + * + * @param deleteFiles Defaults to false 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 sub-directory "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.isFile(), "The workDirectory specified must not point to a file"); + this.workDirectory = workDirectory; + } + + /** + * Defines the format of the data returned after transformation. Available + * options are: + * + *
    + *
  • File
  • + *
  • Byte Array
  • + *
+ * + * 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() throws Exception { + super.onInit(); + + if (!workDirectory.exists()) { + if (logger.isInfoEnabled()) { + logger.info(String.format("Creating work directory '%s'.", this.workDirectory)); + } + workDirectory.mkdirs(); + } + final 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) throws Exception { + Assert.notNull(message, "message must not be null"); + final Object payload = message.getPayload(); + Assert.notNull(payload, "payload must not be null"); + + 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. + * @throws Exception Any exception. + */ + protected abstract Object doZipTransform(Message message) throws Exception; + +} diff --git a/spring-integration-zip/src/main/java/org/springframework/integration/zip/transformer/SpringZipUtils.java b/spring-integration-zip/src/main/java/org/springframework/integration/zip/transformer/SpringZipUtils.java new file mode 100644 index 0000000..f1f2539 --- /dev/null +++ b/spring-integration-zip/src/main/java/org/springframework/integration/zip/transformer/SpringZipUtils.java @@ -0,0 +1,149 @@ +/* + * Copyright 2015 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 + * + * http://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.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.zeroturnaround.zip.ZipEntrySource; +import org.zeroturnaround.zip.ZipException; + +/** + * 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 1.0 + * + */ +public class SpringZipUtils { + + private static final Log logger = LogFactory.getLog(SpringZipUtils.class); + + public static byte[] pack(Collection entries, int compressionLevel) { + + if (logger.isDebugEnabled()) { + logger.debug(String.format("Creating byte array from '%s'.", + entries)); + } + + final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + + pack(entries, outputStream, compressionLevel); + + return outputStream.toByteArray(); + } + + public static void pack(Collection entries, File zip, + int compressionLevel) { + + if (logger.isDebugEnabled()) { + logger.debug("Creating '" + zip + "' from " + + entries + "."); + } + + final 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 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 { + try { + if (zipfile != null) { + zipfile.close(); + zipfile = null; + } + } catch (IOException e) { + } + } + } +} diff --git a/spring-integration-zip/src/main/java/org/springframework/integration/zip/transformer/UnZipTransformer.java b/spring-integration-zip/src/main/java/org/springframework/integration/zip/transformer/UnZipTransformer.java new file mode 100644 index 0000000..c8ec135 --- /dev/null +++ b/spring-integration-zip/src/main/java/org/springframework/integration/zip/transformer/UnZipTransformer.java @@ -0,0 +1,190 @@ +/* + * Copyright 2015 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.integration.zip.transformer; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.SortedMap; +import java.util.TreeMap; +import java.util.zip.ZipEntry; + +import org.apache.commons.io.IOUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.messaging.Message; +import org.springframework.messaging.MessageHandlingException; +import org.springframework.messaging.MessagingException; +import org.zeroturnaround.zip.ZipEntryCallback; +import org.zeroturnaround.zip.ZipUtil; + +/** + * Transformer implementation that applies an UnZip transformation to the message + * payload. + * + * @author Gunnar Hillert + * @since 1.0 + * + */ +public class UnZipTransformer extends AbstractZipTransformer { + + private static final Log logger = LogFactory.getLog(UnZipTransformer.class); + + private volatile boolean expectSingleResult = false; + + /** + * + * This parameter indicates that only one result object shall be returned as + * a result from the executed Unzip operation. If set to true 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 true, then a + * {@link MessagingException} is thrown. + * + * If set to false, the complete result list is returned as the + * payload. This is the {@code default}. + * + * @param expectSingleResult If not set explicitly, will default to false + * + */ + public void setExpectSingleResult(boolean expectSingleResult) { + this.expectSingleResult = expectSingleResult; + } + + @Override + protected Object doZipTransform(final Message message) throws Exception { + + try { + final Object payload = message.getPayload(); + final Object unzippedData; + + InputStream inputStream = null; + + try { + if (payload instanceof File) { + final File filePayload = (File) payload; + + if (filePayload.isDirectory()) { + throw new UnsupportedOperationException(String.format("Cannot unzip a directory: '%s'", filePayload.getAbsolutePath())); + } + + if (!SpringZipUtils.isValid(filePayload)) { + throw new IllegalStateException(String.format("Not a zip file: '%s'.", filePayload.getAbsolutePath())); + } + + inputStream = new FileInputStream(filePayload); + } + else if (payload instanceof InputStream) { + inputStream = (InputStream) payload; + } + else if (payload instanceof byte[]) { + inputStream = new ByteArrayInputStream((byte[]) payload); + } + else { + throw new IllegalArgumentException(String.format("Unsupported payload type '%s'. The only supported payload types are " + + "java.io.File, byte[] and java.io.InputStream", payload.getClass().getSimpleName())); + } + + final SortedMap 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"; + + if (logger.isWarnEnabled()) { + logger.warn(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 tempDir = new File(workDirectory, message.getHeaders().getId().toString()); + tempDir.mkdirs(); + final File destinationFile = new File(tempDir, zipEntryName); + + if (zipEntry.isDirectory()) { + destinationFile.mkdirs(); + } + else { + SpringZipUtils.copy(zipEntryInputStream, destinationFile); + uncompressedData.put(zipEntryName, destinationFile); + } + } + else if (ZipResultType.BYTE_ARRAY.equals(zipResultType)) { + if (!zipEntry.isDirectory()) { + byte[] data = IOUtils.toByteArray(zipEntryInputStream); + uncompressedData.put(zipEntryName, data); + } + } + else { + throw new IllegalStateException("Unsupported zipResultType " + zipResultType); + } + } + }); + + if (uncompressedData.isEmpty()) { + if (logger.isWarnEnabled()) { + logger.warn("No data unzipped from payload with message Id " + message.getHeaders().getId()); + } + unzippedData = null; + } + else { + + if (this.expectSingleResult) { + if (uncompressedData.size() == 1) { + unzippedData = uncompressedData.values().iterator().next(); + } + else { + throw new MessagingException(message, + String.format("The UnZip operation extracted %s " + + "result objects but expectSingleResult was 'true'.", uncompressedData.size())); + } + } + else { + unzippedData = uncompressedData; + } + + } + + if (payload instanceof File && this.deleteFiles) { + final File filePayload = (File) payload; + if (!filePayload.delete() && logger.isWarnEnabled()) { + if (logger.isWarnEnabled()) { + logger.warn("failed to delete File '" + filePayload + "'"); + } + } + } + } + finally { + IOUtils.closeQuietly(inputStream); + } + return unzippedData; + } + catch (Exception e) { + throw new MessageHandlingException(message, "Failed to apply Zip transformation.", e); + } + } +} diff --git a/spring-integration-zip/src/main/java/org/springframework/integration/zip/transformer/ZipResultType.java b/spring-integration-zip/src/main/java/org/springframework/integration/zip/transformer/ZipResultType.java new file mode 100644 index 0000000..5583c74 --- /dev/null +++ b/spring-integration-zip/src/main/java/org/springframework/integration/zip/transformer/ZipResultType.java @@ -0,0 +1,62 @@ +/* + * Copyright 2015 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 + * + * http://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 org.springframework.util.Assert; + +/** + * + * @author Gunnar Hillert + * @since 1.0 + */ +public enum ZipResultType { + + FILE("FILE"), + BYTE_ARRAY("BYTE_ARRAY"); + + private String id; + + private ZipResultType(String id) { + this.id = id; + } + + /** + * Retrieves the matching enum constant for a provided String representation + * of the {@link ZipResultType}. The provided name must match exactly the identifier as + * used to declare the enum constant. + * + * @param zipResultTypeAsString Name of the enum to convert. Must be not null and not empty. + * @return The enumeration that matches. Returns Null of no match was found. + * + */ + public static ZipResultType convertToZipResultType(String zipResultTypeAsString) { + + Assert.hasText(zipResultTypeAsString, "Parameter zipResultTypeAsString must not be null or empty"); + + for (ZipResultType zipResultType : ZipResultType.values()) { + if (zipResultType.name().equalsIgnoreCase(zipResultTypeAsString)) { + return zipResultType; + } + } + + return null; + } + + public String getId() { + return id; + } + +} diff --git a/spring-integration-zip/src/main/java/org/springframework/integration/zip/transformer/ZipTransformer.java b/spring-integration-zip/src/main/java/org/springframework/integration/zip/transformer/ZipTransformer.java new file mode 100644 index 0000000..f0d124f --- /dev/null +++ b/spring-integration-zip/src/main/java/org/springframework/integration/zip/transformer/ZipTransformer.java @@ -0,0 +1,229 @@ +/* + * Copyright 2015 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 + * + * http://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.util.ArrayList; +import java.util.Date; +import java.util.zip.Deflater; + +import org.apache.commons.io.FilenameUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.Environment; +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.messaging.MessageHandlingException; +import org.springframework.util.Assert; +import org.springframework.util.FileCopyUtils; +import org.springframework.util.StringUtils; +import org.zeroturnaround.zip.ByteSource; +import org.zeroturnaround.zip.FileSource; +import org.zeroturnaround.zip.ZipEntrySource; + +/** + * {@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: + * + * See also: http://mindprod.com/jgloss/zip.html + * + * If you want to generate Zip files larger than {@code 4GB}, you must use Java 7: + * + * See also: https://blogs.oracle.com/xuemingshen/entry/zip64_support_for_4g_zipfile + * + * @author Gunnar Hillert + * @since 1.0 + * + */ +public class ZipTransformer extends AbstractZipTransformer { + + private static final Log logger = LogFactory.getLog(ZipTransformer.class); + + private static final String ZIP_EXTENSION = ".zip"; + + private volatile int compressionLevel = Deflater.DEFAULT_COMPRESSION; + + private volatile boolean useFileAttributes = true; + + @Autowired Environment env; + + /** + * Sets 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; + } + + /** + * Specifies 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: + * + *
    + *
  • {@link File} + *...
  • {@link String} + *...
  • byte[] + *...
  • {@link Iterable} + *
+ * + * When providing an {@link Iterable}, nested Iterables are not supported. However, + * payloads can be of of any of the other supported types. + * + */ + @Override + protected Object doZipTransform(Message message) throws Exception { + + try { + + 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 = baseFileName; + } + 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 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, useFileAttributes); + if (logger.isDebugEnabled()) { + logger.debug("ZipEntrySource path: '" + zipEntrySource.getPath() + "'"); + } + entries.add(zipEntrySource); + counter++; + } + } + else { + final ZipEntrySource zipEntrySource = createZipEntrySource(payload, lastModifiedDate, zipEntryName, useFileAttributes); + entries.add(zipEntrySource); + } + + final byte[] zippedBytes = SpringZipUtils.pack(entries, this.compressionLevel); + + if (ZipResultType.FILE.equals(zipResultType)) { + final File zippedFile = new File(this.workDirectory, zipFileName); + FileCopyUtils.copy(zippedBytes, zippedFile); + zippedData = zippedFile; + } + else if (ZipResultType.BYTE_ARRAY.equals(zipResultType)) { + zippedData = zippedBytes; + } + else { + throw new IllegalStateException("Unsupported zipResultType " + zipResultType); + } + + return this.getMessageBuilderFactory() + .withPayload(zippedData).copyHeaders(message.getHeaders()).setHeader(FileHeaders.FILENAME, zipFileName).build(); + } + catch (Exception e) { + throw new MessageHandlingException(message, "Failed to apply Zip transformation.", e); + } + } + + private ZipEntrySource createZipEntrySource(Object item, + Date lastModifiedDate, String zipEntryName, boolean useFileAttributes) { + + if (item instanceof File) { + final File filePayload = (File) item; + + final String fileName = useFileAttributes ? filePayload.getName() : zipEntryName; + + if (((File) item).isDirectory()) { + throw new UnsupportedOperationException("Zipping of directories is not supported."); + } + + final FileSource fileSource = new FileSource(fileName, filePayload); + + if (this.deleteFiles) { + if (!filePayload.delete() && logger.isWarnEnabled()) { + logger.warn("failed to delete File '" + filePayload + "'"); + } + } + + return fileSource; + + } + else if (item instanceof byte[] || item instanceof String) { + + byte[] bytesToCompress = null; + + if (item instanceof String) { + bytesToCompress = ((String) item).getBytes(this.charset); + } + else { + bytesToCompress = (byte[]) item; + } + + final ZipEntrySource zipEntrySource = new ByteSource(zipEntryName, bytesToCompress, lastModifiedDate.getTime()); + + return zipEntrySource; + } + else { + throw new IllegalArgumentException("Unsupported payload type. The only supported payloads are " + + "java.io.File, java.lang.String, and byte[]"); + } + } +} diff --git a/spring-integration-zip/src/main/java/org/springframework/integration/zip/transformer/splitter/UnZipResultSplitter.java b/spring-integration-zip/src/main/java/org/springframework/integration/zip/transformer/splitter/UnZipResultSplitter.java new file mode 100644 index 0000000..3c50853 --- /dev/null +++ b/spring-integration-zip/src/main/java/org/springframework/integration/zip/transformer/splitter/UnZipResultSplitter.java @@ -0,0 +1,52 @@ +/* + * Copyright 2015 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 + * + * http://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.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.support.MessageBuilder; +import org.springframework.integration.zip.ZipHeaders; +import org.springframework.messaging.Message; + +/** + * + * @author Gunnar Hillert + * @since 1.0 + * + */ +public class UnZipResultSplitter { + + public List> splitUnzippedMap(Map unzippedEntries) { + + final List> messages = new ArrayList>(unzippedEntries.size()); + + for (Map.Entry entry : unzippedEntries.entrySet()) { + final String path = FilenameUtils.getPath(entry.getKey()); + final String filename = FilenameUtils.getName(entry.getKey()); + final Message splitMessage = MessageBuilder.withPayload(entry.getValue()) + .setHeader(FileHeaders.FILENAME, filename) + .setHeader(ZipHeaders.ZIP_ENTRY_PATH, path).build(); + messages.add(splitMessage); + } + return messages; + } + +} diff --git a/spring-integration-zip/src/main/resources/META-INF/spring.handlers b/spring-integration-zip/src/main/resources/META-INF/spring.handlers new file mode 100644 index 0000000..fb31de6 --- /dev/null +++ b/spring-integration-zip/src/main/resources/META-INF/spring.handlers @@ -0,0 +1 @@ +http\://www.springframework.org/schema/integration/zip=org.springframework.integration.zip.config.xml.ZipNamespaceHandler diff --git a/spring-integration-zip/src/main/resources/META-INF/spring.schemas b/spring-integration-zip/src/main/resources/META-INF/spring.schemas new file mode 100644 index 0000000..5261bf2 --- /dev/null +++ b/spring-integration-zip/src/main/resources/META-INF/spring.schemas @@ -0,0 +1,2 @@ +http\://www.springframework.org/schema/integration/zip/spring-integration-zip-1.0.xsd=org/springframework/integration/config/xml/spring-integration-zip-1.0.xsd +http\://www.springframework.org/schema/integration/zip/spring-integration-zip.xsd=org/springframework/integration/config/xml/spring-integration-zip-1.0.xsd diff --git a/spring-integration-zip/src/main/resources/META-INF/spring.tooling b/spring-integration-zip/src/main/resources/META-INF/spring.tooling new file mode 100644 index 0000000..c8c2bdc --- /dev/null +++ b/spring-integration-zip/src/main/resources/META-INF/spring.tooling @@ -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/config/xml/spring-integration-zip.gif diff --git a/spring-integration-zip/src/main/resources/org/springframework/integration/config/xml/spring-integration-zip-1.0.xsd b/spring-integration-zip/src/main/resources/org/springframework/integration/config/xml/spring-integration-zip-1.0.xsd new file mode 100644 index 0000000..f39ad96 --- /dev/null +++ b/spring-integration-zip/src/main/resources/org/springframework/integration/config/xml/spring-integration-zip-1.0.xsd @@ -0,0 +1,148 @@ + + + + + + + + + + + + + + + Creates a Transformer that compresses message + payloads using Zip compressions. The following payload types are + supported: + + - java.io.File + - byte[] + - String + + + + + + + + + + Sets the compression level. Default is + java.util.zip.Deflater.DEFAULT_COMPRESSION + + + + + + + + + + + + Creates a Transformer that uncompresses message + payloads using Zip compressions. The following payload types are + supported: + + - java.io.File + - byte[] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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. + + + + + + + + + + + + + + + + diff --git a/spring-integration-zip/src/main/resources/org/springframework/integration/config/xml/spring-integration-zip.gif b/spring-integration-zip/src/main/resources/org/springframework/integration/config/xml/spring-integration-zip.gif new file mode 100644 index 0000000..210e076 Binary files /dev/null and b/spring-integration-zip/src/main/resources/org/springframework/integration/config/xml/spring-integration-zip.gif differ diff --git a/spring-integration-zip/src/test/java/org/springframework/integration/zip/UnZip2FileTests-context.xml b/spring-integration-zip/src/test/java/org/springframework/integration/zip/UnZip2FileTests-context.xml new file mode 100644 index 0000000..fb21f1e --- /dev/null +++ b/spring-integration-zip/src/test/java/org/springframework/integration/zip/UnZip2FileTests-context.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spring-integration-zip/src/test/java/org/springframework/integration/zip/UnZip2FileTests.java b/spring-integration-zip/src/test/java/org/springframework/integration/zip/UnZip2FileTests.java new file mode 100644 index 0000000..3d4d14c --- /dev/null +++ b/spring-integration-zip/src/test/java/org/springframework/integration/zip/UnZip2FileTests.java @@ -0,0 +1,160 @@ +/* + * Copyright 2015 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 + * + * http://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.io.InputStream; +import java.util.Properties; + +import org.apache.commons.io.IOUtils; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.ImportResource; +import org.springframework.core.io.Resource; +import org.springframework.core.io.ResourceLoader; +import org.springframework.integration.support.MessageBuilder; +import org.springframework.messaging.Message; +import org.springframework.messaging.MessageChannel; + +/** + * + * @author Gunnar Hillert + * @since 1.0 + * + */ +public class UnZip2FileTests { + + private AnnotationConfigApplicationContext context; + private ResourceLoader resourceLoader; + private MessageChannel input; + + private static final Properties properties = new Properties(); + + @Rule + public TemporaryFolder testFolder = new TemporaryFolder(); + + private File workDir; + + @Before + public void setup() throws IOException { + this.workDir = testFolder.newFolder(); + properties.put("workDir", workDir); + System.out.print(this.workDir.getAbsolutePath()); + + context = new AnnotationConfigApplicationContext(); + context.register(ContextConfiguration.class); + context.refresh(); + input = context.getBean("input", MessageChannel.class); + resourceLoader = context; + } + + @After + public void cleanup() { + if (context != null) { + context.close(); + } + } + + @Test + @Ignore + public void unZipWithOneEntry() throws Exception { + + final Resource resource = resourceLoader.getResource("classpath:testzipdata/single.zip"); + final InputStream is = resource.getInputStream(); + + byte[] zipdata = IOUtils.toByteArray(is); + + final Message message = MessageBuilder.withPayload(zipdata).build(); + + input.send(message); + + Assert.assertTrue(this.workDir.list().length == 1); + + File fileInWorkDir = this.workDir.listFiles()[0]; + + Assert.assertTrue(fileInWorkDir.isFile()); + Assert.assertEquals("single.txt", fileInWorkDir.getName()); + } + + @Test + public void unZipWithMultipleEntries() throws Exception { + + final Resource resource = resourceLoader.getResource("classpath:testzipdata/countries.zip"); + final InputStream is = resource.getInputStream(); + + byte[] zipdata = IOUtils.toByteArray(is); + + final Message message = MessageBuilder.withPayload(zipdata).build(); + + input.send(message); + + Assert.assertTrue(this.workDir.list().length == 4); + + File[] files = this.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; + Assert.assertTrue(file.isDirectory()); + Assert.assertTrue(file.list().length == 2); + } + if (file.getName().equals("de.txt")) { + de = true; + Assert.assertTrue(file.isFile()); + } + if (file.getName().equals("fr.txt")) { + fr = true; + Assert.assertTrue(file.isFile()); + } + if (file.getName().equals("pl.txt")) { + pl = true; + Assert.assertTrue(file.isFile()); + } + } + + Assert.assertTrue(continents); + Assert.assertTrue(de); + Assert.assertTrue(fr); + Assert.assertTrue(pl); + + } + + @Configuration + @ImportResource("classpath:org/springframework/integration/zip/UnZip2FileTests-context.xml") + public static class ContextConfiguration { + + @Bean + Properties properties() throws IOException { + return properties; + } + + } +} diff --git a/spring-integration-zip/src/test/java/org/springframework/integration/zip/Zip2FileTests-context.xml b/spring-integration-zip/src/test/java/org/springframework/integration/zip/Zip2FileTests-context.xml new file mode 100644 index 0000000..c348a6c --- /dev/null +++ b/spring-integration-zip/src/test/java/org/springframework/integration/zip/Zip2FileTests-context.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + diff --git a/spring-integration-zip/src/test/java/org/springframework/integration/zip/Zip2FileTests.java b/spring-integration-zip/src/test/java/org/springframework/integration/zip/Zip2FileTests.java new file mode 100644 index 0000000..74eeb13 --- /dev/null +++ b/spring-integration-zip/src/test/java/org/springframework/integration/zip/Zip2FileTests.java @@ -0,0 +1,175 @@ +/* + * Copyright 2015 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 + * + * http://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.FileNotFoundException; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Properties; + +import org.apache.commons.io.FileUtils; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.ImportResource; +import org.springframework.integration.file.FileHeaders; +import org.springframework.integration.support.MessageBuilder; +import org.springframework.messaging.Message; +import org.springframework.messaging.MessageChannel; + +/** + * + * @author Gunnar Hillert + * @since 1.0 + * + */ +public class Zip2FileTests { + + private AnnotationConfigApplicationContext context; + private MessageChannel input; + + private static final Properties properties = new Properties(); + + @Rule + public TemporaryFolder testFolder = new TemporaryFolder(); + + private File workDir; + + @Before + public void setup() throws IOException { + this.workDir = testFolder.newFolder(); + properties.put("workDir", workDir); + System.out.print(this.workDir.getAbsolutePath()); + + context = new AnnotationConfigApplicationContext(); + context.register(ContextConfiguration.class); + context.refresh(); + input = context.getBean("input", MessageChannel.class); + } + + @After + public void cleanup() { + if (context != null) { + context.close(); + } + } + + @Test + public void zipStringWithDefaultFileName() throws FileNotFoundException, IOException, InterruptedException { + + final Message message = MessageBuilder.withPayload("Zip me up.").build(); + + input.send(message); + + Assert.assertTrue(this.workDir.list().length == 1); + + File fileInWorkDir = this.workDir.listFiles()[0]; + + Assert.assertTrue(fileInWorkDir.isFile()); + Assert.assertTrue(fileInWorkDir.getName().contains(message.getHeaders().getId().toString())); + Assert.assertTrue("The created file should have a 'zip' file extension.", fileInWorkDir.getName().endsWith(".zip")); + } + + @Test + public void zipStringWithExplicitFileName() throws FileNotFoundException, IOException, InterruptedException { + input.send(MessageBuilder.withPayload("Zip me up.").setHeader(FileHeaders.FILENAME, "zipString.zip").build()); + + Assert.assertTrue(this.workDir.list().length == 1); + Assert.assertEquals("zipString.zip", this.workDir.listFiles()[0].getName()); + } + + @Test + public void zipBytesWithExplicitFileName() throws FileNotFoundException, IOException, InterruptedException { + + input.send(MessageBuilder.withPayload("Zip me up.".getBytes()).setHeader(FileHeaders.FILENAME, "zipString.zip").build()); + + Assert.assertTrue(this.workDir.list().length == 1); + Assert.assertEquals("zipString.zip", this.workDir.listFiles()[0].getName()); + } + + @Test + public void zipFile() throws FileNotFoundException, IOException, InterruptedException { + + final File fileToCompress = testFolder.newFile(); + FileUtils.writeStringToFile(fileToCompress, "hello world"); + + input.send(MessageBuilder.withPayload(fileToCompress).build()); + + Assert.assertTrue(this.workDir.list().length == 1); + Assert.assertEquals(fileToCompress.getName() + ".zip", this.workDir.listFiles()[0].getName()); + } + + @Test + public void zipIterableWithMultipleStrings() throws FileNotFoundException, IOException, InterruptedException { + + String stringToCompress1 = "String1"; + String stringToCompress2 = "String2"; + String stringToCompress3 = "String3"; + String stringToCompress4 = "String4"; + + final List 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()); + + Assert.assertTrue(this.workDir.list().length == 1); + Assert.assertEquals("zipWith4Strings.zip", this.workDir.listFiles()[0].getName()); + } + + @Test + public void zipIterableWithDifferentTypes() throws FileNotFoundException, IOException, InterruptedException { + + String stringToCompress = "String1"; + byte[] bytesToCompress = "String2".getBytes(); + final File fileToCompress = testFolder.newFile(); + FileUtils.writeStringToFile(fileToCompress, "hello world"); + + final List 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()); + + Assert.assertTrue(this.workDir.list().length == 1); + Assert.assertEquals("objects-to-compress.zip", this.workDir.listFiles()[0].getName()); + } + + @Configuration + @ImportResource("classpath:org/springframework/integration/zip/Zip2FileTests-context.xml") + public static class ContextConfiguration { + + @Bean + Properties properties() throws IOException { + return properties; + } + + } +} diff --git a/spring-integration-zip/src/test/java/org/springframework/integration/zip/config/xml/UnZipTransformerParserTests.java b/spring-integration-zip/src/test/java/org/springframework/integration/zip/config/xml/UnZipTransformerParserTests.java new file mode 100644 index 0000000..179e8e8 --- /dev/null +++ b/spring-integration-zip/src/test/java/org/springframework/integration/zip/config/xml/UnZipTransformerParserTests.java @@ -0,0 +1,140 @@ +/* + * Copyright 2015 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 + * + * http://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 static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import java.io.File; +import java.nio.charset.Charset; + +import org.junit.After; +import org.junit.Test; +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.UnZipTransformer; +import org.springframework.integration.zip.transformer.ZipResultType; +import org.springframework.util.Assert; + +/** + * + * @author Gunnar Hillert + * @since 1.0 + * + */ +public class UnZipTransformerParserTests { + + private ConfigurableApplicationContext context; + + @Test + public void testUnZiptransformerParserWithDefaults() { + + setUp("UnZipTransformerParserTests.xml", getClass()); + + EventDrivenConsumer consumer = this.context.getBean("unzipTransformerWithDefaults", EventDrivenConsumer.class); + + final AbstractMessageChannel inputChannel = TestUtils.getPropertyValue(consumer, "inputChannel", AbstractMessageChannel.class); + assertEquals("input", inputChannel.getComponentName()); + + final MessageTransformingHandler handler = TestUtils.getPropertyValue(consumer, "handler", MessageTransformingHandler.class); + + final AbstractMessageChannel outputChannel = TestUtils.getPropertyValue(handler, "outputChannel", AbstractMessageChannel.class); + assertEquals("output", outputChannel.getComponentName()); + + 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); + + assertNotNull(charset); + assertNotNull(fileNameGenerator); + assertNotNull(zipResultType); + assertNotNull(workDirectory); + assertNotNull(deleteFiles); + assertNotNull(expectSingleResult); + + assertEquals(Charset.defaultCharset(), charset); + Assert.isInstanceOf(DefaultFileNameGenerator.class, fileNameGenerator); + assertEquals(ZipResultType.FILE, zipResultType); + assertEquals(new File(System.getProperty("java.io.tmpdir") + File.separator + "ziptransformer"), workDirectory); + assertTrue("WorkDirectory should exist.", workDirectory.exists()); + assertTrue("WorkDirectory should be a directory.", workDirectory.isDirectory()); + assertFalse("By default the 'deleteFiles' property should be false.", deleteFiles); + assertFalse("The 'expectSingleResult' property should be false.", expectSingleResult); + } + + @Test + public void testUnZiptransformerParserWithExplicitSettings() { + + setUp("UnZipTransformerParserTests.xml", getClass()); + + EventDrivenConsumer consumer = this.context.getBean("unzipTransformer", EventDrivenConsumer.class); + + final AbstractMessageChannel inputChannel = TestUtils.getPropertyValue(consumer, "inputChannel", AbstractMessageChannel.class); + assertEquals("input", inputChannel.getComponentName()); + + final MessageTransformingHandler handler = TestUtils.getPropertyValue(consumer, "handler", MessageTransformingHandler.class); + + final AbstractMessageChannel outputChannel = TestUtils.getPropertyValue(handler, "outputChannel", AbstractMessageChannel.class); + assertEquals("output", outputChannel.getComponentName()); + + 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); + + assertNotNull(charset); + assertNotNull(fileNameGenerator); + assertNotNull(zipResultType); + assertNotNull(workDirectory); + assertNotNull(deleteFiles); + assertNotNull(expectSingleResult); + + assertEquals(Charset.defaultCharset(), charset); + Assert.isInstanceOf(DefaultFileNameGenerator.class, fileNameGenerator); + assertEquals(ZipResultType.FILE, zipResultType); + assertEquals(new File(System.getProperty("java.io.tmpdir") + File.separator + "ziptransformer"), workDirectory); + assertTrue("WorkDirectory should exist.", workDirectory.exists()); + assertTrue("WorkDirectory should be a directory.", workDirectory.isDirectory()); + assertTrue("The 'deleteFiles' property should be true.", deleteFiles); + assertTrue("The 'expectSingleResult' property should be true.", expectSingleResult); + } + + @After + public void tearDown(){ + if(context != null){ + context.close(); + } + } + + public void setUp(String name, Class cls){ + context = new ClassPathXmlApplicationContext(name, cls); + } + +} diff --git a/spring-integration-zip/src/test/java/org/springframework/integration/zip/config/xml/UnZipTransformerParserTests.xml b/spring-integration-zip/src/test/java/org/springframework/integration/zip/config/xml/UnZipTransformerParserTests.xml new file mode 100644 index 0000000..167565f --- /dev/null +++ b/spring-integration-zip/src/test/java/org/springframework/integration/zip/config/xml/UnZipTransformerParserTests.xml @@ -0,0 +1,19 @@ + + + + + + + + + + diff --git a/spring-integration-zip/src/test/java/org/springframework/integration/zip/config/xml/ZipTransformerParserTests.java b/spring-integration-zip/src/test/java/org/springframework/integration/zip/config/xml/ZipTransformerParserTests.java new file mode 100644 index 0000000..bcc3f6a --- /dev/null +++ b/spring-integration-zip/src/test/java/org/springframework/integration/zip/config/xml/ZipTransformerParserTests.java @@ -0,0 +1,159 @@ +/* + * Copyright 2015 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 + * + * http://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 static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.io.File; +import java.nio.charset.Charset; +import java.util.zip.Deflater; + +import org.junit.After; +import org.junit.Test; +import org.springframework.beans.factory.parsing.BeanDefinitionParsingException; +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.util.Assert; + +/** + * + * @author Gunnar Hillert + * @since 1.0 + * + */ +public class ZipTransformerParserTests { + + private ConfigurableApplicationContext context; + + @Test + public void testZiptransformerParserWithDefaults() { + + setUp("ZipTransformerParserTests.xml", getClass()); + + EventDrivenConsumer consumer = this.context.getBean("zipTransformerWithDefaults", EventDrivenConsumer.class); + + final AbstractMessageChannel inputChannel = TestUtils.getPropertyValue(consumer, "inputChannel", AbstractMessageChannel.class); + assertEquals("input", inputChannel.getComponentName()); + + final MessageTransformingHandler handler = TestUtils.getPropertyValue(consumer, "handler", MessageTransformingHandler.class); + + final AbstractMessageChannel outputChannel = TestUtils.getPropertyValue(handler, "outputChannel", AbstractMessageChannel.class); + assertEquals("output", outputChannel.getComponentName()); + + 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); + + assertNotNull(charset); + assertNotNull(fileNameGenerator); + assertNotNull(zipResultType); + assertNotNull(workDirectory); + assertNotNull(compressionLevel); + assertNotNull(deleteFiles); + + assertEquals(Charset.defaultCharset(), charset); + Assert.isInstanceOf(DefaultFileNameGenerator.class, fileNameGenerator); + assertEquals(ZipResultType.FILE, zipResultType); + assertEquals(new File(System.getProperty("java.io.tmpdir") + File.separator + "ziptransformer"), workDirectory); + assertTrue("WorkDirectory should exist.", workDirectory.exists()); + assertTrue("WorkDirectory should be a directory.", workDirectory.isDirectory()); + assertEquals(Integer.valueOf(Deflater.DEFAULT_COMPRESSION), Integer.valueOf(compressionLevel)); + assertFalse("By default the 'deleteFiles' property should be false.", deleteFiles); + } + + @Test + public void testZiptransformerParserWithExplicitSettings() { + + setUp("ZipTransformerParserTests.xml", getClass()); + + EventDrivenConsumer consumer = this.context.getBean("zipTransformer", EventDrivenConsumer.class); + + final AbstractMessageChannel inputChannel = TestUtils.getPropertyValue(consumer, "inputChannel", AbstractMessageChannel.class); + assertEquals("input", inputChannel.getComponentName()); + + final MessageTransformingHandler handler = TestUtils.getPropertyValue(consumer, "handler", MessageTransformingHandler.class); + + final AbstractMessageChannel outputChannel = TestUtils.getPropertyValue(handler, "outputChannel", AbstractMessageChannel.class); + assertEquals("output", outputChannel.getComponentName()); + + 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); + + assertNotNull(charset); + assertNotNull(fileNameGenerator); + assertNotNull(zipResultType); + assertNotNull(workDirectory); + assertNotNull(compressionLevel); + assertNotNull(deleteFiles); + + assertEquals(Charset.defaultCharset(), charset); + Assert.isInstanceOf(DefaultFileNameGenerator.class, fileNameGenerator); + assertEquals(ZipResultType.BYTE_ARRAY, zipResultType); + assertEquals(new File(System.getProperty("java.io.tmpdir") + File.separator + "ziptransformer"), workDirectory); + assertTrue("WorkDirectory should exist.", workDirectory.exists()); + assertTrue("WorkDirectory should be a directory.", workDirectory.isDirectory()); + assertEquals(Integer.valueOf(2), Integer.valueOf(compressionLevel)); + assertTrue("The 'deleteFiles' property should be true.", deleteFiles); + } + + @Test + public void testZiptransformerParserWithIncorrectResultType() { + + try { + setUp("ZipTransformerParserTestsWithIncorrectResultType.xml", getClass()); + } + catch (BeanDefinitionParsingException e) { + String expectedErrorMessage = "Unable to convert the provided result-type 'INCORRECT' " + + "to the respective ZipResultType enum."; + assertTrue(String.format("Expected exception message to contain '%s' but got '%s'", expectedErrorMessage, e.getMessage()), + e.getMessage().contains(expectedErrorMessage)); + return; + } + + fail("Expected a BeanDefinitionParsingException to be thrown."); + } + @After + public void tearDown(){ + if(context != null){ + context.close(); + } + } + + public void setUp(String name, Class cls){ + context = new ClassPathXmlApplicationContext(name, cls); + } + +} diff --git a/spring-integration-zip/src/test/java/org/springframework/integration/zip/config/xml/ZipTransformerParserTests.xml b/spring-integration-zip/src/test/java/org/springframework/integration/zip/config/xml/ZipTransformerParserTests.xml new file mode 100644 index 0000000..c98ebbe --- /dev/null +++ b/spring-integration-zip/src/test/java/org/springframework/integration/zip/config/xml/ZipTransformerParserTests.xml @@ -0,0 +1,19 @@ + + + + + + + + + + diff --git a/spring-integration-zip/src/test/java/org/springframework/integration/zip/config/xml/ZipTransformerParserTestsWithIncorrectResultType.xml b/spring-integration-zip/src/test/java/org/springframework/integration/zip/config/xml/ZipTransformerParserTestsWithIncorrectResultType.xml new file mode 100644 index 0000000..e20dc3d --- /dev/null +++ b/spring-integration-zip/src/test/java/org/springframework/integration/zip/config/xml/ZipTransformerParserTestsWithIncorrectResultType.xml @@ -0,0 +1,17 @@ + + + + + + + + + diff --git a/spring-integration-zip/src/test/java/org/springframework/integration/zip/transformer/UnZipTransformerTests.java b/spring-integration-zip/src/test/java/org/springframework/integration/zip/transformer/UnZipTransformerTests.java new file mode 100644 index 0000000..42919ae --- /dev/null +++ b/spring-integration-zip/src/test/java/org/springframework/integration/zip/transformer/UnZipTransformerTests.java @@ -0,0 +1,253 @@ +/* + * Copyright 2015 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 + * + * http://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.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.Map; + +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.IOUtils; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.io.Resource; +import org.springframework.core.io.ResourceLoader; +import org.springframework.integration.support.MessageBuilder; +import org.springframework.messaging.Message; +import org.springframework.messaging.MessagingException; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +/** + * + * @author Gunnar Hillert + * @since 1.0 + * + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration({"classpath:org/springframework/integration/zip/transformer/UnZipTransformerTests.xml"}) +public class UnZipTransformerTests { + + @Rule + public TemporaryFolder testFolder = new TemporaryFolder(); + + @Autowired + private ResourceLoader resourceLoader; + + private File workDir; + + @Before + public void setup() throws IOException { + this.workDir = testFolder.newFolder(); + } + + /** + * UnCompress a ZIP archive containing a single file only. The result will be + * a byte array. + * + * @throws IOException + */ + @Test + public void unzipSingleFileAsInputstreamToByteArray() throws IOException { + + final Resource resource = resourceLoader.getResource("classpath:testzipdata/single.zip"); + final InputStream is = resource.getInputStream(); + + final Message message = MessageBuilder.withPayload(is).build(); + + final UnZipTransformer unZipTransformer = new UnZipTransformer(); + unZipTransformer.setZipResultType(ZipResultType.BYTE_ARRAY); + unZipTransformer.afterPropertiesSet(); + + final Message resultMessage = unZipTransformer.transform(message); + + Assert.assertNotNull(resultMessage); + + @SuppressWarnings("unchecked") + Map unzippedData = (Map) resultMessage.getPayload(); + + Assert.assertNotNull(unzippedData); + Assert.assertTrue(unzippedData.size() == 1); + Assert.assertEquals("Spring Integration Rocks!", new String(unzippedData.values().iterator().next())); + + } + + /** + * + * + * @throws IOException + */ + @Test + public void unzipSingleFileToByteArray() throws IOException { + + final Resource resource = resourceLoader.getResource("classpath:testzipdata/single.zip"); + final InputStream is = resource.getInputStream(); + + final File inputFile = new File(this.workDir, "unzipSingleFileToByteArray"); + + IOUtils.copy(is, new FileOutputStream(inputFile)); + + final Message message = MessageBuilder.withPayload(inputFile).build(); + + final UnZipTransformer unZipTransformer = new UnZipTransformer(); + unZipTransformer.setZipResultType(ZipResultType.BYTE_ARRAY); + unZipTransformer.afterPropertiesSet(); + + final Message resultMessage = unZipTransformer.transform(message); + + Assert.assertNotNull(resultMessage); + + @SuppressWarnings("unchecked") + Map unzippedData = (Map) resultMessage.getPayload(); + + Assert.assertNotNull(unzippedData); + Assert.assertTrue(unzippedData.size() == 1); + Assert.assertTrue(inputFile.exists()); + Assert.assertEquals("Spring Integration Rocks!", new String(unzippedData.values().iterator().next())); + + } + + /** + * + * + * @throws IOException + */ + @Test + public void unzipSingleFileToByteArrayWithDeleteFilesTrue() throws IOException { + + final Resource resource = resourceLoader.getResource("classpath:testzipdata/single.zip"); + final InputStream is = resource.getInputStream(); + + final File inputFile = new File(this.workDir, "unzipSingleFileToByteArray"); + + IOUtils.copy(is, new FileOutputStream(inputFile)); + + final Message 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); + + Assert.assertNotNull(resultMessage); + + @SuppressWarnings("unchecked") + Map unzippedData = (Map) resultMessage.getPayload(); + + Assert.assertNotNull(unzippedData); + Assert.assertTrue(unzippedData.size() == 1); + Assert.assertFalse(inputFile.exists()); + Assert.assertEquals("Spring Integration Rocks!", new String(unzippedData.values().iterator().next())); + + } + + /** + * UnCompress a ZIP archive containing multiple files. The result will be + * a collection of files. + * + * @throws IOException + */ + @Test + public void unzipMultipleFilesAsInputstreamToByteArray() throws IOException { + + final Resource resource = resourceLoader.getResource("classpath:testzipdata/countries.zip"); + final InputStream is = resource.getInputStream(); + + final Message message = MessageBuilder.withPayload(is).build(); + + final UnZipTransformer unZipTransformer = new UnZipTransformer(); + unZipTransformer.setZipResultType(ZipResultType.BYTE_ARRAY); + unZipTransformer.afterPropertiesSet(); + + final Message resultMessage = unZipTransformer.transform(message); + + Assert.assertNotNull(resultMessage); + + @SuppressWarnings("unchecked") + Map unzippedData = (Map) resultMessage.getPayload(); + + Assert.assertNotNull(unzippedData); + Assert.assertTrue(unzippedData.size() == 5); + + } + + /** + * UnCompress a ZIP archive containing multiple files. The result will be + * a collection of files. + * + * @throws IOException + */ + @Test + public void unzipMultipleFilesAsInputstreamWithExpectSingleResultTrue() throws IOException { + + final Resource resource = resourceLoader.getResource("classpath:testzipdata/countries.zip"); + final InputStream is = resource.getInputStream(); + + final Message message = MessageBuilder.withPayload(is).build(); + + final UnZipTransformer unZipTransformer = new UnZipTransformer(); + unZipTransformer.setZipResultType(ZipResultType.BYTE_ARRAY); + unZipTransformer.setExpectSingleResult(true); + unZipTransformer.afterPropertiesSet(); + + try { + unZipTransformer.transform(message); + } + catch (MessagingException e) { + Assert.assertTrue(e.getMessage().contains("The UnZip operation extracted " + + "5 result objects but expectSingleResult was 'true'.")); + return; + } + + Assert.fail("Expected a MessagingException to be thrown."); + + } + + @Test + public void unzipInvalidZipFile() throws FileNotFoundException, IOException, InterruptedException { + + final File fileToUnzip = testFolder.newFile(); + FileUtils.writeStringToFile(fileToUnzip, "hello world"); + + final UnZipTransformer unZipTransformer = new UnZipTransformer(); + unZipTransformer.setZipResultType(ZipResultType.BYTE_ARRAY); + unZipTransformer.setExpectSingleResult(true); + unZipTransformer.afterPropertiesSet(); + + final Message message = MessageBuilder.withPayload(fileToUnzip).build(); + + try { + unZipTransformer.transform(message); + } + catch (MessagingException e) { + Assert.assertTrue(e.getMessage().contains(String.format("Not a zip file: '%s'.", fileToUnzip.getAbsolutePath()))); + return; + } + + Assert.fail("Expected a MessagingException to be thrown."); + } +} diff --git a/spring-integration-zip/src/test/java/org/springframework/integration/zip/transformer/UnZipTransformerTests.xml b/spring-integration-zip/src/test/java/org/springframework/integration/zip/transformer/UnZipTransformerTests.xml new file mode 100644 index 0000000..aa47808 --- /dev/null +++ b/spring-integration-zip/src/test/java/org/springframework/integration/zip/transformer/UnZipTransformerTests.xml @@ -0,0 +1,10 @@ + + + + diff --git a/spring-integration-zip/src/test/java/org/springframework/integration/zip/transformer/ZipTransformerTests.java b/spring-integration-zip/src/test/java/org/springframework/integration/zip/transformer/ZipTransformerTests.java new file mode 100644 index 0000000..678bf02 --- /dev/null +++ b/spring-integration-zip/src/test/java/org/springframework/integration/zip/transformer/ZipTransformerTests.java @@ -0,0 +1,284 @@ +/* + * Copyright 2015 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 + * + * http://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 static org.mockito.Mockito.mock; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +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.Assert; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.springframework.messaging.Message; +import org.springframework.beans.factory.BeanFactory; +import org.springframework.integration.support.MessageBuilder; +import org.springframework.integration.zip.ZipHeaders; +import org.zeroturnaround.zip.ZipUtil; + +/** + * + * @author Gunnar Hillert + * @since 1.0 + * + */ +public class ZipTransformerTests { + + @Rule + public TemporaryFolder testFolder = new TemporaryFolder(); + + /** + * Compress a simple String. The result will be a byte array. + * + * @throws FileNotFoundException + * @throws IOException + */ + @Test + public void zipString() throws FileNotFoundException, IOException { + 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 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(); + + Assert.assertTrue("Expected payload to be an instance of byte but was " + + resultPayload.getClass().getName(), resultPayload instanceof byte[]); + + final File temporaryTestDirectory = testFolder.newFolder(); + + ZipUtil.unpack(new ByteArrayInputStream((byte[]) resultPayload), temporaryTestDirectory); + + final File unzippedEntry = new File(temporaryTestDirectory, "test.txt"); + Assert.assertTrue(unzippedEntry.exists()); + Assert.assertTrue(unzippedEntry.isFile()); + + //See http://stackoverflow.com/questions/3725662/what-is-the-earliest-timestamp-value-that-is-supported-in-zip-file-format + Assert.assertTrue((fileDate.getTime() - 3000) < unzippedEntry.lastModified()); + Assert.assertTrue((fileDate.getTime() + 3000) > unzippedEntry.lastModified()); + } + + @Test + public void zipStringCollection() throws FileNotFoundException, IOException { + 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 strings = new ArrayList(3); + + strings.add(string1ToCompress); + strings.add(string2ToCompress); + strings.add(string3ToCompress); + + final Date fileDate = new Date(); + + final Message> 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(); + + Assert.assertTrue("Expected payload to be an instance of byte but was " + + resultPayload.getClass().getName(), resultPayload instanceof byte[]); + + final File temporaryTestDirectory = testFolder.newFolder(); + + ZipUtil.unpack(new ByteArrayInputStream((byte[]) resultPayload), temporaryTestDirectory); + + File[] files = temporaryTestDirectory.listFiles(); + + Assert.assertTrue(files.length >= 3); + + final Set 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")) { + Assert.assertTrue(file.exists()); + Assert.assertTrue(file.isFile()); + + //See http://stackoverflow.com/questions/3725662/what-is-the-earliest-timestamp-value-that-is-supported-in-zip-file-format + Assert.assertTrue(String.format("%s : %s", fileDate.getTime() - 4000, file.lastModified()), (fileDate.getTime() - 4000) < file.lastModified()); + Assert.assertTrue((fileDate.getTime() + 4000) > file.lastModified()); + + Assert.assertTrue( + String.format("File '%s' did not end with '.txt'.", file.getName()), + file.getName().endsWith(".txt")); + + Assert.assertTrue(expectedFileNames.contains(file.getName())); + } + + } + + } + + @Test + public void zipStringToFile() throws FileNotFoundException, IOException { + final ZipTransformer zipTransformer = new ZipTransformer(); + zipTransformer.setBeanFactory(mock(BeanFactory.class)); + zipTransformer.setZipResultType(ZipResultType.FILE); + zipTransformer.afterPropertiesSet(); + + final String stringToCompress = "Hello World"; + + final String zipEntryFileName = "test.txt"; + final Message message = MessageBuilder.withPayload(stringToCompress) + .setHeader(ZipHeaders.ZIP_ENTRY_FILE_NAME, zipEntryFileName) + .build(); + + final Message result = zipTransformer.transform(message); + + Assert.assertTrue("Expected payload to be an instance of file but was " + + result.getPayload().getClass().getName(), result.getPayload() instanceof File); + + final File payload = (File) result.getPayload(); + + System.out.println(payload.getAbsolutePath()); + + Assert.assertEquals(message.getHeaders().getId().toString() + ".msg.zip", payload.getName()); + Assert.assertTrue(SpringZipUtils.isValid(payload)); + + final byte[] zipEntryData = ZipUtil.unpackEntry(payload, "test.txt"); + + Assert.assertNotNull("Entry '" + zipEntryFileName + "' was not found.", zipEntryData); + Assert.assertTrue("Hello World".equals(new String(zipEntryData))); + + } + + @Test + public void zipFile() throws IOException { + + final ZipTransformer zipTransformer = new ZipTransformer(); + zipTransformer.setBeanFactory(mock(BeanFactory.class)); + zipTransformer.afterPropertiesSet(); + + final File testFile = createTestFile(10); + + Assert.assertTrue(testFile.exists()); + + final Message message = MessageBuilder.withPayload(testFile).build(); + + final Message result = zipTransformer.transform(message); + + Assert.assertTrue(result.getPayload() instanceof File); + + final File payload = (File) result.getPayload(); + + Assert.assertEquals(testFile.getName() + ".zip", payload.getName()); + Assert.assertTrue(SpringZipUtils.isValid(payload)); + } + + @Test + public void zipCollection() throws IOException { + + final File testFile1 = createTestFile(1); + final File testFile2 = createTestFile(2); + final File testFile3 = createTestFile(3); + final File testFile4 = createTestFile(4); + + Assert.assertTrue(testFile1.exists()); + Assert.assertTrue(testFile2.exists()); + Assert.assertTrue(testFile3.exists()); + Assert.assertTrue(testFile4.exists()); + + final Collection 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> message = MessageBuilder.withPayload(files) + .build(); + + final Message result = zipTransformer.transform(message); + + Assert.assertTrue(result.getPayload() instanceof File); + + final File outputZipFile = (File) result.getPayload(); + + Assert.assertTrue(outputZipFile.exists()); + Assert.assertTrue(outputZipFile.isFile()); + Assert.assertTrue(outputZipFile.getName().endsWith("zip")); + Assert.assertTrue(SpringZipUtils.isValid(outputZipFile)); + + } + + private File createTestFile(int size) throws IOException { + + final File temporaryTestDirectory = testFolder.newFolder(); + + final File testFile = new File(temporaryTestDirectory, "testdata" + UUID.randomUUID().toString() + ".data"); + + RandomAccessFile f = null; + try { + f = new RandomAccessFile(testFile, "rw"); + f.setLength(size * 1024 * 1024); + } + catch (Exception e) { + System.err.println(e); + } + finally { + try { + if (f != null) { + f.close(); + } + } catch (IOException e) {} + } + return testFile; + + } + +} diff --git a/spring-integration-zip/src/test/resources/log4j.properties b/spring-integration-zip/src/test/resources/log4j.properties new file mode 100644 index 0000000..1bcfd18 --- /dev/null +++ b/spring-integration-zip/src/test/resources/log4j.properties @@ -0,0 +1,8 @@ +log4j.rootCategory=WARN, stdout + +log4j.appender.stdout=org.apache.log4j.ConsoleAppender +log4j.appender.stdout.layout=org.apache.log4j.PatternLayout +log4j.appender.stdout.layout.ConversionPattern=%d{HH:mm:ss.SSS} %-5p [%t][%c] %m%n + +log4j.category.org.springframework.integration=INFO +log4j.category.org.springframework.integration.zip=INFO diff --git a/spring-integration-zip/src/test/resources/testzipdata/countries.zip b/spring-integration-zip/src/test/resources/testzipdata/countries.zip new file mode 100644 index 0000000..89e7e4d Binary files /dev/null and b/spring-integration-zip/src/test/resources/testzipdata/countries.zip differ diff --git a/spring-integration-zip/src/test/resources/testzipdata/countries/continents/asia.txt b/spring-integration-zip/src/test/resources/testzipdata/countries/continents/asia.txt new file mode 100644 index 0000000..a287965 --- /dev/null +++ b/spring-integration-zip/src/test/resources/testzipdata/countries/continents/asia.txt @@ -0,0 +1 @@ +Asia \ No newline at end of file diff --git a/spring-integration-zip/src/test/resources/testzipdata/countries/continents/europe.txt b/spring-integration-zip/src/test/resources/testzipdata/countries/continents/europe.txt new file mode 100644 index 0000000..c6af3c9 --- /dev/null +++ b/spring-integration-zip/src/test/resources/testzipdata/countries/continents/europe.txt @@ -0,0 +1 @@ +Europe \ No newline at end of file diff --git a/spring-integration-zip/src/test/resources/testzipdata/countries/de.txt b/spring-integration-zip/src/test/resources/testzipdata/countries/de.txt new file mode 100644 index 0000000..646bce2 --- /dev/null +++ b/spring-integration-zip/src/test/resources/testzipdata/countries/de.txt @@ -0,0 +1 @@ +Germany \ No newline at end of file diff --git a/spring-integration-zip/src/test/resources/testzipdata/countries/fr.txt b/spring-integration-zip/src/test/resources/testzipdata/countries/fr.txt new file mode 100644 index 0000000..b9a7c52 --- /dev/null +++ b/spring-integration-zip/src/test/resources/testzipdata/countries/fr.txt @@ -0,0 +1 @@ +France \ No newline at end of file diff --git a/spring-integration-zip/src/test/resources/testzipdata/countries/pl.txt b/spring-integration-zip/src/test/resources/testzipdata/countries/pl.txt new file mode 100644 index 0000000..639acf6 --- /dev/null +++ b/spring-integration-zip/src/test/resources/testzipdata/countries/pl.txt @@ -0,0 +1 @@ +Poland \ No newline at end of file diff --git a/spring-integration-zip/src/test/resources/testzipdata/single.zip b/spring-integration-zip/src/test/resources/testzipdata/single.zip new file mode 100644 index 0000000..9f61c70 Binary files /dev/null and b/spring-integration-zip/src/test/resources/testzipdata/single.zip differ