From 255a74ecac67e13883885440f5b779a09931ab3b Mon Sep 17 00:00:00 2001 From: Gunnar Hillert Date: Mon, 13 Aug 2012 16:00:37 -0400 Subject: [PATCH] Improving the Print Adapter * Add better namespace support * Add documentation * Add test cases (minimal) * Add functionality --- spring-integration-print/README.md | 129 ++++++++---- spring-integration-print/build.gradle | 10 +- .../PrintOutboundChannelAdapterParser.java | 42 +++- .../print/config/xml/PrintParserUtils.java | 43 +++- .../integration/print/core/PrintExecutor.java | 98 --------- .../print/core/PrintServiceExecutor.java | 177 ++++++++++++++++ .../integration/print/core/package-info.java | 4 - .../print/outbound/PrintMessageHandler.java | 189 +++++++++++++++--- .../print/support/AttributeComparator.java | 39 ++++ .../print/support/ChromaticityEnum.java | 57 ++++++ .../DocFlavorComparator.java} | 21 +- .../print/support/MediaSizeNameEnum.java | 126 ++++++++++++ .../print/support/MediaTrayEnum.java | 63 ++++++ ...Utils.java => PrintServiceComparator.java} | 13 +- .../integration/print/support/PrintSides.java | 15 ++ .../xml/spring-integration-print-2.2.xsd | 139 ++++++++++++- .../docbook/SIAdapterLowerPrefix.xml | 73 ------- .../src/reference/docbook/history.xml | 8 - .../src/reference/docbook/images/logo.png | Bin 9627 -> 0 bytes .../src/reference/docbook/index.xml | 68 ------- .../src/reference/docbook/resources.xml | 17 -- .../src/reference/docbook/whats-new.xml | 11 - .../integration/print/PrintTests.java | 38 ---- ...PrintMessageHandlerParserTests-context.xml | 17 +- .../xml/PrintMessageHandlerParserTests.java | 8 - .../outbound/PrintMessageHandlerTest.java | 32 ++- .../PrintOutboundChannelAdapterTest.java | 1 - .../print/support/MockPrintService.java | 97 +++++++++ .../print/support/PrintJobMonitor.java | 66 ++++++ .../print/support/PrintUtilsTest.java | 40 ++++ .../src/test/resources/log4j.properties | 2 +- .../Spring Integration Print Testing.pdf | Bin 0 -> 14263 bytes 32 files changed, 1198 insertions(+), 445 deletions(-) delete mode 100644 spring-integration-print/src/main/java/org/springframework/integration/print/core/PrintExecutor.java create mode 100644 spring-integration-print/src/main/java/org/springframework/integration/print/core/PrintServiceExecutor.java delete mode 100644 spring-integration-print/src/main/java/org/springframework/integration/print/core/package-info.java create mode 100644 spring-integration-print/src/main/java/org/springframework/integration/print/support/AttributeComparator.java create mode 100644 spring-integration-print/src/main/java/org/springframework/integration/print/support/ChromaticityEnum.java rename spring-integration-print/src/main/java/org/springframework/integration/print/{PrintHeaders.java => support/DocFlavorComparator.java} (59%) create mode 100644 spring-integration-print/src/main/java/org/springframework/integration/print/support/MediaSizeNameEnum.java create mode 100644 spring-integration-print/src/main/java/org/springframework/integration/print/support/MediaTrayEnum.java rename spring-integration-print/src/main/java/org/springframework/integration/print/support/{PrintUtils.java => PrintServiceComparator.java} (72%) delete mode 100644 spring-integration-print/src/reference/docbook/SIAdapterLowerPrefix.xml delete mode 100644 spring-integration-print/src/reference/docbook/history.xml delete mode 100644 spring-integration-print/src/reference/docbook/images/logo.png delete mode 100644 spring-integration-print/src/reference/docbook/index.xml delete mode 100644 spring-integration-print/src/reference/docbook/resources.xml delete mode 100644 spring-integration-print/src/reference/docbook/whats-new.xml delete mode 100644 spring-integration-print/src/test/java/org/springframework/integration/print/PrintTests.java create mode 100644 spring-integration-print/src/test/java/org/springframework/integration/print/support/MockPrintService.java create mode 100644 spring-integration-print/src/test/java/org/springframework/integration/print/support/PrintJobMonitor.java create mode 100644 spring-integration-print/src/test/java/org/springframework/integration/print/support/PrintUtilsTest.java create mode 100644 spring-integration-print/src/test/resources/org/springframework/integration/print/outbound/Spring Integration Print Testing.pdf diff --git a/spring-integration-print/README.md b/spring-integration-print/README.md index fb02625..8a76de3 100644 --- a/spring-integration-print/README.md +++ b/spring-integration-print/README.md @@ -1,70 +1,121 @@ -Spring Integration Print Adapter -================================================= +Spring Integration Print Support +================================ -Welcome to the Spring Integration Adapter Template. This template is meant as a starting point for new Spring Integration Adapters. This temaplate provides the following -stubbed out components: +## Overview -* Outbound Channel Adapter -* Outbound Gateway -* Inbound Channel Adapter +The *Spring Integration Print* allows you to print payloads using the [Java Print Service][]. As such one *Outbound Channel Adapter* is provided. -# STS issues to be aware of +## Requirements -* [STS-1790](https://issuetracker.springsource.com/browse/STS-1790) - Allow for more flexibility in defining top level packages in the Spring STS Template Wizard -* [STS-2680](https://issuetracker.springsource.com/browse/STS-2680) - New Template Wizard projects aren't categorized on first launch +This module depends by default on Spring Integration 2.2. When using this module, please be aware that printing using [Java Print Service] can be unpredictable depending on the uses platform and the used printer. For some more details, please see: -# FAQ +* http://hillert.blogspot.com/2011/12/java-print-service-frustrations.html -## I need to add AOP Advices to my Adapters +Currently the Print Support has been tested using a *Brother HL-4070CDW* which exhibits several issues such as the inability to execute a print job that prints more than one copy for each page. If you use the Print Module, please report back any compatibility issues with your used printers. -Use FactoryBeans that wrap your adapter. See JPA Adapter for example. +## Dependency Declaration -## More to come... +### Gradle -# Building + dependencies { + … + compile "org.springframework.integration:spring-integration-print:1.0.0.BUILD-SNAPSHOT" + … + } -If you encounter out of memory errors during the build, increase available heap and permgen for Gradle: +### Maven - GRADLE_OPTS='-XX:MaxPermSize=1024m -Xmx1024m' + + … + + org.springframework.integration + spring-integration-print + 1.0.0.BUILD-SNAPSHOT + + … + -To build and install jars into your local Maven cache: +## Print Outbound Channel Adapter - ./gradlew install +The *Print Outbound Channel Adapter* is implemented using the *PrintMessageHandler* class. It in turn delegates to the *PrintServiceExecutor* class. The *PrintServiceExecutor* is responsible for initializing the Java *[PrintService][]*. As such, you can initialize the *PrintServiceExecutor* with providing a: -To build api Javadoc (results will be in `build/api`): +* Printer Name +* Custom implementation of the *[PrintService][]* interface (Useful for testing) - ./gradlew api +If you provide neither a printer name nor an implementation of the *[PrintService][]* interface, then the systems default printer is chosen. -To build reference documentation (results will be in `build/reference`): +The following properties can be set on the *Print Outbound Channel Adapter*: - ./gradlew reference +### printJobName -To build complete distribution including `-dist`, `-docs`, and `-schema` zip files (results will be in `build/distributions`) +*Optional* property that let's you specify the name of the print job as it is added to the print queue. Under the covers, this property will create a *javax.print.attribute.standard.JobName* and add it to the respective *PrintRequestAttributeSet*. This property is not used, if not specified (No explicit default value). - ./gradlew dist +### mediaSizeName -# Using SpringSource Tool Suite +*Optional* property that let's you specify the [MediaSizeName][]. Under the covers, this property will add the provided [MediaSizeName][] to the respective *PrintRequestAttributeSet*. This property is not used, if not specified (No explicit default value). - Gradle projects can be directly imported into STS +### sides -# Using PLain Eclipse +Let's you specify how the pages are are printed to the selected medium, e.g. duplex or one-sided. Under the covers, this property will add the provided [Sides][] instance to the respective *PrintRequestAttributeSet*. This property is not used, if not specified (No explicit default value). -To generate Eclipse metadata (.classpath and .project files), do the following: +### copies - ./gradlew eclipse +Let's you specify an integer value that indicates how many time each print page shall be printed. Under the covers, this property will add a [Copies][] instance to the respective *PrintRequestAttributeSet*. This property is not used, if not specified (No explicit default value). -Once complete, you may then import the projects into Eclipse as usual: +### mediaTray - *File -> Import -> Existing projects into workspace* +Let's you specify the media tray/bin for the print job. This property can be used instead of providing the *mediaSizeName* property. Under the covers, this property will add the provided [MediaTray][] instance to the respective *PrintRequestAttributeSet*. This property is not used, if not specified (No explicit default value). -Browse to the *'spring-integration'* root directory. All projects should import -free of errors. +### chromaticity -# Using IntelliJ IDEA +Let's you specify whether you want to do monochrome or color printing. Under the covers, this property will add the provided [Chromaticity][] instance to the respective *PrintRequestAttributeSet*. This property is not used, if not specified (No explicit default value). -To generate IDEA metadata (.iml and .ipr files), do the following: +### docFlavor - ./gradlew idea +Let's you specify the [DocFlavor]. If not specified, this property will default to *DocFlavor.STRING.TEXT_PLAIN*. -For more information, please visit the Spring Integration website at: -[http://www.springsource.org/spring-integration](http://www.springsource.org/spring-integration) +**IMPORTANT!** Every property is validated against the to be used [Java Print Service]. Therefore, if the selected [Java Print Service] is not supported, an *IllegalArgumentException* will be thrown. + +### Determining you printer capabilities. + +In order to get some indication what capabilities are supported by your printer, you may want to too look at the *PrintServiceExecutor*'s *getPrinterInfo()* method. When executed, it returns a list of: + +* Supported [DocFlavor][]s +* Supported Attribute Categories +* Supported [Print Attribute][]s + +## Namespace Support + +*Spring Integration* also provides XML Namespace support for the Print support. Simply add the following Namespace declaration: + + + + … + + +An example of declaring a *Print Outbound Channel Adapter* is shown below: + + + + +[Java Print Service]: http://docs.oracle.com/javase/6/docs/technotes/guides/jps/index.html +[PrintService]: http://docs.oracle.com/javase/6/docs/api/javax/print/PrintService.html +[Chromaticity]: http://docs.oracle.com/javase/6/docs/api/javax/print/attribute/standard/Chromaticity.html +[Copies]: http://docs.oracle.com/javase/6/docs/api/javax/print/attribute/standard/Copies.html +[DocFlavor]: http://docs.oracle.com/javase/6/docs/api/javax/print/DocFlavor.html +[MediaTray]: http://docs.oracle.com/javase/6/docs/api/javax/print/attribute/standard/MediaTray.html +[Print Attribute]: http://docs.oracle.com/javase/6/docs/api/javax/print/attribute/Attribute.html +[MediaSizeName]: http://docs.oracle.com/javase/6/docs/api/javax/print/attribute/standard/MediaSizeName.html +[Sides]: http://docs.oracle.com/javase/6/docs/api/javax/print/attribute/standard/Sides.html \ No newline at end of file diff --git a/spring-integration-print/build.gradle b/spring-integration-print/build.gradle index 025c7ad..6cd26ba 100644 --- a/spring-integration-print/build.gradle +++ b/spring-integration-print/build.gradle @@ -4,9 +4,6 @@ buildscript { repositories { maven { url 'https://repo.springsource.org/plugins-snapshot' } } - dependencies { - classpath 'org.springframework.build.gradle:docbook-reference-plugin:0.1.5' - } } apply plugin: 'java' @@ -21,9 +18,8 @@ repositories { maven { url 'http://repo.springsource.org/plugins-release' } // for bundlor } -// ensure JDK 5 compatibility (GRADLE-18; INT-1578) -sourceCompatibility=1.5 -targetCompatibility=1.5 +sourceCompatibility=1.6 +targetCompatibility=1.6 ext { aspectjVersion = '1.6.8' @@ -303,5 +299,5 @@ task dist(dependsOn: assemble) { task wrapper(type: Wrapper) { description = 'Generates gradlew[.bat] scripts' - gradleVersion = '1.0-rc-3' + gradleVersion = '1.0' } diff --git a/spring-integration-print/src/main/java/org/springframework/integration/print/config/xml/PrintOutboundChannelAdapterParser.java b/spring-integration-print/src/main/java/org/springframework/integration/print/config/xml/PrintOutboundChannelAdapterParser.java index ad626f8..621332e 100644 --- a/spring-integration-print/src/main/java/org/springframework/integration/print/config/xml/PrintOutboundChannelAdapterParser.java +++ b/spring-integration-print/src/main/java/org/springframework/integration/print/config/xml/PrintOutboundChannelAdapterParser.java @@ -15,12 +15,17 @@ */ package org.springframework.integration.print.config.xml; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.parsing.BeanComponentDefinition; import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.xml.ParserContext; import org.springframework.integration.config.xml.AbstractOutboundChannelAdapterParser; import org.springframework.integration.config.xml.IntegrationNamespaceUtils; import org.springframework.integration.print.outbound.PrintMessageHandler; +import org.springframework.integration.print.support.ChromaticityEnum; +import org.springframework.integration.print.support.MediaSizeNameEnum; +import org.springframework.integration.print.support.MediaTrayEnum; import org.springframework.integration.print.support.PrintSides; import org.springframework.util.StringUtils; import org.w3c.dom.Element; @@ -30,7 +35,7 @@ import org.w3c.dom.Element; * The parser for the Print Outbound Channel Adapter. * * @author Gunnar Hillert - * @since 2.2 + * @since 1.0 * */ public class PrintOutboundChannelAdapterParser extends AbstractOutboundChannelAdapterParser { @@ -49,6 +54,16 @@ public class PrintOutboundChannelAdapterParser extends AbstractOutboundChannelAd protected AbstractBeanDefinition parseConsumer(Element element, ParserContext parserContext) { final BeanDefinitionBuilder printOutboundChannelAdapterBuilder = BeanDefinitionBuilder.genericBeanDefinition(PrintMessageHandler.class); + final BeanDefinitionBuilder printServiceExecutorBuilder = PrintParserUtils.getPrintServiceExecutorBuilder(element, parserContext); + + final BeanDefinition printServiceExecutorBeanDefinition = printServiceExecutorBuilder.getBeanDefinition(); + + final String channelAdapterId = this.resolveId(element, printServiceExecutorBuilder.getRawBeanDefinition(), parserContext); + final String printServiceExecutorBeanName = channelAdapterId + ".jpaExecutor"; + + parserContext.registerBeanComponent(new BeanComponentDefinition(printServiceExecutorBeanDefinition, printServiceExecutorBeanName)); + + printOutboundChannelAdapterBuilder.addConstructorArgReference(printServiceExecutorBeanName); String docFlavor = element.getAttribute("doc-flavor"); String mimeType = element.getAttribute("mime-type"); @@ -62,11 +77,30 @@ public class PrintOutboundChannelAdapterParser extends AbstractOutboundChannelAd printOutboundChannelAdapterBuilder.addConstructorArgValue(docFlavor); } - String sides = element.getAttribute("sides"); + final String sides = element.getAttribute("sides"); - PrintSides printSides = PrintSides.fromString(sides); + if (StringUtils.hasText(sides)) { + final PrintSides printSides = PrintSides.fromString(sides); + printOutboundChannelAdapterBuilder.addPropertyValue("sides", printSides.getSides()); + } - printOutboundChannelAdapterBuilder.addPropertyValue("sides", printSides.getSides()); + final String mediaSizeName = element.getAttribute("media-size-name"); + + if (StringUtils.hasText(mediaSizeName)) { + printOutboundChannelAdapterBuilder.addPropertyValue("mediaSizeName", MediaSizeNameEnum.getForString(mediaSizeName)); + } + + final String mediaTray = element.getAttribute("media-tray"); + + if (StringUtils.hasText(mediaTray)) { + printOutboundChannelAdapterBuilder.addPropertyValue("mediaTray", MediaTrayEnum.getForString(mediaTray)); + } + + final String chromaticity = element.getAttribute("chromaticity"); + + if (StringUtils.hasText(chromaticity)) { + printOutboundChannelAdapterBuilder.addPropertyValue("chromaticity", ChromaticityEnum.getForString(chromaticity)); + } IntegrationNamespaceUtils.setValueIfAttributeDefined(printOutboundChannelAdapterBuilder, element, "copies"); diff --git a/spring-integration-print/src/main/java/org/springframework/integration/print/config/xml/PrintParserUtils.java b/spring-integration-print/src/main/java/org/springframework/integration/print/config/xml/PrintParserUtils.java index 65f51e7..2bf6c07 100644 --- a/spring-integration-print/src/main/java/org/springframework/integration/print/config/xml/PrintParserUtils.java +++ b/spring-integration-print/src/main/java/org/springframework/integration/print/config/xml/PrintParserUtils.java @@ -18,17 +18,17 @@ package org.springframework.integration.print.config.xml; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.xml.ParserContext; -import org.springframework.integration.print.core.PrintExecutor; +import org.springframework.integration.print.core.PrintServiceExecutor; import org.springframework.util.Assert; +import org.springframework.util.StringUtils; import org.w3c.dom.Element; - /** - * Contains various utility methods for parsing Print Adapter - * specific namesspace elements as well as for the generation of the the - * respective {@link BeanDefinition}s. + * Contains various utility methods for parsing Print Adapter specific namesspace + * elements and generating the respective {@link BeanDefinition}s. * * @author Gunnar Hillert + * * @since 1.0 * */ @@ -40,22 +40,43 @@ public final class PrintParserUtils { } /** - * Create a new {@link BeanDefinitionBuilder} for the class {@link PrintExecutor}. - * Initialize the wrapped {@link PrintExecutor} with common properties. + * Create a new {@link BeanDefinitionBuilder} for the class {@link JpaExecutor}. + * Initialize the wrapped {@link JpaExecutor} with common properties. * * @param element Must not be null * @param parserContext Must not be null - * @return The BeanDefinitionBuilder for the PrintExecutor + * @return The BeanDefinitionBuilder for the JpaExecutor */ - public static BeanDefinitionBuilder getPrintExecutorBuilder(final Element element, + public static BeanDefinitionBuilder getPrintServiceExecutorBuilder(final Element element, final ParserContext parserContext) { Assert.notNull(element, "The provided element must not be null."); Assert.notNull(parserContext, "The provided parserContext must not be null."); - final BeanDefinitionBuilder printExecutorBuilder = BeanDefinitionBuilder.genericBeanDefinition(PrintExecutor.class); + final Object source = parserContext.extractSource(element); - return printExecutorBuilder; + final BeanDefinitionBuilder printServiceExecutorBuilder = BeanDefinitionBuilder.genericBeanDefinition(PrintServiceExecutor.class); + + final String printServiceRef = element.getAttribute("print-service-ref"); + final String printerName = element.getAttribute("printer-name"); + + if (StringUtils.hasText(printServiceRef) + && StringUtils.hasText(printerName)) { + + parserContext.getReaderContext().error("Exactly only one of the attributes 'print-service-ref' or " + + "'printer-name' must be be set.", source); + + } + + if (StringUtils.hasText(printServiceRef)) { + printServiceExecutorBuilder.addConstructorArgReference(printServiceRef); + } + + if (StringUtils.hasText(printerName)) { + printServiceExecutorBuilder.addConstructorArgReference(printerName); + } + + return printServiceExecutorBuilder; } diff --git a/spring-integration-print/src/main/java/org/springframework/integration/print/core/PrintExecutor.java b/spring-integration-print/src/main/java/org/springframework/integration/print/core/PrintExecutor.java deleted file mode 100644 index 804c3ff..0000000 --- a/spring-integration-print/src/main/java/org/springframework/integration/print/core/PrintExecutor.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright 2002-2012 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.print.core; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.springframework.beans.factory.InitializingBean; -import org.springframework.integration.Message; -import org.springframework.integration.support.MessageBuilder; -import org.springframework.util.Assert; - -/** - * Bundles common core logic for the Print components. - * - * @author Gunnar Hillert - * @since 1.0 - * - */ -public class PrintExecutor implements InitializingBean { - - private static final Log logger = LogFactory.getLog(PrintExecutor.class); - - private volatile String exampleProperty; - - /** - * Constructor. - */ - public PrintExecutor() {} - - /** - * Verifies and sets the parameters. E.g. initializes the to be used - */ - public void afterPropertiesSet() { - Assert.hasText(this.exampleProperty, "exampleProperty must not be empty."); - } - - /** - * Executes the outbound Print Operation. - * - */ - public Object executeOutboundOperation(final Message message) { - - if (logger.isWarnEnabled()) { - logger.warn("Logic not implemented, yet."); - } - - return message.getPayload(); - - } - - /** - * Execute the Print operation. Delegates to - * {@link PrintExecutor#poll(Message)}. - */ - public Object poll() { - return poll(null); - } - - /** - * Execute a retrieving (polling) Print operation. - * - * @param requestMessage May be null. - * @return The payload object, which may be null. - */ - public Object poll(final Message requestMessage) { - - if (logger.isWarnEnabled()) { - logger.warn("Logic not implemented, yet."); - } - - return MessageBuilder.fromMessage(requestMessage).build(); - } - - /** - * Example property to illustrate usage of properties in Spring Integration - * components. Replace with your own logic. - * - * @param exampleProperty Must not be null - */ - public void setExampleProperty(String exampleProperty) { - Assert.hasText(exampleProperty, "exampleProperty must be neither null nor empty"); - this.exampleProperty = exampleProperty; - } - -} diff --git a/spring-integration-print/src/main/java/org/springframework/integration/print/core/PrintServiceExecutor.java b/spring-integration-print/src/main/java/org/springframework/integration/print/core/PrintServiceExecutor.java new file mode 100644 index 0000000..2732a2f --- /dev/null +++ b/spring-integration-print/src/main/java/org/springframework/integration/print/core/PrintServiceExecutor.java @@ -0,0 +1,177 @@ +/* + * Copyright 2002-2012 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.print.core; + +import java.util.Arrays; +import java.util.SortedSet; +import java.util.TreeSet; + +import javax.print.DocFlavor; +import javax.print.PrintService; +import javax.print.PrintServiceLookup; +import javax.print.attribute.Attribute; +import javax.print.attribute.AttributeSet; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.integration.print.support.AttributeComparator; +import org.springframework.integration.print.support.DocFlavorComparator; +import org.springframework.integration.print.support.PrintServiceComparator; +import org.springframework.util.Assert; +import org.springframework.util.StringUtils; + +/** + * @author Gunnar Hillert + * @since 1.0 + * + */ +public final class PrintServiceExecutor { + + private final Log logger = LogFactory.getLog(PrintServiceExecutor.class); + + private PrintService printService; + + public PrintServiceExecutor() { + this(""); + } + + public PrintServiceExecutor(PrintService printService) { + this.printService = printService; + } + + public PrintServiceExecutor(String printerName) { + + if (!StringUtils.hasText(printerName)) { + this.printService = PrintServiceLookup.lookupDefaultPrintService(); + Assert.notNull(this.printService, "Did not find a the default print service."); + } + else { + PrintService matchingPrintService = null; + + for (PrintService printService : PrintServiceExecutor.getAvailablePrinterServices()) { + if (printerName.equalsIgnoreCase(printService.getName())) { + matchingPrintService = printService; + break; + } + } + + this.printService = matchingPrintService; + + Assert.notNull(this.printService, String.format("Did not find the" + + "print service for printer '%s'.", printerName)); + } + + if (logger.isInfoEnabled()) { + logger.info("Setting up print service for printer '" + this.printService.getName() + "'."); + } + else if (logger.isDebugEnabled()) { + logger.debug(this.getPrinterInfo()); + } + + } + + public SortedSet getDocFlavors() { + final SortedSet docFlavors = new TreeSet(new DocFlavorComparator()); + docFlavors.addAll(Arrays.asList(this.printService.getSupportedDocFlavors())); + return docFlavors; + } + + public String getPrinterInfo() { + StringBuilder sb = new StringBuilder(); + + final SortedSet docFlavors = this.getDocFlavors(); + final AttributeSet attributes = this.printService.getAttributes(); + + sb.append("\nPrinter Information\n"); + sb.append("==========================================================\n"); + sb.append("Name: " + this.printService.getName() + "\n"); + sb.append("Supported DocFlavors:\n"); + + for (DocFlavor docFlavor : docFlavors) { + sb.append(" " + docFlavor + "\n"); + } + + @SuppressWarnings("unchecked") + Class[] categories = (Class[]) this.printService + .getSupportedAttributeCategories(); + + sb.append("Supported Categories:\n"); + for (Class clazz : categories) { + sb.append(" " + clazz.getName() + "\n"); + } + + final SortedSet supportedAttributes = new TreeSet(new AttributeComparator()); + + for (Class clazz : categories) { + for (DocFlavor docFlavor : docFlavors) { + + Object value = this.printService.getSupportedAttributeValues(clazz, docFlavor, attributes); + + if (value != null) { + if (value instanceof Attribute) { + supportedAttributes.add((Attribute) value); + } + else if (value instanceof Attribute[]) { + supportedAttributes.addAll(Arrays.asList((Attribute[]) value)); + } + } + } + } + + sb.append("Supported Attributes:\n"); + + for (Attribute attribute : supportedAttributes) { + sb.append(" " + attribute + "(" + attribute.getCategory().getSimpleName() + ")\n"); + } + + return sb.toString(); + } + + public static SortedSet getAvailablePrinterServices() { + PrintService[] printServices = PrintServiceLookup.lookupPrintServices(null, null); + printServices[0].getName(); + + SortedSet printServicesList = new TreeSet(new PrintServiceComparator()); + printServicesList.addAll(Arrays.asList(printServices)); + + return printServicesList; + } + + public SortedSet getDocFlavorsSupportingAttribute(Attribute attribute) { + + final SortedSet docFlavors = this.getDocFlavors(); + final AttributeSet attributes = this.printService.getAttributes(); + + final SortedSet matchedDocFlavors = new TreeSet(new DocFlavorComparator()); + + for (DocFlavor docFlavor : docFlavors) { + + final Object value = this.printService.getSupportedAttributeValues(attribute.getCategory(), docFlavor, attributes); + + if (value != null) { + matchedDocFlavors.add(docFlavor); + } + + } + + return matchedDocFlavors; + } + + public PrintService getPrintService() { + return printService; + } + +} diff --git a/spring-integration-print/src/main/java/org/springframework/integration/print/core/package-info.java b/spring-integration-print/src/main/java/org/springframework/integration/print/core/package-info.java deleted file mode 100644 index bf8bf0c..0000000 --- a/spring-integration-print/src/main/java/org/springframework/integration/print/core/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -/** - * Provides core classes of the Print module. - */ -package org.springframework.integration.print.core; diff --git a/spring-integration-print/src/main/java/org/springframework/integration/print/outbound/PrintMessageHandler.java b/spring-integration-print/src/main/java/org/springframework/integration/print/outbound/PrintMessageHandler.java index 645f949..82dda44 100644 --- a/spring-integration-print/src/main/java/org/springframework/integration/print/outbound/PrintMessageHandler.java +++ b/spring-integration-print/src/main/java/org/springframework/integration/print/outbound/PrintMessageHandler.java @@ -16,82 +16,225 @@ package org.springframework.integration.print.outbound; +import java.io.InputStream; +import java.util.Locale; + import javax.print.Doc; import javax.print.DocFlavor; import javax.print.DocPrintJob; -import javax.print.PrintService; -import javax.print.PrintServiceLookup; import javax.print.SimpleDoc; +import javax.print.attribute.DocAttributeSet; +import javax.print.attribute.HashDocAttributeSet; import javax.print.attribute.HashPrintRequestAttributeSet; import javax.print.attribute.PrintRequestAttributeSet; +import javax.print.attribute.standard.Chromaticity; import javax.print.attribute.standard.Copies; +import javax.print.attribute.standard.JobName; +import javax.print.attribute.standard.MediaSizeName; +import javax.print.attribute.standard.MediaTray; import javax.print.attribute.standard.Sides; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.integration.Message; import org.springframework.integration.handler.AbstractMessageHandler; +import org.springframework.integration.print.core.PrintServiceExecutor; +import org.springframework.integration.print.support.PrintJobMonitor; import org.springframework.util.Assert; +import org.springframework.util.StringUtils; /** - * Simple adapter to support printing of payloads. + * Outbound Adapter that supports printing of payloads. * * @author Gunnar Hillert - * @since 2.2 + * @since 1.0 */ public class PrintMessageHandler extends AbstractMessageHandler { private static final Log LOG = LogFactory.getLog(PrintMessageHandler.class); - private volatile Sides sides = Sides.ONE_SIDED; - private volatile Copies copies = new Copies(1); + private final PrintServiceExecutor printServiceExecutor; + private volatile String printJobName; + private volatile MediaSizeName mediaSizeName; + private volatile Sides sides; + private volatile Copies copies; + private volatile MediaTray mediaTray; + private volatile Chromaticity chromaticity; private final DocFlavor docFlavor; - public PrintMessageHandler(String mimeType, String className) { + private volatile PrintRequestAttributeSet printRequestAttributeSet = new HashPrintRequestAttributeSet(); + + public PrintMessageHandler(PrintServiceExecutor printServiceExecutor, DocFlavor docFlavor) { + Assert.notNull(docFlavor, "'docFlavor' must not be null."); + this.docFlavor = docFlavor; + this.printServiceExecutor = printServiceExecutor; + + } + + public PrintMessageHandler(PrintServiceExecutor printServiceExecutor, String mimeType, String className) { Assert.hasText(className, "'className' must be neither null nor empty."); Assert.hasText(mimeType, "'mimeType' must be neither null nor empty."); this.docFlavor = new DocFlavor(mimeType, className); + this.printServiceExecutor = printServiceExecutor; } - public PrintMessageHandler() { - this.docFlavor = DocFlavor.BYTE_ARRAY.AUTOSENSE; + public PrintMessageHandler(PrintServiceExecutor printServiceExecutor) { + this.docFlavor = DocFlavor.STRING.TEXT_PLAIN; + this.printServiceExecutor = printServiceExecutor; } @Override protected void handleMessageInternal(Message message) throws Exception { - final PrintService printService = PrintServiceLookup.lookupDefaultPrintService(); - - Assert.notNull(printService, "Did not find a 'printService'."); - - if (LOG.isInfoEnabled()) { - LOG.info("Printing to default printer: " + printService.getName()); + if (LOG.isDebugEnabled()) { + LOG.debug("Printing using printer '" + this.printServiceExecutor.getPrintService().getName() + "'."); } - final Doc doc = new SimpleDoc(((String) message.getPayload()).getBytes(), docFlavor, null); + DocAttributeSet das = new HashDocAttributeSet(); + das.add(Chromaticity.MONOCHROME); - final PrintRequestAttributeSet attributes = new HashPrintRequestAttributeSet(); + Object payload = message.getPayload(); - attributes.add(copies); - attributes.add(sides); + final Doc doc = new SimpleDoc(message.getPayload(), docFlavor, das); - final DocPrintJob job = printService.createPrintJob(); - job.print(doc, attributes); + final DocPrintJob job = this.printServiceExecutor.getPrintService().createPrintJob(); + + PrintJobMonitor printJobMonitor = new PrintJobMonitor(job); + + job.print(doc, this.printRequestAttributeSet); + + printJobMonitor.waitForDone(); + + if (payload instanceof InputStream) { + ((InputStream) payload).close(); + } } + @Override + protected void onInit() throws Exception { + + if (this.copies != null) { + //Assert.isTrue(printService.isAttributeValueSupported(this.copies, this.docFlavor, this.printService.getAttributes()), + // "Attribute '" + this.copies.getName() + "' with value '" + this.copies.getValue() + "' is not supported."); + this.printRequestAttributeSet.add(this.copies); + } + + if (this.chromaticity != null) { + Assert.isTrue(this.printServiceExecutor.getPrintService().isAttributeValueSupported(this.chromaticity, this.docFlavor, this.printServiceExecutor.getPrintService().getAttributes()), + "Attribute '" + this.chromaticity.getName() + "' with value '" + this.chromaticity.getValue() + "' is not supported."); + this.printRequestAttributeSet.add(this.chromaticity); + } + + if (this.mediaSizeName != null) { + Assert.isTrue(this.printServiceExecutor.getPrintService().isAttributeValueSupported(this.mediaSizeName, null, null), + "Attribute '" + this.mediaSizeName.getName() + + "' with value '" + this.mediaSizeName.getValue() + "' is not supported."); + this.printRequestAttributeSet.add(this.mediaSizeName); + } + + if (this.mediaTray != null) { + Assert.isTrue(this.printServiceExecutor.getPrintService().isAttributeValueSupported(this.mediaTray, this.docFlavor, this.printServiceExecutor.getPrintService().getAttributes()), + "Attribute value '" + this.mediaTray + "' is not supported."); + this.printRequestAttributeSet.add(this.mediaTray); + } + + if (this.sides!=null) { + Assert.isTrue(this.printServiceExecutor.getPrintService().isAttributeValueSupported(this.sides, this.docFlavor, this.printServiceExecutor.getPrintService().getAttributes()), + "Attribute value '" + this.sides + "' is not supported."); + this.printRequestAttributeSet.add(this.sides); + } + + if (StringUtils.hasText(this.printJobName)) { + this.printRequestAttributeSet.add(new JobName(this.printJobName, Locale.getDefault())); + } + + super.onInit(); + + } + + /** + * Specifies how the pages are are printed to the selected medium, + * e.g. duplex or one-sided. Under the covers, this property will add the + * provided {@link Sides} instance to the respective {@link PrintRequestAttributeSet}. + * + * This property is not used, if not specified (No explicit default value). + * + * @param sides Must not be null + */ public void setSides(Sides sides) { - Assert.notNull(sides, "'sides' must not be null"); + Assert.notNull(sides, "'sides' must not be null."); this.sides = sides; } + /** + * LetÕs you specify an integer value that indicates how many time each + * print page shall be printed. Under the covers, this property will add a + * + * {@link Copies} instance to the respective {@link PrintRequestAttributeSet}. + * This property is not used, if not specified (No explicit default value). + * + * @param numberOfCopies Must be greater than 0 + */ public void setCopies(int numberOfCopies) { - Assert.isTrue(numberOfCopies > 0, "'copies' must be greater than 0"); + Assert.isTrue(numberOfCopies > 0, "'copies' must be greater than 0."); this.copies = new Copies(numberOfCopies); } + /** + * Specifies the media tray/bin for the print job. This property can be used + * instead of providing the {@link #mediaSizeName} property. Under the covers, + * this property will add the provided {@link MediaTray} instance to the + * respective {@link PrintRequestAttributeSet}. This property is not used, + * if not specified (No explicit default value). + * + * @param mediaTray Must not be null + */ + public void setMediaTray(MediaTray mediaTray) { + Assert.notNull(mediaTray, "'mediaTray' must not be null."); + this.mediaTray = mediaTray; + } + + /** + * Specifies whether you want to do monochrome or color printing. Under the + * covers, this property will add the provided {@link Chromaticity} instance + * to the respective {@link PrintRequestAttributeSet}. This property is not + * used, if not specified (No explicit default value). + * + * @param chromaticity Must not be null. + */ + public void setChromaticity(Chromaticity chromaticity) { + Assert.notNull(chromaticity, "'chromaticity' must not be null."); + this.chromaticity = chromaticity; + } + + /** + * Sets the {@link MediaSizeName}. Under the covers, this property will add + * the provided {@link MediaSizeName} to the respective {@link PrintRequestAttributeSet}. + * This property is not used, if not specified (No explicit default value). + * + * @param mediaSizeName Must not be null. + */ + public void setMediaSizeName(MediaSizeName mediaSizeName) { + Assert.notNull(mediaSizeName, "'mediaSizeName' must not be null."); + this.mediaSizeName = mediaSizeName; + } + + /** + * Optional property that letÕs you specify the name of the print job as it + * is added to the print queue. Under the covers, this property will create + * a {@link JobName} instance and add it to the respective {@link PrintRequestAttributeSet}. + * This property is not used, if not specified (No explicit default value). + * + * @param printJobName Must neither be null nor empty. + */ + public void setPrintJobName(String printJobName) { + Assert.hasText(printJobName, "'mediaSizeName' must neither be null nor empty."); + this.printJobName = printJobName; + } + } diff --git a/spring-integration-print/src/main/java/org/springframework/integration/print/support/AttributeComparator.java b/spring-integration-print/src/main/java/org/springframework/integration/print/support/AttributeComparator.java new file mode 100644 index 0000000..cab1d98 --- /dev/null +++ b/spring-integration-print/src/main/java/org/springframework/integration/print/support/AttributeComparator.java @@ -0,0 +1,39 @@ +/* + * Copyright 2002-2012 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.print.support; + +import java.util.Comparator; + +import javax.print.attribute.Attribute; + +/** + * @author Gunnar Hillert + * @since 1.0 + */ +public class AttributeComparator implements Comparator { + + public int compare(Attribute attribute1, Attribute attribute2) { + + int comparison = attribute1.getCategory().getName().compareTo(attribute2.getCategory().getName()); + + if (comparison == 0) { + return attribute1.toString().compareTo(attribute2.toString()); + } + + return comparison; + } + +} diff --git a/spring-integration-print/src/main/java/org/springframework/integration/print/support/ChromaticityEnum.java b/spring-integration-print/src/main/java/org/springframework/integration/print/support/ChromaticityEnum.java new file mode 100644 index 0000000..6f40350 --- /dev/null +++ b/spring-integration-print/src/main/java/org/springframework/integration/print/support/ChromaticityEnum.java @@ -0,0 +1,57 @@ +/* + * Copyright 2002-2012 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.print.support; + +import javax.print.attribute.standard.Chromaticity; + +import org.springframework.util.Assert; + +/** + * @author Gunnar Hillert + * @since 1.0 + */ +public enum ChromaticityEnum { + + COLOR(Chromaticity.COLOR), + MONOCHROME(Chromaticity.MONOCHROME); + + private Chromaticity chromaticity; + + private ChromaticityEnum(Chromaticity chromaticity) { + this.chromaticity = chromaticity; + } + + public Chromaticity getChromaticity() { + return chromaticity; + } + + public static Chromaticity getForString(String chromaticityAsString) { + + Assert.hasText(chromaticityAsString, "'chromaticityAsString' must neither be null nor empty."); + + final ChromaticityEnum[] chromaticityEnumValues = ChromaticityEnum.values(); + + for (ChromaticityEnum chromaticityEnum : chromaticityEnumValues) { + if (chromaticityEnum.name().equalsIgnoreCase(chromaticityAsString)) { + return chromaticityEnum.getChromaticity(); + } + } + + throw new IllegalArgumentException("Invalid chromaticity '" + chromaticityAsString + + "' provided."); + + } +} diff --git a/spring-integration-print/src/main/java/org/springframework/integration/print/PrintHeaders.java b/spring-integration-print/src/main/java/org/springframework/integration/print/support/DocFlavorComparator.java similarity index 59% rename from spring-integration-print/src/main/java/org/springframework/integration/print/PrintHeaders.java rename to spring-integration-print/src/main/java/org/springframework/integration/print/support/DocFlavorComparator.java index 98ba0ca..7e0bac5 100644 --- a/spring-integration-print/src/main/java/org/springframework/integration/print/PrintHeaders.java +++ b/spring-integration-print/src/main/java/org/springframework/integration/print/support/DocFlavorComparator.java @@ -13,24 +13,27 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +package org.springframework.integration.print.support; -package org.springframework.integration.print; +import java.util.Comparator; + +import javax.print.DocFlavor; /** - * Print adapter specific message headers. - * * @author Gunnar Hillert * @since 1.0 */ -public class PrintHeaders { +public class DocFlavorComparator implements Comparator { - private static final String PREFIX = "print_"; + public int compare(DocFlavor docFlavor1, DocFlavor docFlavor2) { - public static final String EXAMPLE = PREFIX + "example_"; + int comparison = docFlavor1.getMimeType().compareTo(docFlavor2.getMimeType()); - /** Noninstantiable utility class */ - private PrintHeaders() { - throw new AssertionError(); + if (comparison == 0) { + return docFlavor1.getMediaSubtype().compareTo(docFlavor2.getMediaSubtype()); + } + + return comparison; } } diff --git a/spring-integration-print/src/main/java/org/springframework/integration/print/support/MediaSizeNameEnum.java b/spring-integration-print/src/main/java/org/springframework/integration/print/support/MediaSizeNameEnum.java new file mode 100644 index 0000000..9b01343 --- /dev/null +++ b/spring-integration-print/src/main/java/org/springframework/integration/print/support/MediaSizeNameEnum.java @@ -0,0 +1,126 @@ +/* + * Copyright 2002-2012 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.print.support; + +import javax.print.attribute.standard.MediaSizeName; + +import org.springframework.util.Assert; + +/** + * @author Gunnar Hillert + * @since 1.0 + */ +public enum MediaSizeNameEnum { + ISO_A0(MediaSizeName.ISO_A0), + ISO_A1(MediaSizeName.ISO_A1), + ISO_A2(MediaSizeName.ISO_A2), + ISO_A3(MediaSizeName.ISO_A3), + ISO_A4(MediaSizeName.ISO_A4), + ISO_A5(MediaSizeName.ISO_A5), + ISO_A6(MediaSizeName.ISO_A6), + ISO_A7(MediaSizeName.ISO_A7), + ISO_A8(MediaSizeName.ISO_A8), + ISO_A9(MediaSizeName.ISO_A9), + ISO_A10(MediaSizeName.ISO_A10), + ISO_B0(MediaSizeName.ISO_B0), + ISO_B1(MediaSizeName.ISO_B1), + ISO_B2(MediaSizeName.ISO_B2), + ISO_B3(MediaSizeName.ISO_B3), + ISO_B4(MediaSizeName.ISO_B4), + ISO_B5(MediaSizeName.ISO_B5), + ISO_B6(MediaSizeName.ISO_B6), + ISO_B7(MediaSizeName.ISO_B7), + ISO_B8(MediaSizeName.ISO_B8), + ISO_B9(MediaSizeName.ISO_B9), + ISO_B10(MediaSizeName.ISO_B10), + JIS_B0(MediaSizeName.JIS_B0), + JIS_B1(MediaSizeName.JIS_B1), + JIS_B2(MediaSizeName.JIS_B2), + JIS_B3(MediaSizeName.JIS_B3), + JIS_B4(MediaSizeName.JIS_B4), + JIS_B5(MediaSizeName.JIS_B5), + JIS_B6(MediaSizeName.JIS_B6), + JIS_B7(MediaSizeName.JIS_B7), + JIS_B8(MediaSizeName.JIS_B8), + JIS_B9(MediaSizeName.JIS_B9), + JIS_B10(MediaSizeName.JIS_B10), + ISO_C0(MediaSizeName.ISO_C0), + ISO_C1(MediaSizeName.ISO_C1), + ISO_C2(MediaSizeName.ISO_C2), + ISO_C3(MediaSizeName.ISO_C3), + ISO_C4(MediaSizeName.ISO_C4), + ISO_C5(MediaSizeName.ISO_C5), + ISO_C6(MediaSizeName.ISO_C6), + NA_LETTER(MediaSizeName.NA_LETTER), + NA_LEGAL(MediaSizeName.NA_LEGAL), + LEDGER(MediaSizeName.LEDGER), + TABLOID(MediaSizeName.TABLOID), + INVOICE(MediaSizeName.INVOICE), + FOLIO(MediaSizeName.FOLIO), + QUARTO(MediaSizeName.QUARTO), + JAPANESE_POSTCARD(MediaSizeName.JAPANESE_POSTCARD), + JAPANESE_DOUBLE_POSTCARD(MediaSizeName.JAPANESE_DOUBLE_POSTCARD), + A(MediaSizeName.A), + B(MediaSizeName.B), + C(MediaSizeName.C), + D(MediaSizeName.D), + E(MediaSizeName.E), + ISO_DESIGNATED_LONG(MediaSizeName.ISO_DESIGNATED_LONG), + ITALY_ENVELOPE(MediaSizeName.ITALY_ENVELOPE), + MONARCH_ENVELOPE(MediaSizeName.MONARCH_ENVELOPE), + PERSONAL_ENVELOPE(MediaSizeName.PERSONAL_ENVELOPE), + NA_NUMBER_9_ENVELOPE(MediaSizeName.NA_NUMBER_9_ENVELOPE), + NA_NUMBER_10_ENVELOPE(MediaSizeName.NA_NUMBER_10_ENVELOPE), + NA_NUMBER_11_ENVELOPE(MediaSizeName.NA_NUMBER_11_ENVELOPE), + NA_NUMBER_12_ENVELOPE(MediaSizeName.NA_NUMBER_12_ENVELOPE), + NA_NUMBER_14_ENVELOPE(MediaSizeName.NA_NUMBER_14_ENVELOPE), + NA_6X9_ENVELOPE(MediaSizeName.NA_6X9_ENVELOPE), + NA_7X9_ENVELOPE(MediaSizeName.NA_7X9_ENVELOPE), + NA_9X11_ENVELOPE(MediaSizeName.NA_9X11_ENVELOPE), + NA_9X12_ENVELOPE(MediaSizeName.NA_9X12_ENVELOPE), + NA_10X13_ENVELOPE(MediaSizeName.NA_10X13_ENVELOPE), + NA_10X14_ENVELOPE(MediaSizeName.NA_10X14_ENVELOPE), + NA_10X15_ENVELOPE(MediaSizeName.NA_10X15_ENVELOPE), + NA_5X7(MediaSizeName.NA_5X7), + NA_8X10(MediaSizeName.NA_8X10); + + private MediaSizeName mediaSizeName; + + private MediaSizeNameEnum(MediaSizeName mediaSizeName) { + this.mediaSizeName = mediaSizeName; + } + + public MediaSizeName getMediaSizeName() { + return mediaSizeName; + } + + public static MediaSizeName getForString(String mediaSizeNameAsString) { + + Assert.hasText(mediaSizeNameAsString, "'mediaSizeNameAsString' must neither be null nor empty."); + + final MediaSizeNameEnum[] mediaSizeNameEnumValues = MediaSizeNameEnum.values(); + + for (MediaSizeNameEnum mediaSizeNameEnum : mediaSizeNameEnumValues) { + if (mediaSizeNameEnum.name().equalsIgnoreCase(mediaSizeNameAsString)) { + return mediaSizeNameEnum.mediaSizeName; + } + } + + throw new IllegalArgumentException("Invalid mediaSizeName '" + mediaSizeNameAsString + + "' provided."); + + } +} diff --git a/spring-integration-print/src/main/java/org/springframework/integration/print/support/MediaTrayEnum.java b/spring-integration-print/src/main/java/org/springframework/integration/print/support/MediaTrayEnum.java new file mode 100644 index 0000000..159fb10 --- /dev/null +++ b/spring-integration-print/src/main/java/org/springframework/integration/print/support/MediaTrayEnum.java @@ -0,0 +1,63 @@ +/* + * Copyright 2002-2012 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.print.support; + +import javax.print.attribute.standard.MediaTray; + +import org.springframework.util.Assert; + +/** + * @author Gunnar Hillert + * @since 1.0 + */ +public enum MediaTrayEnum { + + BOTTOM(MediaTray.BOTTOM), + ENVELOPE(MediaTray.ENVELOPE), + LARGE_CAPACITY(MediaTray.LARGE_CAPACITY), + MAIN(MediaTray.MAIN), + MANUAL(MediaTray.MANUAL), + MIDDLE(MediaTray.MIDDLE), + SIDE(MediaTray.SIDE), + TOP(MediaTray.TOP); + + private MediaTray mediaTray; + + private MediaTrayEnum(MediaTray mediaTray) { + this.mediaTray = mediaTray; + } + + public MediaTray getMediaTray() { + return mediaTray; + } + + public static MediaTray getForString(String mediaTrayAsString) { + + Assert.hasText(mediaTrayAsString, "'mediaTrayAsString' must neither be null nor empty."); + + final MediaTrayEnum[] mediaTrayEnumValues = MediaTrayEnum.values(); + + for (MediaTrayEnum mediaTrayEnum : mediaTrayEnumValues) { + if (mediaTrayEnum.name().equalsIgnoreCase(mediaTrayAsString)) { + return mediaTrayEnum.getMediaTray(); + } + } + + throw new IllegalArgumentException("Invalid mediaTray '" + mediaTrayAsString + + "' provided."); + + } +} diff --git a/spring-integration-print/src/main/java/org/springframework/integration/print/support/PrintUtils.java b/spring-integration-print/src/main/java/org/springframework/integration/print/support/PrintServiceComparator.java similarity index 72% rename from spring-integration-print/src/main/java/org/springframework/integration/print/support/PrintUtils.java rename to spring-integration-print/src/main/java/org/springframework/integration/print/support/PrintServiceComparator.java index ce3f5b3..0570006 100644 --- a/spring-integration-print/src/main/java/org/springframework/integration/print/support/PrintUtils.java +++ b/spring-integration-print/src/main/java/org/springframework/integration/print/support/PrintServiceComparator.java @@ -15,19 +15,18 @@ */ package org.springframework.integration.print.support; +import java.util.Comparator; + +import javax.print.PrintService; /** - * Contains utility methods used by the Print components. - * * @author Gunnar Hillert * @since 1.0 - * */ -public final class PrintUtils { +public class PrintServiceComparator implements Comparator { - /** Prevent instantiation. */ - private PrintUtils() { - throw new AssertionError(); + public int compare(PrintService printService1, PrintService printService2) { + return printService1.getName().compareTo(printService2.getName()); } } diff --git a/spring-integration-print/src/main/java/org/springframework/integration/print/support/PrintSides.java b/spring-integration-print/src/main/java/org/springframework/integration/print/support/PrintSides.java index 6225af7..cc76e4a 100644 --- a/spring-integration-print/src/main/java/org/springframework/integration/print/support/PrintSides.java +++ b/spring-integration-print/src/main/java/org/springframework/integration/print/support/PrintSides.java @@ -1,3 +1,18 @@ +/* + * Copyright 2002-2012 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.print.support; import javax.print.attribute.standard.Sides; diff --git a/spring-integration-print/src/main/resources/org/springframework/integration/print/config/xml/spring-integration-print-2.2.xsd b/spring-integration-print/src/main/resources/org/springframework/integration/print/config/xml/spring-integration-print-2.2.xsd index 429d838..7d943f1 100644 --- a/spring-integration-print/src/main/resources/org/springframework/integration/print/config/xml/spring-integration-print-2.2.xsd +++ b/spring-integration-print/src/main/resources/org/springframework/integration/print/config/xml/spring-integration-print-2.2.xsd @@ -78,27 +78,58 @@ - + - The number of copies to print. Defaults to 1 if not set. + If the printer name is not provided the sytem's default printer + is being used if available if available. Otherwise an + exceptions is thrown. - + + + + Allows you to provide a custom print-service implementation. + + + + + + + + + + + + The number of copies to print. Uses the default of the printer. + + + + + + + + + + + + @@ -108,6 +139,7 @@ + See: http://docs.oracle.com/javase/6/docs/api/javax/print/DocFlavor.html @@ -123,6 +155,13 @@ + + + + + + + @@ -136,4 +175,94 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spring-integration-print/src/reference/docbook/SIAdapterLowerPrefix.xml b/spring-integration-print/src/reference/docbook/SIAdapterLowerPrefix.xml deleted file mode 100644 index 68c1390..0000000 --- a/spring-integration-print/src/reference/docbook/SIAdapterLowerPrefix.xml +++ /dev/null @@ -1,73 +0,0 @@ - - - Print Adapter - - The Spring Integration Print Adapter provides... - - - - Outbound Channel adapter - - - Outbound Gateway - - - Inbound Channel Adapter - - - -
- Java Implementation - Each of the provided components will use the - org.springframework.integration.print.core.PrintExecutor - class... - -
-
- Common Configuration Attributes - - Certain configuration parameters are shared amongst all Print - components and are described below: - - - auto-startup - - Lifecycle attribute signaling if this component should - be started during Application Context startup. - Defaults to true. - Optional. - - - id - - Identifies the underlying Spring bean definition, which - is an instance of either EventDrivenConsumer - or PollingConsumer. - Optional. - - -
- -
- Outbound Channel Adapter - - The Print Outbound channel adapter allows you to... - -
-
- Outbound Gateway - - Outbound gateways are similar to outbound channel adapters except that it can also be used to - get a result on the reply channel after performing - the given... - -
-
- Inbound Channel Adapter - - An inbound channel adapter is used to execute... - -
- -
diff --git a/spring-integration-print/src/reference/docbook/history.xml b/spring-integration-print/src/reference/docbook/history.xml deleted file mode 100644 index 549be21..0000000 --- a/spring-integration-print/src/reference/docbook/history.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - Change History - - - diff --git a/spring-integration-print/src/reference/docbook/images/logo.png b/spring-integration-print/src/reference/docbook/images/logo.png deleted file mode 100644 index e1c2a0e3c7864b4c2495fa75fed9733af2558ff9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9627 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 R@&Et;07*qoM6N<$f(p*0Lx diff --git a/spring-integration-print/src/reference/docbook/index.xml b/spring-integration-print/src/reference/docbook/index.xml deleted file mode 100644 index c1ac2a5..0000000 --- a/spring-integration-print/src/reference/docbook/index.xml +++ /dev/null @@ -1,68 +0,0 @@ - - - - Spring Integration Print Adapter - Print Adapter ${version} - Spring Integration - ${version} - - - - - - - - - - - - - - Gunnar Hillert - - - © SpringSource Inc., 2012 - - - - - - - What's new? - - - For those who are already familiar with Spring Integration, this - chapter - provides a brief overview of the new features of version 2.2. If you are - interested in the changes and features, that were introduced in - earlier - versions, please take a look at chapter: - - - - - - - - - - Integration Adapters - - This section covers the various Channel Adapters and Messaging - Gateways provided - by Spring Integration to support Message-based communication with - external systems. - - - - - - Appendices - - Advanced Topics and Additional Resources - - - - diff --git a/spring-integration-print/src/reference/docbook/resources.xml b/spring-integration-print/src/reference/docbook/resources.xml deleted file mode 100644 index 109faa9..0000000 --- a/spring-integration-print/src/reference/docbook/resources.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - Additional Resources - -
- Spring Integration Home - - The definitive source of information about Spring Integration is the - Spring Integration Home at - http://www.springsource.org. That site serves as a hub of - information and is the best place to find up-to-date announcements about the project as well as links to - articles, blogs, and new sample applications. - -
- -
diff --git a/spring-integration-print/src/reference/docbook/whats-new.xml b/spring-integration-print/src/reference/docbook/whats-new.xml deleted file mode 100644 index 6910c61..0000000 --- a/spring-integration-print/src/reference/docbook/whats-new.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - What's new? - - This chapter provides an overview of the new features and improvements - that have been added to the Print Adapter: - - - diff --git a/spring-integration-print/src/test/java/org/springframework/integration/print/PrintTests.java b/spring-integration-print/src/test/java/org/springframework/integration/print/PrintTests.java deleted file mode 100644 index 29468ab..0000000 --- a/spring-integration-print/src/test/java/org/springframework/integration/print/PrintTests.java +++ /dev/null @@ -1,38 +0,0 @@ -package org.springframework.integration.print; - -import java.io.FileNotFoundException; -import java.io.InputStream; - -import javax.print.Doc; -import javax.print.DocFlavor; -import javax.print.DocPrintJob; -import javax.print.PrintException; -import javax.print.PrintService; -import javax.print.PrintServiceLookup; -import javax.print.SimpleDoc; -import javax.print.attribute.HashPrintRequestAttributeSet; -import javax.print.attribute.PrintRequestAttributeSet; -import javax.print.attribute.standard.Copies; -import javax.print.attribute.standard.Sides; - -public class PrintTests { - - public static void main(String args[]) throws FileNotFoundException, PrintException { - InputStream textStream = PrintTests.class.getResourceAsStream("/print.txt"); - DocFlavor myFormat = DocFlavor.INPUT_STREAM.AUTOSENSE; - Doc myDoc = new SimpleDoc(textStream, myFormat, null); - - PrintRequestAttributeSet aset = new HashPrintRequestAttributeSet(); - - aset.add(new Copies(1)); - aset.add(Sides.ONE_SIDED); - - PrintService printService = PrintServiceLookup.lookupDefaultPrintService(); - - System.out.println("Printing to default printer: " + printService.getName()); - - DocPrintJob job = printService.createPrintJob(); - job.print(myDoc, aset); - } - -} diff --git a/spring-integration-print/src/test/java/org/springframework/integration/print/config/xml/PrintMessageHandlerParserTests-context.xml b/spring-integration-print/src/test/java/org/springframework/integration/print/config/xml/PrintMessageHandlerParserTests-context.xml index cee6bc4..8ee941c 100644 --- a/spring-integration-print/src/test/java/org/springframework/integration/print/config/xml/PrintMessageHandlerParserTests-context.xml +++ b/spring-integration-print/src/test/java/org/springframework/integration/print/config/xml/PrintMessageHandlerParserTests-context.xml @@ -9,14 +9,17 @@ http://www.springframework.org/schema/integration/print http://www.springframework.org/schema/integration/print/spring-integration-print.xsd"> + + + print-service-ref="printService" + copies="14" + doc-flavor="java.lang.String" + mime-type="text/plain; charset=utf-16" + sides="DUPLEX" + auto-startup="true" + order="2" + channel="input"> diff --git a/spring-integration-print/src/test/java/org/springframework/integration/print/config/xml/PrintMessageHandlerParserTests.java b/spring-integration-print/src/test/java/org/springframework/integration/print/config/xml/PrintMessageHandlerParserTests.java index e1cb98c..a7da1fc 100644 --- a/spring-integration-print/src/test/java/org/springframework/integration/print/config/xml/PrintMessageHandlerParserTests.java +++ b/spring-integration-print/src/test/java/org/springframework/integration/print/config/xml/PrintMessageHandlerParserTests.java @@ -13,22 +13,15 @@ package org.springframework.integration.print.config.xml; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; import javax.print.DocFlavor; import javax.print.attribute.standard.Copies; import javax.print.attribute.standard.Sides; -import org.junit.After; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.ConfigurableApplicationContext; -import org.springframework.context.support.ClassPathXmlApplicationContext; -import org.springframework.integration.channel.AbstractMessageChannel; import org.springframework.integration.core.MessageHandler; -import org.springframework.integration.endpoint.EventDrivenConsumer; -import org.springframework.integration.print.core.PrintExecutor; import org.springframework.integration.test.util.TestUtils; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @@ -60,7 +53,6 @@ public class PrintMessageHandlerParserTests { final Sides sides = TestUtils.getPropertyValue(messageHandler, "sides", Sides.class); assertEquals(Sides.DUPLEX, sides); - } } diff --git a/spring-integration-print/src/test/java/org/springframework/integration/print/outbound/PrintMessageHandlerTest.java b/spring-integration-print/src/test/java/org/springframework/integration/print/outbound/PrintMessageHandlerTest.java index 7a08a77..47ab73c 100644 --- a/spring-integration-print/src/test/java/org/springframework/integration/print/outbound/PrintMessageHandlerTest.java +++ b/spring-integration-print/src/test/java/org/springframework/integration/print/outbound/PrintMessageHandlerTest.java @@ -1,17 +1,37 @@ package org.springframework.integration.print.outbound; -import org.junit.Ignore; +import java.io.InputStream; + +import javax.print.DocFlavor; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.junit.Test; +import org.springframework.integration.print.core.PrintServiceExecutor; import org.springframework.integration.support.MessageBuilder; public class PrintMessageHandlerTest { - //@Test - //@Ignore - public void test() { - PrintMessageHandler handler = new PrintMessageHandler(); + private static final Log LOG = LogFactory.getLog(PrintMessageHandler.class); + + //@Test + public void testPdfPrint() { + + PrintServiceExecutor printServiceExecutor = new PrintServiceExecutor(); + PrintMessageHandler handler = new PrintMessageHandler(printServiceExecutor, DocFlavor.INPUT_STREAM.AUTOSENSE); + + InputStream is = PrintMessageHandlerTest.class.getResourceAsStream("Spring Integration Print Testing.pdf"); + handler.setCopies(2); + handler.setPrintJobName("My Spring Integration Print test."); + handler.afterPropertiesSet(); + handler.handleMessage(MessageBuilder.withPayload(is).build()); - handler.handleMessage(MessageBuilder.withPayload("This is a test".getBytes()).build()); } + @Test + public void testShowSupportedAttributes() { + final PrintServiceExecutor printServiceExecutor = new PrintServiceExecutor(); + final String info = printServiceExecutor.getPrinterInfo(); + LOG.info(info); + } } diff --git a/spring-integration-print/src/test/java/org/springframework/integration/print/outbound/PrintOutboundChannelAdapterTest.java b/spring-integration-print/src/test/java/org/springframework/integration/print/outbound/PrintOutboundChannelAdapterTest.java index 9a23ed5..a135f39 100644 --- a/spring-integration-print/src/test/java/org/springframework/integration/print/outbound/PrintOutboundChannelAdapterTest.java +++ b/spring-integration-print/src/test/java/org/springframework/integration/print/outbound/PrintOutboundChannelAdapterTest.java @@ -1,6 +1,5 @@ package org.springframework.integration.print.outbound; -import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; diff --git a/spring-integration-print/src/test/java/org/springframework/integration/print/support/MockPrintService.java b/spring-integration-print/src/test/java/org/springframework/integration/print/support/MockPrintService.java new file mode 100644 index 0000000..97fbf15 --- /dev/null +++ b/spring-integration-print/src/test/java/org/springframework/integration/print/support/MockPrintService.java @@ -0,0 +1,97 @@ +package org.springframework.integration.print.support; + +import javax.print.DocFlavor; +import javax.print.DocPrintJob; +import javax.print.PrintService; +import javax.print.ServiceUIFactory; +import javax.print.attribute.Attribute; +import javax.print.attribute.AttributeSet; +import javax.print.attribute.PrintServiceAttribute; +import javax.print.attribute.PrintServiceAttributeSet; +import javax.print.event.PrintServiceAttributeListener; + +public class MockPrintService implements PrintService { + + public void addPrintServiceAttributeListener( + PrintServiceAttributeListener listener) { + // TODO Auto-generated method stub + + } + + public DocPrintJob createPrintJob() { + // TODO Auto-generated method stub + return null; + } + + public T getAttribute(Class category) { + // TODO Auto-generated method stub + return null; + } + + public PrintServiceAttributeSet getAttributes() { + // TODO Auto-generated method stub + return null; + } + + public Object getDefaultAttributeValue(Class category) { + // TODO Auto-generated method stub + return null; + } + + public String getName() { + // TODO Auto-generated method stub + return "Mock Printer"; + } + + public ServiceUIFactory getServiceUIFactory() { + // TODO Auto-generated method stub + return null; + } + + public Class[] getSupportedAttributeCategories() { + // TODO Auto-generated method stub + return null; + } + + public Object getSupportedAttributeValues( + Class category, DocFlavor flavor, + AttributeSet attributes) { + // TODO Auto-generated method stub + return null; + } + + public DocFlavor[] getSupportedDocFlavors() { + // TODO Auto-generated method stub + return null; + } + + public AttributeSet getUnsupportedAttributes(DocFlavor flavor, + AttributeSet attributes) { + // TODO Auto-generated method stub + return null; + } + + public boolean isAttributeCategorySupported( + Class category) { + // TODO Auto-generated method stub + return false; + } + + public boolean isAttributeValueSupported(Attribute attrval, + DocFlavor flavor, AttributeSet attributes) { + // TODO Auto-generated method stub + return true; + } + + public boolean isDocFlavorSupported(DocFlavor flavor) { + // TODO Auto-generated method stub + return false; + } + + public void removePrintServiceAttributeListener( + PrintServiceAttributeListener listener) { + // TODO Auto-generated method stub + + } + +} diff --git a/spring-integration-print/src/test/java/org/springframework/integration/print/support/PrintJobMonitor.java b/spring-integration-print/src/test/java/org/springframework/integration/print/support/PrintJobMonitor.java new file mode 100644 index 0000000..bb7635f --- /dev/null +++ b/spring-integration-print/src/test/java/org/springframework/integration/print/support/PrintJobMonitor.java @@ -0,0 +1,66 @@ +package org.springframework.integration.print.support; + +import javax.print.DocPrintJob; +import javax.print.event.PrintJobAdapter; +import javax.print.event.PrintJobEvent; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +public class PrintJobMonitor { + + private static final Log LOG = LogFactory.getLog(PrintJobMonitor.class); + + boolean done = false; + + public PrintJobMonitor(DocPrintJob job) { + + job.addPrintJobListener(new PrintJobAdapter() { + + @Override + public void printJobCanceled(PrintJobEvent arg0) { + if (LOG.isWarnEnabled()) { + LOG.warn("PrintJob Job canceled."); + } + allDone(); + } + + @Override + public void printJobCompleted(PrintJobEvent printJobEvent) { + if (LOG.isDebugEnabled()) { + LOG.debug("PrintJob Job completed."); + } + allDone(); + } + + public void printJobFailed(PrintJobEvent pje) { + allDone(); + } + + public void printJobNoMoreEvents(PrintJobEvent pje) { + if (LOG.isDebugEnabled()) { + LOG.debug("PrintJob Job completed (No More Events)."); + } + allDone(); + } + + void allDone() { + synchronized (PrintJobMonitor.this) { + done = true; + PrintJobMonitor.this.notify(); + } + } + + }); + } + + public synchronized void waitForDone() { + try { + while (!done) { + wait(); + } + } catch (InterruptedException e) { + } + } + +} diff --git a/spring-integration-print/src/test/java/org/springframework/integration/print/support/PrintUtilsTest.java b/spring-integration-print/src/test/java/org/springframework/integration/print/support/PrintUtilsTest.java new file mode 100644 index 0000000..30c23c4 --- /dev/null +++ b/spring-integration-print/src/test/java/org/springframework/integration/print/support/PrintUtilsTest.java @@ -0,0 +1,40 @@ +/** + * + */ +package org.springframework.integration.print.support; + +import java.util.SortedSet; + +import javax.print.PrintService; + +import org.apache.log4j.Logger; +import org.junit.Test; +import org.springframework.integration.print.core.PrintServiceExecutor; +import org.springframework.util.StringUtils; + +/** + * @author ghillert + * + */ +public class PrintUtilsTest { + + private static final Logger LOGGER = Logger.getLogger(PrintUtilsTest.class); + + private PrintServiceExecutor printServiceExecutor = new PrintServiceExecutor(); + + /** + * Test method for {@link org.springframework.integration.print.core.PrintServiceExecutor#getAvailablePrinterServices()}. + */ + @Test + public void testGetAvailablePrinterServices() { + SortedSet printers = PrintServiceExecutor.getAvailablePrinterServices(); + LOGGER.info("Available Printers: " + StringUtils.collectionToCommaDelimitedString(printers)); + } + + @Test + public void testGetPrintServiceByName() { + PrintService printService = printServiceExecutor.getPrintService(); + LOGGER.info("Default Printer found: " + printService); + } + +} diff --git a/spring-integration-print/src/test/resources/log4j.properties b/spring-integration-print/src/test/resources/log4j.properties index 5513e80..62c983a 100644 --- a/spring-integration-print/src/test/resources/log4j.properties +++ b/spring-integration-print/src/test/resources/log4j.properties @@ -5,4 +5,4 @@ log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - <%m>%n log4j.category.org.springframework.integration=WARN -log4j.category.org.springframework.integration.print=INFO +log4j.category.org.springframework.integration.print=DEBUG \ No newline at end of file diff --git a/spring-integration-print/src/test/resources/org/springframework/integration/print/outbound/Spring Integration Print Testing.pdf b/spring-integration-print/src/test/resources/org/springframework/integration/print/outbound/Spring Integration Print Testing.pdf new file mode 100644 index 0000000000000000000000000000000000000000..ca14d0418d5a999bebb57a054be74f5e58084e5f GIT binary patch literal 14263 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 =mT@us_laR2H99$YE z?V*i_#^((UhUXQ9<{@Nx(Ig&J$a+od4W^xxtXo}s^e#azCuq*ol@^9MxwyC#=2zOP z1?aL)Xd@Fu&rPz!_+8gqqt(#mE zpGGvr$69sf7?#&3KxvI6P2MWe4VE|C3og8yih;2v6Tee{TAb2h6QaSKOe)&~RJ}DU zp?gdqV~d0`2=dpwO(kH({MNrr{x%Vs9G0=rZWrVE$tbP-XfA5>YJ8{|C4#YIx?$rs ziaexUeUWQXnfNQm*ZZWv#Wd5Y@7P*N*BGmqb~R5Kt?Y<{h~g{IH%L7TccZS-3S@t!=CNY^zGLqb|}l- zku}v^+RM=vOWNTc3H2}da#p2knCohoPjfaUt)D$p@beINU(q6}A78(+NrAs(CG|6r z2z7ed+Ilp)z&t<|vqpkyCjs8|COL+HsSrzPtyEv}!GJw-j@jEl=7A%^W(={BFYTE@YmVF)NDwqvp+S!4DzdG}I@Mn|G^ zI4`Ib-!)lv=1vB{@Zxbn;TpMq_+I4o!-tohLz}ji8U@M)7Y);OdIqR)pOW;e@fQz^>6dr*A<);`#c zDKoSY3TX9*pX<;NRpI`q>Cp@`x9mRFDtgOyey;3jF{EJ1TFbKDujDe;8|jQlL{gyH z3p8#tu4>p3PI@N6%8{zyC?B)US|yxtQk|*~;$WA#DX&p+t2LX|l+`=!jpRZOk2+qA zE^|ZI7vK>%8OJ2Qvx)qUE^uH`Z6#F+JICzxPH0d_#0)}BP3T&ZLS9RDfz9GE+~Ss3 zU8asyXxf4Kr+R>ra#+b=K$Un2jd+8gYf+Tr{*W`iyi=as)Yp9vXPQWXd%)f;IW*{ax z{BMr8&~FwwJ&=`y4M5M#%FY5{=49spumFJ^Ad2{_4><#yR|>a`nw+!(t+0WOp@W41 zh(iP7uvi!g+L~FL0GQwzm7Pp%)Ij7HMnM-dbqiyV`hXp@WJXy7w?A6UTtF@WqsE_$ zdUf`DZjd&?gc-zU`AwVo8wKVs`nooNm5Uv~#>Ng{1+oH|IXFT0oFE(Mo|y^2!psU_ zVF9%{|M+KP0Rq@Sycjkv762;?(|>q9z`w{pe^W;QIe$C-Kjjg+apSfDOo$<;yrNNy zx!np2m2ZX8xK%f*1+*GqU_#nC`FBq&2BHFd@YiSN~%K+;SS>q9eY`-xXS8Zzq@mc(i%UBdW~dZatn#M z*bE{jiB}=P9FJurh^V^rs=@I*oSI9TYh4rwEA6zDs)*Dle>WV23#Y(zOfD{O{8V6( z{L5V}5n|F*H&+4~N`&XFt}r+&Hhqq-c?}=@tTQs5K;B4|o6}%F; z{&@J$4E^u=fd5@VN&v>+`RE7${;8}Gvv6>95;iw*0A&(LIb-l=gOvkRW_GH!7Qf{( zzw<}w@2sxBR!AjlVdP-vXlLpKP`7h12G9dU?2MdkK!pgT{%bd&O#Nd!RI&y}0C{DA z28ejZz|O!4P;@qMaPk0vR2jbo9&S$m8S|>7czp#_X66>w)+P>rd-NBh>>s}WGxje6 z8I=e(GZT=RiIa(o83^P8GHWq0QG)*d)AO&RhU!12zYZGhzm?D5!(rhBs(4T_|JI=h zIy;$z&K~AJbsr$5iLtX0NNn_b%b=Gn4AL0A9!{WR=Xd|#Z3{bF5s)kdp81cmXOy)w zer*H(YD=5AyMo@OBLGz1e|Qm8km3H1^>u}R4~CnAi77l2fSDbh>Hqrx*xA@v*Z`)0 zKWso2R%TGZUq1lbKW$7*tgN7^{@cdF`nt9MwlOiiF8?34|1}>Q>%Yc=jwjGA|1lQS z{D%$5!o&#3^uOl=**N~`1IWzwS}Xsx z7LbkWUp5vdrhkoP0fNrre~e{e29?&oY@Gj|j|HS-`e#3uf5iw0dZ+*P%gXe(y*W7; zfPPpUev9FhEj&P}#SUOpva