diff --git a/.classpath b/.classpath deleted file mode 100644 index 4edb3e41..00000000 --- a/.classpath +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - - - - - - - - - - diff --git a/.gitignore b/.gitignore index b5adb2a4..fcd3466c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,9 @@ /bin/ /classes/ /tests/ +*.iml +.idea/* +.project +.classpath +pom.xml.versionsBackup +*/dist/* \ No newline at end of file diff --git a/.mvn/wrapper/maven-wrapper.jar b/.mvn/wrapper/maven-wrapper.jar new file mode 100644 index 00000000..c6feb8bb Binary files /dev/null and b/.mvn/wrapper/maven-wrapper.jar differ diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 00000000..eb919476 --- /dev/null +++ b/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1 @@ +distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.3.3/apache-maven-3.3.3-bin.zip \ No newline at end of file diff --git a/.project b/.project deleted file mode 100644 index e25569e2..00000000 --- a/.project +++ /dev/null @@ -1,34 +0,0 @@ - - - OpenAS2 - - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.eclipse.m2e.core.maven2Builder - - - - - org.eclipse.pde.ManifestBuilder - - - - - org.eclipse.pde.SchemaBuilder - - - - - - org.eclipse.m2e.core.maven2Nature - org.eclipse.jdt.core.javanature - org.eclipse.pde.PluginNature - - diff --git a/Bundle/.classpath b/Bundle/.classpath deleted file mode 100644 index 0a1daddd..00000000 --- a/Bundle/.classpath +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Bundle/.project b/Bundle/.project deleted file mode 100644 index 89a6a4e5..00000000 --- a/Bundle/.project +++ /dev/null @@ -1,23 +0,0 @@ - - - Bundle - - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.eclipse.m2e.core.maven2Builder - - - - - - org.eclipse.jdt.core.javanature - org.eclipse.m2e.core.maven2Nature - - diff --git a/Bundle/pom.xml b/Bundle/pom.xml index eba0b65b..b4000919 100644 --- a/Bundle/pom.xml +++ b/Bundle/pom.xml @@ -1,138 +1,84 @@ - - - org.openas2 - OpenAS2 - 2.2.0 - - 4.0.0 - org.openas2 - openas2-osgi - 2.2.0 - OpenAS2 OSGi Server - https://sourceforge.net/projects/openas2 - Open source implementation of the AS2 standard for signed encrypted and compressed document transfer - - ${project.parent.artifactId}ServerOSGi-${project.version}.zip - UTF-8 - ${basedir}/lib - OpenAS2HowTo.pdf - ../docs/${help.filename} - dist - - - src/main/java - target - ${project.build.directory}/classes - - ${project.artifactId} - ${project.build.directory}/test-classes - src/main/scripts - ${project.basedir}/src/test/java - - - - maven-antrun-plugin - 1.8 - - - default-cli - - run - - package - - - - + - - - - - - - - + 4.0.0 - - - - + + org.openas2 + OpenAS2 + 2.3.0-SNAPSHOT + + + openas2-osgi + + OpenAS2 OSGi Bundle + + OpenAS2 server OSGi bundle + + + + org.openas2.osgi.Activator + ${project.parent.artifactId}Bundle-${project.version}.zip + UTF-8 + ${project.basedir}/lib + OpenAS2HowTo.pdf + ../docs/${help.filename} + ${project.basedir}/dist + + + + + + + org.apache.maven.plugins + maven-dependency-plugin + + + org.apache.maven.plugins + maven-antrun-plugin + + + default-cli + + run + + package + + + + + + + + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + org.apache.maven.plugins + maven-jar-plugin + + + + + + + org.osgi + org.osgi.core + + + org.openas2 + openas2-server + ${project.version} + + - - - maven-compiler-plugin - 3.1 - - 1.5 - 1.5 - - - - org.apache.maven.plugins - maven-jar-plugin - 3.0.2 - - - true - - true - org.openas2.osgi.Activator - true - true - - - - - - - - - org.dom4j - dom4j - 2.0.0 - - - org.bouncycastle - bcmail-jdk15on - 1.54 - - - org.bouncycastle - bcpkix-jdk15on - 1.54 - - - org.bouncycastle - bcprov-jdk15on - 1.54 - - - org.apache.commons - commons-lang3 - 3.4 - - - commons-logging - commons-logging - 1.2 - - - javax.mail - mail - 1.4.7 - - - com.h2database - h2 - 1.4.193 - - - org.bouncycastle - bcpg-jdk15on - 1.54 - - \ No newline at end of file diff --git a/Bundle/src/main/java/org/openas2/osgi/Activator.java b/Bundle/src/main/java/org/openas2/osgi/Activator.java index d2410d81..37826295 100644 --- a/Bundle/src/main/java/org/openas2/osgi/Activator.java +++ b/Bundle/src/main/java/org/openas2/osgi/Activator.java @@ -7,31 +7,33 @@ public class Activator implements BundleActivator { - private static BundleContext context; - private ServiceRegistration openAS2Registration; + private static BundleContext context; + private ServiceRegistration openAS2Registration; - static BundleContext getContext() { - return context; - } + static BundleContext getContext() + { + return context; + } - /* - * (non-Javadoc) - * @see org.osgi.framework.BundleActivator#start(org.osgi.framework.BundleContext) - */ - public void start(BundleContext bundleContext) throws Exception { - Activator.context = bundleContext; - OpenAS2Server openAS2Service = new OpenAS2Server(); - openAS2Registration = bundleContext.registerService(OpenAS2Server.class.getName(), openAS2Service, null); - openAS2Service.start(null); - } + /* + * (non-Javadoc) + * @see org.osgi.framework.BundleActivator#start(org.osgi.framework.BundleContext) + */ + public void start(BundleContext bundleContext) throws Exception + { + Activator.context = bundleContext; + OpenAS2Server openAS2Server = new OpenAS2Server.Builder().run(); + openAS2Registration = bundleContext.registerService(OpenAS2Server.class.getName(), openAS2Server, null); + } - /* - * (non-Javadoc) - * @see org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext) - */ - public void stop(BundleContext bundleContext) throws Exception { - openAS2Registration.unregister(); - Activator.context = null; - } + /* + * (non-Javadoc) + * @see org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext) + */ + public void stop(BundleContext bundleContext) throws Exception + { + openAS2Registration.unregister(); + Activator.context = null; + } } diff --git a/README.md b/README.md index c660d048..d6ce433f 100644 --- a/README.md +++ b/README.md @@ -2,5 +2,15 @@ The OpenAS2 application enables you to transmit and receive AS2 messages with EDI-X12, EDIFACT, XML, or binary payloads between trading partners. # Development -There is a build.xml in the Server folder to compile and create the jar and build the distribution package -The current version is stored in org.openas2.Session.java - this must be updated whenever a new release is set up. The build file will automatically use the value entered there. \ No newline at end of file +There is a pom.xml in the Server folder to compile and create the jar and build the distribution package using Maven +The current version of the application is extracted from the POM and instered into the MANIFEST.MF at build time. +More detailed information is available in the DeveloperGuide.odt in the docs folder in Github + +## Build + +Maven is used as a build. Therefore in order to build a snapshot the following command should be used: + +`./mvnw clean package` + + +`./mvnw versions:set -DnewVersion=2.3.0-SNAPSHOT` \ No newline at end of file diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt index 2a32d715..344ba969 100644 --- a/RELEASE-NOTES.txt +++ b/RELEASE-NOTES.txt @@ -1,18 +1,23 @@ OpenAS2 Server - Version 2.2.0 + Version 2.3.0 RELEASE NOTES -The OpenAS2 project is pleased to announce the release of OpenAS2 2.2.0 +The OpenAS2 project is pleased to announce the release of OpenAS2 2.3.0 -The release download file is: OpenAS2Server-2.2.0.zip +The release download file is: OpenAS2Server-2.3.0.zip The zip file contains a PDF document providing information on installing and using the application. -Version 2.2.1 - 2017-03-03 -This is a minor bugfix release: +Version 2.3.0 - 2017-05-04 +This is an enhancement and minor bugfix release: IMPORTANT NOTE: Please review ALL release notes for intermediate versions if you are upgrading - 1. Remove the encapsulating "<" and ">" from the generated message ID as windows no longer allows that in file names - 2. Minor update to documentation on handling SSL certificate issues + 1. Enhance message state tracking to correctly handle resends + 2. Support external databases that have JDBC drivers for message state tracking via configuration. + 3. Allow AS2 Message ID format to be configurable globally and overridden on a per partnership basis + 4. Persist message attributes for the full lifecycle of the message including resends. + 5. Provide support for proxy server authentication. + 6. Fix resending ASYNC MDN messages + 7. Document all of the enhancements and imporve existing documentation. Upgrade Notes (if upgrading from versions older than 2.1.0): @@ -21,7 +26,7 @@ Upgrade Notes (if upgrading from versions older than 2.1.0): Java 1.6 or later is required. -NOTE FOR JAVA 1.5: No longer supported. Use a version prior to 2.2.0 to run on Java 1.5 +NOTE FOR JAVA 1.5: No longer supported. Use a version prior to 2.3.0 to run on Java 1.5 NOTE FOR JAVA 1.6: The version of H2 database included in this release used for storing tracking messages will only support Java 1.7. diff --git a/Remote/lib/servlet.jar b/Remote/lib/servlet.jar deleted file mode 100644 index 7ed4be2a..00000000 Binary files a/Remote/lib/servlet.jar and /dev/null differ diff --git a/Remote/pom.xml b/Remote/pom.xml index a4b3a38e..96d80fdf 100644 --- a/Remote/pom.xml +++ b/Remote/pom.xml @@ -1,98 +1,72 @@ - - - org.openas2 - OpenAS2 - 2.2.0 - - 4.0.0 - org.openas2 - openas2-remote - 1.0.0 - OpenAS2 Remote - https://sourceforge.net/projects/openas2 - Open source implementation of the AS2 stndard for signed encrypted and compressed document transfer - - ${project.parent.artifactId}Remote-${project.version}.zip - UTF-8 - ${basedir}/lib - OpenAS2HowTo.pdf - ../docs/${help.filename} - dist - - - src/main/java - target - ${project.build.directory}/classes - - ${project.artifactId} - ${project.build.directory}/test-classes - src/main/scripts - ${project.basedir}/src/test/java - - - - maven-antrun-plugin - 1.8 - - - default-cli - - run - - package - - - - + + + + org.openas2 + OpenAS2 + 2.3.0-SNAPSHOT + - - - - - - - - + 4.0.0 + openas2-remote + + OpenAS2 Remote + + Remote management module for OpenAS2 server + - - - - - - - - maven-compiler-plugin - 3.1 - - 1.6 - 1.6 - - - - org.apache.maven.plugins - maven-jar-plugin - 3.0.2 - - - true - - true - org.openas2.remote.CommandLine - true - true - - - - - - - - - javax.servlet - javax.servlet-api - 3.1.0 - - + + org.openas2.remote.CommandLine + ${project.parent.artifactId}Remote-${project.version}.zip + UTF-8 + ${project.basedir}/../docs/OpenAS2HowTo.pdf + + + + + org.apache.maven.plugins + maven-dependency-plugin + + + org.apache.maven.plugins + maven-antrun-plugin + + + default-cli + + run + + package + + + + + + + + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + org.apache.maven.plugins + maven-jar-plugin + + + + + + javax.servlet + javax.servlet-api + 3.1.0 + + \ No newline at end of file diff --git a/Server/.classpath b/Server/.classpath deleted file mode 100644 index 0a1daddd..00000000 --- a/Server/.classpath +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Server/.project b/Server/.project deleted file mode 100644 index 482f2fd8..00000000 --- a/Server/.project +++ /dev/null @@ -1,23 +0,0 @@ - - - Server - - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.eclipse.m2e.core.maven2Builder - - - - - - org.eclipse.jdt.core.javanature - org.eclipse.m2e.core.maven2Nature - - diff --git a/Server/build.xml b/Server/build.xml deleted file mode 100644 index 568625df..00000000 --- a/Server/build.xml +++ /dev/null @@ -1,95 +0,0 @@ - - - Open source implementation of the AS2 standard for signed - encrypted and compressed document transfer - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Server/dist/OpenAS2Server-2.2.1.zip b/Server/dist/OpenAS2Server-2.2.1.zip deleted file mode 100644 index bc23335c..00000000 Binary files a/Server/dist/OpenAS2Server-2.2.1.zip and /dev/null differ diff --git a/Server/lib/bcmail-jdk15on-154.jar b/Server/lib/bcmail-jdk15on-154.jar deleted file mode 100644 index cf3aed7d..00000000 Binary files a/Server/lib/bcmail-jdk15on-154.jar and /dev/null differ diff --git a/Server/lib/bcpg-jdk15on-154.jar b/Server/lib/bcpg-jdk15on-154.jar deleted file mode 100644 index f208522b..00000000 Binary files a/Server/lib/bcpg-jdk15on-154.jar and /dev/null differ diff --git a/Server/lib/bcpkix-jdk15on-154.jar b/Server/lib/bcpkix-jdk15on-154.jar deleted file mode 100644 index 86f7f0be..00000000 Binary files a/Server/lib/bcpkix-jdk15on-154.jar and /dev/null differ diff --git a/Server/lib/bcprov-jdk15on-154.jar b/Server/lib/bcprov-jdk15on-154.jar deleted file mode 100644 index bd95185a..00000000 Binary files a/Server/lib/bcprov-jdk15on-154.jar and /dev/null differ diff --git a/Server/lib/commons-lang3-3.4.jar b/Server/lib/commons-lang3-3.4.jar deleted file mode 100644 index 8ec91d45..00000000 Binary files a/Server/lib/commons-lang3-3.4.jar and /dev/null differ diff --git a/Server/lib/commons-logging-1.2.jar b/Server/lib/commons-logging-1.2.jar deleted file mode 100644 index 93a3b9f6..00000000 Binary files a/Server/lib/commons-logging-1.2.jar and /dev/null differ diff --git a/Server/lib/dom4j-2.0.0.jar b/Server/lib/dom4j-2.0.0.jar deleted file mode 100644 index fd9e2ccd..00000000 Binary files a/Server/lib/dom4j-2.0.0.jar and /dev/null differ diff --git a/Server/lib/javax.mail.jar b/Server/lib/javax.mail.jar deleted file mode 100644 index ff26e484..00000000 Binary files a/Server/lib/javax.mail.jar and /dev/null differ diff --git a/Server/lib/openas2-server.jar b/Server/lib/openas2-server.jar deleted file mode 100644 index b3dc9103..00000000 Binary files a/Server/lib/openas2-server.jar and /dev/null differ diff --git a/Server/pom.xml b/Server/pom.xml index 0f44d1bf..c58a6de6 100644 --- a/Server/pom.xml +++ b/Server/pom.xml @@ -1,139 +1,159 @@ - - - org.openas2 - OpenAS2 - 2.2.0 - - 4.0.0 - org.openas2 - openas2-server - 2.2.1 - OpenAS2 Server - https://sourceforge.net/projects/openas2 - Open source implementation of the AS2 standard for signed encrypted and compressed document transfer - - ${project.parent.artifactId}Server-${project.version}.zip - UTF-8 - ${basedir}/lib - OpenAS2HowTo.pdf - ../docs/${help.filename} - dist - - - src/main/java - target - ${project.build.directory}/classes - - ${project.artifactId} - ${project.build.directory}/test-classes - src/main/scripts - ${project.basedir}/src/test/java - - - - maven-antrun-plugin - 1.8 - - - default-cli - - run - - package - - - - + + 4.0.0 - - - - - - - - + + org.openas2 + OpenAS2 + 2.3.0 + - - - - + openas2-server - - - maven-compiler-plugin - 3.1 - - 1.6 - 1.6 - - - - org.apache.maven.plugins - maven-jar-plugin - 3.0.2 - - - true - - true - org.openas2.app.OpenAS2Server - true - - true - - - - - - - - - org.dom4j - dom4j - 2.0.0 - - - org.bouncycastle - bcmail-jdk15on - 1.54 - - - org.bouncycastle - bcpkix-jdk15on - 1.54 - - - org.bouncycastle - bcprov-jdk15on - 1.54 - - - org.apache.commons - commons-lang3 - 3.4 - - - commons-logging - commons-logging - 1.2 - - - javax.mail - mail - 1.4.7 - - - com.h2database - h2 - 1.4.193 - - - org.bouncycastle - bcpg-jdk15on - 1.54 - - + OpenAS2 Server + + Open source implementation of the AS2 standard for signed encrypted and compressed document transfer + + + + org.openas2.app.OpenAS2Server + ${project.parent.artifactId}Server-${project.version}.zip + UTF-8 + ${project.basedir}/../docs/OpenAS2HowTo.pdf + + + + + openas2.sf.net + openas2.sf.net + sftp://web.sourceforge.net/home/project-web/openas2/htdocs/test + + + + + + + org.apache.maven.plugins + maven-dependency-plugin + + + org.apache.maven.plugins + maven-antrun-plugin + + + default-cli + + run + + package + + + + + + + + + + + + + + + + + + + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + org.apache.maven.plugins + maven-jar-plugin + + + org.apache.maven.plugins + maven-site-plugin + + ${project.basedir}/../docs + false + false + false + false + false + + + + + + + org.dom4j + dom4j + + + org.bouncycastle + bcmail-jdk15on + + + org.bouncycastle + bcpkix-jdk15on + + + org.bouncycastle + bcprov-jdk15on + + + org.apache.commons + commons-lang3 + + + commons-logging + commons-logging + + + javax.mail + mail + + + com.h2database + h2 + + + org.bouncycastle + bcpg-jdk15on + + + com.google.code.findbugs + findbugs + 3.0.1 + + + junit + junit + test + + + org.hamcrest + hamcrest-all + test + + + org.mockito + mockito-all + test + + + commons-io + commons-io + + \ No newline at end of file diff --git a/Server/bin/commons-logging.properties b/Server/src/bin/commons-logging.properties similarity index 100% rename from Server/bin/commons-logging.properties rename to Server/src/bin/commons-logging.properties diff --git a/Server/bin/gen_p12_key_par.sh b/Server/src/bin/gen_p12_key_par.sh similarity index 100% rename from Server/bin/gen_p12_key_par.sh rename to Server/src/bin/gen_p12_key_par.sh diff --git a/Server/bin/openas2.d b/Server/src/bin/openas2.d similarity index 100% rename from Server/bin/openas2.d rename to Server/src/bin/openas2.d diff --git a/Server/bin/openas2.service b/Server/src/bin/openas2.service similarity index 100% rename from Server/bin/openas2.service rename to Server/src/bin/openas2.service diff --git a/Server/bin/start-openas2.bat b/Server/src/bin/start-openas2.bat similarity index 92% rename from Server/bin/start-openas2.bat rename to Server/src/bin/start-openas2.bat index af131086..c5a7e45b 100755 --- a/Server/bin/start-openas2.bat +++ b/Server/src/bin/start-openas2.bat @@ -1,75 +1,75 @@ -@echo off -rem Purpose: runs the OpenAS2 application - -rem Set some of the base system properties for the Java environment and logging -rem remove -Dorg.apache.commons.logging.Log=org.openas2.logging.Log if using another logging package -rem -set EXTRA_PARMS=-Xms32m -Xmx384m -Dorg.apache.commons.logging.Log=org.openas2.logging.Log -rem For versions of Java that prevent restricted HTTP headers (see documentation for discussion on this) -rem set EXTRA_PARMS=%EXTRA_PARMS% -Dsun.net.http.allowRestrictedHeaders=true - - -rem set EXTRA_PARMS=%EXTRA_PARMS% -Dhttps.protocols=TLSv1.2 - -rem Uncomment any of the following for enhanced debug -rem set EXTRA_PARMS=%EXTRA_PARMS% -Dmaillogger.debug.enabled=true -rem set EXTRA_PARMS=%EXTRA_PARMS% -DlogRxdMsgMimeBodyParts=true -rem set EXTRA_PARMS=%EXTRA_PARMS% -DlogRxdMdnMimeBodyParts=true -rem set EXTRA_PARMS=%EXTRA_PARMS% -Djavax.net.debug=SSL -rem set EXTRA_PARMS=%EXTRA_PARMS% -DCmdProcessorSocketCipher=SSL_DH_anon_WITH_RC4_128_MD5 - -rem Setup the Java Virtual Machine -if not "%JAVA%" == "" goto :Check_JAVA_END - if not "%JAVA_HOME%" == "" goto :TryJDKEnd - call :warn JAVA_HOME not set; results may vary -:TryWOWJRE - FOR /F "usebackq tokens=3*" %%A IN (`REG QUERY "HKLM\Software\WOW6432NODE\JavaSoft\Java Runtime Environment" /s /v CurrentVersion ^| find "CurrentVersion"`) DO ( - set JAVA_VERSION=%%A - ) - FOR /F "usebackq tokens=3*" %%A IN (`REG QUERY "HKLM\Software\WOW6432NODE\JavaSoft\Java Runtime Environment\%JAVA_VERSION%" /s /v JavaHome ^| find "JavaHome"`) DO ( - set JAVA_HOME=%%A %%B - ) - if not exist "%JAVA_HOME%" goto :TryWOWJDK - goto TryJDKEnd -:TryWOWJDK - FOR /F "usebackq tokens=3*" %%A IN (`REG QUERY "HKLM\Software\WOW6432NODE\JavaSoft\Java Development Kit" /s /v CurrentVersion ^| find "CurrentVersion"`) DO ( - set JAVA_VERSION=%%A - ) - FOR /F "usebackq tokens=3*" %%A IN (`REG QUERY "HKLM\Software\WOW6432NODE\JavaSoft\Java Development Kit\%JAVA_VERSION%" /s /v JavaHome ^| find "JavaHome"`) DO ( - set JAVA_HOME=%%A %%B - ) - if not exist "%JAVA_HOME%" goto :TryJRE - goto TryJDKEnd -:TryJRE - FOR /F "usebackq tokens=3*" %%A IN (`REG QUERY "HKLM\Software\JavaSoft\Java Runtime Environment" /s /v CurrentVersion ^| find "CurrentVersion"`) DO ( - set JAVA_VERSION=%%A - ) - FOR /F "usebackq tokens=3*" %%A IN (`REG QUERY "HKLM\Software\JavaSoft\Java Runtime Environment\%JAVA_VERSION%" /s /v JavaHome ^| find "JavaHome"`) DO ( - set JAVA_HOME=%%A %%B - ) - if not exist "%JAVA_HOME%" goto :TryJDK - goto TryJDKEnd -:TryJDK - FOR /F "usebackq tokens=3*" %%A IN (`REG QUERY "HKLM\Software\JavaSoft\Java Development Kit" /s /v CurrentVersion ^| find "CurrentVersion"`) DO ( - set JAVA_VERSION=%%A - ) - FOR /F "usebackq tokens=3*" %%A IN (`REG QUERY "HKLM\Software\JavaSoft\Java Development Kit\%JAVA_VERSION%" /s /v JavaHome ^| find "JavaHome"`) DO ( - set JAVA_HOME=%%A %%B - ) - if not exist "%JAVA_HOME%" ( - call :warn Unable to retrieve JAVA_HOME from Registry - ) -:TryJDKEnd - if not exist "%JAVA_HOME%" ( - call :warn JAVA_HOME is not valid: "%JAVA_HOME%" - goto END - ) - set JAVA=%JAVA_HOME%\bin\java -:Check_JAVA_END -set LIB_JARS=../lib/h2-1.4.192.jar;../lib/javax.mail.jar;../lib/bcpkix-jdk15on-154.jar;../lib/bcprov-jdk15on-154.jar;../lib/bcmail-jdk15on-154.jar;../lib/commons-logging-1.2.jar;../lib/openas2-server.jar -rem -"%JAVA%" %EXTRA_PARMS% -cp .;%LIB_JARS% org.openas2.app.OpenAS2Server ../config/config.xml - -:warn -:END - +@echo off +rem Purpose: runs the OpenAS2 application + +rem Set some of the base system properties for the Java environment and logging +rem remove -Dorg.apache.commons.logging.Log=org.openas2.logging.Log if using another logging package +rem +set EXTRA_PARMS=-Xms32m -Xmx384m -Dorg.apache.commons.logging.Log=org.openas2.logging.Log +rem For versions of Java that prevent restricted HTTP headers (see documentation for discussion on this) +rem set EXTRA_PARMS=%EXTRA_PARMS% -Dsun.net.http.allowRestrictedHeaders=true + + +rem set EXTRA_PARMS=%EXTRA_PARMS% -Dhttps.protocols=TLSv1.2 + +rem Uncomment any of the following for enhanced debug +rem set EXTRA_PARMS=%EXTRA_PARMS% -Dmaillogger.debug.enabled=true +rem set EXTRA_PARMS=%EXTRA_PARMS% -DlogRxdMsgMimeBodyParts=true +rem set EXTRA_PARMS=%EXTRA_PARMS% -DlogRxdMdnMimeBodyParts=true +rem set EXTRA_PARMS=%EXTRA_PARMS% -Djavax.net.debug=SSL +rem set EXTRA_PARMS=%EXTRA_PARMS% -DCmdProcessorSocketCipher=SSL_DH_anon_WITH_RC4_128_MD5 + +rem Setup the Java Virtual Machine +if not "%JAVA%" == "" goto :Check_JAVA_END + if not "%JAVA_HOME%" == "" goto :TryJDKEnd + call :warn JAVA_HOME not set; results may vary +:TryWOWJRE + FOR /F "usebackq tokens=3*" %%A IN (`REG QUERY "HKLM\Software\WOW6432NODE\JavaSoft\Java Runtime Environment" /s /v CurrentVersion ^| find "CurrentVersion"`) DO ( + set JAVA_VERSION=%%A + ) + FOR /F "usebackq tokens=3*" %%A IN (`REG QUERY "HKLM\Software\WOW6432NODE\JavaSoft\Java Runtime Environment\%JAVA_VERSION%" /s /v JavaHome ^| find "JavaHome"`) DO ( + set JAVA_HOME=%%A %%B + ) + if not exist "%JAVA_HOME%" goto :TryWOWJDK + goto TryJDKEnd +:TryWOWJDK + FOR /F "usebackq tokens=3*" %%A IN (`REG QUERY "HKLM\Software\WOW6432NODE\JavaSoft\Java Development Kit" /s /v CurrentVersion ^| find "CurrentVersion"`) DO ( + set JAVA_VERSION=%%A + ) + FOR /F "usebackq tokens=3*" %%A IN (`REG QUERY "HKLM\Software\WOW6432NODE\JavaSoft\Java Development Kit\%JAVA_VERSION%" /s /v JavaHome ^| find "JavaHome"`) DO ( + set JAVA_HOME=%%A %%B + ) + if not exist "%JAVA_HOME%" goto :TryJRE + goto TryJDKEnd +:TryJRE + FOR /F "usebackq tokens=3*" %%A IN (`REG QUERY "HKLM\Software\JavaSoft\Java Runtime Environment" /s /v CurrentVersion ^| find "CurrentVersion"`) DO ( + set JAVA_VERSION=%%A + ) + FOR /F "usebackq tokens=3*" %%A IN (`REG QUERY "HKLM\Software\JavaSoft\Java Runtime Environment\%JAVA_VERSION%" /s /v JavaHome ^| find "JavaHome"`) DO ( + set JAVA_HOME=%%A %%B + ) + if not exist "%JAVA_HOME%" goto :TryJDK + goto TryJDKEnd +:TryJDK + FOR /F "usebackq tokens=3*" %%A IN (`REG QUERY "HKLM\Software\JavaSoft\Java Development Kit" /s /v CurrentVersion ^| find "CurrentVersion"`) DO ( + set JAVA_VERSION=%%A + ) + FOR /F "usebackq tokens=3*" %%A IN (`REG QUERY "HKLM\Software\JavaSoft\Java Development Kit\%JAVA_VERSION%" /s /v JavaHome ^| find "JavaHome"`) DO ( + set JAVA_HOME=%%A %%B + ) + if not exist "%JAVA_HOME%" ( + call :warn Unable to retrieve JAVA_HOME from Registry + ) +:TryJDKEnd + if not exist "%JAVA_HOME%" ( + call :warn JAVA_HOME is not valid: "%JAVA_HOME%" + goto END + ) + set JAVA=%JAVA_HOME%\bin\java +:Check_JAVA_END +set LIB_JARS=../lib/* +rem +"%JAVA%" %EXTRA_PARMS% -cp .;%LIB_JARS% org.openas2.app.OpenAS2Server ../config/config.xml + +:warn +:END + diff --git a/Server/bin/start-openas2.sh b/Server/src/bin/start-openas2.sh similarity index 82% rename from Server/bin/start-openas2.sh rename to Server/src/bin/start-openas2.sh index 537e8c78..d93efab7 100755 --- a/Server/bin/start-openas2.sh +++ b/Server/src/bin/start-openas2.sh @@ -1,4 +1,5 @@ #!/bin/bash +set -e # purpose: runs the OpenAS2 application x=`basename $0` @@ -52,10 +53,10 @@ if [ -z $JAVA_HOME ]; then exit 1 fi -LIB_JARS="${binDir}/../lib/h2-1.4.192.jar:${binDir}/../lib/javax.mail.jar:${binDir}/../lib/bcpkix-jdk15on-154.jar:${binDir}/../lib/bcprov-jdk15on-154.jar:${binDir}/../lib/bcmail-jdk15on-154.jar:${binDir}/../lib/commons-logging-1.2.jar:${binDir}/../lib/openas2-server.jar" -JAVA_EXE=$JAVA_HOME/bin/java -# -CMD="$JAVA_EXE ${PWD_OVERRIDE} ${EXTRA_PARMS} -cp .:${LIB_JARS} org.openas2.app.OpenAS2Server" +CMD=`echo "${JAVA_HOME}/bin/java ${PWD_OVERRIDE} ${EXTRA_PARMS} -cp .:${binDir}/../lib/* org.openas2.app.OpenAS2Server"` +echo +echo Running ${CMD} +echo if [ "true" = "$OPENAS2_AS_DAEMON" ]; then $CMD & RETVAL="$?" @@ -65,7 +66,7 @@ if [ "true" = "$OPENAS2_AS_DAEMON" ]; then echo $PID > $PID_FILE fi else - $CMD + ${CMD} RETVAL="$?" fi exit $RETVAL \ No newline at end of file diff --git a/Server/src/config/DB/openas2.mv.db b/Server/src/config/DB/openas2.mv.db new file mode 100644 index 00000000..cf5ec531 Binary files /dev/null and b/Server/src/config/DB/openas2.mv.db differ diff --git a/Server/config/as2_certs.p12 b/Server/src/config/as2_certs.p12 similarity index 100% rename from Server/config/as2_certs.p12 rename to Server/src/config/as2_certs.p12 diff --git a/Server/config/commands.xml b/Server/src/config/commands.xml similarity index 98% rename from Server/config/commands.xml rename to Server/src/config/commands.xml index 60576689..898b2b19 100644 --- a/Server/config/commands.xml +++ b/Server/src/config/commands.xml @@ -1,28 +1,28 @@ - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Server/config/config.xml b/Server/src/config/config.xml similarity index 81% rename from Server/config/config.xml rename to Server/src/config/config.xml index 8be660c1..b4c74204 100644 --- a/Server/config/config.xml +++ b/Server/src/config/config.xml @@ -1,145 +1,140 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Server/src/config/db_ddl.sql b/Server/src/config/db_ddl.sql new file mode 100644 index 00000000..453b50cb --- /dev/null +++ b/Server/src/config/db_ddl.sql @@ -0,0 +1,39 @@ +-- ----------------------------------------------------------------------- +-- msg_metadata +-- ----------------------------------------------------------------------- + +DROP TABLE msg_metadata IF EXISTS; + +-- ----------------------------------------------------------------------- +-- msg_metadata +-- ----------------------------------------------------------------------- + +CREATE TABLE msg_metadata +( + ID INTEGER NOT NULL IDENTITY, + MSG_ID VARCHAR NOT NULL, + PRIOR_MSG_ID VARCHAR, + MDN_ID VARCHAR, + DIRECTION VARCHAR(25), + IS_RESEND VARCHAR(1), + RESEND_COUNT INTEGER, + SENDER_ID VARCHAR(255) NOT NULL, + RECEIVER_ID VARCHAR(255) NOT NULL, + STATUS VARCHAR(255), + STATE VARCHAR(255), + SIGNATURE_ALGORITHM VARCHAR(255), + ENCRYPTION_ALGORITHM VARCHAR(255), + COMPRESSION VARCHAR(255), + FILE_NAME VARCHAR(255), + CONTENT_TYPE VARCHAR(255), + CONTENT_TRANSFER_ENCODING VARCHAR(255), + MDN_MODE VARCHAR(255), + MDN_RESPONSE VARCHAR, + STATE_MSG VARCHAR, + CREATE_DT TIMESTAMP, + UPDATE_DT TIMESTAMP, + PRIMARY KEY (ID) +); + +CREATE UNIQUE INDEX MSG_ID_UNIQUE ON msg_metadata (MSG_ID); + diff --git a/Server/config/emailtemplate.txt b/Server/src/config/emailtemplate.txt similarity index 94% rename from Server/config/emailtemplate.txt rename to Server/src/config/emailtemplate.txt index 961e782b..4db363dd 100644 --- a/Server/config/emailtemplate.txt +++ b/Server/src/config/emailtemplate.txt @@ -1,9 +1,9 @@ -senderid=$message.sender.as2_id$ -receiverid=$message.receiver.as2_id$ - -exception=$exception.name$ -description=$exception.message$ - -trace: -$exception.trace$ - +senderid=$message.sender.as2_id$ +receiverid=$message.receiver.as2_id$ + +exception=$exception.name$ +description=$exception.message$ + +trace: +$exception.trace$ + diff --git a/Server/config/partnerships.xml b/Server/src/config/partnerships.xml similarity index 98% rename from Server/config/partnerships.xml rename to Server/src/config/partnerships.xml index c14991b2..ea9c1488 100644 --- a/Server/config/partnerships.xml +++ b/Server/src/config/partnerships.xml @@ -1,68 +1,68 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Server/config/ssl_certs.jks b/Server/src/config/ssl_certs.jks similarity index 100% rename from Server/config/ssl_certs.jks rename to Server/src/config/ssl_certs.jks diff --git a/Server/src/main/java/org/openas2/BaseComponent.java b/Server/src/main/java/org/openas2/BaseComponent.java index 37e020f0..86ed2a07 100644 --- a/Server/src/main/java/org/openas2/BaseComponent.java +++ b/Server/src/main/java/org/openas2/BaseComponent.java @@ -11,43 +11,50 @@ public class BaseComponent implements Component { private Map parameters; private Session session; - public String getName() { + public String getName() + { String clippedName = this.getClass().getName(); // this clips off the package information StringTokenizer classParts = new StringTokenizer(clippedName, ".", false); - while (classParts.hasMoreTokens()) { + while (classParts.hasMoreTokens()) + { clippedName = classParts.nextToken(); } return clippedName; } - public void setParameter(String key, String value) { + public void setParameter(String key, String value) + { getParameters().put(key, value); } - public void setParameter(String key, int value) { + public void setParameter(String key, int value) + { setParameter(key, Integer.toString(value)); } public String getParameter(String key, boolean required) - throws InvalidParameterException { - String parameter = (String) getParameters().get(key); + throws InvalidParameterException + { + String parameter = getParameters().get(key); - if (required && (parameter == null)) { + if (required && (parameter == null)) + { throw new InvalidParameterException("Missing required parameter.", this, key, null); } return parameter; } - public String getParameter(String key, String defaultValue) - throws InvalidParameterException { + public String getParameter(String key, String defaultValue) throws InvalidParameterException + { String value = getParameter(key, false); - if (value == null) { + if (value == null) + { return defaultValue; } @@ -55,31 +62,42 @@ public String getParameter(String key, String defaultValue) } public int getParameterInt(String key, boolean required) - throws InvalidParameterException { + throws InvalidParameterException + { String value = getParameter(key, required); - if (value != null) { + if (value != null) + { return Integer.parseInt(value); } return 0; } - public Map getParameters() { - if (parameters == null) { + public Map getParameters() + { + if (parameters == null) + { parameters = new HashMap(); } return parameters; } - public Session getSession() { + public Session getSession() + { return session; } - public void init(Session session, Map parameters) throws OpenAS2Exception { + public void init(Session session, Map parameters) throws OpenAS2Exception + { this.session = session; this.parameters = parameters; } - + + @Override + public void destroy() throws Exception + { + // override if needed + } } diff --git a/Server/src/main/java/org/openas2/BaseSession.java b/Server/src/main/java/org/openas2/BaseSession.java index 65a4fbde..e98666aa 100644 --- a/Server/src/main/java/org/openas2/BaseSession.java +++ b/Server/src/main/java/org/openas2/BaseSession.java @@ -12,53 +12,78 @@ public abstract class BaseSession implements Session { - private Map components; - private String baseDirectory; + private Map components = new HashMap(); + private String baseDirectory; /** * Creates a BaseSession object, then calls the init() method. * * @throws OpenAS2Exception - * * @see #init() */ - public BaseSession() throws OpenAS2Exception { + public BaseSession() throws OpenAS2Exception + { init(); } - public CertificateFactory getCertificateFactory() throws ComponentNotFoundException { + @Override + public void start() throws OpenAS2Exception + { + getProcessor().startActiveModules(); + } + + @Override + public void stop() throws Exception + { + for (Component component : components.values()) + { + component.destroy(); + } + } + + public CertificateFactory getCertificateFactory() throws ComponentNotFoundException + { return (CertificateFactory) getComponent(CertificateFactory.COMPID_CERTIFICATE_FACTORY); } - public void setComponent(String componentID, Component comp) { + /** + * Registers a component to a specified ID. + * + * @param componentID registers the component to this ID + * @param comp component to register + * @see Component + */ + void setComponent(String componentID, Component comp) + { Map objects = getComponents(); objects.put(componentID, comp); } - public Component getComponent(String componentID) throws ComponentNotFoundException { + public Component getComponent(String componentID) throws ComponentNotFoundException + { Map comps = getComponents(); Component comp = comps.get(componentID); - if (comp == null) { + if (comp == null) + { throw new ComponentNotFoundException(componentID); } return comp; } - public Map getComponents() { - if (components == null) { - components = new HashMap(); - } - + public Map getComponents() + { return components; } - public PartnershipFactory getPartnershipFactory() throws ComponentNotFoundException { + public PartnershipFactory getPartnershipFactory() throws ComponentNotFoundException + { return (PartnershipFactory) getComponent(PartnershipFactory.COMPID_PARTNERSHIP_FACTORY); } - public Processor getProcessor() throws ComponentNotFoundException { + public Processor getProcessor() throws ComponentNotFoundException + { return (Processor) getComponent(Processor.COMPID_PROCESSOR); } @@ -68,7 +93,8 @@ public Processor getProcessor() throws ComponentNotFoundException { * * @throws OpenAS2Exception If an error occurs while initializing systems */ - protected void init() throws OpenAS2Exception { + protected void init() throws OpenAS2Exception + { initJavaMail(); } @@ -78,19 +104,22 @@ protected void init() throws OpenAS2Exception { * * @throws OpenAS2Exception If an error occurs while initializing mime types */ - protected void initJavaMail() throws OpenAS2Exception { + private void initJavaMail() throws OpenAS2Exception + { MailcapCommandMap mc = (MailcapCommandMap) CommandMap.getDefaultCommandMap(); mc.addMailcap( - "message/disposition-notification;; x-java-content-handler=org.openas2.util.DispositionDataContentHandler"); + "message/disposition-notification;; x-java-content-handler=org.openas2.util.DispositionDataContentHandler"); CommandMap.setDefaultCommandMap(mc); } - public String getBaseDirectory() { - return baseDirectory; - } + public String getBaseDirectory() + { + return baseDirectory; + } - public void setBaseDirectory(String dir) { - baseDirectory = dir; - } + void setBaseDirectory(String dir) + { + baseDirectory = dir; + } } diff --git a/Server/src/main/java/org/openas2/Component.java b/Server/src/main/java/org/openas2/Component.java index 8a8ad584..70657158 100644 --- a/Server/src/main/java/org/openas2/Component.java +++ b/Server/src/main/java/org/openas2/Component.java @@ -12,7 +12,6 @@ * parameters should be passed to the init method. * * @author Aaron Silinskas - * * @see BaseComponent * @see Session */ @@ -43,19 +42,26 @@ public interface Component { public Session getSession(); /** - * After creating a Component object, this method should be called to set any + * Component lifecycle hook. After creating a Component object, this method should be called to set any * parameters used by the component. Component implementations typically have * required parameter checking and code to start timers and threads within this method. * - * @param session the component uses this object to access other components + * @param session the component uses this object to access other components * @param parameters configuration values for the component - * - * @throws OpenAS2Exception If an error occurs while initializing the component + * @throws OpenAS2Exception If an error occurs while initializing the component * @throws InvalidParameterException If a required parameter is null in the parameters - * Map - * + * Map * @see #getParameter(String key, boolean required) * @see Session */ public void init(Session session, Map parameters) throws OpenAS2Exception; + + /** + * Component lifecycle hook. If lifecycle of {@link Component} requires a destroy function this method can be used. + * + * @throws Exception + * @see #init(Session, Map) + * @see Session + */ + void destroy() throws Exception; } diff --git a/Server/src/main/java/org/openas2/ComponentNotFoundException.java b/Server/src/main/java/org/openas2/ComponentNotFoundException.java index 17700329..3f25f711 100644 --- a/Server/src/main/java/org/openas2/ComponentNotFoundException.java +++ b/Server/src/main/java/org/openas2/ComponentNotFoundException.java @@ -1,22 +1,18 @@ package org.openas2; public class ComponentNotFoundException extends OpenAS2Exception { - /** - * - */ - private static final long serialVersionUID = 1L; - private String componentName; - public ComponentNotFoundException(String componentName) { - super(componentName); - this.componentName = componentName; - } + private static final long serialVersionUID = 1L; + private String componentName; - public String getComponentName() { - return componentName; + ComponentNotFoundException(String componentName) + { + this.componentName = componentName; } - public void setComponentName(String componentName) { - this.componentName = componentName; + @Override + public String getMessage() + { + return "Component '" + componentName + "' not found."; } } \ No newline at end of file diff --git a/Server/src/main/java/org/openas2/OpenAS2Exception.java b/Server/src/main/java/org/openas2/OpenAS2Exception.java index fd73011e..dcbd403a 100644 --- a/Server/src/main/java/org/openas2/OpenAS2Exception.java +++ b/Server/src/main/java/org/openas2/OpenAS2Exception.java @@ -5,63 +5,63 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; - ; public class OpenAS2Exception extends Exception { - /** - * - */ - private static final long serialVersionUID = 1L; - public static final String SOURCE_MESSAGE = "message"; + public static final String SOURCE_MESSAGE = "message"; public static final String SOURCE_FILE = "file"; - private Map sources; - private Log logger = LogFactory.getLog(OpenAS2Exception.class.getSimpleName()); - - public OpenAS2Exception() { + /** + * + */ + private static final long serialVersionUID = 1L; + private Map sources = new HashMap(); + private Log logger = LogFactory.getLog(OpenAS2Exception.class.getSimpleName()); + + public OpenAS2Exception() + { log(false); } - public OpenAS2Exception(String msg) { + public OpenAS2Exception(String msg) + { super(msg); } - public OpenAS2Exception(String msg, Throwable cause) { + public OpenAS2Exception(String msg, Throwable cause) + { super(msg, cause); } - public OpenAS2Exception(Throwable cause) { + public OpenAS2Exception(Throwable cause) + { super(cause); } - public Object getSource(String id) { - Map sources = getSources(); + public Object getSource(String id) + { + Map sources = getSources(); return sources.get(id); } - public void setSources(Map sources) { - this.sources = sources; - } - - public Map getSources() { - if (sources == null) { - sources = new HashMap(); - } - + public Map getSources() + { return sources; } - public void addSource(String id, Object source) { - Map sources = getSources(); + public void addSource(String id, Object source) + { + Map sources = getSources(); sources.put(id, source); } - public void terminate() { + public void terminate() + { log(true); } - protected void log(boolean terminated) { - logger.error("Error occurred:: " + org.openas2.logging.Log.getExceptionMsg(this) + "\n Sources: "+ this.getSources(), this); + protected void log(boolean terminated) + { + logger.error("Error occurred:: " + org.openas2.logging.Log.getExceptionMsg(this) + "\n Sources: " + this.getSources(), this); } } diff --git a/Server/src/main/java/org/openas2/Session.java b/Server/src/main/java/org/openas2/Session.java index 38a6b886..beeb8091 100644 --- a/Server/src/main/java/org/openas2/Session.java +++ b/Server/src/main/java/org/openas2/Session.java @@ -10,89 +10,83 @@ /** * The Session interface provides configuration and resource information, and a means for * components to access the functionality of other components. + * The Session has its own lifecycle controlled by two methods {@link #start()} and {@link #close()}. * * @author Aaron Silinskas - * * @see Component * @see org.openas2.cert.CertificateFactory * @see org.openas2.partner.PartnerFactory - * @see org.openas2.processor.Processor + * @see org.openas2.processor.Processor */ public interface Session { - public static final String DEFAULT_CONTENT_TRANSFER_ENCODING = "binary"; + String DEFAULT_CONTENT_TRANSFER_ENCODING = "binary"; /** - * Short-cut method to retrieve a certificate factory. - * - * @return the currently registered CertificateFactory component - * - * @throws ComponentNotFound If a CertificateFactory component has not been - * registered + * Lifecycle control method. * - * @see CertificateFactory - * @see Component + * @throws Exception */ - public CertificateFactory getCertificateFactory() throws ComponentNotFoundException; + void start() throws Exception; /** - * Registers a component to a specified ID. + * Lifecycle control method. * - * @param componentID registers the component to this ID - * @param comp component to register + * @throws Exception + */ + void stop() throws Exception; + + /** + * Short-cut method to retrieve a certificate factory. * + * @return the currently registered CertificateFactory component + * @throws ComponentNotFound If a CertificateFactory component has not been + * registered + * @see CertificateFactory * @see Component */ - public void setComponent(String componentID, Component comp); + CertificateFactory getCertificateFactory() throws ComponentNotFoundException; /** * Gets the Component currently registered with an ID * * @param componentID ID to search for - * * @return the component registered to the ID or null - * * @throws ComponentNotFound If a component is not registered with the ID */ - public Component getComponent(String componentID) throws ComponentNotFoundException; + Component getComponent(String componentID) throws ComponentNotFoundException; /** * Return a map of component ID's to Component objects. * * @return all registered components, mapped by ID */ - public Map getComponents(); + Map getComponents(); /** * Short-cut method to retrieve a partner factory. * * @return the currently registered PartnerFactory component - * * @throws ComponentNotFound If a PartnerFactory component has not been registered - * * @see PartnershipFactory * @see Component */ - public PartnershipFactory getPartnershipFactory() throws ComponentNotFoundException; + PartnershipFactory getPartnershipFactory() throws ComponentNotFoundException; /** * Short-cut method to retrieve a processor. * * @return the currently registered Processor component - * * @throws ComponentNotFound If a Processor component has not been registered - * * @see Processor * @see Component */ - public Processor getProcessor() throws ComponentNotFoundException; - - public String getBaseDirectory(); - - public void setBaseDirectory(String dir); - - public String getAppVersion(); - - public String getAppTitle(); + Processor getProcessor() throws ComponentNotFoundException; + + String getBaseDirectory(); + + String getAppVersion(); + + String getAppTitle(); } diff --git a/Server/src/main/java/org/openas2/XMLSession.java b/Server/src/main/java/org/openas2/XMLSession.java index 941ffccc..62b10dfa 100644 --- a/Server/src/main/java/org/openas2/XMLSession.java +++ b/Server/src/main/java/org/openas2/XMLSession.java @@ -5,26 +5,32 @@ import java.io.IOException; import java.io.InputStream; import java.net.URL; +import java.util.Collections; import java.util.Enumeration; import java.util.Map; import java.util.jar.Attributes; import java.util.jar.JarFile; import java.util.jar.Manifest; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.openas2.app.OpenAS2Server; import org.openas2.cert.CertificateFactory; import org.openas2.cmd.CommandManager; import org.openas2.cmd.CommandRegistry; -import org.openas2.cmd.CommandRegistryFactory; import org.openas2.cmd.processor.BaseCommandProcessor; import org.openas2.logging.LogManager; import org.openas2.logging.Logger; import org.openas2.partner.PartnershipFactory; import org.openas2.processor.Processor; import org.openas2.processor.ProcessorModule; +import org.openas2.schedule.SchedulerComponent; import org.openas2.util.Properties; import org.openas2.util.XMLUtil; import org.w3c.dom.Document; @@ -35,232 +41,292 @@ /** * original author unknown - * + *

* in this release added command registry methods - * @author joseph mcverry * + * @author joseph mcverry */ -public class XMLSession extends BaseSession implements CommandRegistryFactory { - public static final String EL_PROPERTIES = "properties"; - public static final String EL_CERTIFICATES = "certificates"; - public static final String EL_CMDPROCESSOR = "commandProcessors"; - public static final String EL_PROCESSOR = "processor"; - public static final String EL_PARTNERSHIPS = "partnerships"; - public static final String EL_COMMANDS = "commands"; - public static final String EL_LOGGERS = "loggers"; - public static final String PARAM_BASE_DIRECTORY = "basedir"; - private CommandRegistry commandRegistry; - private CommandManager cmdManager; - - private String VERSION; - private String TITLE; - - - public XMLSession(InputStream in) throws OpenAS2Exception, - ParserConfigurationException, SAXException, IOException { - super(); - load(in); - } - - public XMLSession(String filename) throws OpenAS2Exception, - ParserConfigurationException, SAXException, IOException { - File file = new File(filename).getAbsoluteFile(); - setBaseDirectory(file.getParent()); - FileInputStream fin = new FileInputStream(file); - try { - load(fin); - } finally { - fin.close(); - } - } - - public void setCommandRegistry(CommandRegistry registry) { - commandRegistry = registry; - } - - public CommandRegistry getCommandRegistry() { - return commandRegistry; - } - - protected void load(InputStream in) throws ParserConfigurationException, - SAXException, IOException, OpenAS2Exception { - DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); - - DocumentBuilder parser = factory.newDocumentBuilder(); - Document document = parser.parse(in); - Element root = document.getDocumentElement(); - - NodeList rootNodes = root.getChildNodes(); - Node rootNode; - String nodeName; - - for (int i = 0; i < rootNodes.getLength(); i++) { - rootNode = rootNodes.item(i); - - nodeName = rootNode.getNodeName(); - - if (nodeName.equals(EL_PROPERTIES)) { - loadProperties(rootNode); - } else if (nodeName.equals(EL_CERTIFICATES)) { - loadCertificates(rootNode); - } else if (nodeName.equals(EL_PROCESSOR)) { - loadProcessor(rootNode); - } else if (nodeName.equals(EL_CMDPROCESSOR)) { - loadCommandProcessors(rootNode); - } else if (nodeName.equals(EL_PARTNERSHIPS)) { - loadPartnerships(rootNode); - } else if (nodeName.equals(EL_COMMANDS)) { - loadCommands(rootNode); - } else if (nodeName.equals(EL_LOGGERS)) { - loadLoggers(rootNode); - } else if (nodeName.equals("#text")) { - // do nothing - } else if (nodeName.equals("#comment")) { - // do nothing - } else { - throw new OpenAS2Exception("Undefined tag: " + nodeName); - } - } - } - - protected void loadProperties(Node propNode) +public class XMLSession extends BaseSession { + private static final String EL_PROPERTIES = "properties"; + private static final String EL_CERTIFICATES = "certificates"; + private static final String EL_CMDPROCESSOR = "commandProcessors"; + private static final String EL_PROCESSOR = "processor"; + private static final String EL_PARTNERSHIPS = "partnerships"; + private static final String EL_COMMANDS = "commands"; + private static final String EL_LOGGERS = "loggers"; + private static final String PARAM_BASE_DIRECTORY = "basedir"; + + private CommandRegistry commandRegistry; + private CommandManager cmdManager = new CommandManager(); + + private String VERSION; + private String TITLE; + + private static final Log LOGGER = LogFactory.getLog(XMLSession.class.getSimpleName()); + + public XMLSession(String configAbsPath) throws OpenAS2Exception, + ParserConfigurationException, SAXException, IOException + { + File configXml = new File(configAbsPath); + File configDir = configXml.getParentFile(); + + FileInputStream configAsStream = new FileInputStream(configXml); + setBaseDirectory(configDir.getAbsolutePath()); + + load(configAsStream); + + // scheduler should be initializer after all modules + addSchedulerComponent(); + } + + private void addSchedulerComponent() throws OpenAS2Exception + { + SchedulerComponent comp = new SchedulerComponent(); + setComponent("scheduler", comp); + comp.init(this, Collections.emptyMap()); + } + + + protected void load(InputStream in) throws ParserConfigurationException, + SAXException, IOException, OpenAS2Exception + { + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + + DocumentBuilder parser = factory.newDocumentBuilder(); + Document document = parser.parse(in); + Element root = document.getDocumentElement(); + + NodeList rootNodes = root.getChildNodes(); + Node rootNode; + String nodeName; + + // this is used by all other objects to access global configs and functionality + LOGGER.info("Loading configuration..."); + for (int i = 0; i < rootNodes.getLength(); i++) + { + rootNode = rootNodes.item(i); + + nodeName = rootNode.getNodeName(); + + // enter the command processing loop + if (nodeName.equals(EL_PROPERTIES)) + { + loadProperties(rootNode); + } else if (nodeName.equals(EL_CERTIFICATES)) + { + loadCertificates(rootNode); + } else if (nodeName.equals(EL_PROCESSOR)) + { + loadProcessor(rootNode); + } else if (nodeName.equals(EL_CMDPROCESSOR)) + { + loadCommandProcessors(rootNode); + } else if (nodeName.equals(EL_PARTNERSHIPS)) + { + loadPartnerships(rootNode); + } else if (nodeName.equals(EL_COMMANDS)) + { + loadCommands(rootNode); + } else if (nodeName.equals(EL_LOGGERS)) + { + loadLoggers(rootNode); + } else if (nodeName.equals("#text")) + { + // do nothing + } else if (nodeName.equals("#comment")) + { + // do nothing + } else + { + throw new OpenAS2Exception("Undefined tag: " + nodeName); + } + } + + cmdManager.registerCommands(commandRegistry); + } + + private void loadProperties(Node propNode) + { + LOGGER.info("Loading properties..."); + + Map properties = XMLUtil.mapAttributes(propNode, false); + // Make key things accessible via static object for things that do not have accesss to session object + properties.put(Properties.APP_TITLE_PROP, getAppTitle()); + properties.put(Properties.APP_VERSION_PROP, getAppVersion()); + Properties.setProperties(properties); + } + + private void loadCertificates(Node rootNode) throws OpenAS2Exception + { + CertificateFactory certFx = (CertificateFactory) XMLUtil.getComponent( + rootNode, this); + setComponent(CertificateFactory.COMPID_CERTIFICATE_FACTORY, certFx); + } + + private void loadCommands(Node rootNode) throws OpenAS2Exception + { + Component component = XMLUtil.getComponent(rootNode, this); + commandRegistry = (CommandRegistry) component; + } + + private void loadLoggers(Node rootNode) throws OpenAS2Exception + { + LOGGER.info("Loading log manager(s)..."); + + LogManager manager = LogManager.getLogManager(); + if (LogManager.isRegisteredWithApache()) + { + ; // continue + } else + { + // if using the OpenAS2 loggers the log manager must registered with the jvm argument + // -Dorg.apache.commons.logging.Log=org.openas2.logging.Log + throw new OpenAS2Exception("the OpenAS2 loggers' log manager must registered with the jvm argument -Dorg.apache.commons.logging.Log=org.openas2.logging.Log"); + } + NodeList loggers = rootNode.getChildNodes(); + Node logger; + + for (int i = 0; i < loggers.getLength(); i++) + { + logger = loggers.item(i); + + if (logger.getNodeName().equals("logger")) + { + loadLogger(manager, logger); + } + } + } + + private void loadLogger(LogManager manager, Node loggerNode) + throws OpenAS2Exception + { + Logger logger = (Logger) XMLUtil.getComponent(loggerNode, this); + manager.addLogger(logger); + } + + private void loadCommandProcessors(Node rootNode) throws OpenAS2Exception { - Map properties = XMLUtil.mapAttributes(propNode); - Properties.setProperties(properties); + + // get a registry of Command objects, and add Commands for the Session + LOGGER.info("Loading command processor(s)..."); + + NodeList cmdProcessor = rootNode.getChildNodes(); + Node processor; + + for (int i = 0; i < cmdProcessor.getLength(); i++) + { + processor = cmdProcessor.item(i); + + if (processor.getNodeName().equals("commandProcessor")) + { + loadCommandProcessor(cmdManager, processor); + } + } + } + + private void loadCommandProcessor(CommandManager manager, + Node cmdPrcessorNode) throws OpenAS2Exception + { + BaseCommandProcessor cmdProcesor = (BaseCommandProcessor) XMLUtil + .getComponent(cmdPrcessorNode, this); + manager.addProcessor(cmdProcesor); + + setComponent(cmdProcesor.getName(), cmdProcesor); } - protected void loadCertificates(Node rootNode) throws OpenAS2Exception { - CertificateFactory certFx = (CertificateFactory) XMLUtil.getComponent( - rootNode, this); - setComponent(CertificateFactory.COMPID_CERTIFICATE_FACTORY, certFx); - } - - protected void loadCommands(Node rootNode) throws OpenAS2Exception { - CommandRegistry cmdReg = (CommandRegistry) XMLUtil.getComponent( - rootNode, this); - setCommandRegistry(cmdReg); - } - - protected void loadLoggers(Node rootNode) throws OpenAS2Exception { - - LogManager manager = LogManager.getLogManager(); - if (LogManager.isRegisteredWithApache()) - ; // continue - else { - // if using the OpenAS2 loggers the log manager must registered with the jvm argument - // -Dorg.apache.commons.logging.Log=org.openas2.logging.Log - throw new OpenAS2Exception("the OpenAS2 loggers' log manager must registered with the jvm argument -Dorg.apache.commons.logging.Log=org.openas2.logging.Log"); - } - NodeList loggers = rootNode.getChildNodes(); - Node logger; - - for (int i = 0; i < loggers.getLength(); i++) { - logger = loggers.item(i); - - if (logger.getNodeName().equals("logger")) { - loadLogger(manager, logger); - } - } - } - - protected void loadLogger(LogManager manager, Node loggerNode) - throws OpenAS2Exception { - Logger logger = (Logger) XMLUtil.getComponent(loggerNode, this); - manager.addLogger(logger); - } - - protected void loadCommandProcessors(Node rootNode) throws OpenAS2Exception { - cmdManager = CommandManager.getCmdManager(); - - NodeList cmdProcessor = rootNode.getChildNodes(); - Node processor; - - for (int i = 0; i < cmdProcessor.getLength(); i++) { - processor = cmdProcessor.item(i); - - if (processor.getNodeName().equals("commandProcessor")) { - loadCommandProcessor(cmdManager, processor); - } - } - } - - public CommandManager getCommandManager() { - return cmdManager; - } - - protected void loadCommandProcessor(CommandManager manager, - Node cmdPrcessorNode) throws OpenAS2Exception { - BaseCommandProcessor cmdProcesor = (BaseCommandProcessor) XMLUtil - .getComponent(cmdPrcessorNode, this); - manager.addProcessor(cmdProcesor); - } - protected void loadPartnerships(Node rootNode) throws OpenAS2Exception { - PartnershipFactory partnerFx = (PartnershipFactory) XMLUtil - .getComponent(rootNode, this); - setComponent(PartnershipFactory.COMPID_PARTNERSHIP_FACTORY, partnerFx); - } - - protected void loadProcessor(Node rootNode) throws OpenAS2Exception { - Processor proc = (Processor) XMLUtil.getComponent(rootNode, this); - setComponent(Processor.COMPID_PROCESSOR, proc); - - NodeList modules = rootNode.getChildNodes(); - Node module; - - for (int i = 0; i < modules.getLength(); i++) { - module = modules.item(i); - - if (module.getNodeName().equals("module")) { - loadProcessorModule(proc, module); - } - } - } - - protected void loadProcessorModule(Processor proc, Node moduleNode) - throws OpenAS2Exception { - ProcessorModule procmod = (ProcessorModule) XMLUtil.getComponent( - moduleNode, this); - proc.getModules().add(procmod); - } - - private String getManifestAttribValue(String attrib) { - Enumeration resEnum; - try { - resEnum = Thread.currentThread().getContextClassLoader().getResources(JarFile.MANIFEST_NAME); - while (resEnum.hasMoreElements()) { - try { - URL url = (URL)resEnum.nextElement(); - if (!url.getPath().contains("openas2")) continue; - InputStream is = url.openStream(); - if (is != null) { - Manifest manifest = new Manifest(is); - Attributes mainAttribs = manifest.getMainAttributes(); - String value = mainAttribs.getValue(attrib); - if(value != null) { - return value; - } - } - } - catch (Exception e) { - // Silently ignore wrong manifests on classpath? - } - } - } catch (IOException e1) { - // Silently ignore wrong manifests on classpath? - } - return null; - } - - public String getAppVersion() - { - if (VERSION == null) VERSION = getManifestAttribValue("Implementation-Version"); - return VERSION; - } - - public String getAppTitle() - { - if (TITLE == null) TITLE = getManifestAttribValue("Implementation-Title") + " v" + getAppVersion(); - return TITLE; - - } + private void loadPartnerships(Node rootNode) throws OpenAS2Exception + { + LOGGER.info("Loading partnerships..."); + + PartnershipFactory partnerFx = (PartnershipFactory) XMLUtil + .getComponent(rootNode, this); + setComponent(PartnershipFactory.COMPID_PARTNERSHIP_FACTORY, partnerFx); + } + + private void loadProcessor(Node rootNode) throws OpenAS2Exception + { + Processor proc = (Processor) XMLUtil.getComponent(rootNode, this); + setComponent(Processor.COMPID_PROCESSOR, proc); + + LOGGER.info("Loading processor modules..."); + + NodeList modules = rootNode.getChildNodes(); + Node module; + + for (int i = 0; i < modules.getLength(); i++) + { + module = modules.item(i); + + if (module.getNodeName().equals("module")) + { + loadProcessorModule(proc, module); + } + } + } + + private void loadProcessorModule(Processor proc, Node moduleNode) + throws OpenAS2Exception + { + ProcessorModule procmod = (ProcessorModule) XMLUtil.getComponent( + moduleNode, this); + proc.getModules().add(procmod); + } + + @Nullable + private String getManifestAttribValue(@Nonnull String attrib) + { + Enumeration resEnum; + try + { + resEnum = Thread.currentThread().getContextClassLoader().getResources(JarFile.MANIFEST_NAME); + while (resEnum.hasMoreElements()) + { + try + { + URL url = (URL) resEnum.nextElement(); + if (!url.getPath().contains("openas2")) + { + continue; + } + InputStream is = url.openStream(); + if (is != null) + { + Manifest manifest = new Manifest(is); + Attributes mainAttribs = manifest.getMainAttributes(); + String value = mainAttribs.getValue(attrib); + if (value != null) + { + return value; + } + } + } catch (Exception e) + { + // Silently ignore wrong manifests on classpath? + } + } + } catch (IOException e1) + { + // Silently ignore wrong manifests on classpath? + } + return null; + } + + public String getAppVersion() + { + if (VERSION == null) + { + VERSION = getManifestAttribValue("Implementation-Version"); + } + return VERSION; + } + + public String getAppTitle() + { + if (TITLE == null) + { + TITLE = getManifestAttribValue("Implementation-Title") + " v" + getAppVersion(); + } + return TITLE; + + } } diff --git a/Server/src/main/java/org/openas2/app/OpenAS2Server.java b/Server/src/main/java/org/openas2/app/OpenAS2Server.java index be9c464e..5aad9c88 100644 --- a/Server/src/main/java/org/openas2/app/OpenAS2Server.java +++ b/Server/src/main/java/org/openas2/app/OpenAS2Server.java @@ -1,155 +1,117 @@ package org.openas2.app; -import java.io.BufferedWriter; import java.io.File; -import java.io.OutputStreamWriter; -import java.util.List; + +import javax.annotation.Nonnull; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.openas2.OpenAS2Exception; +import org.openas2.Session; import org.openas2.XMLSession; -import org.openas2.cmd.CommandManager; -import org.openas2.cmd.CommandRegistry; -import org.openas2.cmd.processor.BaseCommandProcessor; -/** +/** * original author unknown - * + *

* in this release added ability to have multiple command processors - * @author joseph mcverry * */ public class OpenAS2Server { - protected BufferedWriter sysOut; - BaseCommandProcessor cmd = null; - XMLSession session = null; + private static final Log LOGGER = LogFactory.getLog(OpenAS2Server.class.getSimpleName()); + + @Nonnull + private final Session session; + + + public OpenAS2Server(@Nonnull Session session) + { + this.session = session; + } + + + public static void main(String[] args) throws Exception + { + new OpenAS2Server.Builder() + .registerShutdownHook() + .run(args); + } - - public static void main(String[] args) { - OpenAS2Server server = new OpenAS2Server(); - server.start(args); - } + private void start() throws Exception + { + LOGGER.info("Starting " + session.getAppTitle() + "..."); + session.start(); + LOGGER.info(session.getAppTitle() + " started."); + } - public void start(String[] args) { - int exitStatus = 0; + public void shutdown() + { + try + { + session.getProcessor().stopActiveModules(); + } catch (OpenAS2Exception same) + { + same.terminate(); + } + + LOGGER.info("OpenAS2 has shut down\r\n"); + } - Runtime.getRuntime().addShutdownHook(new Thread() + public static class Builder { + private boolean registerShutdownHook; + + @Nonnull + private static File findConfig(@Nonnull String[] runArgs) throws Exception { - @Override - public void run() + LOGGER.info("Retrieving config file..."); + // Check for passed in as parameter first then look for system property + String configFile = (runArgs.length > 0) ? runArgs[0] : System.getProperty("openas2.config.file"); + if (configFile == null || configFile.length() < 1) + { + // Try the default location assuming the app was started in the bin folder + configFile = System.getProperty("user.dir") + "/../config/config.xml"; + } + File cfg = new File(configFile); + if (!cfg.exists()) { - shutdown(); - System.out.println("Shutdown due to process interruption!"); + LOGGER.error("No config file found: " + configFile); + LOGGER.error("Pass as the first paramter on the command line or set the system property \"openas2.config.file\" to identify the configuration file to start OpenAS2"); + throw new Exception("Missing configuration file"); } - }); - try { - Log logger = LogFactory.getLog(OpenAS2Server.class.getSimpleName()); - - write("Retrieving config file..." + System.getProperty("line.separator")); - // Check for passed in as parameter first then look for system property - String configFile = (args.length > 0)?args[0]:System.getProperty("openas2.config.file"); - if (configFile == null || configFile.length() < 1) { - // Try the default location assuming the app was started in the bin folder - configFile = System.getProperty("user.dir") + "/../config/config.xml"; - } - File cfg = new File(configFile); - if (!cfg.exists()) { - write("No config file found: " + configFile + System.getProperty("line.separator")); - write("Pass as the first paramter on the command line or set the system property \"openas2.config.file\" to identify the configuration file to start OpenAS2" + System.getProperty("line.separator")); - throw new Exception("Missing configuration file"); - } - session = new XMLSession(configFile); - - write("Starting Server..." + System.getProperty("line.separator")); - - // create the OpenAS2 Session object - // this is used by all other objects to access global configs and functionality - write("Loading configuration..." + System.getProperty("line.separator")); - - write(session.getAppTitle() + ": Session instantiated." + System.getProperty("line.separator")); - // create a command processor - - // get a registry of Command objects, and add Commands for the Session - write("Registering Session to Command Processor..." + System.getProperty("line.separator")); - - CommandRegistry reg = session.getCommandRegistry(); - - // start the active processor modules - write("Starting Active Modules..." + System.getProperty("line.separator")); - session.getProcessor().startActiveModules(); - - // enter the command processing loop - write(session.getAppTitle() + " Started" + System.getProperty("line.separator")); - - - logger.info("- OpenAS2 Started - V" + session.getAppVersion()); - - CommandManager cmdMgr = session.getCommandManager(); - List processors = cmdMgr.getProcessors(); - for (int i = 0; i < processors.size(); i++) { - write("Loading Command Processor..." + processors.toString() - + System.getProperty("line.separator")); - cmd = (BaseCommandProcessor) processors.get(i); - cmd.init(); - cmd.addCommands(reg); - cmd.start(); - } - breakOut : while (true) { - for (int i = 0; i < processors.size(); i++) { - cmd = (BaseCommandProcessor) processors.get(i); - if (cmd.isTerminated()) - break breakOut; - Thread.sleep(100); - } - } - logger.info("- OpenAS2 Stopped -"); - } catch (Exception e) { - exitStatus = -1; - e.printStackTrace(); - } catch (Error err) { - exitStatus = -1; - err.printStackTrace(); - } finally { - shutdown(); - System.exit(exitStatus); - } - } - - public void shutdown() - { - if (session != null) { - try { - session.getProcessor().stopActiveModules(); - } catch (OpenAS2Exception same) { - same.terminate(); - } - } - - if (cmd != null) { - try { - cmd.deInit(); - } catch (OpenAS2Exception cdie) { - cdie.terminate(); - } - } - - write("OpenAS2 has shut down\r\n"); - - - } - - public void write(String msg) { - if (sysOut == null) { - sysOut = new BufferedWriter(new OutputStreamWriter(System.out)); - } - - try { - sysOut.write(msg); - sysOut.flush(); - } catch (java.io.IOException e) { - e.printStackTrace(); - } - } + return cfg; + } + + public OpenAS2Server run(String... args) throws Exception + { + + XMLSession session = new XMLSession(findConfig(args).getAbsolutePath()); + final OpenAS2Server server = new OpenAS2Server(session); + + registerShutdownHookIfNeeded(server); + + server.start(); + return server; + } + + private void registerShutdownHookIfNeeded(final OpenAS2Server server) + { + if (registerShutdownHook) + { + Runtime.getRuntime().addShutdownHook(new Thread() { + @Override + public void run() + { + server.shutdown(); + } + }); + LOGGER.info("Shutdown hook registered."); + } + } + + public Builder registerShutdownHook() + { + this.registerShutdownHook = true; + return this; + } + } } diff --git a/Server/src/main/java/org/openas2/cert/PKCS12CertificateFactory.java b/Server/src/main/java/org/openas2/cert/PKCS12CertificateFactory.java index 480db371..1d5e94ed 100644 --- a/Server/src/main/java/org/openas2/cert/PKCS12CertificateFactory.java +++ b/Server/src/main/java/org/openas2/cert/PKCS12CertificateFactory.java @@ -16,6 +16,8 @@ import java.util.Enumeration; import java.util.HashMap; import java.util.Map; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -27,351 +29,398 @@ import org.openas2.params.InvalidParameterException; import org.openas2.partner.Partnership; import org.openas2.partner.SecurePartnership; +import org.openas2.schedule.HasSchedule; +import org.openas2.support.FileMonitorAdapter; import org.openas2.util.AS2Util; -import org.openas2.util.FileMonitor; -import org.openas2.util.FileMonitorListener; public class PKCS12CertificateFactory extends BaseCertificateFactory implements AliasedCertificateFactory, KeyStoreCertificateFactory, StorableCertificateFactory, - FileMonitorListener { + HasSchedule { public static final String PARAM_FILENAME = "filename"; public static final String PARAM_PASSWORD = "password"; public static final String PARAM_INTERVAL = "interval"; - private FileMonitor fileMonitor; private KeyStore keyStore; - private Log logger = LogFactory.getLog(PKCS12CertificateFactory.class.getSimpleName()); - - public String getAlias(Partnership partnership, String partnershipType) throws OpenAS2Exception { + private Log logger = LogFactory.getLog(PKCS12CertificateFactory.class.getSimpleName()); + + public String getAlias(Partnership partnership, String partnershipType) throws OpenAS2Exception + { String alias = null; - if (partnershipType == Partnership.PTYPE_RECEIVER) { + if (partnershipType == Partnership.PTYPE_RECEIVER) + { alias = partnership.getReceiverID(SecurePartnership.PID_X509_ALIAS); - } else if (partnershipType == Partnership.PTYPE_SENDER) { + } else if (partnershipType == Partnership.PTYPE_SENDER) + { alias = partnership.getSenderID(SecurePartnership.PID_X509_ALIAS); } - if (alias == null) { + if (alias == null) + { throw new CertificateNotFoundException(partnershipType, null); } return alias; } - public X509Certificate getCertificate(String alias) throws OpenAS2Exception { - try { + public X509Certificate getCertificate(String alias) throws OpenAS2Exception + { + try + { KeyStore ks = getKeyStore(); X509Certificate cert = (X509Certificate) ks.getCertificate(alias); - if (cert == null) { + if (cert == null) + { throw new CertificateNotFoundException(null, alias); } return cert; - } catch (KeyStoreException kse) { + } catch (KeyStoreException kse) + { throw new WrappedException(kse); } } public X509Certificate getCertificate(Message msg, String partnershipType) - throws OpenAS2Exception { - try { + throws OpenAS2Exception + { + try + { return getCertificate(getAlias(msg.getPartnership(), partnershipType)); - } catch (CertificateNotFoundException cnfe) { + } catch (CertificateNotFoundException cnfe) + { cnfe.setPartnershipType(partnershipType); throw cnfe; } } public X509Certificate getCertificate(MessageMDN mdn, String partnershipType) - throws OpenAS2Exception { - try { + throws OpenAS2Exception + { + try + { return getCertificate(getAlias(mdn.getPartnership(), partnershipType)); - } catch (CertificateNotFoundException cnfe) { + } catch (CertificateNotFoundException cnfe) + { cnfe.setPartnershipType(partnershipType); throw cnfe; } } - public Map getCertificates() throws OpenAS2Exception { + public Map getCertificates() throws OpenAS2Exception + { KeyStore ks = getKeyStore(); - try { - Map certs = new HashMap(); + try + { + Map certs = new HashMap(); String certAlias; Enumeration e = ks.aliases(); - while (e.hasMoreElements()) { - certAlias = (String) e.nextElement(); + while (e.hasMoreElements()) + { + certAlias = e.nextElement(); certs.put(certAlias, (X509Certificate) ks.getCertificate(certAlias)); } return certs; - } catch (GeneralSecurityException gse) { + } catch (GeneralSecurityException gse) + { throw new WrappedException(gse); } } - public void setFileMonitor(FileMonitor fileMonitor) { - this.fileMonitor = fileMonitor; + private int getRefreshInterval() throws InvalidParameterException + { + return getParameterInt(PARAM_INTERVAL, false); } - public FileMonitor getFileMonitor() throws InvalidParameterException { - boolean createMonitor = ((fileMonitor == null) && (getParameter(PARAM_INTERVAL, false) != null)); - - if (!createMonitor && fileMonitor != null) { - String filename = fileMonitor.getFilename(); - createMonitor = ((filename != null) && !filename.equals(getFilename())); - } - - if (createMonitor) { - if (fileMonitor != null) { - fileMonitor.stop(); - } - - int interval = getParameterInt(PARAM_INTERVAL, true); - File file = new File(getFilename()); - fileMonitor = new FileMonitor(file, interval); - fileMonitor.addListener(this); - } - - return fileMonitor; + public String getFilename() throws InvalidParameterException + { + return getParameter(PARAM_FILENAME, true); } - public void setFilename(String filename) { + public void setFilename(String filename) + { getParameters().put(PARAM_FILENAME, filename); } - public String getFilename() throws InvalidParameterException { - return getParameter(PARAM_FILENAME, true); + public KeyStore getKeyStore() + { + return keyStore; } - public void setKeyStore(KeyStore keyStore) { + public void setKeyStore(KeyStore keyStore) + { this.keyStore = keyStore; } - public KeyStore getKeyStore() { - return keyStore; + public char[] getPassword() throws InvalidParameterException + { + return getParameter(PARAM_PASSWORD, true).toCharArray(); } - public void setPassword(char[] password) { + public void setPassword(char[] password) + { getParameters().put(PARAM_PASSWORD, new String(password)); } - public char[] getPassword() throws InvalidParameterException { - return getParameter(PARAM_PASSWORD, true).toCharArray(); - } - - public PrivateKey getPrivateKey(X509Certificate cert) throws OpenAS2Exception { + private PrivateKey getPrivateKey(X509Certificate cert) throws OpenAS2Exception + { KeyStore ks = getKeyStore(); String alias = null; - try { + try + { alias = ks.getCertificateAlias(cert); - if (alias == null) { + if (alias == null) + { throw new KeyNotFoundException(cert, "-- alias null from getCertificateAlias(cert) call"); } PrivateKey key = (PrivateKey) ks.getKey(alias, getPassword()); - if (key == null) { + if (key == null) + { throw new KeyNotFoundException(cert, "-- key null from getKey(" + alias + ") call"); } return key; - } catch (GeneralSecurityException e) { + } catch (GeneralSecurityException e) + { throw new KeyNotFoundException(cert, alias, e); } } - public PrivateKey getPrivateKey(Message msg, X509Certificate cert) throws OpenAS2Exception { + public PrivateKey getPrivateKey(Message msg, X509Certificate cert) throws OpenAS2Exception + { return getPrivateKey(cert); } - public PrivateKey getPrivateKey(MessageMDN mdn, X509Certificate cert) throws OpenAS2Exception { + public PrivateKey getPrivateKey(MessageMDN mdn, X509Certificate cert) throws OpenAS2Exception + { return getPrivateKey(cert); } public void addCertificate(String alias, X509Certificate cert, boolean overwrite) - throws OpenAS2Exception { + throws OpenAS2Exception + { KeyStore ks = getKeyStore(); - try { - if (ks.containsAlias(alias) && !overwrite) { + try + { + if (ks.containsAlias(alias) && !overwrite) + { throw new CertificateExistsException(alias); } ks.setCertificateEntry(alias, cert); save(getFilename(), getPassword()); - } catch (GeneralSecurityException gse) { + } catch (GeneralSecurityException gse) + { throw new WrappedException(gse); } } - public void addPrivateKey(String alias, Key key, String password) throws OpenAS2Exception { + public void addPrivateKey(String alias, Key key, String password) throws OpenAS2Exception + { KeyStore ks = getKeyStore(); - try { - if (!ks.containsAlias(alias)) { + try + { + if (!ks.containsAlias(alias)) + { throw new CertificateNotFoundException(null, alias); } Certificate[] certChain = ks.getCertificateChain(alias); if (certChain == null) { - X509Certificate x509cert = (X509Certificate)ks.getCertificate(alias); - if (x509cert.getSubjectDN().equals(x509cert.getIssuerDN())) + X509Certificate x509cert = (X509Certificate) ks.getCertificate(alias); + if (x509cert.getSubjectDN().equals(x509cert.getIssuerDN())) { - // Trust chain is to itself - certChain = new X509Certificate[] { x509cert, x509cert }; - if (logger.isInfoEnabled()) logger.info("Detected self-signed certificate and allowed import. Alias: " + alias); + // Trust chain is to itself + certChain = new X509Certificate[]{x509cert, x509cert}; + if (logger.isInfoEnabled()) + { + logger.info("Detected self-signed certificate and allowed import. Alias: " + alias); + } } } ks.setKeyEntry(alias, key, password.toCharArray(), certChain); save(getFilename(), getPassword()); - } catch (GeneralSecurityException gse) { + } catch (GeneralSecurityException gse) + { throw new WrappedException(gse); } } - public void clearCertificates() throws OpenAS2Exception { + public void clearCertificates() throws OpenAS2Exception + { KeyStore ks = getKeyStore(); - try { + try + { Enumeration aliases = ks.aliases(); - while (aliases.hasMoreElements()) { - ks.deleteEntry((String) aliases.nextElement()); + while (aliases.hasMoreElements()) + { + ks.deleteEntry(aliases.nextElement()); } save(getFilename(), getPassword()); - } catch (GeneralSecurityException gse) { + } catch (GeneralSecurityException gse) + { throw new WrappedException(gse); } } - public void handle(FileMonitor monitor, File file, int eventID) { - switch (eventID) { - case FileMonitorListener.EVENT_MODIFIED: - - try { - load(); - logger.info("- Certificates Reloaded -"); - } catch (OpenAS2Exception oae) { - oae.terminate(); - } - - break; - } - } - - public void init(Session session, Map options) throws OpenAS2Exception { + public void init(Session session, Map options) throws OpenAS2Exception + { super.init(session, options); - + // Override the password if it was passed as a system property String pwd = System.getProperty("org.openas2.cert.Password"); if (pwd != null) { - setPassword(pwd.toCharArray()); + setPassword(pwd.toCharArray()); } - try { + try + { this.keyStore = AS2Util.getCryptoHelper().getKeyStore(); - } catch (Exception e) { + } catch (Exception e) + { throw new WrappedException(e); } - - load(getFilename(), getPassword()); + load(); } - public void load(String filename, char[] password) throws OpenAS2Exception { - try { + public void load(String filename, char[] password) throws OpenAS2Exception + { + try + { FileInputStream fIn = new FileInputStream(filename); load(fIn, password); fIn.close(); - } catch (IOException ioe) { + } catch (IOException ioe) + { throw new WrappedException(ioe); } } - public void load(InputStream in, char[] password) throws OpenAS2Exception { - try { + public void load(InputStream in, char[] password) throws OpenAS2Exception + { + try + { KeyStore ks = getKeyStore(); - synchronized (ks) { + synchronized (ks) + { ks.load(in, password); } - - getFileMonitor(); - } catch (IOException ioe) { + } catch (IOException ioe) + { throw new WrappedException(ioe); - } catch (GeneralSecurityException gse) { + } catch (GeneralSecurityException gse) + { throw new WrappedException(gse); } } - public void load() throws OpenAS2Exception { + public void load() throws OpenAS2Exception + { load(getFilename(), getPassword()); } - public void removeCertificate(X509Certificate cert) throws OpenAS2Exception { + public void removeCertificate(X509Certificate cert) throws OpenAS2Exception + { KeyStore ks = getKeyStore(); - try { + try + { String alias = ks.getCertificateAlias(cert); - if (alias == null) { + if (alias == null) + { throw new CertificateNotFoundException(cert); } removeCertificate(alias); - } catch (GeneralSecurityException gse) { + } catch (GeneralSecurityException gse) + { throw new WrappedException(gse); } } - public void removeCertificate(String alias) throws OpenAS2Exception { + public void removeCertificate(String alias) throws OpenAS2Exception + { KeyStore ks = getKeyStore(); - try { - if (ks.getCertificate(alias) == null) { + try + { + if (ks.getCertificate(alias) == null) + { throw new CertificateNotFoundException(null, alias); } ks.deleteEntry(alias); save(getFilename(), getPassword()); - } catch (GeneralSecurityException gse) { + } catch (GeneralSecurityException gse) + { throw new WrappedException(gse); } } - public void save() throws OpenAS2Exception { + public void save() throws OpenAS2Exception + { save(getFilename(), getPassword()); } - public void save(String filename, char[] password) throws OpenAS2Exception { - try { + public void save(String filename, char[] password) throws OpenAS2Exception + { + try + { FileOutputStream fOut = new FileOutputStream(filename, false); save(fOut, password); fOut.close(); - } catch (IOException ioe) { + } catch (IOException ioe) + { throw new WrappedException(ioe); } } - public void save(OutputStream out, char[] password) throws OpenAS2Exception { - try { + public void save(OutputStream out, char[] password) throws OpenAS2Exception + { + try + { getKeyStore().store(out, password); - } catch (IOException ioe) { + } catch (IOException ioe) + { throw new WrappedException(ioe); - } catch (GeneralSecurityException gse) { + } catch (GeneralSecurityException gse) + { throw new WrappedException(gse); } } + + @Override + public void schedule(ScheduledExecutorService executor) throws OpenAS2Exception + { + new FileMonitorAdapter() { + @Override + public void onConfigFileChanged() throws OpenAS2Exception + { + load(); + logger.info("- Certificates Reloaded -"); + } + }.scheduleIfNeed(executor, new File(getFilename()), getRefreshInterval(), TimeUnit.SECONDS); + } } \ No newline at end of file diff --git a/Server/src/main/java/org/openas2/cmd/BaseCommand.java b/Server/src/main/java/org/openas2/cmd/BaseCommand.java index c6345af1..1c97c318 100644 --- a/Server/src/main/java/org/openas2/cmd/BaseCommand.java +++ b/Server/src/main/java/org/openas2/cmd/BaseCommand.java @@ -11,60 +11,78 @@ public abstract class BaseCommand extends BaseComponent implements Command { public static final String PARAM_NAME = "name"; public static final String PARAM_DESCRIPTION = "description"; - public static final String PARAM_USAGE = "usage"; - - public void init(Session session, Map parameters) throws OpenAS2Exception { - super.init(session, parameters); - if (getName() == null) { - setName(getDefaultName()); - }; - if (getDescription() == null) { - setDescription(getDefaultDescription()); - } - if (getUsage() == null) { - setUsage(getDefaultUsage()); - } - } + public static final String PARAM_USAGE = "usage"; - public String getDescription() { - try { + public void init(Session session, Map parameters) throws OpenAS2Exception + { + super.init(session, parameters); + if (getName() == null) + { + setName(getDefaultName()); + } + if (getDescription() == null) + { + setDescription(getDefaultDescription()); + } + if (getUsage() == null) + { + setUsage(getDefaultUsage()); + } + } + + public String getDescription() + { + try + { return getParameter(PARAM_DESCRIPTION, false); - } catch (InvalidParameterException e) { + } catch (InvalidParameterException e) + { return null; } } - public String getName() { - try { - return getParameter(PARAM_NAME, false); - } catch (InvalidParameterException e) { - return null; - } + public void setDescription(String desc) + { + setParameter(PARAM_DESCRIPTION, desc); } - public String getUsage() { - try { - return getParameter(PARAM_USAGE, false); - } catch (InvalidParameterException e) { - return null; - } + public String getName() + { + try + { + return getParameter(PARAM_NAME, false); + } catch (InvalidParameterException e) + { + return null; + } } - - public abstract String getDefaultName(); - public abstract String getDefaultDescription(); - public abstract String getDefaultUsage(); - - public abstract CommandResult execute(Object[] params); - public void setDescription(String desc) { - setParameter(PARAM_DESCRIPTION, desc); + public void setName(String name) + { + setParameter(PARAM_NAME, name); } - public void setName(String name) { - setParameter(PARAM_NAME, name); + public String getUsage() + { + try + { + return getParameter(PARAM_USAGE, false); + } catch (InvalidParameterException e) + { + return null; + } } - public void setUsage(String usage) { + public void setUsage(String usage) + { setParameter(PARAM_USAGE, usage); } + + public abstract String getDefaultName(); + + public abstract String getDefaultDescription(); + + public abstract String getDefaultUsage(); + + public abstract CommandResult execute(Object[] params); } diff --git a/Server/src/main/java/org/openas2/cmd/BaseCommandRegistry.java b/Server/src/main/java/org/openas2/cmd/BaseCommandRegistry.java index 6eecf6c7..70c8f557 100644 --- a/Server/src/main/java/org/openas2/cmd/BaseCommandRegistry.java +++ b/Server/src/main/java/org/openas2/cmd/BaseCommandRegistry.java @@ -1,22 +1,21 @@ package org.openas2.cmd; -import java.util.ArrayList; +import java.util.LinkedList; import java.util.List; import org.openas2.BaseComponent; public class BaseCommandRegistry extends BaseComponent implements CommandRegistry { - private List commands; - - public List getCommands() { - if (commands == null) { - commands = new ArrayList(); - } - return commands; - } + private List commands = new LinkedList(); + + public List getCommands() + { + return commands; + } + + public void setCommands(List commands) + { + this.commands = commands; + } - public void setCommands(List commands) { - this.commands = commands; - } - } diff --git a/Server/src/main/java/org/openas2/cmd/CommandManager.java b/Server/src/main/java/org/openas2/cmd/CommandManager.java index 41fa9b7e..58bca817 100644 --- a/Server/src/main/java/org/openas2/cmd/CommandManager.java +++ b/Server/src/main/java/org/openas2/cmd/CommandManager.java @@ -3,41 +3,28 @@ import java.util.ArrayList; import java.util.List; +import org.openas2.OpenAS2Exception; import org.openas2.cmd.processor.BaseCommandProcessor; /** * command calls the registered command processors - * - * @author joseph mcverry * + * @author joseph mcverry */ public class CommandManager { - private static CommandManager defaultManager; - private List processors; - - public static CommandManager getCmdManager() { - if (defaultManager == null) { - defaultManager = new CommandManager(); - } - - return defaultManager; - } - - public void setProcessors(List listeners) { - this.processors = listeners; - } - - public List getProcessors() { - if (processors == null) { - processors = new ArrayList(); - } - return processors; - } + private List processors = new ArrayList(); - public void addProcessor(BaseCommandProcessor processor) { - List processors = getProcessors(); - processors.add(processor); - } + public void addProcessor(BaseCommandProcessor processor) + { + processors.add(processor); + } + public void registerCommands(CommandRegistry reg) throws OpenAS2Exception + { + for (BaseCommandProcessor processor : processors) + { + processor.addCommands(reg); + } + } } diff --git a/Server/src/main/java/org/openas2/cmd/CommandRegistryFactory.java b/Server/src/main/java/org/openas2/cmd/CommandRegistryFactory.java deleted file mode 100644 index 770c437b..00000000 --- a/Server/src/main/java/org/openas2/cmd/CommandRegistryFactory.java +++ /dev/null @@ -1,5 +0,0 @@ -package org.openas2.cmd; - -public interface CommandRegistryFactory { - public CommandRegistry getCommandRegistry(); -} diff --git a/Server/src/main/java/org/openas2/cmd/MultiCommand.java b/Server/src/main/java/org/openas2/cmd/MultiCommand.java index f4f7fc5d..bbfc218e 100644 --- a/Server/src/main/java/org/openas2/cmd/MultiCommand.java +++ b/Server/src/main/java/org/openas2/cmd/MultiCommand.java @@ -28,7 +28,7 @@ public Command getCommand(String name) { Command cmd; for (int i = 0; i < commands.size(); i++) { - cmd = (Command) commands.get(i); + cmd = commands.get(i); if (cmd.getName().equals(name)) { return cmd; diff --git a/Server/src/main/java/org/openas2/cmd/XMLCommandRegistry.java b/Server/src/main/java/org/openas2/cmd/XMLCommandRegistry.java index e4b4983d..fea92253 100644 --- a/Server/src/main/java/org/openas2/cmd/XMLCommandRegistry.java +++ b/Server/src/main/java/org/openas2/cmd/XMLCommandRegistry.java @@ -4,7 +4,6 @@ import java.io.IOException; import java.io.InputStream; import java.util.Map; - import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; @@ -12,6 +11,7 @@ import org.openas2.OpenAS2Exception; import org.openas2.Session; import org.openas2.WrappedException; +import org.openas2.XMLSession; import org.openas2.util.XMLUtil; import org.w3c.dom.Document; import org.w3c.dom.Element; @@ -23,14 +23,16 @@ public class XMLCommandRegistry extends BaseCommandRegistry { public static final String PARAM_FILENAME = "filename"; - public void init(Session session, Map parameters) throws OpenAS2Exception { + public void init(Session session, Map parameters) throws OpenAS2Exception + { super.init(session, parameters); refresh(); } public void load(InputStream in) - throws ParserConfigurationException, SAXException, IOException, OpenAS2Exception { + throws ParserConfigurationException, SAXException, IOException, OpenAS2Exception + { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder parser = factory.newDocumentBuilder(); @@ -42,46 +44,58 @@ public void load(InputStream in) getCommands().clear(); - for (int i = 0; i < rootNodes.getLength(); i++) { + for (int i = 0; i < rootNodes.getLength(); i++) + { rootNode = rootNodes.item(i); nodeName = rootNode.getNodeName(); - if (nodeName.equals("command")) { + if (nodeName.equals("command")) + { loadCommand(rootNode, null); - } else if (nodeName.equals("multicommand")) { + } else if (nodeName.equals("multicommand")) + { loadMultiCommand(rootNode, null); } } } - public void refresh() throws OpenAS2Exception { - try { + public void refresh() throws OpenAS2Exception + { + try + { load(new FileInputStream(getParameter(PARAM_FILENAME, true))); - } catch (Exception e) { + } catch (Exception e) + { throw new WrappedException(e); } } protected void loadCommand(Node node, MultiCommand parent) - throws OpenAS2Exception { - Command cmd = (Command) XMLUtil.getComponent(node, getSession()); + throws OpenAS2Exception + { + Command cmd = (Command) XMLUtil.getComponent(node, (XMLSession) getSession()); - if (parent != null) { + if (parent != null) + { parent.getCommands().add(cmd); - } else { + } else + { getCommands().add(cmd); } } protected void loadMultiCommand(Node node, MultiCommand parent) - throws OpenAS2Exception { + throws OpenAS2Exception + { MultiCommand cmd = new MultiCommand(); cmd.init(getSession(), XMLUtil.mapAttributes(node)); - if (parent != null) { + if (parent != null) + { parent.getCommands().add(cmd); - } else { + } else + { getCommands().add(cmd); } @@ -90,14 +104,17 @@ protected void loadMultiCommand(Node node, MultiCommand parent) Node childNode; String childName; - for (int i = 0; i < childCmds.getLength(); i++) { + for (int i = 0; i < childCmds.getLength(); i++) + { childNode = childCmds.item(i); childName = childNode.getNodeName(); - if (childName.equals("command")) { + if (childName.equals("command")) + { loadCommand(childNode, cmd); - } else if (childName.equals("multicommand")) { + } else if (childName.equals("multicommand")) + { loadMultiCommand(childNode, cmd); } } diff --git a/Server/src/main/java/org/openas2/cmd/processor/BaseCommandProcessor.java b/Server/src/main/java/org/openas2/cmd/processor/BaseCommandProcessor.java index 74506b71..f448f5e3 100644 --- a/Server/src/main/java/org/openas2/cmd/processor/BaseCommandProcessor.java +++ b/Server/src/main/java/org/openas2/cmd/processor/BaseCommandProcessor.java @@ -1,85 +1,114 @@ package org.openas2.cmd.processor; import java.util.ArrayList; -import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.concurrent.Callable; +import java.util.concurrent.ScheduledExecutorService; +import javax.annotation.Nullable; + +import org.apache.commons.lang3.ClassUtils; import org.openas2.Component; import org.openas2.OpenAS2Exception; import org.openas2.Session; import org.openas2.cmd.Command; import org.openas2.cmd.CommandRegistry; +import org.openas2.schedule.HasSchedule; -public abstract class BaseCommandProcessor extends Thread implements CommandProcessor, Component { - - public Map getParameters() { - // TODO Auto-generated method stub - return null; - } +public abstract class BaseCommandProcessor implements CommandProcessor, Component, HasSchedule { - public Session getSession() { - // TODO Auto-generated method stub - return null; - } + private static final Void VOID = null; + private List commands = new ArrayList(); + private Session session; + private Map parameters; + private boolean running = true; - public void init(Session session, Map parameters) throws OpenAS2Exception { - // TODO Auto-generated method stub - - } - private List commands; - private boolean terminated; - - public BaseCommandProcessor() { - super(); - terminated = false; + public List getCommands() + { + return commands; } - public void setCommands(List list) { - commands = list; + @Override + public String getName() + { + return ClassUtils.getSimpleName(getClass()); } - public List getCommands() { - if (commands == null) { - commands = new ArrayList(); - } + @Override + public void init(Session session, Map parameters) throws OpenAS2Exception + { + this.session = session; + this.parameters = parameters; + } - return commands; + @Override + public Map getParameters() + { + return parameters; } - - public Command getCommand(String name) { - Command currentCmd; - Iterator commandIt = getCommands().iterator(); - while (commandIt.hasNext()) { - currentCmd = commandIt.next(); - if (currentCmd.getName().equals(name)) { - return currentCmd; - } - } - return null; - } - - public boolean isTerminated() { - return terminated; + + @Override + public Session getSession() + { + return session; } - public void processCommand() throws OpenAS2Exception { - throw new OpenAS2Exception("super class method call, not initialized correctly"); + @Nullable + Command getCommand(String name) + { + Command currentCmd; + for (Command command : getCommands()) + { + currentCmd = command; + if (currentCmd.getName().equals(name)) + { + return currentCmd; + } + } + return null; } - - public void addCommands(CommandRegistry reg) { - ; + + public abstract void processCommand() throws Exception; + + public void addCommands(CommandRegistry reg) + { List regCmds = reg.getCommands(); - if (regCmds.size() > 0) { - getCommands().addAll(regCmds); + if (regCmds.size() > 0) + { + commands.addAll(regCmds); } } - public void terminate() { - terminated = true; + public void terminate() throws Exception + { + running = false; + getSession().stop(); + } + + @Override + public void destroy() throws Exception + { + running = false; + } + + @Override + public void schedule(ScheduledExecutorService executor) throws OpenAS2Exception + { + executor.submit(new Callable() { + @Override + public Void call() throws Exception + { + while (running) + { + processCommand(); + } + return VOID; + } + }); } } diff --git a/Server/src/main/java/org/openas2/cmd/processor/CommandProcessor.java b/Server/src/main/java/org/openas2/cmd/processor/CommandProcessor.java index eab40e89..47ddca82 100644 --- a/Server/src/main/java/org/openas2/cmd/processor/CommandProcessor.java +++ b/Server/src/main/java/org/openas2/cmd/processor/CommandProcessor.java @@ -3,24 +3,16 @@ import java.util.List; -import org.openas2.OpenAS2Exception; import org.openas2.cmd.Command; import org.openas2.cmd.CommandRegistry; - public interface CommandProcessor { - public List getCommands(); - - public boolean isTerminated(); - - public void addCommands(CommandRegistry reg); + List getCommands(); - public void deInit() throws OpenAS2Exception; + void addCommands(CommandRegistry reg); - public void init() throws OpenAS2Exception; + void terminate() throws Exception; - public void terminate(); - - public void processCommand() throws OpenAS2Exception; + void processCommand() throws Exception; } \ No newline at end of file diff --git a/Server/src/main/java/org/openas2/cmd/processor/SocketCommandProcessor.java b/Server/src/main/java/org/openas2/cmd/processor/SocketCommandProcessor.java index 481a8889..c70162f8 100644 --- a/Server/src/main/java/org/openas2/cmd/processor/SocketCommandProcessor.java +++ b/Server/src/main/java/org/openas2/cmd/processor/SocketCommandProcessor.java @@ -5,6 +5,7 @@ import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; +import java.net.SocketException; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -13,193 +14,194 @@ import javax.net.ssl.SSLServerSocketFactory; import javax.net.ssl.SSLSocket; +import org.apache.commons.io.IOUtils; import org.openas2.OpenAS2Exception; import org.openas2.Session; +import org.openas2.WrappedException; import org.openas2.cmd.Command; import org.openas2.cmd.CommandResult; import org.openas2.util.CommandTokenizer; -import org.xml.sax.SAXException; -/** actual socket command processor - * takes commands from socket/port and passes them to the OpenAS2Server - * message format - * the actual command - * +/** + * actual socket command processor + * takes commands from socket/port and passes them to the OpenAS2Server + * message format + * the actual command + *

* when inited the valid userid and password is passed, then as each * command is processed the processCommand method verifies the two fields correctness - * - * @author joseph mcverry * + * @author joseph mcverry */ -public class SocketCommandProcessor extends BaseCommandProcessor - implements - Runnable { - - private BufferedReader rdr = null; - private BufferedWriter wrtr = null; - private SSLServerSocket sslserversocket = null; - - private String userid, password; - - public SocketCommandProcessor() { - } - - public void deInit() throws OpenAS2Exception { - } - - SocketCommandParser parser; - - public void init() throws OpenAS2Exception { - } - - public void init(Session session, Map parameters) throws OpenAS2Exception { - String p = (String) parameters.get("portid"); - try { - int port = Integer.parseInt(p); - - SSLServerSocketFactory sslserversocketfactory = - (SSLServerSocketFactory) SSLServerSocketFactory.getDefault(); - sslserversocket = - (SSLServerSocket) sslserversocketfactory.createServerSocket(port); - String cipherSuites = System.getProperty("CmdProcessorSocketCipher", "TLS_DH_anon_WITH_AES_256_CBC_SHA"); - final String[] enabledCipherSuites = { cipherSuites }; - try - { - sslserversocket.setEnabledCipherSuites(enabledCipherSuites); - } catch (IllegalArgumentException e) - { - throw new OpenAS2Exception( - "Cipher is not supported. Use command line switch -DCmdProcessorSocketCipher= to use one supported by your version of java security." - , e); - } - - - } catch (IOException e) { - e.printStackTrace(); - throw new OpenAS2Exception(e); - } catch (NumberFormatException e) { - e.printStackTrace(); - throw new OpenAS2Exception("error converting portid parameter " + e); - } - userid = (String) parameters.get("userid"); - if (userid == null || userid.length() < 1) - throw new OpenAS2Exception("missing userid parameter"); - - password = (String) parameters.get("password"); - if (password == null || password.length() < 1) - throw new OpenAS2Exception("missing password parameter"); - - try { - parser = new SocketCommandParser(); - } catch (Exception e) { - // TODO Auto-generated catch block - new OpenAS2Exception(e); - } - } - - public void processCommand() throws OpenAS2Exception { - - SSLSocket socket = null; - try { - socket = (SSLSocket) sslserversocket.accept(); - socket.setSoTimeout(2000); - rdr = new BufferedReader(new InputStreamReader(socket.getInputStream())); - wrtr = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())); - - String line; - line = rdr.readLine(); - - parser.parse(line); - - if (parser.getUserid().equals(userid) == false) { - wrtr.write("Bad userid/password"); - throw new OpenAS2Exception("Bad userid"); - } - - if (parser.getPassword().equals(password) == false) { - wrtr.write("Bad userid/password"); - throw new OpenAS2Exception("Bad password"); - } - - String str = parser.getCommandText(); - if (str != null && str.length() > 0) { - CommandTokenizer cmdTkn = new CommandTokenizer(str); - - if (cmdTkn.hasMoreTokens()) { - String commandName = cmdTkn.nextToken().toLowerCase(); - - if (commandName.equals(StreamCommandProcessor.EXIT_COMMAND)) { - terminate(); - } else { - List params = new ArrayList(); - - while (cmdTkn.hasMoreTokens()) { - params.add(cmdTkn.nextToken()); - } - - Command cmd = getCommand(commandName); - - if (cmd != null) { - CommandResult result = cmd.execute(params.toArray()); - - if (result.getType() == CommandResult.TYPE_OK) { - wrtr.write(result.toXML()); - } else { - wrtr.write("\r\n" +StreamCommandProcessor.COMMAND_ERROR + "\r\n"); - wrtr.write(result.getResult()); - } - } else { - wrtr.write(StreamCommandProcessor.COMMAND_NOT_FOUND - + "> " + commandName + "\r\n"); - List l = getCommands(); - wrtr.write("List of commands:" + "\r\n"); - for (int i = 0; i < l.size(); i++) { - cmd = l.get(i); - wrtr.write(cmd.getName() + "\r\n"); - } - } - } - } - - - } - wrtr.flush(); - } catch (IOException ioe) { - ioe.printStackTrace(); - } catch (SAXException e) { - try { - if (socket != null) - socket.close(); - } catch (IOException e1) { - } - new OpenAS2Exception(e); - } catch (Exception e) { - new OpenAS2Exception(e); - } - finally { - if (socket != null) - try { - socket.close(); - } catch (IOException e) { - ; - } - - } - - } - - /* (non-Javadoc) - * @see java.lang.Runnable#run() - */ - public void run() { - try { - while (true) { - processCommand(); - } - } catch (OpenAS2Exception e) { - e.printStackTrace(); - } - - } - +public class SocketCommandProcessor extends BaseCommandProcessor { + + SocketCommandParser parser; + private BufferedReader rdr = null; + private BufferedWriter wrtr = null; + private SSLServerSocket sslserversocket = null; + private String userid, password; + + public void init(Session session, Map parameters) throws OpenAS2Exception + { + String p = parameters.get("portid"); + try + { + int port = Integer.parseInt(p); + + SSLServerSocketFactory sslserversocketfactory = + (SSLServerSocketFactory) SSLServerSocketFactory.getDefault(); + sslserversocket = + (SSLServerSocket) sslserversocketfactory.createServerSocket(port); + String cipherSuites = System.getProperty("CmdProcessorSocketCipher", "TLS_DH_anon_WITH_AES_256_CBC_SHA"); + final String[] enabledCipherSuites = {cipherSuites}; + try + { + sslserversocket.setEnabledCipherSuites(enabledCipherSuites); + } catch (IllegalArgumentException e) + { + throw new OpenAS2Exception( + "Cipher is not supported. Use command line switch -DCmdProcessorSocketCipher= to use one supported by your version of java security." + , e); + } + + + } catch (IOException e) + { + e.printStackTrace(); + throw new OpenAS2Exception(e); + } catch (NumberFormatException e) + { + e.printStackTrace(); + throw new OpenAS2Exception("error converting portid parameter " + e); + } + userid = (String) parameters.get("userid"); + if (userid == null || userid.length() < 1) + { + throw new OpenAS2Exception("missing userid parameter"); + } + + password = (String) parameters.get("password"); + if (password == null || password.length() < 1) + { + throw new OpenAS2Exception("missing password parameter"); + } + + try + { + parser = new SocketCommandParser(); + } catch (Exception e) + { + // TODO Auto-generated catch block + new OpenAS2Exception(e); + } + } + + public void processCommand() throws OpenAS2Exception + { + + SSLSocket socket = null; + try + { + socket = (SSLSocket) sslserversocket.accept(); + socket.setSoTimeout(2000); + rdr = new BufferedReader(new InputStreamReader(socket.getInputStream())); + wrtr = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())); + + String line; + line = rdr.readLine(); + + parser.parse(line); + + if (parser.getUserid().equals(userid) == false) + { + wrtr.write("Bad userid/password"); + throw new OpenAS2Exception("Bad userid"); + } + + if (parser.getPassword().equals(password) == false) + { + wrtr.write("Bad userid/password"); + throw new OpenAS2Exception("Bad password"); + } + + String str = parser.getCommandText(); + if (str != null && str.length() > 0) + { + CommandTokenizer cmdTkn = new CommandTokenizer(str); + + if (cmdTkn.hasMoreTokens()) + { + String commandName = cmdTkn.nextToken().toLowerCase(); + + if (commandName.equals(StreamCommandProcessor.EXIT_COMMAND)) + { + terminate(); + } else + { + List params = new ArrayList(); + + while (cmdTkn.hasMoreTokens()) + { + params.add(cmdTkn.nextToken()); + } + + Command cmd = getCommand(commandName); + + if (cmd != null) + { + CommandResult result = cmd.execute(params.toArray()); + + if (result.getType() == CommandResult.TYPE_OK) + { + wrtr.write(result.toXML()); + } else + { + wrtr.write("\r\n" + StreamCommandProcessor.COMMAND_ERROR + "\r\n"); + wrtr.write(result.getResult()); + } + } else + { + wrtr.write(StreamCommandProcessor.COMMAND_NOT_FOUND + + "> " + commandName + "\r\n"); + List l = getCommands(); + wrtr.write("List of commands:" + "\r\n"); + for (int i = 0; i < l.size(); i++) + { + cmd = l.get(i); + wrtr.write(cmd.getName() + "\r\n"); + } + } + } + } + + + } + wrtr.flush(); + } catch (SocketException socketError) + { + // shutdown case + if (!sslserversocket.isClosed()) + { + throw new WrappedException(socketError); + } + } catch (IOException e) + { + //nothing + } catch (Exception e) + { + //nothing + } finally + { + IOUtils.closeQuietly(socket); + } + + } + + @Override + public void destroy() throws Exception + { + IOUtils.closeQuietly(sslserversocket); // closes remote session + super.destroy(); + + } } diff --git a/Server/src/main/java/org/openas2/cmd/processor/StreamCommandProcessor.java b/Server/src/main/java/org/openas2/cmd/processor/StreamCommandProcessor.java index 99f406ec..db57cc80 100644 --- a/Server/src/main/java/org/openas2/cmd/processor/StreamCommandProcessor.java +++ b/Server/src/main/java/org/openas2/cmd/processor/StreamCommandProcessor.java @@ -8,7 +8,7 @@ import java.util.ArrayList; import java.util.List; -import org.openas2.OpenAS2Exception; +import org.apache.commons.io.IOUtils; import org.openas2.WrappedException; import org.openas2.cmd.Command; import org.openas2.cmd.CommandResult; @@ -16,131 +16,137 @@ /** * original author unknown - * + *

* in this release made the process a thread so it can be shared with other command processors like * the SocketCommandProcessor * created innerclass CommandTokenizer so it could handle quotes and spaces within quotes - * @author joseph mcverry * + * @author joseph mcverry */ -public class StreamCommandProcessor extends BaseCommandProcessor - implements - Runnable { - public static final String COMMAND_NOT_FOUND = "Error: command not found"; - public static final String COMMAND_ERROR = "Error executing command"; - public static final String EXIT_COMMAND = "exit"; - public static final String PROMPT = "#>"; - private BufferedReader reader = null; - private BufferedWriter writer = null; - - public StreamCommandProcessor() { - reader = new BufferedReader(new InputStreamReader(System.in)); - writer = new BufferedWriter(new OutputStreamWriter(System.out)); - } - - public BufferedReader getReader() { - return reader; - } - - public BufferedWriter getWriter() { - return writer; - } - - public void deInit() throws OpenAS2Exception { - } - - public void init() throws OpenAS2Exception { - } - - /* (non-Javadoc) - * @see java.lang.Runnable#run() - */ - public void run() { - try { - while (true) - processCommand(); - - } catch (OpenAS2Exception e) { - e.printStackTrace(); - } - - } - - public void processCommand() throws OpenAS2Exception { - try { - - String str = readLine(); - - if (str != null) { - CommandTokenizer strTkn = new CommandTokenizer(str); - - if (strTkn.hasMoreTokens()) { - String commandName = strTkn.nextToken().toLowerCase(); - - if (commandName.equals(EXIT_COMMAND)) { - terminate(); - } else { - List params = new ArrayList(); - - while (strTkn.hasMoreTokens()) { - - params.add(strTkn.nextToken()); - } - - Command cmd = getCommand(commandName); - - if (cmd != null) { - CommandResult result = cmd - .execute(params.toArray()); - - if (result.getType() == CommandResult.TYPE_OK) { - writeLine(result.toString()); - } else { - writeLine(COMMAND_ERROR); - writeLine(result.getResult()); - } - } else { - writeLine(COMMAND_NOT_FOUND + "> " + commandName); - List l = getCommands(); - writeLine("List of commands:"); - writeLine(EXIT_COMMAND); - for (int i = 0; i < l.size(); i++) { - cmd = l.get(i); - writeLine(cmd.getName()); - } - } - } - } - - write(PROMPT); - } else { - try { - Thread.sleep(100); - } catch (InterruptedException e) { - } - } - - } catch (IOException ioe) { - throw new WrappedException(ioe); - } - } - - public String readLine() throws java.io.IOException { - BufferedReader rd = getReader(); - - return rd.readLine().trim(); - } - - public void write(String text) throws java.io.IOException { - BufferedWriter wr = getWriter(); - wr.write(text); - wr.flush(); - } - - public void writeLine(String line) throws java.io.IOException { - BufferedWriter wr = getWriter(); - wr.write(line + "\r\n"); - wr.flush(); - } - +public class StreamCommandProcessor extends BaseCommandProcessor { + public static final String COMMAND_NOT_FOUND = "Error: command not found"; + public static final String COMMAND_ERROR = "Error executing command"; + public static final String EXIT_COMMAND = "exit"; + public static final String PROMPT = "#>"; + private BufferedReader reader = null; + private BufferedWriter writer = null; + + public StreamCommandProcessor() + { + reader = new BufferedReader(new InputStreamReader(System.in)); + writer = new BufferedWriter(new OutputStreamWriter(System.out)); + } + + public BufferedReader getReader() + { + return reader; + } + + public BufferedWriter getWriter() + { + return writer; + } + + public void processCommand() throws Exception + { + try + { + + String str = readLine(); + + if (str != null) + { + CommandTokenizer strTkn = new CommandTokenizer(str); + + if (strTkn.hasMoreTokens()) + { + String commandName = strTkn.nextToken().toLowerCase(); + + if (commandName.equals(EXIT_COMMAND)) + { + terminate(); + } else + { + List params = new ArrayList(); + + while (strTkn.hasMoreTokens()) + { + + params.add(strTkn.nextToken()); + } + + Command cmd = getCommand(commandName); + + if (cmd != null) + { + CommandResult result = cmd + .execute(params.toArray()); + + if (result.getType() == CommandResult.TYPE_OK) + { + writeLine(result.toString()); + } else + { + writeLine(COMMAND_ERROR); + writeLine(result.getResult()); + } + } else + { + writeLine(COMMAND_NOT_FOUND + "> " + commandName); + List l = getCommands(); + writeLine("List of commands:"); + writeLine(EXIT_COMMAND); + for (int i = 0; i < l.size(); i++) + { + cmd = l.get(i); + writeLine(cmd.getName()); + } + } + } + } + + write(PROMPT); + } else + { + try + { + Thread.sleep(100); + } catch (InterruptedException e) + { + } + } + + } catch (IOException ioe) + { + throw new WrappedException(ioe); + } + } + + public String readLine() throws java.io.IOException + { + BufferedReader rd = getReader(); + + return rd.readLine().trim(); + } + + public void write(String text) throws java.io.IOException + { + BufferedWriter wr = getWriter(); + wr.write(text); + wr.flush(); + } + + public void writeLine(String line) throws java.io.IOException + { + BufferedWriter wr = getWriter(); + wr.write(line + "\r\n"); + wr.flush(); + } + + @Override + public void destroy() throws Exception + { + IOUtils.closeQuietly(reader); // stops terminal + super.destroy(); + } } diff --git a/Server/src/main/java/org/openas2/database/H2DBHandler.java b/Server/src/main/java/org/openas2/database/H2DBHandler.java deleted file mode 100644 index 0864b4c8..00000000 --- a/Server/src/main/java/org/openas2/database/H2DBHandler.java +++ /dev/null @@ -1,112 +0,0 @@ -package org.openas2.database; - -import java.sql.Connection; -import java.sql.DriverManager; -import java.sql.SQLException; -import java.sql.Statement; -import java.util.concurrent.TimeUnit; - -import org.h2.jdbcx.JdbcConnectionPool; -import org.openas2.OpenAS2Exception; - -public class H2DBHandler implements IDBHandler -{ - JdbcConnectionPool cp = null; - - private String jdbcDriver = "org.h2.Driver"; - - private String connectString = "jdbc:h2:file:DB/openas2"; - - public H2DBHandler() - { - } - - /** - * @param jdbcDriver - */ - public H2DBHandler(String jdbcDriver) - { - setJdbcDriver(jdbcDriver); - } - - /** - * @param jdbcDriver - */ - public void setJdbcDriver(String jdbcDriver) - { - this.jdbcDriver = jdbcDriver; - } - - public void createConnectionPool(String connectString, String userName, String pwd) throws OpenAS2Exception - { - // Check that a connection pool is not already running - if (cp != null) - { - throw new OpenAS2Exception( - "Connection pool already initialized. Cannot create a new connection pool. Stop current one first. DB connect string:" - + connectString + " :: Active pool connect string: " + this.connectString); - } - this.connectString = connectString; - // Load the Database Engine JDBC driver - // Class.forName(jdbcDriver); - - cp = JdbcConnectionPool.create(connectString, userName, pwd); - } - - public void destroyConnectionPool() - { - if (cp == null) - return; - cp.dispose(); - cp = null; - } - - public Connection getConnection() throws SQLException, OpenAS2Exception - { - // Check that a connection pool is running - if (cp == null) - { - throw new OpenAS2Exception("Connection pool not initialized."); - } - return cp.getConnection(); - } - - public Connection connect(String connectString, String userName, String password) throws Exception - { - - // Load the Database Engine JDBC driver - Class.forName(jdbcDriver); - try - { - - return DriverManager.getConnection(connectString, userName, password); - } catch (SQLException e) - { - throw new Exception("Failed to obtain connection to database: " + connectString, e); - } - } - - public boolean shutdown(String connectString) throws SQLException, OpenAS2Exception - { - // Wait briefly if there are active connections - int waitCount = 0; - try - { - while (cp.getActiveConnections() > 0 && waitCount < 10) - { - TimeUnit.MILLISECONDS.sleep(100); - waitCount++; - } - } catch (InterruptedException e) - { - // Do nothing - } - Connection c = getConnection(); - Statement st = c.createStatement(); - - boolean result = st.execute("SHUTDOWN"); - c.close(); - return result; - } - -} diff --git a/Server/src/main/java/org/openas2/database/IDBHandler.java b/Server/src/main/java/org/openas2/database/IDBHandler.java deleted file mode 100644 index a12d52b4..00000000 --- a/Server/src/main/java/org/openas2/database/IDBHandler.java +++ /dev/null @@ -1,21 +0,0 @@ -package org.openas2.database; - -import java.sql.Connection; -import java.sql.SQLException; - -import org.openas2.OpenAS2Exception; - -public interface IDBHandler -{ - public void setJdbcDriver(String jdbcDriver); - - public void createConnectionPool(String connectString, String userName, String pwd) throws OpenAS2Exception; - - public void destroyConnectionPool(); - - public Connection getConnection() throws SQLException, OpenAS2Exception; - - public Connection connect(String connectString, String userName, String password) throws Exception; - - public boolean shutdown(String connectString) throws SQLException, OpenAS2Exception; -} diff --git a/Server/src/main/java/org/openas2/lib/Info.java b/Server/src/main/java/org/openas2/lib/Info.java deleted file mode 100644 index 02e26f1a..00000000 --- a/Server/src/main/java/org/openas2/lib/Info.java +++ /dev/null @@ -1,7 +0,0 @@ -package org.openas2.lib; - -public class Info { - public static final String NAME = "OpenAS2"; - public static final String VERSION = "v1.0"; - public static final String NAME_VERSION = NAME + " " + VERSION; -} diff --git a/Server/src/main/java/org/openas2/lib/MDNEngine.java b/Server/src/main/java/org/openas2/lib/MDNEngine.java index af967b62..8ffe218d 100644 --- a/Server/src/main/java/org/openas2/lib/MDNEngine.java +++ b/Server/src/main/java/org/openas2/lib/MDNEngine.java @@ -23,6 +23,7 @@ import org.openas2.lib.partner.IPartnershipChooser; import org.openas2.message.Message; import org.openas2.partner.Partnership; +import org.openas2.util.Properties; public class MDNEngine { private EDIINTHelper ediintHelper; @@ -97,7 +98,7 @@ protected AS2MessageMDN createAS2MDN(AS2Message msg, EngineResults results) thro // generate the MDN data MDNData mdnData = mdn.getMDNData(); - mdnData.setReportingUA(Info.NAME_VERSION); + mdnData.setReportingUA(Properties.getProperty(Properties.APP_TITLE_PROP, "OpenAS2 Server")); mdnData.setOriginalRecipient("rfc822; " + msg.getAS2To()); if (results.getPartnership() != null) { mdnData.setFinalRecipient("rfc822; " diff --git a/Server/src/main/java/org/openas2/lib/helper/BCCryptoHelper.java b/Server/src/main/java/org/openas2/lib/helper/BCCryptoHelper.java index 6a25be94..29916509 100644 --- a/Server/src/main/java/org/openas2/lib/helper/BCCryptoHelper.java +++ b/Server/src/main/java/org/openas2/lib/helper/BCCryptoHelper.java @@ -31,6 +31,7 @@ import javax.mail.internet.MimeBodyPart; import javax.mail.internet.MimeMultipart; +import org.apache.commons.io.IOUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.bouncycastle.asn1.ASN1ObjectIdentifier; @@ -81,7 +82,6 @@ import org.openas2.DispositionException; import org.openas2.OpenAS2Exception; import org.openas2.Session; -import org.openas2.lib.util.IOUtil; import org.openas2.message.AS2Message; import org.openas2.message.Message; import org.openas2.processor.receiver.AS2ReceiverModule; @@ -89,91 +89,120 @@ import org.openas2.util.DispositionType; public class BCCryptoHelper implements ICryptoHelper { - private Log logger = LogFactory.getLog(BCCryptoHelper.class.getSimpleName()); + private Log logger = LogFactory.getLog(BCCryptoHelper.class.getSimpleName()); - public boolean isEncrypted(MimeBodyPart part) throws MessagingException { + public boolean isEncrypted(MimeBodyPart part) throws MessagingException + { ContentType contentType = new ContentType(part.getContentType()); String baseType = contentType.getBaseType().toLowerCase(); - if (baseType.equalsIgnoreCase("application/pkcs7-mime")) { + if (baseType.equalsIgnoreCase("application/pkcs7-mime")) + { String smimeType = contentType.getParameter("smime-type"); boolean checkResult = (smimeType != null) && smimeType.equalsIgnoreCase("enveloped-data"); if (!checkResult && logger.isDebugEnabled()) - logger.debug("Check for encrypted data failed on SMIME content type: " + smimeType); + { + logger.debug("Check for encrypted data failed on SMIME content type: " + smimeType); + } return (checkResult); } - if (logger.isDebugEnabled()) logger.debug("Check for encrypted data failed on BASE content type: " + baseType); + if (logger.isDebugEnabled()) + { + logger.debug("Check for encrypted data failed on BASE content type: " + baseType); + } return false; } - public boolean isSigned(MimeBodyPart part) throws MessagingException { + public boolean isSigned(MimeBodyPart part) throws MessagingException + { ContentType contentType = new ContentType(part.getContentType()); String baseType = contentType.getBaseType().toLowerCase(); return baseType.equalsIgnoreCase("multipart/signed"); } - public boolean isCompressed(MimeBodyPart part) throws MessagingException { + public boolean isCompressed(MimeBodyPart part) throws MessagingException + { ContentType contentType = new ContentType(part.getContentType()); String baseType = contentType.getBaseType().toLowerCase(); - if (logger.isTraceEnabled()) - { - try - { - logger.trace("Compression check. MIME Base Content-Type:" + contentType.getBaseType()); - logger.trace("Compression check. SMIME-TYPE:" + contentType.getParameter("smime-type")); - logger.trace("Compressed MIME msg AFTER COMPRESSION Content-Disposition:" + part.getDisposition()); - } catch (MessagingException e) - { - // TODO Auto-generated catch block - e.printStackTrace(); - } - } - if (baseType.equalsIgnoreCase("application/pkcs7-mime")) { + if (logger.isTraceEnabled()) + { + try + { + logger.trace("Compression check. MIME Base Content-Type:" + contentType.getBaseType()); + logger.trace("Compression check. SMIME-TYPE:" + contentType.getParameter("smime-type")); + logger.trace("Compressed MIME msg AFTER COMPRESSION Content-Disposition:" + part.getDisposition()); + } catch (MessagingException e) + { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + if (baseType.equalsIgnoreCase("application/pkcs7-mime")) + { String smimeType = contentType.getParameter("smime-type"); boolean checkResult = (smimeType != null) && smimeType.equalsIgnoreCase("compressed-data"); if (!checkResult && logger.isDebugEnabled()) - logger.debug("Check for compressed data failed on SMIME content type: " + smimeType); + { + logger.debug("Check for compressed data failed on SMIME content type: " + smimeType); + } return (checkResult); } - if (logger.isDebugEnabled()) logger.debug("Check for compressed data failed on BASE content type: " + baseType); + if (logger.isDebugEnabled()) + { + logger.debug("Check for compressed data failed on BASE content type: " + baseType); + } return false; } public String calculateMIC(MimeBodyPart part, String digest, boolean includeHeaders) - throws GeneralSecurityException, MessagingException, IOException + throws GeneralSecurityException, MessagingException, IOException { - return calculateMIC(part, digest, includeHeaders, false); + return calculateMIC(part, digest, includeHeaders, false); } public String calculateMIC(MimeBodyPart part, String digest, boolean includeHeaders, boolean noCanonicalize) - throws GeneralSecurityException, MessagingException, IOException { + throws GeneralSecurityException, MessagingException, IOException + { String micAlg = convertAlgorithm(digest, true); if (logger.isDebugEnabled()) - logger.debug("Calc MIC called with digest: " + digest + " ::: Incl headers? " + includeHeaders - + " ::: Prevent canonicalization: " + noCanonicalize + " ::: Encoding: " + part.getEncoding()); + { + logger.debug("Calc MIC called with digest: " + digest + " ::: Incl headers? " + includeHeaders + + " ::: Prevent canonicalization: " + noCanonicalize + " ::: Encoding: " + part.getEncoding()); + } MessageDigest md = MessageDigest.getInstance(micAlg, "BC"); - if (includeHeaders && logger.isTraceEnabled()) { - logger.trace("Calculating MIC on MIMEPART Headers: " + AS2Util.printHeaders(part.getAllHeaders())); + if (includeHeaders && logger.isTraceEnabled()) + { + logger.trace("Calculating MIC on MIMEPART Headers: " + AS2Util.printHeaders(part.getAllHeaders())); } // convert the Mime data to a byte array, then to an InputStream ByteArrayOutputStream bOut = new ByteArrayOutputStream(); - + // Canonicalize the data if not binary content transfer encoding - OutputStream os = null; + OutputStream os = null; String encoding = part.getEncoding(); // Default encoding in case the bodypart does not have a transfer encoding set - if (encoding == null) encoding = Session.DEFAULT_CONTENT_TRANSFER_ENCODING; - if ("binary".equals(encoding) || noCanonicalize) os = bOut; - else os = new CRLFOutputStream(bOut); - - if (includeHeaders) { + if (encoding == null) + { + encoding = Session.DEFAULT_CONTENT_TRANSFER_ENCODING; + } + if ("binary".equals(encoding) || noCanonicalize) + { + os = bOut; + } else + { + os = new CRLFOutputStream(bOut); + } + + if (includeHeaders) + { part.writeTo(os); - } else { - IOUtil.copy(part.getInputStream(), os); + } else + { + IOUtils.copy(part.getInputStream(), os); } byte[] data = bOut.toByteArray(); @@ -185,7 +214,8 @@ public String calculateMIC(MimeBodyPart part, String digest, boolean includeHead byte[] buf = new byte[4096]; - while (digIn.read(buf) >= 0) { + while (digIn.read(buf) >= 0) + { } bOut.close(); @@ -200,9 +230,11 @@ public String calculateMIC(MimeBodyPart part, String digest, boolean includeHead public MimeBodyPart decrypt(MimeBodyPart part, Certificate cert, Key key) throws GeneralSecurityException, MessagingException, CMSException, IOException, - SMIMEException { + SMIMEException + { // Make sure the data is encrypted - if (!isEncrypted(part)) { + if (!isEncrypted(part)) + { throw new GeneralSecurityException("Content-Type indicates data isn't encrypted"); } @@ -214,76 +246,80 @@ public MimeBodyPart decrypt(MimeBodyPart part, Certificate cert, Key key) // Get the recipient object for decryption if (logger.isDebugEnabled()) - logger.debug("Extracted X500 info:: PRINCIPAL : " + x509Cert.getIssuerX500Principal() - + " :: NAME : " + x509Cert.getIssuerX500Principal().getName()); + { + logger.debug("Extracted X500 info:: PRINCIPAL : " + x509Cert.getIssuerX500Principal() + + " :: NAME : " + x509Cert.getIssuerX500Principal().getName()); + } X500Name x500Name = new X500Name(x509Cert.getIssuerX500Principal().getName()); - KeyTransRecipientId certRecId = new KeyTransRecipientId(x500Name,x509Cert.getSerialNumber()); + KeyTransRecipientId certRecId = new KeyTransRecipientId(x500Name, x509Cert.getSerialNumber()); RecipientInformationStore recipientInfoStore = envelope.getRecipientInfos(); - + Collection recipients = recipientInfoStore.getRecipients(); - if (recipients == null) { + if (recipients == null) + { throw new GeneralSecurityException("Certificate recipients could not be extracted"); } //RecipientInformation recipientInfo = recipientInfoStore.get(recId); //Object recipient = null; - + boolean foundRecipient = false; - for (Iterator iterator = recipients.iterator(); iterator.hasNext();) - { - RecipientInformation recipientInfo = iterator.next(); - //recipient = iterator.next(); - if (recipientInfo instanceof KeyTransRecipientInformation) { - // X509CertificateHolder x509CertHolder = new X509CertificateHolder(x509Cert.getEncoded()); - - //RecipientId rid = recipientInfo.getRID(); - if (certRecId.match(recipientInfo) && !foundRecipient) { - foundRecipient = true; - // byte[] decryptedData = recipientInfo.getContent(new JceKeyTransEnvelopedRecipient((PrivateKey)key).setProvider("BC")); - byte[] decryptedData = recipientInfo.getContent( - new BcRSAKeyTransEnvelopedRecipient(PrivateKeyFactory.createKey(PrivateKeyInfo.getInstance(key.getEncoded())))); - - return SMIMEUtil.toMimeBodyPart(decryptedData); - } - else - { - if (logger.isDebugEnabled()) - logger.debug("Failed match on recipient ID's:\n RID from msg:" - + recipientInfo.getRID().toString() + " \n RID from priv cert: " + certRecId.toString()); - } - } + for (Iterator iterator = recipients.iterator(); iterator.hasNext(); ) + { + RecipientInformation recipientInfo = iterator.next(); + //recipient = iterator.next(); + if (recipientInfo instanceof KeyTransRecipientInformation) + { + // X509CertificateHolder x509CertHolder = new X509CertificateHolder(x509Cert.getEncoded()); + + //RecipientId rid = recipientInfo.getRID(); + if (certRecId.match(recipientInfo) && !foundRecipient) + { + foundRecipient = true; + // byte[] decryptedData = recipientInfo.getContent(new JceKeyTransEnvelopedRecipient((PrivateKey)key).setProvider("BC")); + byte[] decryptedData = recipientInfo.getContent( + new BcRSAKeyTransEnvelopedRecipient(PrivateKeyFactory.createKey(PrivateKeyInfo.getInstance(key.getEncoded())))); + + return SMIMEUtil.toMimeBodyPart(decryptedData); + } else + { + if (logger.isDebugEnabled()) + { + logger.debug("Failed match on recipient ID's:\n RID from msg:" + + recipientInfo.getRID().toString() + " \n RID from priv cert: " + certRecId.toString()); + } + } + } } throw new GeneralSecurityException("Matching certificate recipient could not be found"); } - public void deinitialize() { + public void deinitialize() + { } public MimeBodyPart encrypt(MimeBodyPart part, Certificate cert, String algorithm, String contentTxfrEncoding) - throws GeneralSecurityException, SMIMEException, MessagingException { + throws GeneralSecurityException, SMIMEException, MessagingException + { X509Certificate x509Cert = castCertificate(cert); - - + + SMIMEEnvelopedGenerator gen = new SMIMEEnvelopedGenerator(); - gen.setContentTransferEncoding(getEncoding(contentTxfrEncoding)); + gen.setContentTransferEncoding(getEncoding(contentTxfrEncoding)); if (logger.isDebugEnabled()) { - logger.debug("Encrypting on MIME part containing the following headers: " + AS2Util.printHeaders(part.getAllHeaders())); + logger.debug("Encrypting on MIME part containing the following headers: " + AS2Util.printHeaders(part.getAllHeaders())); } gen.addRecipientInfoGenerator(new JceKeyTransRecipientInfoGenerator(x509Cert).setProvider("BC")); - MimeBodyPart encData = gen.generate(part, getOutputEncryptor(algorithm)); - - //TODO: Check if this gc call makes sense - System.gc(); - - return encData; + return gen.generate(part, getOutputEncryptor(algorithm)); } - public void initialize() { + public void initialize() + { Security.addProvider(new BouncyCastleProvider()); MailcapCommandMap mc = (MailcapCommandMap) CommandMap.getDefaultCommandMap(); @@ -296,43 +332,50 @@ public void initialize() { } public MimeBodyPart sign(MimeBodyPart part, Certificate cert, Key key, String digest, String contentTxfrEncoding - , boolean adjustDigestToOldName, boolean isRemoveCmsAlgorithmProtectionAttr) - throws GeneralSecurityException, SMIMEException, MessagingException { + , boolean adjustDigestToOldName, boolean isRemoveCmsAlgorithmProtectionAttr) + throws GeneralSecurityException, SMIMEException, MessagingException + { //String signDigest = convertAlgorithm(digest, true); X509Certificate x509Cert = castCertificate(cert); PrivateKey privKey = castKey(key); String encryptAlg = cert.getPublicKey().getAlgorithm(); SMIMESignedGenerator sGen = new SMIMESignedGenerator(); - sGen.setContentTransferEncoding(getEncoding(contentTxfrEncoding)); + sGen.setContentTransferEncoding(getEncoding(contentTxfrEncoding)); SignerInfoGenerator sig; - try { + try + { if (logger.isDebugEnabled()) { - logger.debug("Params for creating SMIME signed generator:: SIGN DIGEST: " + digest - + " PUB ENCRYPT ALG: " + encryptAlg - + " X509 CERT: " + x509Cert); - logger.debug("Signing on MIME part containing the following headers: " + AS2Util.printHeaders(part.getAllHeaders())); + logger.debug("Params for creating SMIME signed generator:: SIGN DIGEST: " + digest + + " PUB ENCRYPT ALG: " + encryptAlg + + " X509 CERT: " + x509Cert); + logger.debug("Signing on MIME part containing the following headers: " + AS2Util.printHeaders(part.getAllHeaders())); } // Remove the dash for SHA based digest for signing call - if (digest.toUpperCase().startsWith("SHA-")) digest = digest.replaceAll("-", ""); + if (digest.toUpperCase().startsWith("SHA-")) + { + digest = digest.replaceAll("-", ""); + } JcaSimpleSignerInfoGeneratorBuilder jSig = new JcaSimpleSignerInfoGeneratorBuilder().setProvider("BC"); - sig = jSig.build(digest+"with"+encryptAlg, privKey, x509Cert); + sig = jSig.build(digest + "with" + encryptAlg, privKey, x509Cert); // Some AS2 systems cannot handle certain OID's ... - if (isRemoveCmsAlgorithmProtectionAttr) - { - final CMSAttributeTableGenerator sAttrGen = sig.getSignedAttributeTableGenerator(); - sig = new SignerInfoGenerator(sig, new DefaultSignedAttributeTableGenerator(){ - @Override - public AttributeTable getAttributes(@SuppressWarnings("rawtypes") Map parameters) { - AttributeTable ret = sAttrGen.getAttributes(parameters); - return ret.remove(CMSAttributes.cmsAlgorithmProtect); - } - }, sig.getUnsignedAttributeTableGenerator()); - } - } catch (OperatorCreationException e) { - throw new GeneralSecurityException(e); - } + if (isRemoveCmsAlgorithmProtectionAttr) + { + final CMSAttributeTableGenerator sAttrGen = sig.getSignedAttributeTableGenerator(); + sig = new SignerInfoGenerator(sig, new DefaultSignedAttributeTableGenerator() { + @Override + public AttributeTable getAttributes(@SuppressWarnings("rawtypes") Map parameters) + { + AttributeTable ret = sAttrGen.getAttributes(parameters); + return ret.remove(CMSAttributes.cmsAlgorithmProtect); + } + }, sig.getUnsignedAttributeTableGenerator()); + } + } catch (OperatorCreationException e) + { + throw new GeneralSecurityException(e); + } sGen.addSignerInfoGenerator(sig); MimeMultipart signedData; @@ -343,7 +386,10 @@ public AttributeTable getAttributes(@SuppressWarnings("rawtypes") Map parameters tmpBody.setContent(signedData); String ct = signedData.getContentType(); // FIX for latest BC version setting the micalg value to sha-1 when passed sha1 as digest - if (adjustDigestToOldName && digest.equalsIgnoreCase("SHA1")) ct = ct.replaceAll("-1", "1"); + if (adjustDigestToOldName && digest.equalsIgnoreCase("SHA1")) + { + ct = ct.replaceAll("-1", "1"); + } tmpBody.setHeader("Content-Type", ct); //tmpBody.setHeader("Content-Transfer-Encoding", contentTxfrEncoding); @@ -351,9 +397,11 @@ public AttributeTable getAttributes(@SuppressWarnings("rawtypes") Map parameters } public MimeBodyPart verifySignature(MimeBodyPart part, Certificate cert) - throws GeneralSecurityException, IOException, MessagingException, CMSException, OperatorCreationException { + throws GeneralSecurityException, IOException, MessagingException, CMSException, OperatorCreationException + { // Make sure the data is signed - if (!isSigned(part)) { + if (!isSigned(part)) + { throw new GeneralSecurityException("Content-Type indicates data isn't signed"); } @@ -362,123 +410,130 @@ public MimeBodyPart verifySignature(MimeBodyPart part, Certificate cert) MimeMultipart mainParts = (MimeMultipart) part.getContent(); SMIMESigned signedPart = new SMIMESigned(mainParts); - //SignerInformationStore signers = signedPart.getSignerInfos(); - + //SignerInformationStore signers = signedPart.getSignerInfos(); + DigestCalculatorProvider dcp = new JcaDigestCalculatorProviderBuilder().setProvider("BC").build(); String contentTxfrEnc = signedPart.getContent().getEncoding(); if (contentTxfrEnc == null || contentTxfrEnc.length() < 1) { - contentTxfrEnc = Session.DEFAULT_CONTENT_TRANSFER_ENCODING; + contentTxfrEnc = Session.DEFAULT_CONTENT_TRANSFER_ENCODING; } SMIMESignedParser ssp = new SMIMESignedParser(dcp, mainParts, contentTxfrEnc); - SignerInformationStore sis = ssp.getSignerInfos(); + SignerInformationStore sis = ssp.getSignerInfos(); if (logger.isTraceEnabled()) { - String headers = null; + String headers = null; try - { - headers = AS2Util.printHeaders(part.getAllHeaders()); - logger.trace("Headers on MimeBodyPart passed in to signature verifier: " + headers); - headers = AS2Util.printHeaders(ssp.getContent().getAllHeaders()); - logger.trace("Checking signature on SIGNED MIME part extracted from multipart contains headers: " + headers); - } catch (Throwable e) - { - logger.trace("Error logging mime part for signer: " + org.openas2.logging.Log.getExceptionMsg(e), e); - } - - } - - Iterator it = sis.getSigners().iterator(); - SignerInformationVerifier signerInfoVerifier = new JcaSimpleSignerInfoVerifierBuilder().setProvider("BC").build(x509Cert); - while (it.hasNext()) - { - SignerInformation signer = it.next(); - if (logger.isTraceEnabled()) - { - AttributeTable attrTbl = signer.getSignedAttributes(); - logger.trace("Signer Attributes: " + (attrTbl==null?"NULL":attrTbl.toHashtable())); - } - if (signer.verify(signerInfoVerifier)) - { - logSignerInfo("Verified signature for signer info", signer, part, x509Cert); - return signedPart.getContent(); - } - logSignerInfo("Failed to verify signature for signer info", signer, part, x509Cert); - } - throw new SignatureException("Signature Verification failed"); + { + headers = AS2Util.printHeaders(part.getAllHeaders()); + logger.trace("Headers on MimeBodyPart passed in to signature verifier: " + headers); + headers = AS2Util.printHeaders(ssp.getContent().getAllHeaders()); + logger.trace("Checking signature on SIGNED MIME part extracted from multipart contains headers: " + headers); + } catch (Throwable e) + { + logger.trace("Error logging mime part for signer: " + org.openas2.logging.Log.getExceptionMsg(e), e); + } + } + + Iterator it = sis.getSigners().iterator(); + SignerInformationVerifier signerInfoVerifier = new JcaSimpleSignerInfoVerifierBuilder().setProvider("BC").build(x509Cert); + while (it.hasNext()) + { + SignerInformation signer = it.next(); + if (logger.isTraceEnabled()) + { + AttributeTable attrTbl = signer.getSignedAttributes(); + logger.trace("Signer Attributes: " + (attrTbl == null ? "NULL" : attrTbl.toHashtable())); + } + if (signer.verify(signerInfoVerifier)) + { + logSignerInfo("Verified signature for signer info", signer, part, x509Cert); + return signedPart.getContent(); + } + logSignerInfo("Failed to verify signature for signer info", signer, part, x509Cert); + } + throw new SignatureException("Signature Verification failed"); + } + + public MimeBodyPart compress(Message msg, MimeBodyPart mbp, String compressionType, String contentTxfrEncoding) + throws SMIMEException, OpenAS2Exception + { + OutputCompressor compressor = null; + if (compressionType != null) + { + if (compressionType.equalsIgnoreCase(ICryptoHelper.COMPRESSION_ZLIB)) + { + compressor = new ZlibCompressor(); + } else + { + throw new OpenAS2Exception("Unsupported compression type: " + compressionType); + } + } + SMIMECompressedGenerator sCompGen = new SMIMECompressedGenerator(); + sCompGen.setContentTransferEncoding(getEncoding(contentTxfrEncoding)); + MimeBodyPart smime = sCompGen.generate(mbp, compressor); + if (logger.isTraceEnabled()) + { + try + { + logger.trace("Compressed MIME msg AFTER COMPRESSION Content-Type:" + smime.getContentType()); + logger.trace("Compressed MIME msg AFTER COMPRESSION Content-Disposition:" + smime.getDisposition()); + } catch (MessagingException e) + { + } + } + return smime; + } + + public void decompress(AS2Message msg) throws DispositionException + { + try + { + if (logger.isDebugEnabled()) + { + logger.debug("Decompressing a compressed message"); + } + SMIMECompressed compressed = new SMIMECompressed(msg.getData()); + // decompression step MimeBodyPart + MimeBodyPart recoveredPart = SMIMEUtil.toMimeBodyPart(compressed.getContent(new ZlibExpanderProvider())); + // Update the message object + msg.setData(recoveredPart); + } catch (Exception ex) + { + + msg.setLogMsg("Error decompressing received message: " + ex.getCause()); + logger.error(msg, ex); + throw new DispositionException(new DispositionType("automatic-action", "MDN-sent-automatically", + "processed", "Error", "unexpected-processing-error"), AS2ReceiverModule.DISP_DECOMPRESSION_ERROR, + ex); + } } - public MimeBodyPart compress(Message msg, MimeBodyPart mbp, String compressionType, String contentTxfrEncoding) - throws SMIMEException, OpenAS2Exception - { - OutputCompressor compressor = null; - if (compressionType != null) - { - if (compressionType.equalsIgnoreCase(ICryptoHelper.COMPRESSION_ZLIB)) - { - compressor = new ZlibCompressor(); - } else - throw new OpenAS2Exception("Unsupported compression type: " + compressionType); - } - SMIMECompressedGenerator sCompGen = new SMIMECompressedGenerator(); - sCompGen.setContentTransferEncoding(getEncoding(contentTxfrEncoding)); - MimeBodyPart smime = sCompGen.generate(mbp, compressor); - if (logger.isTraceEnabled()) - { - try - { - logger.trace("Compressed MIME msg AFTER COMPRESSION Content-Type:" + smime.getContentType()); - logger.trace("Compressed MIME msg AFTER COMPRESSION Content-Disposition:" + smime.getDisposition()); - } catch (MessagingException e) - { - } - } - return smime; - } - - public void decompress(AS2Message msg) throws DispositionException - { - try - { - if (logger.isDebugEnabled()) logger.debug("Decompressing a compressed message"); - SMIMECompressed compressed = new SMIMECompressed(msg.getData()); - // decompression step MimeBodyPart - MimeBodyPart recoveredPart = SMIMEUtil.toMimeBodyPart(compressed.getContent(new ZlibExpanderProvider())); - // Update the message object - msg.setData(recoveredPart); - } - - catch (Exception ex) - { - - msg.setLogMsg("Error decompressing received message: " + ex.getCause()); - logger.error(msg, ex); - throw new DispositionException(new DispositionType("automatic-action", "MDN-sent-automatically", - "processed", "Error", "unexpected-processing-error"), AS2ReceiverModule.DISP_DECOMPRESSION_ERROR, - ex); - } - } - - protected String getEncoding(String contentTxfrEncoding) - { + protected String getEncoding(String contentTxfrEncoding) + { // Bouncy castle only deals with binary or base64 so pass base64 for 7bit, 8bit etc - return "binary".equalsIgnoreCase(contentTxfrEncoding)?"binary":"base64"; - } + return "binary".equalsIgnoreCase(contentTxfrEncoding) ? "binary" : "base64"; + } - protected X509Certificate castCertificate(Certificate cert) throws GeneralSecurityException { - if (cert == null) { + protected X509Certificate castCertificate(Certificate cert) throws GeneralSecurityException + { + if (cert == null) + { throw new GeneralSecurityException("Certificate is null"); } - if (!(cert instanceof X509Certificate)) { + if (!(cert instanceof X509Certificate)) + { throw new GeneralSecurityException("Certificate must be an instance of X509Certificate"); } return (X509Certificate) cert; } - protected PrivateKey castKey(Key key) throws GeneralSecurityException { - if (!(key instanceof PrivateKey)) { + protected PrivateKey castKey(Key key) throws GeneralSecurityException + { + if (!(key instanceof PrivateKey)) + { throw new GeneralSecurityException("Key must implement PrivateKey interface"); } @@ -486,75 +541,112 @@ protected PrivateKey castKey(Key key) throws GeneralSecurityException { } protected String convertAlgorithm(String algorithm, boolean toBC) - throws NoSuchAlgorithmException { - if (algorithm == null) { + throws NoSuchAlgorithmException + { + if (algorithm == null) + { throw new NoSuchAlgorithmException("Algorithm is null"); } - if (toBC) { - if (algorithm.toUpperCase().startsWith("SHA-")) algorithm = algorithm.replaceAll("-", ""); - if (algorithm.equalsIgnoreCase(DIGEST_MD5)) { + if (toBC) + { + if (algorithm.toUpperCase().startsWith("SHA-")) + { + algorithm = algorithm.replaceAll("-", ""); + } + if (algorithm.equalsIgnoreCase(DIGEST_MD5)) + { return SMIMESignedGenerator.DIGEST_MD5; - } else if (algorithm.equalsIgnoreCase(DIGEST_SHA1)) { + } else if (algorithm.equalsIgnoreCase(DIGEST_SHA1)) + { return SMIMESignedGenerator.DIGEST_SHA1; - } else if (algorithm.equalsIgnoreCase(DIGEST_SHA224)) { + } else if (algorithm.equalsIgnoreCase(DIGEST_SHA224)) + { return SMIMESignedGenerator.DIGEST_SHA224; - } else if (algorithm.equalsIgnoreCase(DIGEST_SHA256)) { + } else if (algorithm.equalsIgnoreCase(DIGEST_SHA256)) + { return SMIMESignedGenerator.DIGEST_SHA256; - } else if (algorithm.equalsIgnoreCase(DIGEST_SHA384)) { + } else if (algorithm.equalsIgnoreCase(DIGEST_SHA384)) + { return SMIMESignedGenerator.DIGEST_SHA384; - } else if (algorithm.equalsIgnoreCase(DIGEST_SHA512)) { + } else if (algorithm.equalsIgnoreCase(DIGEST_SHA512)) + { return SMIMESignedGenerator.DIGEST_SHA512; - } else if (algorithm.equalsIgnoreCase(CRYPT_3DES)) { + } else if (algorithm.equalsIgnoreCase(CRYPT_3DES)) + { return SMIMEEnvelopedGenerator.DES_EDE3_CBC; - } else if (algorithm.equalsIgnoreCase(CRYPT_CAST5)) { + } else if (algorithm.equalsIgnoreCase(CRYPT_CAST5)) + { return SMIMEEnvelopedGenerator.CAST5_CBC; - } else if (algorithm.equalsIgnoreCase(CRYPT_IDEA)) { + } else if (algorithm.equalsIgnoreCase(CRYPT_IDEA)) + { return SMIMEEnvelopedGenerator.IDEA_CBC; - } else if (algorithm.equalsIgnoreCase(CRYPT_RC2)) { + } else if (algorithm.equalsIgnoreCase(CRYPT_RC2)) + { return SMIMEEnvelopedGenerator.RC2_CBC; - } else if (algorithm.equalsIgnoreCase(CRYPT_RC2_CBC)) { + } else if (algorithm.equalsIgnoreCase(CRYPT_RC2_CBC)) + { return SMIMEEnvelopedGenerator.RC2_CBC; - } else if (algorithm.equalsIgnoreCase(AES256_CBC)) { + } else if (algorithm.equalsIgnoreCase(AES256_CBC)) + { return SMIMEEnvelopedGenerator.AES256_CBC; - } else if (algorithm.equalsIgnoreCase(AES192_CBC)) { + } else if (algorithm.equalsIgnoreCase(AES192_CBC)) + { return SMIMEEnvelopedGenerator.AES192_CBC; - } else if (algorithm.equalsIgnoreCase(AES128_CBC)) { + } else if (algorithm.equalsIgnoreCase(AES128_CBC)) + { return SMIMEEnvelopedGenerator.AES128_CBC; - } else if (algorithm.equalsIgnoreCase(AES256_WRAP)) { + } else if (algorithm.equalsIgnoreCase(AES256_WRAP)) + { return SMIMEEnvelopedGenerator.AES256_WRAP; - } else { + } else + { throw new NoSuchAlgorithmException("Unsupported or invalid algorithm: " + algorithm); } } - if (algorithm.equalsIgnoreCase(SMIMESignedGenerator.DIGEST_MD5)) { + if (algorithm.equalsIgnoreCase(SMIMESignedGenerator.DIGEST_MD5)) + { return DIGEST_MD5; - } else if (algorithm.equalsIgnoreCase(SMIMESignedGenerator.DIGEST_SHA1)) { + } else if (algorithm.equalsIgnoreCase(SMIMESignedGenerator.DIGEST_SHA1)) + { return DIGEST_SHA1; - } else if (algorithm.equalsIgnoreCase(SMIMESignedGenerator.DIGEST_SHA224)) { + } else if (algorithm.equalsIgnoreCase(SMIMESignedGenerator.DIGEST_SHA224)) + { return DIGEST_SHA224; - } else if (algorithm.equalsIgnoreCase(SMIMESignedGenerator.DIGEST_SHA256)) { + } else if (algorithm.equalsIgnoreCase(SMIMESignedGenerator.DIGEST_SHA256)) + { return DIGEST_SHA256; - } else if (algorithm.equalsIgnoreCase(SMIMESignedGenerator.DIGEST_SHA384)) { + } else if (algorithm.equalsIgnoreCase(SMIMESignedGenerator.DIGEST_SHA384)) + { return DIGEST_SHA384; - } else if (algorithm.equalsIgnoreCase(SMIMESignedGenerator.DIGEST_SHA512)) { + } else if (algorithm.equalsIgnoreCase(SMIMESignedGenerator.DIGEST_SHA512)) + { return DIGEST_SHA512; - } else if (algorithm.equalsIgnoreCase(SMIMEEnvelopedGenerator.CAST5_CBC)) { + } else if (algorithm.equalsIgnoreCase(SMIMEEnvelopedGenerator.CAST5_CBC)) + { return CRYPT_CAST5; - } else if (algorithm.equalsIgnoreCase(SMIMEEnvelopedGenerator.AES128_CBC)) { + } else if (algorithm.equalsIgnoreCase(SMIMEEnvelopedGenerator.AES128_CBC)) + { return AES128_CBC; - } else if (algorithm.equalsIgnoreCase(SMIMEEnvelopedGenerator.AES192_CBC)) { + } else if (algorithm.equalsIgnoreCase(SMIMEEnvelopedGenerator.AES192_CBC)) + { return AES192_CBC; - } else if (algorithm.equalsIgnoreCase(SMIMEEnvelopedGenerator.AES256_CBC)) { + } else if (algorithm.equalsIgnoreCase(SMIMEEnvelopedGenerator.AES256_CBC)) + { return AES256_CBC; - } else if (algorithm.equalsIgnoreCase(SMIMEEnvelopedGenerator.AES256_WRAP)) { + } else if (algorithm.equalsIgnoreCase(SMIMEEnvelopedGenerator.AES256_WRAP)) + { return AES256_WRAP; - } else if (algorithm.equalsIgnoreCase(SMIMEEnvelopedGenerator.DES_EDE3_CBC)) { + } else if (algorithm.equalsIgnoreCase(SMIMEEnvelopedGenerator.DES_EDE3_CBC)) + { return CRYPT_3DES; - } else if (algorithm.equalsIgnoreCase(SMIMEEnvelopedGenerator.IDEA_CBC)) { + } else if (algorithm.equalsIgnoreCase(SMIMEEnvelopedGenerator.IDEA_CBC)) + { return CRYPT_IDEA; - } else if (algorithm.equalsIgnoreCase(SMIMEEnvelopedGenerator.RC2_CBC)) { + } else if (algorithm.equalsIgnoreCase(SMIMEEnvelopedGenerator.RC2_CBC)) + { return CRYPT_RC2; - } else { + } else + { throw new NoSuchAlgorithmException("Unknown algorithm: " + algorithm); } @@ -566,10 +658,10 @@ protected String convertAlgorithm(String algorithm, boolean toBC) * @return the OutputEncryptor of the given hash algorithm * @throws NoSuchAlgorithmException * @description Looks up the correct ASN1 OID of the passed in algorithm string and returns the encryptor. - * The encryption key length is set where necessary - * + * The encryption key length is set where necessary + *

* TODO: Possibly just use new ASN1ObjectIdentifier(algorithm) instead of explicit lookup to support random configured algorithms - * but will require determining if this has any side effects from a security point of view. + * but will require determining if this has any side effects from a security point of view. */ protected OutputEncryptor getOutputEncryptor(String algorithm) throws NoSuchAlgorithmException @@ -583,94 +675,86 @@ protected OutputEncryptor getOutputEncryptor(String algorithm) if (algorithm.equalsIgnoreCase(DIGEST_MD2)) { asn1ObjId = new ASN1ObjectIdentifier(PKCSObjectIdentifiers.md2.getId()); - } - else if (algorithm.equalsIgnoreCase(DIGEST_MD5)) + } else if (algorithm.equalsIgnoreCase(DIGEST_MD5)) { asn1ObjId = new ASN1ObjectIdentifier(PKCSObjectIdentifiers.md5.getId()); - } - else if (algorithm.equalsIgnoreCase(DIGEST_SHA1)) + } else if (algorithm.equalsIgnoreCase(DIGEST_SHA1)) { asn1ObjId = new ASN1ObjectIdentifier(OIWObjectIdentifiers.idSHA1.getId()); - } - else if (algorithm.equalsIgnoreCase(DIGEST_SHA224)) + } else if (algorithm.equalsIgnoreCase(DIGEST_SHA224)) { asn1ObjId = new ASN1ObjectIdentifier(NISTObjectIdentifiers.id_sha224.getId()); - } - else if (algorithm.equalsIgnoreCase(DIGEST_SHA256)) + } else if (algorithm.equalsIgnoreCase(DIGEST_SHA256)) { asn1ObjId = new ASN1ObjectIdentifier(NISTObjectIdentifiers.id_sha256.getId()); - } - else if (algorithm.equalsIgnoreCase(DIGEST_SHA384)) + } else if (algorithm.equalsIgnoreCase(DIGEST_SHA384)) { asn1ObjId = new ASN1ObjectIdentifier(NISTObjectIdentifiers.id_sha384.getId()); - } - else if (algorithm.equalsIgnoreCase(DIGEST_SHA512)) + } else if (algorithm.equalsIgnoreCase(DIGEST_SHA512)) { asn1ObjId = new ASN1ObjectIdentifier(NISTObjectIdentifiers.id_sha512.getId()); - } - else if (algorithm.equalsIgnoreCase(CRYPT_3DES)) + } else if (algorithm.equalsIgnoreCase(CRYPT_3DES)) { asn1ObjId = new ASN1ObjectIdentifier(PKCSObjectIdentifiers.des_EDE3_CBC.getId()); - } - else if (algorithm.equalsIgnoreCase(CRYPT_RC2_CBC) || algorithm.equalsIgnoreCase(CRYPT_RC2_CBC)) + } else if (algorithm.equalsIgnoreCase(CRYPT_RC2_CBC) || algorithm.equalsIgnoreCase(CRYPT_RC2_CBC)) { asn1ObjId = new ASN1ObjectIdentifier(PKCSObjectIdentifiers.RC2_CBC.getId()); keyLen = 40; - } - - else if (algorithm.equalsIgnoreCase(AES128_CBC)) + } else if (algorithm.equalsIgnoreCase(AES128_CBC)) { - asn1ObjId = CMSAlgorithm.AES128_CBC; - } - else if (algorithm.equalsIgnoreCase(AES192_CBC)) + asn1ObjId = CMSAlgorithm.AES128_CBC; + } else if (algorithm.equalsIgnoreCase(AES192_CBC)) { - asn1ObjId = CMSAlgorithm.AES192_CBC; - } - else if (algorithm.equalsIgnoreCase(AES256_CBC)) + asn1ObjId = CMSAlgorithm.AES192_CBC; + } else if (algorithm.equalsIgnoreCase(AES256_CBC)) { - asn1ObjId = CMSAlgorithm.AES256_CBC; - } - else if (algorithm.equalsIgnoreCase(AES256_WRAP)) + asn1ObjId = CMSAlgorithm.AES256_CBC; + } else if (algorithm.equalsIgnoreCase(AES256_WRAP)) { - asn1ObjId = CMSAlgorithm.AES256_WRAP; - } - - else if (algorithm.equalsIgnoreCase(CRYPT_CAST5)) + asn1ObjId = CMSAlgorithm.AES256_WRAP; + } else if (algorithm.equalsIgnoreCase(CRYPT_CAST5)) { - asn1ObjId = CMSAlgorithm.CAST5_CBC; - } - else if (algorithm.equalsIgnoreCase(CRYPT_IDEA)) + asn1ObjId = CMSAlgorithm.CAST5_CBC; + } else if (algorithm.equalsIgnoreCase(CRYPT_IDEA)) { - asn1ObjId = CMSAlgorithm.IDEA_CBC; - } - else + asn1ObjId = CMSAlgorithm.IDEA_CBC; + } else { - throw new NoSuchAlgorithmException("Unsupported or invalid algorithm: " + algorithm); + throw new NoSuchAlgorithmException("Unsupported or invalid algorithm: " + algorithm); } OutputEncryptor oe = null; - try { - if (keyLen < 0) - oe = new JceCMSContentEncryptorBuilder(asn1ObjId).setProvider("BC").build(); - else - oe = new JceCMSContentEncryptorBuilder(asn1ObjId, keyLen).setProvider("BC").build(); - } catch (CMSException e1) { - throw new NoSuchAlgorithmException("Error creating encryptor builder using algorithm: " + algorithm + " Cause:" + e1.getCause()); - } - return oe; + try + { + if (keyLen < 0) + { + oe = new JceCMSContentEncryptorBuilder(asn1ObjId).setProvider("BC").build(); + } else + { + oe = new JceCMSContentEncryptorBuilder(asn1ObjId, keyLen).setProvider("BC").build(); + } + } catch (CMSException e1) + { + throw new NoSuchAlgorithmException("Error creating encryptor builder using algorithm: " + algorithm + " Cause:" + e1.getCause()); + } + return oe; } - protected InputStream trimCRLFPrefix(byte[] data) { + protected InputStream trimCRLFPrefix(byte[] data) + { ByteArrayInputStream bIn = new ByteArrayInputStream(data); int scanPos = 0; int len = data.length; - while (scanPos < (len - 1)) { - if (new String(data, scanPos, 2).equals("\r\n")) { + while (scanPos < (len - 1)) + { + if (new String(data, scanPos, 2).equals("\r\n")) + { bIn.read(); bIn.read(); scanPos += 2; - } else { + } else + { return bIn; } } @@ -678,60 +762,68 @@ protected InputStream trimCRLFPrefix(byte[] data) { return bIn; } - public KeyStore getKeyStore() throws KeyStoreException, NoSuchProviderException { + public KeyStore getKeyStore() throws KeyStoreException, NoSuchProviderException + { return KeyStore.getInstance("PKCS12", "BC"); } - public KeyStore loadKeyStore(InputStream in, char[] password) throws Exception { + public KeyStore loadKeyStore(InputStream in, char[] password) throws Exception + { KeyStore ks = getKeyStore(); ks.load(in, password); return ks; } - public KeyStore loadKeyStore(String filename, char[] password) throws Exception { + public KeyStore loadKeyStore(String filename, char[] password) throws Exception + { FileInputStream fIn = new FileInputStream(filename); - try { + try + { return loadKeyStore(fIn, password); - } finally { + } finally + { fIn.close(); } } - + public String getHeaderValue(MimeBodyPart part, String headerName) { - try - { - String[] values = part.getHeader(headerName); - if (values == null) return null; - return values[0]; - } catch (MessagingException e) - { - return null; - } + try + { + String[] values = part.getHeader(headerName); + if (values == null) + { + return null; + } + return values[0]; + } catch (MessagingException e) + { + return null; + } } - + public void logSignerInfo(String msgPrefix, SignerInformation signer, MimeBodyPart part, X509Certificate cert) { if (logger.isDebugEnabled()) { - try - { - logger.debug(msgPrefix + ": \n Digest Alg OID: " + signer.getDigestAlgOID() - + "\n Encrypt Alg OID: " + signer.getEncryptionAlgOID() - + "\n Signer Version: " + signer.getVersion() - + "\n Content Digest: " + Arrays.toString(signer.getContentDigest()) - + "\n Content Type: " + signer.getContentType() - + "\n SID: " + signer.getSID().getIssuer() - + "\n Signature: " + signer.getSignature() - + "\n Unsigned attribs: " + signer.getUnsignedAttributes() - + "\n Content-transfer-encoding: " + part.getEncoding() - + "\n Certificate: " + cert - ); - } catch (Throwable e) - { - logger.debug("Error logging signer info: " + org.openas2.logging.Log.getExceptionMsg(e), e); - } + try + { + logger.debug(msgPrefix + ": \n Digest Alg OID: " + signer.getDigestAlgOID() + + "\n Encrypt Alg OID: " + signer.getEncryptionAlgOID() + + "\n Signer Version: " + signer.getVersion() + + "\n Content Digest: " + Arrays.toString(signer.getContentDigest()) + + "\n Content Type: " + signer.getContentType() + + "\n SID: " + signer.getSID().getIssuer() + + "\n Signature: " + Arrays.toString(signer.getSignature()) + + "\n Unsigned attribs: " + signer.getUnsignedAttributes() + + "\n Content-transfer-encoding: " + part.getEncoding() + + "\n Certificate: " + cert + ); + } catch (Throwable e) + { + logger.debug("Error logging signer info: " + org.openas2.logging.Log.getExceptionMsg(e), e); + } } } } diff --git a/Server/src/main/java/org/openas2/lib/helper/ICryptoHelper.java b/Server/src/main/java/org/openas2/lib/helper/ICryptoHelper.java index 591e35d3..9eeedbe9 100644 --- a/Server/src/main/java/org/openas2/lib/helper/ICryptoHelper.java +++ b/Server/src/main/java/org/openas2/lib/helper/ICryptoHelper.java @@ -4,7 +4,6 @@ import java.security.Key; import java.security.KeyStore; import java.security.cert.Certificate; - import javax.mail.internet.MimeBodyPart; import org.bouncycastle.mail.smime.SMIMEException; @@ -53,8 +52,6 @@ public interface ICryptoHelper { MimeBodyPart decrypt(MimeBodyPart part, Certificate cert, Key key) throws Exception; - void deinitialize() throws Exception; - MimeBodyPart encrypt(MimeBodyPart part, Certificate cert, String algorithm, String contentTxfrEncoding) throws Exception; void initialize() throws Exception; diff --git a/Server/src/main/java/org/openas2/lib/message/AS2Message.java b/Server/src/main/java/org/openas2/lib/message/AS2Message.java index 7d658591..7d4d6a7c 100644 --- a/Server/src/main/java/org/openas2/lib/message/AS2Message.java +++ b/Server/src/main/java/org/openas2/lib/message/AS2Message.java @@ -6,8 +6,7 @@ import javax.mail.MessagingException; import javax.mail.internet.MimeBodyPart; -import org.openas2.lib.Info; - +import org.openas2.util.Properties; public class AS2Message extends EDIINTMessage { public AS2Message() { @@ -57,9 +56,9 @@ public String getAS2Version() { public void setDefaults() { super.setDefaults(); - setAS2Version("1.1"); - setUserAgent("OpenAS2"); - setServer(Info.NAME_VERSION); + setAS2Version(Properties.getProperty(Properties.APP_VERSION_PROP, "")); + setUserAgent(Properties.getProperty(Properties.APP_TITLE_PROP, "OpenAS2 Server")); + setServer(Properties.getProperty(Properties.APP_TITLE_PROP, "OpenAS2 Server")); } public void setDispositionNotificationOptions(String options) { diff --git a/Server/src/main/java/org/openas2/lib/message/AS2MessageMDN.java b/Server/src/main/java/org/openas2/lib/message/AS2MessageMDN.java index 7aa4e651..e3d42b0f 100644 --- a/Server/src/main/java/org/openas2/lib/message/AS2MessageMDN.java +++ b/Server/src/main/java/org/openas2/lib/message/AS2MessageMDN.java @@ -6,7 +6,7 @@ import javax.mail.MessagingException; import javax.mail.internet.MimeBodyPart; -import org.openas2.lib.Info; +import org.openas2.util.Properties; public class AS2MessageMDN extends EDIINTMessageMDN { private AS2MDNData mdnData; @@ -65,8 +65,8 @@ public String getAS2Version() { public void setDefaults() { super.setDefaults(); - setAS2Version("1.1"); - setServer(Info.NAME_VERSION); + setAS2Version(Properties.getProperty(Properties.APP_VERSION_PROP, "")); + setServer(Properties.getProperty(Properties.APP_TITLE_PROP, "OpenAS2 Server")); } public void setServer(String server) { diff --git a/Server/src/main/java/org/openas2/lib/util/GeneralUtil.java b/Server/src/main/java/org/openas2/lib/util/GeneralUtil.java index b4f83d81..cd0d45c5 100644 --- a/Server/src/main/java/org/openas2/lib/util/GeneralUtil.java +++ b/Server/src/main/java/org/openas2/lib/util/GeneralUtil.java @@ -1,7 +1,5 @@ package org.openas2.lib.util; -import java.io.PrintWriter; -import java.io.StringWriter; import java.util.Collections; import java.util.Enumeration; import java.util.Iterator; @@ -11,48 +9,21 @@ public class GeneralUtil { - public static String convert(Object[] array, String delimiter) { - StringBuffer buffer = new StringBuffer(); - for (int i = 0; i < array.length; i++) { - if (buffer.length() > 0) { - buffer.append(delimiter); - } - buffer.append(array[i].toString()); - } - - return buffer.toString(); - } - - public static boolean contains(String[] array, String value) { - for (int i = 0; i < array.length; i++) { - if (array[i] == null) { - if (value == null) { - return true; - } - } else if (value != null && array[i].equals(value)) { - return true; - } - } - return false; - } public static String[] convert(Enumeration en) { List list = Collections.list(en); return convert(list); } - - public static String[] convert(List list) { - String[] values = new String[0]; - return (String[]) list.toArray(values); - } - - public static String convert(List list, String delimiter) { - return convert(list.toArray(), delimiter); + + static String[] convert(List list) + { + String[] values = new String[0]; + return list.toArray(values); } public static String[] convertKeys(Map map) { String[] keys = new String[0]; - return (String[]) map.keySet().toArray(keys); + return map.keySet().toArray(keys); } public static String convert(Map map, String valueDelimiter, String pairDelimiter) { @@ -69,12 +40,6 @@ public static String convert(Map map, String valueDelimiter, String pairDe } return strBuf.toString(); } - - public static String convertTrace(Exception e) { - StringWriter writer = new StringWriter(); - e.printStackTrace(new PrintWriter(writer)); - return writer.toString(); - } public static Object getKey(Map map, Object value) { Iterator it = map.entrySet().iterator(); diff --git a/Server/src/main/java/org/openas2/lib/util/IOUtil.java b/Server/src/main/java/org/openas2/lib/util/IOUtil.java deleted file mode 100644 index fbeffd5e..00000000 --- a/Server/src/main/java/org/openas2/lib/util/IOUtil.java +++ /dev/null @@ -1,90 +0,0 @@ -package org.openas2.lib.util; - -import java.io.BufferedReader; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.io.PrintStream; - -public class IOUtil { - public static final String MSG_WAIT_FOR_KEYPRESS = "Waiting for keypress..."; - public static final String MSG_PROMPT = "> "; - - public static int copy(InputStream in, OutputStream out) throws IOException { - int totalCount = 0; - byte[] buf = new byte[4096]; - int count = 0; - - while (count >= 0) { - count = in.read(buf); - totalCount += count; - - if (count > 0) { - out.write(buf, 0, count); - } - } - - return totalCount; - } - - public static byte[] toBytes(InputStream in) throws IOException { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - copy(in, baos); - - return baos.toByteArray(); - } - - public static void waitForKeypress(String message, PrintStream out, InputStream in) - throws IOException { - out.println(); - if (message == null) { - message = MSG_WAIT_FOR_KEYPRESS; - } - out.println(message); - - in.read(); - int num = in.available(); - for (int i = 0; i < num; i++) { - in.read(); - } - } - - public static void waitForKeypress(String message) throws IOException { - waitForKeypress(message, System.out, System.in); - } - - public static String prompt(String message, String defaultValue, PrintStream out, InputStream in) - throws IOException { - // show the prompt - if (message == null) { - message = MSG_PROMPT; - } - out.print(message); - - // read user input - String answer = new BufferedReader(new InputStreamReader(in)).readLine(); - - // return the user input, or the default value if no user input was - // given - if (answer != null && !answer.toString().equals("")) { - return answer.toString(); - } - return defaultValue; - } - - public static String prompt(String message, String defaultValue) throws IOException { - return prompt(message, defaultValue, System.out, System.in); - } - - public static int prompt(String message, int defaultValue, PrintStream out, InputStream in) - throws IOException { - String value = prompt(message, Integer.toString(defaultValue), out, in); - return Integer.parseInt(value); - } - - public static int prompt(String message, int defaultValue) throws IOException { - return prompt(message, defaultValue, System.out, System.in); - } -} \ No newline at end of file diff --git a/Server/src/main/java/org/openas2/lib/util/RandomUtil.java b/Server/src/main/java/org/openas2/lib/util/RandomUtil.java deleted file mode 100644 index b59c49d6..00000000 --- a/Server/src/main/java/org/openas2/lib/util/RandomUtil.java +++ /dev/null @@ -1,30 +0,0 @@ -package org.openas2.lib.util; - -import java.text.DecimalFormat; -import java.util.Arrays; -import java.util.Random; - -public class RandomUtil { - private static final Random randomInstance = new Random(); - - public static Random getInstance() { - return randomInstance; - } - - public static String nextDecimal(int length) { - if (length == 0) { - return ""; - } - byte[] format = new byte[length]; - Arrays.fill(format, (byte) '0'); - DecimalFormat randomFormatter = new DecimalFormat(new String(format)); - return randomFormatter.format(getInstance().nextInt((int) Math.pow(10, length))); - } - - public static byte[] nextBytes(int length) { - Random r = getInstance(); - byte[] buf = new byte[length]; - r.nextBytes(buf); - return buf; - } -} diff --git a/Server/src/main/java/org/openas2/lib/util/javamail/DispositionDataContentHandler.java b/Server/src/main/java/org/openas2/lib/util/javamail/DispositionDataContentHandler.java deleted file mode 100644 index 39f7d3b3..00000000 --- a/Server/src/main/java/org/openas2/lib/util/javamail/DispositionDataContentHandler.java +++ /dev/null @@ -1,84 +0,0 @@ -package org.openas2.lib.util.javamail; - -import java.awt.datatransfer.DataFlavor; -import java.io.BufferedInputStream; -import java.io.BufferedOutputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.OutputStream; - -import javax.activation.ActivationDataFlavor; -import javax.activation.DataContentHandler; -import javax.activation.DataSource; -import javax.mail.MessagingException; -import javax.mail.internet.MimeBodyPart; -import javax.mail.internet.MimeMultipart; - - -public class DispositionDataContentHandler implements DataContentHandler { - private static final ActivationDataFlavor ADF1; - private static final DataFlavor[] ADFs; - - static { - ADF1 = new ActivationDataFlavor(MimeBodyPart.class, "message/disposition-notification", - "Disposition Notification"); - ADFs = new DataFlavor[] { ADF1 }; - } - - public DispositionDataContentHandler() { - super(); - } - - public Object getContent(DataSource ds) throws IOException { - byte[] buf = new byte[4096]; - BufferedInputStream bIn = new BufferedInputStream(ds.getInputStream()); - ByteArrayOutputStream baOut = new ByteArrayOutputStream(); - BufferedOutputStream bOut = new BufferedOutputStream(baOut); - int count = bIn.read(buf); - - while (count > -1) { - bOut.write(buf, 0, count); - count = bIn.read(buf); - } - - bIn.close(); - bOut.close(); - - return baOut.toByteArray(); - } - - public Object getTransferData(DataFlavor df, DataSource ds) - throws IOException { - if (ADF1.equals(df)) { - return getContent(ds); - } - return null; - } - - public DataFlavor[] getTransferDataFlavors() { - return ADFs; - } - - public void writeTo(Object obj, String mimeType, OutputStream os) - throws IOException { - if (obj instanceof MimeBodyPart) { - try { - ((MimeBodyPart) obj).writeTo(os); - } catch (MessagingException me) { - throw new IOException(me.getMessage()); - } - } else if (obj instanceof MimeMultipart) { - try { - ((MimeMultipart) obj).writeTo(os); - } catch (MessagingException me) { - throw new IOException(me.getMessage()); - } - } else if (obj instanceof byte[]) { - os.write((byte[]) obj); - } else if (obj instanceof String) { - os.write(((String) obj).getBytes()); - } else { - throw new IOException("Unknown object type: " + obj.getClass().getName()); - } - } -} diff --git a/Server/src/main/java/org/openas2/logging/LogManager.java b/Server/src/main/java/org/openas2/logging/LogManager.java index 8919894d..6c4627bd 100644 --- a/Server/src/main/java/org/openas2/logging/LogManager.java +++ b/Server/src/main/java/org/openas2/logging/LogManager.java @@ -1,100 +1,95 @@ package org.openas2.logging; import java.util.ArrayList; -import java.util.Iterator; +import java.util.Collections; import java.util.List; +import javax.annotation.Nonnull; + import org.openas2.message.Message; public class LogManager { - private static LogManager defaultManager; - private List loggers; - - public static LogManager getLogManager() { - if (defaultManager == null) { - defaultManager = new LogManager(); - } - - return defaultManager; - } - + /** + * A default logger. It used when no loggers are configured in config.xml + */ + private final static ConsoleLogger DEFAULT_LOGGER = new ConsoleLogger(); private static boolean registeredWithApache = false; + private final List requestors = Collections.synchronizedList(new ArrayList()); + private List loggers = Collections.synchronizedList(new ArrayList()); - public void setLoggers(List listeners) { - this.loggers = listeners; + public static LogManager getLogManager() + { + return DefaultManager.INSTANCE; } - public List getLoggers() { - if (loggers == null) { - loggers = new ArrayList(); - } + /** + * @return the registeredWithApache + */ + public static boolean isRegisteredWithApache() + { + return registeredWithApache; + } - return loggers; + public void setLoggers(List listeners) + { + this.loggers = listeners; } - public void addLogger(Logger logger) { - List loggers = getLoggers(); + public void addLogger(Logger logger) + { loggers.add(logger); } - public void log(Throwable e, boolean terminated) { - Iterator loggerIt = getLoggers().iterator(); - - if (loggerIt.hasNext()) { - while (loggerIt.hasNext()) { - Logger logger = (Logger) loggerIt.next(); + public void log(@Nonnull Throwable e, boolean terminated) + { + if (loggers.isEmpty()) + { + DEFAULT_LOGGER.log(e, Level.ERROR, terminated); + } else + { + for (Logger logger : loggers) + { logger.log(e, Level.ERROR, terminated); // might want to pass LEVEL in from caller } - } else { - e.printStackTrace(); } } - /** * @param level * @param clazzName - the name of the class that the log was generated in - * @param message - the logging object to create the message from + * @param msg - the logging object to create the message from */ - public void log(Level level, String clazzName, Object msg) { - Iterator loggerIt = getLoggers().iterator(); - - if (loggerIt.hasNext()) { - while (loggerIt.hasNext()) { - Logger logger = (Logger) loggerIt.next(); - if (msg instanceof Message) logger.log(level, clazzName + ": " + ((Message)msg).getLogMsg(), (Message) msg); - else logger.log(level, clazzName + ": " + msg.toString(), null); + public void log(Level level, String clazzName, @Nonnull Object msg) + { + if (loggers.isEmpty()) + { + DEFAULT_LOGGER.log(level, clazzName + ": " + msg.toString(), null); + } else + { + for (Logger logger : loggers) + { + if (msg instanceof Message) + { + logger.log(level, clazzName + ": " + ((Message) msg).getLogMsg(), (Message) msg); + } else + { + logger.log(level, clazzName + ": " + msg.toString(), null); + } } - } else { - System.out.println(level.getName() + " " + msg.toString()); } } - ArrayList requestors = new ArrayList(); - /** - * @param log - */ - public void addRequestors(String inName) { - requestors.add(inName); - setRegisteredWithApache(true); - } - - /** - * @param registeredWithApache the registedWithApache to set - */ - public static void setRegisteredWithApache(boolean registedWithApache) { - LogManager.registeredWithApache = registedWithApache; - } + /** + * @param inName + */ + void addRequestors(String inName) + { + requestors.add(inName); + LogManager.registeredWithApache = true; + } - /** - * @return the registeredWithApache - */ - public static boolean isRegisteredWithApache() { - return registeredWithApache; - } - - - - + private static class DefaultManager { + private static LogManager INSTANCE = new LogManager(); + } } diff --git a/Server/src/main/java/org/openas2/message/AS1Message.java b/Server/src/main/java/org/openas2/message/AS1Message.java deleted file mode 100644 index d1848125..00000000 --- a/Server/src/main/java/org/openas2/message/AS1Message.java +++ /dev/null @@ -1,40 +0,0 @@ -package org.openas2.message; - -public class AS1Message extends BaseMessage { - /** - * - */ - private static final long serialVersionUID = 1L; - public static final String PROTOCOL_AS1 = "as1"; - - public String getProtocol() { - return PROTOCOL_AS1; - } - - public boolean isRequestingMDN() { - // TODO Auto-generated method stub - return false; - } - - public boolean isRequestingAsynchMDN() { - // TODO Auto-generated method stub - return false; - } - - public String generateMessageID() { - // TODO Auto-generated method stub - return null; - } - - public boolean isConfiguredForMDN() - { - // TODO Auto-generated method stub - return false; - } - - public boolean isConfiguredForAsynchMDN() - { - // TODO Auto-generated method stub - return false; - } -} diff --git a/Server/src/main/java/org/openas2/message/AS1MessageMDN.java b/Server/src/main/java/org/openas2/message/AS1MessageMDN.java deleted file mode 100644 index 5cd35ea2..00000000 --- a/Server/src/main/java/org/openas2/message/AS1MessageMDN.java +++ /dev/null @@ -1,17 +0,0 @@ -package org.openas2.message; - -public class AS1MessageMDN extends BaseMessageMDN { - /** - * - */ - private static final long serialVersionUID = 1L; - - public AS1MessageMDN(AS1Message msg) { - super(msg); - } - - public String generateMessageID() { - // TODO Auto-generated method stub - return null; - } -} diff --git a/Server/src/main/java/org/openas2/message/AS2Message.java b/Server/src/main/java/org/openas2/message/AS2Message.java index 369fcc82..772bf7c4 100644 --- a/Server/src/main/java/org/openas2/message/AS2Message.java +++ b/Server/src/main/java/org/openas2/message/AS2Message.java @@ -8,6 +8,7 @@ import org.openas2.params.RandomParameters; import org.openas2.partner.AS2Partnership; import org.openas2.partner.Partnership; +import org.openas2.util.Properties; public class AS2Message extends BaseMessage implements Message { @@ -22,17 +23,7 @@ public String getProtocol() { } public String generateMessageID() throws InvalidParameterException { - CompositeParameters params = - new CompositeParameters(false). - add("date", new DateParameters()). - add("msg", new MessageParameters(this)). - add("rand", new RandomParameters()); - - String idFormat = getPartnership().getAttribute(AS2Partnership.PA_MESSAGEID); - if (idFormat == null) { - idFormat = "OPENAS2-$date.ddMMyyyyHHmmssZ$-$rand.1234$@$msg.sender.as2_id$_$msg.receiver.as2_id$"; - } - return ParameterParser.parse(idFormat, params); + return org.openas2.util.AS2Util.generateMessageID(this); } public boolean isRequestingMDN() { diff --git a/Server/src/main/java/org/openas2/message/AS2MessageMDN.java b/Server/src/main/java/org/openas2/message/AS2MessageMDN.java index 0063491b..88ee3c78 100644 --- a/Server/src/main/java/org/openas2/message/AS2MessageMDN.java +++ b/Server/src/main/java/org/openas2/message/AS2MessageMDN.java @@ -1,55 +1,46 @@ package org.openas2.message; -import java.text.DecimalFormat; - +import org.apache.commons.lang3.RandomStringUtils; +import org.openas2.params.InvalidParameterException; import org.openas2.partner.AS2Partnership; import org.openas2.partner.CustomIDPartnership; import org.openas2.partner.Partnership; import org.openas2.util.DateUtil; -import org.openas2.util.RandomUtil; public class AS2MessageMDN extends BaseMessageMDN { - /** - * - */ - private static final long serialVersionUID = 1L; - public static final String MDNA_REPORTING_UA = "REPORTING_UA"; + public static final String MDNA_REPORTING_UA = "REPORTING_UA"; public static final String MDNA_ORIG_RECIPIENT = "ORIGINAL_RECIPIENT"; public static final String MDNA_FINAL_RECIPIENT = "FINAL_RECIPIENT"; public static final String MDNA_ORIG_MESSAGEID = "ORIGINAL_MESSAGE_ID"; public static final String MDNA_DISPOSITION = "DISPOSITION"; public static final String MDNA_MIC = "MIC"; + /** + * + */ + private static final long serialVersionUID = 1L; - public AS2MessageMDN(AS2Message msg, boolean copyMsgHeaders) { + public AS2MessageMDN(AS2Message msg, boolean copyMsgHeaders) + { super(msg); - if (copyMsgHeaders) copyHeaders(msg.getHeaders()); + if (copyMsgHeaders) + { + copyHeaders(msg.getHeaders()); + } setHeader("AS2-To", msg.getHeader("AS2-From")); setHeader("AS2-From", msg.getHeader("AS2-To")); } - public String generateMessageID() { - StringBuffer buf = new StringBuffer(); - String dateFormat = getPartnership().getAttribute(CustomIDPartnership.PA_DATE_FORMAT); - if (dateFormat == null) { - dateFormat = "ddMMyyyyHHmmssZ"; - } - buf.append(""); - return buf.toString(); - } } \ No newline at end of file diff --git a/Server/src/main/java/org/openas2/message/BaseMessage.java b/Server/src/main/java/org/openas2/message/BaseMessage.java index c454f817..7cd476d4 100644 --- a/Server/src/main/java/org/openas2/message/BaseMessage.java +++ b/Server/src/main/java/org/openas2/message/BaseMessage.java @@ -22,6 +22,7 @@ import org.openas2.params.InvalidParameterException; import org.openas2.partner.Partnership; import org.openas2.processor.msgtracking.TrackingModule; +import org.openas2.util.Properties; public abstract class BaseMessage implements Message { @@ -44,7 +45,6 @@ public abstract class BaseMessage implements Message { private String status = MSG_STATUS_MSG_INIT; private Map customOuterMimeHeaders = new HashMap(); private String payloadFilename = null; - private String appTitle; public BaseMessage() { @@ -53,13 +53,7 @@ public BaseMessage() { public String getAppTitle() { - return appTitle; - } - - public void setAppTitle(String title) - { - appTitle = title; - + return Properties.getProperty(Properties.APP_TITLE_PROP, "OpenAS2 Server"); } public Map getOptions() { @@ -111,10 +105,6 @@ public String getAttribute(String key) { return (String) getAttributes().get(key); } - public void setAttributes(Map attributes) { - this.attributes = attributes; - } - public Map getAttributes() { if (attributes == null) { attributes = new HashMap(); @@ -123,14 +113,20 @@ public Map getAttributes() { return attributes; } - public void setContentType(String contentType) { - setHeader("Content-Type", contentType); + public void setAttributes(Map attributes) + { + this.attributes = attributes; } public String getContentType() { return getHeader("Content-Type"); } + public void setContentType(String contentType) + { + setHeader("Content-Type", contentType); + } + public String getCompressionType() { return (compressionType); } @@ -141,18 +137,19 @@ public void setCompressionType(String myCompressionType) { /** * @since 2007-06-01 - * @param contentDisposition + * @return */ - public void setContentDisposition(String contentDisposition) { - setHeader("Content-Disposition", contentDisposition); + public String getContentDisposition() { + return getHeader("Content-Disposition"); } /** + * @param contentDisposition * @since 2007-06-01 - * @return */ - public String getContentDisposition() { - return getHeader("Content-Disposition"); + public void setContentDisposition(String contentDisposition) + { + setHeader("Content-Disposition", contentDisposition); } public void setData(MimeBodyPart data, DataHistoryItem historyItem) { @@ -204,10 +201,6 @@ public String getHeader(String key, String delimiter) { return getHeaders().getHeader(key, delimiter); } - public void setHeaders(InternetHeaders headers) { - this.headers = headers; - } - public InternetHeaders getHeaders() { if (headers == null) { headers = new InternetHeaders(); @@ -216,8 +209,9 @@ public InternetHeaders getHeaders() { return headers; } - public void setHistory(DataHistory history) { - this.history = history; + public void setHeaders(InternetHeaders headers) + { + this.headers = headers; } public DataHistory getHistory() { @@ -228,24 +222,27 @@ public DataHistory getHistory() { return history; } - public void setMDN(MessageMDN mdn) { - this.MDN = mdn; + public void setHistory(DataHistory history) + { + this.history = history; } public MessageMDN getMDN() { return MDN; } - public void setMessageID(String messageID) { - setHeader("Message-ID", messageID); + public void setMDN(MessageMDN mdn) + { + this.MDN = mdn; } public String getMessageID() { return getHeader("Message-ID"); } - public void setPartnership(Partnership partnership) { - this.partnership = partnership; + public void setMessageID(String messageID) + { + setHeader("Message-ID", messageID); } public Partnership getPartnership() { @@ -256,16 +253,22 @@ public Partnership getPartnership() { return partnership; } - public abstract String generateMessageID() throws InvalidParameterException; - - public void setSubject(String subject) { - setHeader("Subject", subject); + public void setPartnership(Partnership partnership) + { + this.partnership = partnership; } + public abstract String generateMessageID() throws InvalidParameterException; + public String getSubject() { return getHeader("Subject"); } + public void setSubject(String subject) + { + setHeader("Subject", subject); + } + public boolean isRxdMsgWasSigned() { return rxdMsgWasSigned; @@ -370,7 +373,7 @@ private void writeObject(java.io.ObjectOutputStream out) Enumeration en = headers.getAllHeaderLines(); while (en.hasMoreElements()) { - out.writeBytes(en.nextElement().toString() + "\r\n"); + out.writeBytes(en.nextElement() + "\r\n"); } out.writeBytes(new String("\r\n")); @@ -400,14 +403,15 @@ public String getLogMsgID() { return " [" + getMessageID() + "]"; } - public void setLogMsg(String msg) { - logMsg = msg; - } - public String getLogMsg() { return logMsg; } + public void setLogMsg(String msg) + { + logMsg = msg; + } + public String getCalculatedMIC() { return calculatedMIC; diff --git a/Server/src/main/java/org/openas2/message/BaseMessageMDN.java b/Server/src/main/java/org/openas2/message/BaseMessageMDN.java index d5173fc5..5a4925b2 100644 --- a/Server/src/main/java/org/openas2/message/BaseMessageMDN.java +++ b/Server/src/main/java/org/openas2/message/BaseMessageMDN.java @@ -2,10 +2,12 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; +import org.openas2.params.InvalidParameterException; import java.util.Enumeration; import java.util.HashMap; import java.util.Map; +import javax.annotation.Nonnull; import javax.mail.Header; import javax.mail.MessagingException; import javax.mail.internet.InternetHeaders; @@ -16,18 +18,20 @@ public abstract class BaseMessageMDN implements MessageMDN { /** - * - */ - private static final long serialVersionUID = 1L; - private DataHistory history; + * + */ + private static final long serialVersionUID = 1L; + private DataHistory history; private InternetHeaders headers; private Partnership partnership; private Map attributes; + @Nonnull private Message message; private MimeBodyPart data; private String text; - public BaseMessageMDN(Message msg) { + public BaseMessageMDN(@Nonnull Message msg) + { super(); this.message = msg; msg.setMDN(this); @@ -38,11 +42,7 @@ public void setAttribute(String key, String value) { } public String getAttribute(String key) { - return (String) getAttributes().get(key); - } - - public void setAttributes(Map attributes) { - this.attributes = attributes; + return getAttributes().get(key); } public Map getAttributes() { @@ -53,14 +53,20 @@ public Map getAttributes() { return attributes; } - public void setData(MimeBodyPart data) { - this.data = data; + public void setAttributes(Map attributes) + { + this.attributes = attributes; } public MimeBodyPart getData() { return data; } + public void setData(MimeBodyPart data) + { + this.data = data; + } + public void setHeader(String key, String value) { getHeaders().setHeader(key, value); } @@ -73,10 +79,6 @@ public String getHeader(String key, String delimiter) { return getHeaders().getHeader(key, delimiter); } - public void setHeaders(InternetHeaders headers) { - this.headers = headers; - } - public InternetHeaders getHeaders() { if (headers == null) { headers = new InternetHeaders(); @@ -85,32 +87,37 @@ public InternetHeaders getHeaders() { return headers; } + public void setHeaders(InternetHeaders headers) + { + this.headers = headers; + } + public void copyHeaders(InternetHeaders srcHeaders) { Enumeration
headerEn = srcHeaders.getAllHeaders(); while (headerEn.hasMoreElements()) { Header header = headerEn.nextElement(); setHeader(header.getName(), header.getValue()); - } - } - - public void setMessage(Message message) { - this.message = message; + } } + @Nonnull public Message getMessage() { return message; } - public void setMessageID(String messageID) { - setHeader("Message-ID", messageID); + @Override + public void setMessage(@Nonnull Message message) + { + this.message = message; } public String getMessageID() { return getHeader("Message-ID"); } - - public void setPartnership(Partnership partnership) { - this.partnership = partnership; + + public void setMessageID(String messageID) + { + setHeader("Message-ID", messageID); } public Partnership getPartnership() { @@ -120,24 +127,26 @@ public Partnership getPartnership() { return partnership; } - - public void setText(String text) { - this.text = text; + + public void setPartnership(Partnership partnership) + { + this.partnership = partnership; } public String getText() { return text; } + public void setText(String text) + { + this.text = text; + } + public void addHeader(String key, String value) { getHeaders().addHeader(key, value); } - public abstract String generateMessageID(); - - public void setHistory(DataHistory history) { - this.history = history; - } + public abstract String generateMessageID() throws InvalidParameterException; public DataHistory getHistory() { if (history == null) { @@ -147,11 +156,16 @@ public DataHistory getHistory() { return history; } + public void setHistory(DataHistory history) + { + this.history = history; + } + public String toString() { StringBuffer buf = new StringBuffer(); buf.append("MDN From:").append(getPartnership().getReceiverIDs()); buf.append("To:").append(getPartnership().getSenderIDs()); - + Enumeration
headerEn = getHeaders().getAllHeaders(); buf.append(System.getProperty("line.separator") + "Headers:{"); @@ -172,17 +186,18 @@ public String toString() { return buf.toString(); } - public void updateMessageID() { + public void updateMessageID() throws InvalidParameterException { setMessageID(generateMessageID()); } private void readObject(java.io.ObjectInputStream in) - throws IOException, ClassNotFoundException { + throws IOException, ClassNotFoundException + { // read in partnership partnership = (Partnership) in.readObject(); - + // read in attributes - attributes = (Map) in.readObject(); + attributes = (Map) in.readObject(); // read in text text = (String) in.readObject(); @@ -203,10 +218,11 @@ private void readObject(java.io.ObjectInputStream in) } private void writeObject(java.io.ObjectOutputStream out) - throws IOException { + throws IOException + { // write partnership info out.writeObject(partnership); - + // write attributes out.writeObject(attributes); @@ -217,10 +233,10 @@ private void writeObject(java.io.ObjectOutputStream out) Enumeration en = headers.getAllHeaderLines(); while (en.hasMoreElements()) { - out.writeBytes(en.nextElement().toString() + "\r\n"); + out.writeBytes(en.nextElement() + "\r\n"); } - out.writeBytes(new String("\r\n")); + out.writeBytes("\r\n"); // write the mime body ByteArrayOutputStream baos = new ByteArrayOutputStream(); diff --git a/Server/src/main/java/org/openas2/message/CryptoHistoryItem.java b/Server/src/main/java/org/openas2/message/CryptoHistoryItem.java deleted file mode 100644 index 7a5f757f..00000000 --- a/Server/src/main/java/org/openas2/message/CryptoHistoryItem.java +++ /dev/null @@ -1,8 +0,0 @@ -package org.openas2.message; - -public interface CryptoHistoryItem { - public static final String HIA_ENCRYPTED = "encrypted"; - public static final String HIA_DECRYPTED = "decrypted"; - public static final String HIA_ALGORITHM = "algorithm"; - public static final String HIA_KEYSIZE = "keysize"; -} diff --git a/Server/src/main/java/org/openas2/message/InvalidMessageException.java b/Server/src/main/java/org/openas2/message/InvalidMessageException.java index e2d36412..ca31790d 100644 --- a/Server/src/main/java/org/openas2/message/InvalidMessageException.java +++ b/Server/src/main/java/org/openas2/message/InvalidMessageException.java @@ -4,14 +4,6 @@ public class InvalidMessageException extends OpenAS2Exception { - /** - * - */ - private static final long serialVersionUID = 1L; - - public InvalidMessageException() { - super(); - } public InvalidMessageException(String msg) { super(msg); diff --git a/Server/src/main/java/org/openas2/message/Message.java b/Server/src/main/java/org/openas2/message/Message.java index 8c702228..9b731284 100644 --- a/Server/src/main/java/org/openas2/message/Message.java +++ b/Server/src/main/java/org/openas2/message/Message.java @@ -3,7 +3,6 @@ import java.io.Serializable; import java.util.HashMap; import java.util.Map; - import javax.mail.internet.InternetHeaders; import javax.mail.internet.MimeBodyPart; import javax.mail.internet.ParseException; @@ -15,42 +14,43 @@ public interface Message extends Serializable { - - public static final String MSG_STATUS_MSG_INIT = "initializing_msg"; - public static final String MSG_STATUS_MSG_SEND = "sending_msg"; - public static final String MSG_STATUS_MSG_RESEND = "resending_msg"; - public static final String MSG_STATUS_MDN_SEND = "sending_mdn"; - public static final String MSG_STATUS_MDN_WAIT = "awaiting_mdn"; - public static final String MSG_STATUS_MDN_PARSE = "parsing_mdn"; - public static final String MSG_STATUS_MDN_VERIFY = "verifying_mdn"; - public static final String MSG_STATUS_MDN_PROCESS_INIT = "init_processing_mdn"; - public static final String MSG_STATUS_MSG_CLEANUP = "cleanup"; - - public static final String MSG_STATE_SEND_START = "msg_send_start"; - public static final String MSG_STATE_SEND_EXCEPTION = "msg_send_exception"; - public static final String MSG_STATE_SEND_FAIL = "msg_send_fail"; - public static final String MSG_STATE_RECEIVE_START = "msg_receive_start"; - public static final String MSG_STATE_RECEIVE_EXCEPTION = "msg_receive_exception"; - public static final String MSG_STATE_RECEIVE_FAIL = "msg_receive_fail"; - public static final String MSG_STATE_MDN_ERROR_RESPONSE_START = "msg_receive_error_sending_mdn_error"; - public static final String MSG_STATE_MDN_SENDING_EXCEPTION = "mdn_sending_exception"; - public static final String MSG_STATE_MDN_RECEIVING_EXCEPTION = "mdn_receiving_exception"; - public static final String MSG_STATE_MDN_SEND_START = "mdn_send_start"; - public static final String MSG_STATE_MDN_RECEIVE_START = "mdn_receive_start"; - public static final String MSG_STATE_MSG_SENT_MDN_RECEIVED_ERROR = "msg_sent_mdn_received_error"; - public static final String MSG_STATE_MSG_SENT_MDN_RECEIVED_OK = "msg_sent_mdn_received_ok"; - public static final String MSG_STATE_MSG_RXD_MDN_SENDING_FAIL = "msg_rxd_mdn_sending_fail"; - public static final String MSG_STATE_MSG_RXD_MDN_SENT_OK = "msg_rxd_mdn_sent_ok"; - public static final String MSG_STATE_MIC_MISMATCH = "msg_sent_mdn_received_mic_mismatch"; - - public static Map STATE_MSGS = new HashMap() - { + + String MSG_STATUS_MSG_INIT = "initializing_msg"; + String MSG_STATUS_MSG_SEND = "sending_msg"; + String MSG_STATUS_MSG_RESEND = "resending_msg"; + String MSG_STATUS_MDN_SEND = "sending_mdn"; + String MSG_STATUS_MDN_WAIT = "awaiting_mdn"; + String MSG_STATUS_MDN_PARSE = "parsing_mdn"; + String MSG_STATUS_MDN_VERIFY = "verifying_mdn"; + String MSG_STATUS_MDN_PROCESS_INIT = "init_processing_mdn"; + String MSG_STATUS_MSG_CLEANUP = "cleanup"; + + String MSG_STATE_SEND_START = "msg_send_start"; + String MSG_STATE_SEND_EXCEPTION = "msg_send_exception"; + String MSG_STATE_SEND_FAIL = "msg_send_fail"; + String MSG_STATE_SEND_FAIL_RESEND_QUEUED = "msg_send_fail_resend_queued"; + String MSG_STATE_RECEIVE_START = "msg_receive_start"; + String MSG_STATE_RECEIVE_EXCEPTION = "msg_receive_exception"; + String MSG_STATE_RECEIVE_FAIL = "msg_receive_fail"; + String MSG_STATE_MDN_ERROR_RESPONSE_START = "msg_receive_error_sending_mdn_error"; + String MSG_STATE_MDN_SENDING_EXCEPTION = "mdn_sending_exception"; + String MSG_STATE_MDN_RECEIVING_EXCEPTION = "mdn_receiving_exception"; + String MSG_STATE_MDN_SEND_START = "mdn_send_start"; + String MSG_STATE_MDN_RECEIVE_START = "mdn_receive_start"; + String MSG_STATE_MSG_SENT_MDN_RECEIVED_ERROR = "msg_sent_mdn_received_error"; + String MSG_STATE_MSG_SENT_MDN_RECEIVED_OK = "msg_sent_mdn_received_ok"; + String MSG_STATE_MSG_RXD_MDN_SENDING_FAIL = "msg_rxd_mdn_sending_fail"; + String MSG_STATE_MSG_RXD_MDN_SENT_OK = "msg_rxd_mdn_sent_ok"; + String MSG_STATE_MIC_MISMATCH = "msg_sent_mdn_received_mic_mismatch"; + + Map STATE_MSGS = new HashMap() { private static final long serialVersionUID = 5L; { put(MSG_STATE_SEND_START, "Message sending started"); put(MSG_STATE_SEND_EXCEPTION, "Message sending exception occurred. Resend queued"); put(MSG_STATE_SEND_FAIL, "Message sending failed."); + put(MSG_STATE_SEND_FAIL_RESEND_QUEUED, "Message failed to send and resend will be attempted using a different message ID"); put(MSG_STATE_RECEIVE_START, "Message receiving started"); put(MSG_STATE_RECEIVE_EXCEPTION, "Processing exception occurred receiving message. Resend queued"); put(MSG_STATE_RECEIVE_FAIL, "Failed to receive inbound message successfully."); @@ -69,112 +69,114 @@ public interface Message extends Serializable { } }; - public static final String SMIME_TYPE_COMPRESSED_DATA = "smime-type=compressed-data"; - - public String getPayloadFilename(); - public void setPayloadFilename(String filename); - public String extractPayloadFilename() throws ParseException; - - public void setStatus(String status); + String SMIME_TYPE_COMPRESSED_DATA = "smime-type=compressed-data"; + + String getPayloadFilename(); + + void setPayloadFilename(String filename); + + String extractPayloadFilename() throws ParseException; + + String getStatus(); + + void setStatus(String status); + + Map getCustomOuterMimeHeaders(); + + void setCustomOuterMimeHeaders(Map customOuterMimeHeaders); + + void addCustomOuterMimeHeader(String key, String value); + + Map getOptions(); + + void setOption(Object key, Object value); + + Object getOption(Object key); - public String getStatus(); - - public Map getCustomOuterMimeHeaders(); + void setAttribute(String key, String value); - public void setCustomOuterMimeHeaders(Map customOuterMimeHeaders); + String getAttribute(String key); - public void addCustomOuterMimeHeader(String key, String value); + Map getAttributes(); - public Map getOptions(); + void setAttributes(Map attributes); - public void setOption(Object key, Object value); + String getContentType(); - public Object getOption(Object key); - - public void setAttribute(String key, String value); + void setContentType(String contentType); - public String getAttribute(String key); + String getCompressionType(); - public void setAttributes(Map attributes); + void setCompressionType(String compressionType); - public Map getAttributes(); + String getContentDisposition(); - public void setContentType(String contentType); + void setContentDisposition(String contentDisposition); - public String getContentType(); + void setData(MimeBodyPart data, DataHistoryItem historyItem) throws OpenAS2Exception; - public String getCompressionType(); + DataHistoryItem setData(MimeBodyPart data) throws OpenAS2Exception; - public void setCompressionType(String compressionType); - - public void setContentDisposition(String contentDisposition); + MimeBodyPart getData(); - public String getContentDisposition(); + void setHeader(String key, String value); - public void setData(MimeBodyPart data, DataHistoryItem historyItem) throws OpenAS2Exception; - - public DataHistoryItem setData(MimeBodyPart data) throws OpenAS2Exception; + String getHeader(String key); - public MimeBodyPart getData(); + String getHeader(String key, String delimiter); - public void setHeader(String key, String value); + InternetHeaders getHeaders(); - public String getHeader(String key); + void setHeaders(InternetHeaders headers); - public String getHeader(String key, String delimiter); + DataHistory getHistory(); - public void setHeaders(InternetHeaders headers); + void setHistory(DataHistory history); - public InternetHeaders getHeaders(); + MessageMDN getMDN(); - public void setHistory(DataHistory history); + void setMDN(MessageMDN mdn); - public DataHistory getHistory(); + String getMessageID(); - public void setMDN(MessageMDN mdn); + void setMessageID(String messageID); - public MessageMDN getMDN(); + Partnership getPartnership(); - public void setMessageID(String messageID); + void setPartnership(Partnership partnership); - public String getMessageID(); + String getProtocol(); - public void setPartnership(Partnership partnership); + boolean isRequestingMDN(); - public Partnership getPartnership(); + boolean isConfiguredForMDN(); - public String getProtocol(); + boolean isRequestingAsynchMDN(); - public boolean isRequestingMDN(); + boolean isConfiguredForAsynchMDN(); - public boolean isConfiguredForMDN(); + String getSubject(); - public boolean isRequestingAsynchMDN(); + void setSubject(String subject); - public boolean isConfiguredForAsynchMDN(); + void addHeader(String key, String value); - public void setSubject(String subject); + String generateMessageID() throws InvalidParameterException; - public String getSubject(); + void updateMessageID() throws InvalidParameterException; - public void addHeader(String key, String value); + String getLogMsgID(); - public String generateMessageID() throws InvalidParameterException; + String getLogMsg(); - public void updateMessageID() throws InvalidParameterException; - - public String getLogMsgID(); + void setLogMsg(String msg); - public String getLogMsg(); + void trackMsgState(Session session); - public void setLogMsg(String msg); - - public void trackMsgState(Session session); + String getCalculatedMIC(); - public String getCalculatedMIC(); + void setCalculatedMIC(String calculatedMIC); - public void setCalculatedMIC(String calculatedMIC); + String getAppTitle(); - public String getAppTitle(); - public void setAppTitle(String title); } diff --git a/Server/src/main/java/org/openas2/message/MessageMDN.java b/Server/src/main/java/org/openas2/message/MessageMDN.java index f92bd6f0..0b44c15a 100644 --- a/Server/src/main/java/org/openas2/message/MessageMDN.java +++ b/Server/src/main/java/org/openas2/message/MessageMDN.java @@ -6,6 +6,7 @@ import javax.mail.internet.InternetHeaders; import javax.mail.internet.MimeBodyPart; +import org.openas2.params.InvalidParameterException; import org.openas2.partner.Partnership; @@ -54,7 +55,7 @@ public interface MessageMDN extends Serializable { public void addHeader(String key, String value); - public String generateMessageID(); + public String generateMessageID() throws InvalidParameterException; - public void updateMessageID(); + public void updateMessageID() throws InvalidParameterException; } diff --git a/Server/src/main/java/org/openas2/params/MessageParameters.java b/Server/src/main/java/org/openas2/params/MessageParameters.java index 98e52a34..0b1c67cf 100644 --- a/Server/src/main/java/org/openas2/params/MessageParameters.java +++ b/Server/src/main/java/org/openas2/params/MessageParameters.java @@ -76,9 +76,11 @@ public String getParameter(String key) throws InvalidParameterException { { logger.warn("Failed to extract filename from content-disposition: " + org.openas2.logging.Log.getExceptionMsg(e), e); } + if (s == null || s.length() < 1) + s = getTarget().getPayloadFilename(); if (s != null && s.length() > 0) return s; - return filename; + return filename; } else { throw new InvalidParameterException("Invalid area in key", this, key, null); } diff --git a/Server/src/main/java/org/openas2/params/RandomParameters.java b/Server/src/main/java/org/openas2/params/RandomParameters.java index d0550d71..32932995 100644 --- a/Server/src/main/java/org/openas2/params/RandomParameters.java +++ b/Server/src/main/java/org/openas2/params/RandomParameters.java @@ -1,10 +1,9 @@ package org.openas2.params; import java.text.DecimalFormat; +import java.util.Random; import java.util.UUID; -import org.openas2.util.RandomUtil; - public class RandomParameters extends ParameterParser { public void setParameter(String key, String value) throws InvalidParameterException { throw new InvalidParameterException("Set not supported", this, key, value); @@ -28,7 +27,7 @@ public String getParameter(String key) throws InvalidParameterException { } DecimalFormat randomFormatter = new DecimalFormat(fmt); - return randomFormatter.format(RandomUtil.getRandomGenerator().nextInt(max)); + return randomFormatter.format(new Random().nextInt(max)); } } diff --git a/Server/src/main/java/org/openas2/partner/AS1Partnership.java b/Server/src/main/java/org/openas2/partner/AS1Partnership.java deleted file mode 100644 index 04bbb0b7..00000000 --- a/Server/src/main/java/org/openas2/partner/AS1Partnership.java +++ /dev/null @@ -1,9 +0,0 @@ - -package org.openas2.partner; - - -public interface AS1Partnership { - public static final String PID_AS1 = "as1_id"; // AS1 ID - - -} diff --git a/Server/src/main/java/org/openas2/partner/EDIPartnership.java b/Server/src/main/java/org/openas2/partner/EDIPartnership.java deleted file mode 100644 index 8e85612d..00000000 --- a/Server/src/main/java/org/openas2/partner/EDIPartnership.java +++ /dev/null @@ -1,6 +0,0 @@ -package org.openas2.partner; - - -public interface EDIPartnership { - public static final String PID_EDI = "edi_id"; // EDI ID -} diff --git a/Server/src/main/java/org/openas2/partner/Partnership.java b/Server/src/main/java/org/openas2/partner/Partnership.java index da811439..b9750cd6 100644 --- a/Server/src/main/java/org/openas2/partner/Partnership.java +++ b/Server/src/main/java/org/openas2/partner/Partnership.java @@ -9,10 +9,6 @@ public class Partnership implements Serializable { - /** - * - */ - private static final long serialVersionUID = -8365608387462470629L; public static final String PTYPE_SENDER = "sender"; // Sender partner type public static final String PTYPE_RECEIVER = "receiver"; // Receiver partner type public static final String PID_EMAIL = "email"; // Email address @@ -21,31 +17,30 @@ public class Partnership implements Serializable { public static final String PA_CONTENT_TRANSFER_ENCODING = "content_transfer_encoding"; // optional content transfer enc value public static final String PA_REMOVE_PROTECTION_ATTRIB = "remove_cms_algorithm_protection_attrib"; // Some AS2 systems do not support the attribute public static final String PA_SET_CONTENT_TRANSFER_ENCODING_OMBP = "set_content_transfer_encoding_on_outer_mime_bodypart"; // optional content transfer enc value - - + /** + * + */ + private static final long serialVersionUID = -8365608387462470629L; private Map attributes; private Map receiverIDs; private Map senderIDs; private String name; - public void setName(String name) { - this.name = name; - } - public String getName() { return name; } + public void setName(String name) + { + this.name = name; + } + public void setAttribute(String id, String value) { getAttributes().put(id, value); } public String getAttribute(String id) { - return (String) getAttributes().get(id); - } - - public void setAttributes(Map attributes) { - this.attributes = attributes; + return getAttributes().get(id); } public Map getAttributes() { @@ -56,6 +51,11 @@ public Map getAttributes() { return attributes; } + public void setAttributes(Map attributes) + { + this.attributes = attributes; + } + public void setReceiverID(String id, String value) { getReceiverIDs().put(id, value); } @@ -64,10 +64,6 @@ public String getReceiverID(String id) { return (String) getReceiverIDs().get(id); } - public void setReceiverIDs(Map receiverIDs) { - this.receiverIDs = receiverIDs; - } - public Map getReceiverIDs() { if (receiverIDs == null) { receiverIDs = new HashMap(); @@ -76,6 +72,11 @@ public Map getReceiverIDs() { return receiverIDs; } + public void setReceiverIDs(Map receiverIDs) + { + this.receiverIDs = receiverIDs; + } + public void setSenderID(String id, String value) { getSenderIDs().put(id, value); } @@ -84,10 +85,6 @@ public String getSenderID(String id) { return (String) getSenderIDs().get(id); } - public void setSenderIDs(Map senderIDs) { - this.senderIDs = senderIDs; - } - public Map getSenderIDs() { if (senderIDs == null) { senderIDs = new HashMap(); @@ -96,6 +93,11 @@ public Map getSenderIDs() { return senderIDs; } + public void setSenderIDs(Map senderIDs) + { + this.senderIDs = senderIDs; + } + public boolean matches(Partnership partnership) { Map senderIDs = partnership.getSenderIDs(); Map receiverIDs = partnership.getReceiverIDs(); diff --git a/Server/src/main/java/org/openas2/partner/XMLPartnershipFactory.java b/Server/src/main/java/org/openas2/partner/XMLPartnershipFactory.java index 425f10e2..0143bc2c 100644 --- a/Server/src/main/java/org/openas2/partner/XMLPartnershipFactory.java +++ b/Server/src/main/java/org/openas2/partner/XMLPartnershipFactory.java @@ -4,8 +4,6 @@ import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; import java.io.PrintWriter; import java.text.DecimalFormat; import java.util.ArrayList; @@ -14,196 +12,172 @@ import java.util.List; import java.util.ListIterator; import java.util.Map; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; -import javax.xml.parsers.ParserConfigurationException; +import org.apache.commons.io.IOUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.openas2.OpenAS2Exception; +import org.openas2.schedule.HasSchedule; import org.openas2.Session; import org.openas2.WrappedException; import org.openas2.params.InvalidParameterException; -import org.openas2.util.FileMonitor; -import org.openas2.util.FileMonitorListener; +import org.openas2.support.FileMonitorAdapter; import org.openas2.util.XMLUtil; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; -import org.xml.sax.SAXException; -/** original author unknown - * +/** + * original author unknown + *

* this release added logic to store partnerships and provide methods for partner/partnership command line processor - * @author joseph mcverry * + * @author joseph mcverry */ -public class XMLPartnershipFactory extends BasePartnershipFactory - implements RefreshablePartnershipFactory, FileMonitorListener { +public class XMLPartnershipFactory extends BasePartnershipFactory implements HasSchedule { public static final String PARAM_FILENAME = "filename"; public static final String PARAM_INTERVAL = "interval"; - private FileMonitor fileMonitor; - private Map partners; - private Log logger = LogFactory.getLog(XMLPartnershipFactory.class.getSimpleName()); - - - public void setFileMonitor(FileMonitor fileMonitor) { - this.fileMonitor = fileMonitor; - } + private Map partners; - public FileMonitor getFileMonitor() throws InvalidParameterException { - boolean createMonitor = ((fileMonitor == null) && - (getParameter(PARAM_INTERVAL, false) != null)); + private Log logger = LogFactory.getLog(XMLPartnershipFactory.class.getSimpleName()); - if (!createMonitor && (fileMonitor != null)) { - String filename = fileMonitor.getFilename(); - createMonitor = ((filename != null) && !filename.equals(getFilename())); - } - - if (createMonitor) { - if (fileMonitor != null) { - fileMonitor.stop(); - } - int interval = getParameterInt(PARAM_INTERVAL, true); - File file = new File(getFilename()); - fileMonitor = new FileMonitor(file, interval); - fileMonitor.addListener(this); - } - - return fileMonitor; - } - - public void setFilename(String filename) { - getParameters().put(PARAM_FILENAME, filename); + private int getRefreshInterval() throws InvalidParameterException + { + return getParameterInt(PARAM_INTERVAL, false); } - public String getFilename() throws InvalidParameterException { + String getFilename() throws InvalidParameterException + { return getParameter(PARAM_FILENAME, true); } - public void setPartners(Map map) { - partners = map; - } - - public Map getPartners() { - if (partners == null) { - partners = new HashMap(); + public Map getPartners() + { + if (partners == null) + { + partners = new HashMap(); } return partners; } - public void handle(FileMonitor monitor, File file, int eventID) { - switch (eventID) { - case FileMonitorListener.EVENT_MODIFIED: - - try { - refresh(); - logger.debug("- Partnerships Reloaded -"); - } catch (OpenAS2Exception oae) { - oae.terminate(); - } - - break; - } + private void setPartners(Map map) + { + partners = map; } - public void init(Session session, Map parameters) throws OpenAS2Exception { + public void init(Session session, Map parameters) throws OpenAS2Exception + { super.init(session, parameters); refresh(); } - public void refresh() throws OpenAS2Exception { - try { - load(new FileInputStream(getFilename())); - - getFileMonitor(); - } catch (Exception e) { - throw new WrappedException(e); - } - } - - protected void load(InputStream in) - throws ParserConfigurationException, SAXException, IOException, OpenAS2Exception { - DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); - - DocumentBuilder parser = factory.newDocumentBuilder(); - Document document = parser.parse(in); - Element root = document.getDocumentElement(); - NodeList rootNodes = root.getChildNodes(); - Node rootNode; - String nodeName; - - Map newPartners = new HashMap(); - List newPartnerships = new ArrayList(); - - for (int i = 0; i < rootNodes.getLength(); i++) { - rootNode = rootNodes.item(i); - - nodeName = rootNode.getNodeName(); - - if (nodeName.equals("partner")) { - loadPartner(newPartners, rootNode); - } else if (nodeName.equals("partnership")) { - loadPartnership(newPartners, newPartnerships, rootNode); + void refresh() throws OpenAS2Exception + { + FileInputStream inputStream = null; + try + { + inputStream = new FileInputStream(getFilename()); + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + + DocumentBuilder parser = factory.newDocumentBuilder(); + Document document = parser.parse(inputStream); + Element root = document.getDocumentElement(); + NodeList rootNodes = root.getChildNodes(); + Node rootNode; + String nodeName; + + Map newPartners = new HashMap(); + List newPartnerships = new ArrayList(); + + for (int i = 0; i < rootNodes.getLength(); i++) + { + rootNode = rootNodes.item(i); + + nodeName = rootNode.getNodeName(); + + if (nodeName.equals("partner")) + { + loadPartner(newPartners, rootNode); + } else if (nodeName.equals("partnership")) + { + loadPartnership(newPartners, newPartnerships, rootNode); + } } - } - synchronized (this) { - setPartners(newPartners); - setPartnerships(newPartnerships); + synchronized (this) + { + setPartners(newPartners); + setPartnerships(newPartnerships); + } + } catch (Exception e) + { + throw new WrappedException(e); + } finally + { + IOUtils.closeQuietly(inputStream); } } - protected void loadAttributes(Node node, Partnership partnership) - throws OpenAS2Exception { + private void loadAttributes(Node node, Partnership partnership) + throws OpenAS2Exception + { Map nodes = XMLUtil.mapAttributeNodes(node.getChildNodes(), "attribute", "name", "value"); partnership.getAttributes().putAll(nodes); } - public void loadPartner(Map partners, Node node) - throws OpenAS2Exception { + public void loadPartner(Map partners, Node node) + throws OpenAS2Exception + { String[] requiredAttributes = {"name"}; - Map newPartner = XMLUtil.mapAttributes(node, requiredAttributes); - String name = (String) newPartner.get("name"); + Map newPartner = XMLUtil.mapAttributes(node, requiredAttributes); + String name = newPartner.get("name"); - if (partners.get(name) != null) { + if (partners.get(name) != null) + { throw new OpenAS2Exception("Partner is defined more than once: " + name); } partners.put(name, newPartner); } - - - protected void loadPartnerIDs(Map partners, String partnershipName, Node partnershipNode, - String partnerType, Map idMap) throws OpenAS2Exception { + + private void loadPartnerIDs(Map partners, String partnershipName, Node partnershipNode, + String partnerType, Map idMap) throws OpenAS2Exception + { Node partnerNode = XMLUtil.findChildNode(partnershipNode, partnerType); - if (partnerNode == null) { + if (partnerNode == null) + { throw new OpenAS2Exception("Partnership " + partnershipName + " is missing sender"); } Map partnerAttr = XMLUtil.mapAttributes(partnerNode); // check for a partner name, and look up in partners list if one is found - String partnerName = (String) partnerAttr.get("name"); + String partnerName = partnerAttr.get("name"); - if (partnerName != null) { - Map map = (Map) partners.get(partnerName); - Map partner = map; + if (partnerName != null) + { + Map map = (Map) partners.get(partnerName); + Map partner = map; - if (partner == null) { + if (partner == null) + { throw new OpenAS2Exception("Partnership " + partnershipName + " has an undefined " + - partnerType + ": " + partnerName); + partnerType + ": " + partnerName); } idMap.putAll(partner); @@ -213,15 +187,17 @@ protected void loadPartnerIDs(Map partners, String partnershipNam idMap.putAll(partnerAttr); } - public void loadPartnership(Map partners, List partnerships, Node node) - throws OpenAS2Exception { + public void loadPartnership(Map partners, List partnerships, Node node) + throws OpenAS2Exception + { Partnership partnership = new Partnership(); String[] requiredAttributes = {"name"}; - Map psAttributes = XMLUtil.mapAttributes(node, requiredAttributes); - String name = (String) psAttributes.get("name"); + Map psAttributes = XMLUtil.mapAttributes(node, requiredAttributes); + String name = psAttributes.get("name"); - if (getPartnership(partnerships, name) != null) { + if (getPartnership(partnerships, name) != null) + { throw new OpenAS2Exception("Partnership is defined more than once: " + name); } @@ -237,69 +213,94 @@ public void loadPartnership(Map partners, List partn // add the partnership to the list of available partnerships partnerships.add(partnership); } - + public void storePartnership() - throws OpenAS2Exception { - String fn = getFilename(); - - - DecimalFormat df = new DecimalFormat("0000000"); - long l = 0; - File f = null; - while (true) { - f = new File(fn+'.'+df.format(l)); - if (f.exists() == false) - break; - l++; - } - - logger.info("backing up "+ fn +" to "+f.getName()); - - File fr = new File(fn); - fr.renameTo(f); - - try { - PrintWriter pw = new PrintWriter(new FileOutputStream(fn)); - - - Map partner = partners; - pw.println(""); - Iterator> partnerIt = partner.entrySet().iterator(); - while (partnerIt.hasNext()) { - Map.Entry ptrnData = (Map.Entry) partnerIt.next(); - HashMap partnerMap = (HashMap) ptrnData.getValue(); - pw.print(" > attrIt = partnerMap.entrySet().iterator(); - while (attrIt.hasNext()) { - Map.Entry attribute = (Map.Entry) attrIt.next(); - pw.print(attribute.getKey()+"=\""+attribute.getValue()+"\""); - if (attrIt.hasNext()) - pw.print("\n "); - } - pw.println("/>"); - } - List partnerShips = getPartnerships(); - ListIterator partnerLIt = (ListIterator) partnerShips.listIterator(); - while (partnerLIt.hasNext()) { - Partnership partnership = (Partnership) partnerLIt.next(); - pw.println(" "); - pw.println(" "); - pw.println(" "); - Map partnershipMap = partnership.getAttributes(); - - Iterator> partnershipIt = partnershipMap.entrySet().iterator(); - while (partnershipIt.hasNext()) { - Map.Entry partnershipData = (Map.Entry) partnershipIt.next(); - pw.println(" " ); - - } - pw.println(" "); - } - pw.println(""); - pw.flush(); - pw.close(); - } catch (FileNotFoundException e) { - throw new WrappedException(e); - } + throws OpenAS2Exception + { + String fn = getFilename(); + + + DecimalFormat df = new DecimalFormat("0000000"); + long l = 0; + File f = null; + while (true) + { + f = new File(fn + '.' + df.format(l)); + if (f.exists() == false) + { + break; + } + l++; + } + + logger.info("backing up " + fn + " to " + f.getName()); + + File fr = new File(fn); + fr.renameTo(f); + + try + { + PrintWriter pw = new PrintWriter(new FileOutputStream(fn)); + + + Map partner = partners; + pw.println(""); + Iterator> partnerIt = partner.entrySet().iterator(); + while (partnerIt.hasNext()) + { + Map.Entry ptrnData = partnerIt.next(); + HashMap partnerMap = (HashMap) ptrnData.getValue(); + pw.print(" > attrIt = partnerMap.entrySet().iterator(); + while (attrIt.hasNext()) + { + Map.Entry attribute = attrIt.next(); + pw.print(attribute.getKey() + "=\"" + attribute.getValue() + "\""); + if (attrIt.hasNext()) + { + pw.print("\n "); + } + } + pw.println("/>"); + } + List partnerShips = getPartnerships(); + ListIterator partnerLIt = partnerShips.listIterator(); + while (partnerLIt.hasNext()) + { + Partnership partnership = partnerLIt.next(); + pw.println(" "); + pw.println(" "); + pw.println(" "); + Map partnershipMap = partnership.getAttributes(); + + Iterator> partnershipIt = partnershipMap.entrySet().iterator(); + while (partnershipIt.hasNext()) + { + Map.Entry partnershipData = partnershipIt.next(); + pw.println(" "); + + } + pw.println(" "); + } + pw.println(""); + pw.flush(); + pw.close(); + } catch (FileNotFoundException e) + { + throw new WrappedException(e); + } + } + + @Override + public void schedule(ScheduledExecutorService executor) throws OpenAS2Exception + { + new FileMonitorAdapter() { + @Override + public void onConfigFileChanged() throws OpenAS2Exception + { + refresh(); + logger.debug("- Partnerships Reloaded -"); + } + }.scheduleIfNeed(executor, new File(getFilename()), getRefreshInterval(), TimeUnit.SECONDS); } } diff --git a/Server/src/main/java/org/openas2/processor/BaseMDNProcessor.java b/Server/src/main/java/org/openas2/processor/BaseMDNProcessor.java deleted file mode 100644 index 43ca992d..00000000 --- a/Server/src/main/java/org/openas2/processor/BaseMDNProcessor.java +++ /dev/null @@ -1,7 +0,0 @@ -package org.openas2.processor; - -import org.openas2.BaseComponent; - -public abstract class BaseMDNProcessor extends BaseComponent implements MDNProcessorModule { - -} diff --git a/Server/src/main/java/org/openas2/processor/BaseMDNProcessorModule.java b/Server/src/main/java/org/openas2/processor/BaseMDNProcessorModule.java deleted file mode 100644 index 6a57544c..00000000 --- a/Server/src/main/java/org/openas2/processor/BaseMDNProcessorModule.java +++ /dev/null @@ -1,8 +0,0 @@ -package org.openas2.processor; - -import org.openas2.BaseComponent; - - -public abstract class BaseMDNProcessorModule extends BaseComponent implements MDNProcessorModule { - -} diff --git a/Server/src/main/java/org/openas2/processor/DefaultProcessor.java b/Server/src/main/java/org/openas2/processor/DefaultProcessor.java index 0395dad3..1ee83589 100644 --- a/Server/src/main/java/org/openas2/processor/DefaultProcessor.java +++ b/Server/src/main/java/org/openas2/processor/DefaultProcessor.java @@ -5,64 +5,68 @@ import java.util.List; import java.util.Map; +import org.apache.commons.lang3.ClassUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.openas2.BaseComponent; import org.openas2.OpenAS2Exception; import org.openas2.message.Message; -public class DefaultProcessor extends BaseProcessor { - private List modules; - private Log logger = LogFactory.getLog(DefaultProcessor.class.getSimpleName()); +public class DefaultProcessor extends BaseComponent implements Processor { + private List modules = new ArrayList(); + private Log logger = LogFactory.getLog(DefaultProcessor.class.getSimpleName()); - public List getActiveModules() { - List activeMods = new ArrayList(); + public List getActiveModules() + { + List activeMods = new ArrayList(); Iterator moduleIt = getModules().iterator(); ProcessorModule procMod; - while (moduleIt.hasNext()) { - procMod = (ProcessorModule) moduleIt.next(); + while (moduleIt.hasNext()) + { + procMod = moduleIt.next(); - if (procMod instanceof ActiveModule) { - activeMods.add(procMod); + if (procMod instanceof ActiveModule) + { + activeMods.add((ActiveModule)procMod); } } return activeMods; } - public void setModules(List modules) { - this.modules = modules; - } - - public List getModules() { - if (modules == null) { - modules = new ArrayList(); - } - + public List getModules() + { return modules; } public void handle(String action, Message msg, Map options) - throws OpenAS2Exception { + throws OpenAS2Exception + { Iterator moduleIt = getModules().iterator(); ProcessorModule module; ProcessorException pex = null; boolean moduleFound = false; - if (logger.isDebugEnabled()) - { - logger.debug("Processor searching for module handler for action: " + action); - } - - while (moduleIt.hasNext()) { - module = (ProcessorModule) moduleIt.next(); + if (logger.isDebugEnabled()) + { + logger.debug("Processor searching for module handler for action: " + action); + } + + while (moduleIt.hasNext()) + { + module = moduleIt.next(); - if (module.canHandle(action, msg, options)) { - try { + if (module.canHandle(action, msg, options)) + { + try + { moduleFound = true; module.handle(action, msg, options); - } catch (OpenAS2Exception oae) { - if (pex == null) { + } catch (OpenAS2Exception oae) + { + if (pex == null) + { pex = new ProcessorException(this); pex.getCauses().add(oae); } @@ -70,38 +74,71 @@ public void handle(String action, Message msg, Map options) } } - if (pex != null) { + if (pex != null) + { throw pex; - } else if (!moduleFound) { - if ("true".equalsIgnoreCase((String)options.get("OPTIONAL_MODULE"))) return; + } else if (!moduleFound) + { + if ("true".equalsIgnoreCase((String) options.get("OPTIONAL_MODULE"))) + { + return; + } msg.setLogMsg("No handler found for action: " + action); logger.error(msg); throw new NoModuleException(action, msg, options); } } - public void startActiveModules() throws OpenAS2Exception { - Iterator activeIt = getActiveModules().iterator(); - - while (activeIt.hasNext()) { - try { - ((ActiveModule) activeIt.next()).start(); - } catch (OpenAS2Exception e) { + public void startActiveModules() throws OpenAS2Exception + { + + List activeModules = getActiveModules(); + for (ActiveModule activeModule : activeModules) + { + try + { + activeModule.start(); + logger.info(ClassUtils.getSimpleName(activeModule.getClass()) + " started."); + } catch (OpenAS2Exception e) + { e.terminate(); throw e; } } + logger.info(activeModules.size() + " active module(s) started."); } - public void stopActiveModules() { - Iterator activeIt = getActiveModules().iterator(); - - while (activeIt.hasNext()) { - try { - ((ActiveModule) activeIt.next()).stop(); - } catch (OpenAS2Exception e) { + public void stopActiveModules() + { + + List activeModules = getActiveModules(); + int stopCnt = 0; + for (ActiveModule activeModule : activeModules) + { + try + { + if (activeModule.isRunning()) + { + activeModule.stop(); + stopCnt++; + logger.info(ClassUtils.getSimpleName(activeModule.getClass()) + " stopped."); + } + } catch (OpenAS2Exception e) + { e.terminate(); } } + + if (logger.isInfoEnabled()) + { + if (stopCnt >0) logger.info(stopCnt + " active module(s) stopped."); + else logger.info("No active module(s) are running."); + } + } + + @Override + public void destroy() throws Exception + { + stopActiveModules(); } } diff --git a/Server/src/main/java/org/openas2/processor/MDNProcessorModule.java b/Server/src/main/java/org/openas2/processor/MDNProcessorModule.java deleted file mode 100644 index f6c0379e..00000000 --- a/Server/src/main/java/org/openas2/processor/MDNProcessorModule.java +++ /dev/null @@ -1,15 +0,0 @@ -package org.openas2.processor; - -import java.util.Map; - -import org.openas2.Component; -import org.openas2.OpenAS2Exception; -import org.openas2.message.MessageMDN; - - -public interface MDNProcessorModule extends Component { - - public boolean canHandle(String action, MessageMDN msg, Map options); - - public void handle(String action, MessageMDN msg, Map options) throws OpenAS2Exception; -} diff --git a/Server/src/main/java/org/openas2/processor/Processor.java b/Server/src/main/java/org/openas2/processor/Processor.java index cb308c39..26bd0929 100644 --- a/Server/src/main/java/org/openas2/processor/Processor.java +++ b/Server/src/main/java/org/openas2/processor/Processor.java @@ -8,13 +8,15 @@ import org.openas2.message.Message; public interface Processor extends Component { - public static final String COMPID_PROCESSOR = "processor"; - - public void handle(String action, Message msg, Map options) throws OpenAS2Exception; - public List getModules(); - public void setModules(List modules); - - public void startActiveModules() throws OpenAS2Exception; - public void stopActiveModules(); - public List getActiveModules(); + String COMPID_PROCESSOR = "processor"; + + void handle(String action, Message msg, Map options) throws OpenAS2Exception; + + List getModules(); + + void startActiveModules() throws OpenAS2Exception; + + void stopActiveModules() throws OpenAS2Exception; + + List getActiveModules(); } diff --git a/Server/src/main/java/org/openas2/processor/msgtracking/BaseMsgTrackingModule.java b/Server/src/main/java/org/openas2/processor/msgtracking/BaseMsgTrackingModule.java index a593e6d0..15240605 100644 --- a/Server/src/main/java/org/openas2/processor/msgtracking/BaseMsgTrackingModule.java +++ b/Server/src/main/java/org/openas2/processor/msgtracking/BaseMsgTrackingModule.java @@ -1,127 +1,107 @@ -package org.openas2.processor.msgtracking; - -import java.util.HashMap; -import java.util.Map; - -import org.openas2.OpenAS2Exception; -import org.openas2.Session; -import org.openas2.lib.partner.IPartnership; -import org.openas2.message.AS2MessageMDN; -import org.openas2.message.Message; -import org.openas2.message.MessageMDN; -import org.openas2.partner.AS2Partnership; -import org.openas2.processor.BaseProcessorModule; - -public abstract class BaseMsgTrackingModule extends BaseProcessorModule implements TrackingModule { - /* - Enum Field - { - MSG_ID ("MSG_ID"), - MDN_ID ("MDN_ID"), - DIRECTION ("DIRECTION"), - IS_RESEND ("IS_RESEND"), - RESEND_COUNT ("RESEND_COUNT"), - SENDER_ID ("SENDER_ID"), - RECEIVER_ID ("RECEIVER_ID"), - STATUS ("STATUS"), - STATE ("STATE"), - SIGNATURE_ALGORITHM ("SIGNATURE_ALGORITHM"), - ENCRYPTION_ALGORITHM ("ENCRYPTION_ALGORITHM"), - COMPRESSION ("COMPRESSION"), - FILE_NAME ("FILE_NAME"), - CONTENT_TYPE ("CONTENT_TYPE"), - CONTENT_TRANSFER_ENCODING ("CONTENT_TRANSFER_ENCODING"), - MDN_MODE ("MDN_MODE"), - MDN_RESPONSE ("MDN_RESPONSE"), - STATE_MSG ("STATE_MSG"), - CREATE_DT ("CREATE_DT"), - UPDATE_DT ("UPDATE_DT"); - }; - */ - - public static class FIELDS - { - public static final String MSG_ID = "MSG_ID"; - public static final String MDN_ID = "MDN_ID"; - public static final String DIRECTION = "DIRECTION"; - public static final String IS_RESEND = "IS_RESEND"; - public static final String RESEND_COUNT = "RESEND_COUNT"; - public static final String SENDER_ID = "SENDER_ID"; - public static final String RECEIVER_ID = "RECEIVER_ID"; - public static final String STATUS = "STATUS"; - public static final String STATE = "STATE"; - public static final String SIGNATURE_ALGORITHM = "SIGNATURE_ALGORITHM"; - public static final String ENCRYPTION_ALGORITHM = "ENCRYPTION_ALGORITHM"; - public static final String COMPRESSION = "COMPRESSION"; - public static final String FILE_NAME = "FILE_NAME"; - public static final String CONTENT_TYPE = "CONTENT_TYPE"; - public static final String CONTENT_TRANSFER_ENCODING = "CONTENT_TRANSFER_ENCODING"; - public static final String MDN_MODE = "MDN_MODE"; - public static final String MDN_RESPONSE = "MDN_RESPONSE"; - public static final String STATE_MSG = "STATE_MSG"; - public static final String CREATE_DT = "CREATE_DT"; - public static final String UPDATE_DT = "UPDATE_DT"; - } - - public void handle(String action, Message msg, Map options) throws OpenAS2Exception { - - Map fields = buildMap(msg, options); - persist(msg, fields); - - } - - public boolean canHandle(String action, Message msg, Map options) { - return action.equals(getModuleAction()); - } - - public void init(Session session, Map options) throws OpenAS2Exception { - super.init(session, options); - } - - protected abstract String getModuleAction(); - - - protected abstract void persist(Message msg, Map map); - - protected Map buildMap(Message msg, Map options) - { - Map map = new HashMap(); - String msgId = msg.getMessageID(); - MessageMDN mdn = msg.getMDN(); - if (mdn != null) - { - map.put(FIELDS.MDN_ID, mdn.getMessageID()); - map.put(FIELDS.MDN_RESPONSE, msg.getMDN().getText()); - // Make sure we log against the original message ID since MDN can have different ID - String originalMsgId = mdn.getAttribute(AS2MessageMDN.MDNA_ORIG_MESSAGEID); - if (originalMsgId != null && !msgId.equals(originalMsgId)) msgId = originalMsgId; - } - map.put(FIELDS.MSG_ID, msgId); - // Default DIRECTION to SEND for now... - String direction = (String) options.get("DIRECTION"); - map.put(FIELDS.DIRECTION,direction==null?"SEND":direction); - String isResend = (String) options.get("IS_RESEND"); - if (isResend != null) map.put(FIELDS.IS_RESEND, isResend); - //map.put(FIELDS.RESEND_COUNT, ); - String sender = msg.getPartnership().getSenderID(AS2Partnership.PID_AS2); - if (sender == null) sender = mdn.getPartnership().getSenderID(AS2Partnership.PID_AS2); - map.put(FIELDS.SENDER_ID, sender); - String receiver = msg.getPartnership().getReceiverID(AS2Partnership.PID_AS2); - if (receiver == null) receiver = mdn.getPartnership().getReceiverID(AS2Partnership.PID_AS2); - map.put(FIELDS.RECEIVER_ID, receiver); - map.put(FIELDS.STATUS, msg.getStatus()); - String state = (String) options.get("STATE"); - map.put(FIELDS.STATE, state); - map.put(FIELDS.STATE_MSG, (String) Message.STATE_MSGS.get(state)); - map.put(FIELDS.SIGNATURE_ALGORITHM, msg.getPartnership().getAttribute(IPartnership.ATTRIBUTE_SIGNATURE_ALGORITHM)); - map.put(FIELDS.ENCRYPTION_ALGORITHM, msg.getPartnership().getAttribute(IPartnership.ATTRIBUTE_ENCRYPTION_ALGORITHM)); - map.put(FIELDS.COMPRESSION, msg.getPartnership().getAttribute(IPartnership.ATTRIBUTE_COMPRESSION_TYPE)); - map.put(FIELDS.FILE_NAME, msg.getPayloadFilename()); - map.put(FIELDS.CONTENT_TYPE, msg.getContentType()); - map.put(FIELDS.CONTENT_TRANSFER_ENCODING, msg.getHeader("Content-Transfer-Encoding")); - map.put(FIELDS.MDN_MODE, (msg.getPartnership().isAsyncMDN()?"ASYNC":"SYNC")); - - return map; - } - +package org.openas2.processor.msgtracking; + +import java.util.HashMap; +import java.util.Map; + +import org.openas2.OpenAS2Exception; +import org.openas2.Session; +import org.openas2.lib.partner.IPartnership; +import org.openas2.message.AS2MessageMDN; +import org.openas2.message.Message; +import org.openas2.message.MessageMDN; +import org.openas2.partner.AS2Partnership; +import org.openas2.processor.BaseProcessorModule; +import org.openas2.processor.resender.ResenderModule; + +public abstract class BaseMsgTrackingModule extends BaseProcessorModule implements TrackingModule { + + public void handle(String action, Message msg, Map options) throws OpenAS2Exception { + + Map fields = buildMap(msg, options); + persist(msg, fields); + + } + + public boolean canHandle(String action, Message msg, Map options) { + return action.equals(getModuleAction()); + } + + public void init(Session session, Map options) throws OpenAS2Exception { + super.init(session, options); + } + + protected abstract String getModuleAction(); + + protected abstract void persist(Message msg, Map map); + + protected Map buildMap(Message msg, Map options) + { + Map map = new HashMap(); + String msgId = msg.getMessageID(); + MessageMDN mdn = msg.getMDN(); + if (mdn != null) + { + map.put(FIELDS.MDN_ID, mdn.getMessageID()); + map.put(FIELDS.MDN_RESPONSE, msg.getMDN().getText()); + // Make sure we log against the original message ID since MDN can have different ID + String originalMsgId = mdn.getAttribute(AS2MessageMDN.MDNA_ORIG_MESSAGEID); + if (originalMsgId != null && !msgId.equals(originalMsgId)) msgId = originalMsgId; + } + map.put(FIELDS.MSG_ID, msgId); + map.put(FIELDS.PRIOR_MSG_ID, msg.getAttribute(FIELDS.PRIOR_MSG_ID)); + // Default DIRECTION to SEND for now... + String direction = (String) options.get("DIRECTION"); + map.put(FIELDS.DIRECTION,direction==null?"SEND":direction); + String isResend = (String) options.get("IS_RESEND"); + if (isResend != null) + { + map.put(FIELDS.IS_RESEND, isResend); + map.put(FIELDS.RESEND_COUNT, (String) options.get(ResenderModule.OPTION_RETRIES)); + } + //map.put(FIELDS.RESEND_COUNT, ); + String sender = msg.getPartnership().getSenderID(AS2Partnership.PID_AS2); + if (sender == null) sender = mdn.getPartnership().getSenderID(AS2Partnership.PID_AS2); + map.put(FIELDS.SENDER_ID, sender); + String receiver = msg.getPartnership().getReceiverID(AS2Partnership.PID_AS2); + if (receiver == null) receiver = mdn.getPartnership().getReceiverID(AS2Partnership.PID_AS2); + map.put(FIELDS.RECEIVER_ID, receiver); + map.put(FIELDS.STATUS, msg.getStatus()); + String state = (String) options.get("STATE"); + map.put(FIELDS.STATE, state); + map.put(FIELDS.STATE_MSG, Message.STATE_MSGS.get(state)); + map.put(FIELDS.SIGNATURE_ALGORITHM, msg.getPartnership().getAttribute(IPartnership.ATTRIBUTE_SIGNATURE_ALGORITHM)); + map.put(FIELDS.ENCRYPTION_ALGORITHM, msg.getPartnership().getAttribute(IPartnership.ATTRIBUTE_ENCRYPTION_ALGORITHM)); + map.put(FIELDS.COMPRESSION, msg.getPartnership().getAttribute(IPartnership.ATTRIBUTE_COMPRESSION_TYPE)); + map.put(FIELDS.FILE_NAME, msg.getPayloadFilename()); + map.put(FIELDS.CONTENT_TYPE, msg.getContentType()); + map.put(FIELDS.CONTENT_TRANSFER_ENCODING, msg.getHeader("Content-Transfer-Encoding")); + map.put(FIELDS.MDN_MODE, (msg.getPartnership().isAsyncMDN()?"ASYNC":"SYNC")); + + return map; + } + + public static class FIELDS { + public static final String MSG_ID = "MSG_ID"; + public static final String PRIOR_MSG_ID = "PRIOR_MSG_ID"; + public static final String MDN_ID = "MDN_ID"; + public static final String DIRECTION = "DIRECTION"; + public static final String IS_RESEND = "IS_RESEND"; + public static final String RESEND_COUNT = "RESEND_COUNT"; + public static final String SENDER_ID = "SENDER_ID"; + public static final String RECEIVER_ID = "RECEIVER_ID"; + public static final String STATUS = "STATUS"; + public static final String STATE = "STATE"; + public static final String SIGNATURE_ALGORITHM = "SIGNATURE_ALGORITHM"; + public static final String ENCRYPTION_ALGORITHM = "ENCRYPTION_ALGORITHM"; + public static final String COMPRESSION = "COMPRESSION"; + public static final String FILE_NAME = "FILE_NAME"; + public static final String CONTENT_TYPE = "CONTENT_TYPE"; + public static final String CONTENT_TRANSFER_ENCODING = "CONTENT_TRANSFER_ENCODING"; + public static final String MDN_MODE = "MDN_MODE"; + public static final String MDN_RESPONSE = "MDN_RESPONSE"; + public static final String STATE_MSG = "STATE_MSG"; + public static final String CREATE_DT = "CREATE_DT"; + public static final String UPDATE_DT = "UPDATE_DT"; + } + } \ No newline at end of file diff --git a/Server/src/main/java/org/openas2/processor/msgtracking/DbTrackingModule.java b/Server/src/main/java/org/openas2/processor/msgtracking/DbTrackingModule.java index 975244e6..fe4ff1f1 100644 --- a/Server/src/main/java/org/openas2/processor/msgtracking/DbTrackingModule.java +++ b/Server/src/main/java/org/openas2/processor/msgtracking/DbTrackingModule.java @@ -1,6 +1,7 @@ package org.openas2.processor.msgtracking; import java.sql.Connection; +import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.SQLException; @@ -10,10 +11,8 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.h2.tools.Server; import org.openas2.OpenAS2Exception; import org.openas2.Session; -import org.openas2.database.H2DBHandler; import org.openas2.message.Message; import org.openas2.params.ComponentParameters; import org.openas2.params.CompositeParameters; @@ -35,18 +34,20 @@ public class DbTrackingModule extends BaseMsgTrackingModule public static final String PARAM_JDBC_SERVER_URL = "jdbc_server_url"; public static final String PARAM_JDBC_PARAMS = "jdbc_extra_paramters"; - public static final String DEFAULT_TRACKING_DB_HANDLER_CLASS = "org.openas2.processor.msgtracking.DBHandler"; - public static final String PARAM_TRACKING_DB_HANDLER_CLASS = "tracking_db_handler_class"; + public static final String PARAM_USE_EMBEDDED_DB = "use_embedded_db"; + public static final String PARAM_FORCE_LOAD_JDBC_DRIVER = "force_load_jdbc_driver"; private String dbUser = null; private String dbPwd = null; - private String dbDirectory = null; private String jdbcConnectString = null; private String configBaseDir = null; - //private String jdbcDriver = null; + private String jdbcDriver = null; private boolean isRunning = false; private String sqlEscapeChar = "'"; - Server server = null; + private boolean useEmbeddedDB = true; + private boolean forceLoadJdbcDriver = false; + private String dbPlatform = "h2"; + IDBHandler dbHandler = null; private Log logger = LogFactory.getLog(DbTrackingModule.class.getSimpleName()); @@ -56,14 +57,32 @@ public void init(Session session, Map options) throws OpenAS2Exc CompositeParameters paramParser = createParser(); dbUser = getParameter(PARAM_DB_USER, true); dbPwd = getParameter(PARAM_DB_PWD, true); - dbDirectory = getParameter(PARAM_DB_DIRECTORY, true); configBaseDir = session.getBaseDirectory(); jdbcConnectString = getParameter(PARAM_JDBC_CONNECT_STRING, true); jdbcConnectString.replace("%home%", configBaseDir); // Support component attributes in connect string jdbcConnectString = ParameterParser.parse(jdbcConnectString, paramParser); - //jdbcDriver = getParameter(PARAM_JDBC_DRIVER, false); + dbPlatform = jdbcConnectString.replaceAll(".*jdbc:([^:]*):.*", "$1"); + jdbcDriver = getParameter(PARAM_JDBC_DRIVER, false); sqlEscapeChar = Properties.getProperty("sql_escape_character", "'"); + useEmbeddedDB = "true".equals(getParameter(PARAM_USE_EMBEDDED_DB, "true")); + forceLoadJdbcDriver = "true".equals(getParameter(PARAM_USE_EMBEDDED_DB, "false")); + if (!useEmbeddedDB && forceLoadJdbcDriver) + { + try + { + + Class.forName(jdbcDriver); + + } catch (ClassNotFoundException e) + { + + logger.error("Failed to load JDBC driver: " + jdbcDriver, e); + e.printStackTrace(); + return; + + } + } } protected String getModuleAction() @@ -84,24 +103,31 @@ protected void persist(Message msg, Map map) Connection conn = null; try { - conn = DBConnection.getConnection(jdbcConnectString, dbUser, dbPwd); + if (useEmbeddedDB) + conn = dbHandler.getConnection(); + else + { + conn = DriverManager.getConnection(jdbcConnectString, dbUser, dbPwd); + } Statement s = conn.createStatement(); String msgIdField = FIELDS.MSG_ID; - ResultSet rs = s.executeQuery("select * from msg_metadata where " + msgIdField + " = '" - + map.get(msgIdField) + "'"); + ResultSet rs = s.executeQuery( + "select * from msg_metadata where " + msgIdField + " = '" + map.get(msgIdField) + "'"); ResultSetMetaData meta = rs.getMetaData(); boolean isUpdate = rs.next(); // Record already exists so update StringBuffer fieldStmt = new StringBuffer(); + StringBuffer valuesStmt = new StringBuffer(); for (int i = 0; i < meta.getColumnCount(); i++) { String colName = meta.getColumnLabel(i + 1); - if (colName.equalsIgnoreCase("ID")) continue; + if (colName.equalsIgnoreCase("ID")) + continue; else if (colName.equalsIgnoreCase(FIELDS.UPDATE_DT)) { // Ignore if not update mode - if (isUpdate) appendField(colName, DateUtil.getSqlTimestamp(), fieldStmt, meta.getColumnType(i + 1)); - } - else if (colName.equalsIgnoreCase(FIELDS.CREATE_DT)) + if (isUpdate) + appendFieldForUpdate(colName, DateUtil.getSqlTimestamp(), fieldStmt, meta.getColumnType(i + 1)); + } else if (colName.equalsIgnoreCase(FIELDS.CREATE_DT)) map.remove(FIELDS.CREATE_DT); else if (isUpdate) { @@ -117,12 +143,16 @@ else if (isUpdate) // Unchanged value so remove from map continue; } - appendField(colName, mapVal, fieldStmt, meta.getColumnType(i + 1)); - } - else + appendFieldForUpdate(colName, mapVal, fieldStmt, meta.getColumnType(i + 1)); + } else { - // For new record add every field - appendField(colName, map.get(colName.toUpperCase()), fieldStmt, meta.getColumnType(i + 1)); + // For new record add every field that is not NULL + String mapVal = map.get(colName.toUpperCase()); + if (mapVal == null) + { + continue; + } + appendFieldForInsert(colName, mapVal, fieldStmt, valuesStmt, meta.getColumnType(i + 1)); } } if (fieldStmt.length() > 0) @@ -130,40 +160,35 @@ else if (isUpdate) String stmt = ""; if (isUpdate) { - stmt = "update msg_metadata set " + fieldStmt.toString() + " where " + FIELDS.MSG_ID + " = '" + map.get(msgIdField) + "'"; - } - else - stmt = "insert into msg_metadata set " + fieldStmt.toString(); + stmt = "update msg_metadata set " + fieldStmt.toString() + " where " + FIELDS.MSG_ID + " = '" + + map.get(msgIdField) + "'"; + } else + stmt = "insert into msg_metadata (" + fieldStmt.toString() + ") values (" + valuesStmt.toString() + ")"; if (s.executeUpdate(stmt) > 0) { - if (logger.isDebugEnabled()) logger.debug("Tracking record successfully persisted to database: " + map); - } - else + if (logger.isDebugEnabled()) + logger.debug("Tracking record successfully persisted to database: " + map); + } else { throw new OpenAS2Exception("Failed to persist tracking record to DB: " + map); } - } - else + } else { - if (logger.isInfoEnabled()) logger.info("No change from existing record in DB. Tracking record not updated: " + map); + if (logger.isInfoEnabled()) + logger.info("No change from existing record in DB. Tracking record not updated: " + map); } } catch (Exception e) { msg.setLogMsg("Failed to persist a tracking event: " + org.openas2.logging.Log.getExceptionMsg(e) + " ::: Data map: " + map); logger.error(msg, e); - } - finally + } finally { if (conn != null) { try { - DBConnection.releaseConnection(conn); - } catch (OpenAS2Exception e) - { - // TODO Auto-generated catch block - e.printStackTrace(); + conn.close(); } catch (SQLException e) { // TODO Auto-generated catch block @@ -173,126 +198,93 @@ else if (isUpdate) } } - - private void appendField(String name, String value, StringBuffer sb, int dataType) + + private String formatField(String value, int dataType) { - String valueEncap = "'"; - boolean requiresEncap = true; // Assume it is a field requiring encapsulation - if (value == null) requiresEncap = false; // setting field to NULL - else + if (value == null) + return "NULL"; + switch (dataType) { - switch (dataType) + case Types.BIGINT: + case Types.DECIMAL: + case Types.DOUBLE: + case Types.FLOAT: + case Types.INTEGER: + case Types.NUMERIC: + case Types.REAL: + case Types.SMALLINT: + case Types.BINARY: + case Types.TINYINT: + //case Types.ROWID: + return value; + case Types.TIME_WITH_TIMEZONE: + case Types.TIMESTAMP_WITH_TIMEZONE: + case Types.DATE: + case Types.TIME: + case Types.TIMESTAMP: + if ("oracle".equalsIgnoreCase(dbPlatform)) { - case Types.BIGINT: - case Types.DECIMAL: - case Types.DOUBLE: - case Types.FLOAT: - case Types.INTEGER: - case Types.NUMERIC: - case Types.REAL: - case Types.ROWID: - case Types.SMALLINT: - requiresEncap = false; - break; - } + if (value.length() > 19) + return ("TO_TIMESTAMP('" + value + "','YYYY-MM-DD HH24:MI:SS.FF')"); + else + return ("TO_DATE('" + value + "','YYYY-MM-DD HH24:MI:SS')"); + } else if ("mssql".equalsIgnoreCase(dbPlatform)) + return ("CAST('" + value + "' AS DATETIME)"); + else + return "'" + value + "'"; + } + // Must be some kind of string value if it gets here + return "'" + value.replaceAll("'", sqlEscapeChar + "'") + "'"; + } + + private void appendFieldForUpdate(String name, String value, StringBuffer sb, int dataType) + { if (sb.length() > 0) sb.append(","); - sb.append(name).append("="); - if (requiresEncap) sb.append(valueEncap).append(value.replaceAll("'", sqlEscapeChar+"'")).append(valueEncap); - else sb.append(value); + sb.append(name).append("=").append(formatField(value, dataType)); } - public boolean isRunning() + private void appendFieldForInsert(String name, String value, StringBuffer names, StringBuffer values, int dataType) { - return isRunning; - } - - public void start() throws OpenAS2Exception - { - DBConnection.start(jdbcConnectString, dbUser, dbPwd); - isRunning = true; - if ("true".equalsIgnoreCase(getParameter(PARAM_TCP_SERVER_START, "true"))) + if (names.length() > 0) { - String tcpPort = getParameter(PARAM_TCP_SERVER_PORT, "9092"); - String tcpPwd = getParameter(PARAM_TCP_SERVER_PWD, "OpenAS2"); - - try - { - server = Server.createTcpServer( "-tcpPort", tcpPort, "-tcpPassword", tcpPwd, "-baseDir", dbDirectory, "-tcpAllowOthers").start(); - } catch (SQLException e) - { - throw new OpenAS2Exception("Failed to start TCP server", e); - } + names.append(","); + values.append(","); } + + names.append(name); + values.append(formatField(value, dataType)); + } - public void stop() + public boolean isRunning() { - try - { - // Stopping the TCP server will stop the database so only do one of them - if (server != null) - { - server.shutdown(); - } - else - { - DBConnection.stop(jdbcConnectString); - } - isRunning = false; - } catch (Exception e) - { - if (logger.isErrorEnabled()) - logger.error("Failed to stop database for message tracking module.", e); - } + if (useEmbeddedDB) + return isRunning; + else + return true; } - /* - * Use a static class to make sure we only have one instance of the database - * connection pool no matter how many DbTrackingModule instances there are. - */ - private static class DBConnection + public void start() throws OpenAS2Exception { - private static H2DBHandler dbHandler = null; - - public static Connection getConnection(String connectString, String userId, String pwd) - throws OpenAS2Exception, SQLException - { - if (dbHandler == null) - throw new OpenAS2Exception("Database has not been started: " + connectString); - return dbHandler.getConnection(); - } + if (!useEmbeddedDB) + return; - public static void releaseConnection(Connection c) throws OpenAS2Exception, SQLException - { - if (dbHandler == null) - throw new OpenAS2Exception("Database has not been started trying to release connection"); - c.close(); - - } + dbHandler = new EmbeddedDBHandler(); + dbHandler.start(jdbcConnectString, dbUser, dbPwd, getParameters()); + isRunning = true; + } - public static void start(String connectString, String userId, String pwd) throws OpenAS2Exception - { - if (dbHandler == null) - { - dbHandler = new H2DBHandler(); - dbHandler.createConnectionPool(connectString, userId, pwd); - } else - throw new OpenAS2Exception("Database was already started: " + connectString); - } + public void stop() + { + if (!useEmbeddedDB) + return; - public static void stop(String connectString) throws OpenAS2Exception, SQLException - { - if (dbHandler != null) - { - dbHandler.shutdown(connectString); - dbHandler.destroyConnectionPool(); - } - } + dbHandler.stop(); } } \ No newline at end of file diff --git a/Server/src/main/java/org/openas2/processor/msgtracking/EmbeddedDBHandler.java b/Server/src/main/java/org/openas2/processor/msgtracking/EmbeddedDBHandler.java new file mode 100644 index 00000000..18fd6007 --- /dev/null +++ b/Server/src/main/java/org/openas2/processor/msgtracking/EmbeddedDBHandler.java @@ -0,0 +1,123 @@ +package org.openas2.processor.msgtracking; + +import java.sql.Connection; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +import javax.annotation.Nullable; + +import org.h2.jdbcx.JdbcConnectionPool; +import org.h2.tools.Server; +import org.openas2.OpenAS2Exception; + +class EmbeddedDBHandler extends DbTrackingModule implements IDBHandler { + + @Nullable + private JdbcConnectionPool cp = null; + + private Server server = null; + + private String connectString = "jdbc:h2:file:DB/openas2"; + + public void createConnectionPool(String connectString, String userName, String pwd) throws OpenAS2Exception + { + // Check that a connection pool is not already running + if (cp != null) + { + throw new OpenAS2Exception( + "Connection pool already initialized. Cannot create a new connection pool. Stop current one first. DB connect string:" + + connectString + " :: Active pool connect string: " + this.connectString); + } + this.connectString = connectString; + + cp = JdbcConnectionPool.create(connectString, userName, pwd); + } + + public void start(String connectString, String userName, String pwd, Map params) throws OpenAS2Exception + { + createConnectionPool(connectString, userName, pwd); + if ("true".equalsIgnoreCase(getParameter(PARAM_TCP_SERVER_START, "true"))) + { + String tcpPort = params.getOrDefault(PARAM_TCP_SERVER_PORT, "9092"); + String tcpPwd = params.getOrDefault(PARAM_TCP_SERVER_PWD, "OpenAS2"); + String dbDirectory = params.get(PARAM_DB_DIRECTORY); + if (dbDirectory == null || dbDirectory.length() < 1) + throw new OpenAS2Exception("TCP server requireds parameter: " + PARAM_DB_DIRECTORY); + + try + { + server = Server.createTcpServer( "-tcpPort", tcpPort, "-tcpPassword", tcpPwd, "-baseDir", dbDirectory, "-tcpAllowOthers").start(); + } catch (SQLException e) + { + throw new OpenAS2Exception("Failed to start TCP server", e); + } + } + } + + public void stop() + { + // Stopping the TCP server will stop the database so only do one of them + if (server != null) + { + server.stop(); + } + else + { + try + { + shutdown(connectString); + } catch (Exception e) + { + // TODO Auto-generated catch block + e.printStackTrace(); + } + destroyConnectionPool(); + } + } + + public void destroyConnectionPool() + { + if (cp == null) + { + return; + } + cp.dispose(); + cp = null; + } + + public Connection getConnection() throws SQLException, OpenAS2Exception + { + // Check that a connection pool is running + if (cp == null) + { + throw new OpenAS2Exception("Connection pool not initialized."); + } + return cp.getConnection(); + } + + public boolean shutdown(String connectString) throws SQLException, OpenAS2Exception + { + // Wait briefly if there are active connections + int waitCount = 0; + try + { + while (cp != null && cp.getActiveConnections() > 0 && waitCount < 10) + { + TimeUnit.MILLISECONDS.sleep(100); + waitCount++; + } + } catch (InterruptedException e) + { + // Do nothing + } + Connection c = getConnection(); + Statement st = c.createStatement(); + + boolean result = st.execute("SHUTDOWN"); + c.close(); + return result; + } + +} diff --git a/Server/src/main/java/org/openas2/processor/msgtracking/IDBHandler.java b/Server/src/main/java/org/openas2/processor/msgtracking/IDBHandler.java new file mode 100644 index 00000000..1b7283fb --- /dev/null +++ b/Server/src/main/java/org/openas2/processor/msgtracking/IDBHandler.java @@ -0,0 +1,22 @@ +package org.openas2.processor.msgtracking; + +import java.sql.Connection; +import java.sql.SQLException; +import java.util.Map; + +import org.openas2.OpenAS2Exception; + +interface IDBHandler { + + void createConnectionPool(String connectString, String userName, String pwd) throws OpenAS2Exception; + + void destroyConnectionPool(); + + Connection getConnection() throws SQLException, OpenAS2Exception; + + boolean shutdown(String connectString) throws SQLException, OpenAS2Exception; + + void start(String jdbcConnectString, String dbUser, String dbPwd, Map params) throws OpenAS2Exception; + + void stop(); +} diff --git a/Server/src/main/java/org/openas2/processor/msgtracking/TrackingModule.java b/Server/src/main/java/org/openas2/processor/msgtracking/TrackingModule.java index 6ae493e7..ce485f31 100644 --- a/Server/src/main/java/org/openas2/processor/msgtracking/TrackingModule.java +++ b/Server/src/main/java/org/openas2/processor/msgtracking/TrackingModule.java @@ -5,6 +5,6 @@ public interface TrackingModule extends ActiveModule { - public static final String DO_TRACK_MSG = "track_msg"; - public static final String TRACK_MSG_TCP_SERVER = "track_msg_tcp_server"; + String DO_TRACK_MSG = "track_msg"; + String TRACK_MSG_TCP_SERVER = "track_msg_tcp_server"; } diff --git a/Server/src/main/java/org/openas2/processor/receiver/AS2MDNReceiverHandler.java b/Server/src/main/java/org/openas2/processor/receiver/AS2MDNReceiverHandler.java index 02adb545..31ab2a73 100644 --- a/Server/src/main/java/org/openas2/processor/receiver/AS2MDNReceiverHandler.java +++ b/Server/src/main/java/org/openas2/processor/receiver/AS2MDNReceiverHandler.java @@ -49,8 +49,9 @@ public void handle(NetModule owner, Socket s) AS2Message msg = new AS2Message(); - Map options = new HashMap(); - options.put("DIRECTION", "SEND"); + + msg.setOption("DIRECTION", "SEND"); + byte[] data = null; // Read in the message request, headers, and data diff --git a/Server/src/main/java/org/openas2/processor/receiver/AS2ReceiverHandler.java b/Server/src/main/java/org/openas2/processor/receiver/AS2ReceiverHandler.java index ca28785a..a343de23 100644 --- a/Server/src/main/java/org/openas2/processor/receiver/AS2ReceiverHandler.java +++ b/Server/src/main/java/org/openas2/processor/receiver/AS2ReceiverHandler.java @@ -8,8 +8,6 @@ import java.security.PrivateKey; import java.security.cert.X509Certificate; import java.util.Enumeration; -import java.util.HashMap; -import java.util.Map; import javax.activation.DataHandler; import javax.mail.MessagingException; @@ -17,6 +15,7 @@ import javax.mail.internet.MimeBodyPart; import javax.mail.internet.ParseException; +import org.apache.commons.io.IOUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.openas2.DispositionException; @@ -70,8 +69,9 @@ public void handle(NetModule owner, Socket s) { byte[] data = null; BufferedOutputStream out; - Map options = new HashMap(); - options.put("DIRECTION", "RECEIVE"); + + msg.setOption("DIRECTION", "RECEIVE"); + try { out = new BufferedOutputStream(s.getOutputStream()); } catch (IOException e1) { @@ -459,8 +459,8 @@ protected void sendMDNResponse(AS2Message msg, BufferedOutputStream out, Disposi // make sure to set the content-length header ByteArrayOutputStream data = new ByteArrayOutputStream(); MimeBodyPart part = mdn.getData(); - IOUtilOld.copy(part.getInputStream(), data); - mdn.setHeader("Content-Length", Integer.toString(data.size())); + IOUtils.copy(part.getInputStream(), data); + mdn.setHeader("Content-Length", Integer.toString(data.size())); Enumeration headers = mdn.getHeaders().getAllHeaderLines(); diff --git a/Server/src/main/java/org/openas2/processor/receiver/DirectoryPollingModule.java b/Server/src/main/java/org/openas2/processor/receiver/DirectoryPollingModule.java index f2a20e7f..931f71f4 100644 --- a/Server/src/main/java/org/openas2/processor/receiver/DirectoryPollingModule.java +++ b/Server/src/main/java/org/openas2/processor/receiver/DirectoryPollingModule.java @@ -6,7 +6,6 @@ import java.io.FileOutputStream; import java.io.IOException; import java.util.HashMap; -import java.util.Iterator; import java.util.Map; import org.apache.commons.logging.Log; @@ -41,9 +40,9 @@ public void init(Session session, Map options) throws OpenAS2Exc sentDir = getParameter(PARAM_SENT_DIRECTORY, false); if (sentDir != null) IOUtilOld.getDirectoryFile(sentDir); - String pendingInfoFolder = (String) getSession().getProcessor().getParameters().get("pendingmdninfo"); + String pendingInfoFolder = getSession().getProcessor().getParameters().get("pendingmdninfo"); IOUtilOld.getDirectoryFile(pendingInfoFolder); - String pendingFolder = (String) getSession().getProcessor().getParameters().get("pendingmdn"); + String pendingFolder = getSession().getProcessor().getParameters().get("pendingmdn"); IOUtilOld.getDirectoryFile(pendingFolder); } catch (IOException e) @@ -121,7 +120,6 @@ protected boolean checkFile(File file) logger.debug("Directory poller detected a non-writable file and will be ignored: " + file.getCanonicalPath()); } catch (IOException e) { - // TODO Auto-generated catch block e.printStackTrace(); } } @@ -130,64 +128,63 @@ protected boolean checkFile(File file) return false; } - protected void trackFile(File file) - { + private void trackFile(File file) + { Map trackedFiles = getTrackedFiles(); String filePath = file.getAbsolutePath(); if (trackedFiles.get(filePath) == null) { - trackedFiles.put(filePath, new Long(file.length())); - } + trackedFiles.put(filePath, file.length()); + } } - protected void updateTracking() - { + private void updateTracking() + { // clone the trackedFiles map, iterator through the clone and modify the // original to avoid iterator exceptions // is there a better way to do this? Map trackedFiles = getTrackedFiles(); Map trackedFilesClone = new HashMap(trackedFiles); - for (Iterator> it = trackedFilesClone.entrySet().iterator(); it.hasNext();) - { - // get the file and it's stored length - Map.Entry fileEntry = it.next(); - File file = new File((String) fileEntry.getKey()); - long fileLength = ((Long) fileEntry.getValue()).longValue(); + for (Map.Entry fileEntry : trackedFilesClone.entrySet()) + { + // get the file and it's stored length + File file = new File(fileEntry.getKey()); + long fileLength = fileEntry.getValue().longValue(); // if the file no longer exists, remove it from the tracker - if (!checkFile(file)) - { - trackedFiles.remove(fileEntry.getKey()); - } else - { - // if the file length has changed, update the tracker + if (!checkFile(file)) + { + trackedFiles.remove(fileEntry.getKey()); + } else + { + // if the file length has changed, update the tracker long newLength = file.length(); - if (newLength != fileLength) - { - trackedFiles.put((String) fileEntry.getKey(), new Long(newLength)); - } else - { - // if the file length has stayed the same, process the file + if (newLength != fileLength) + { + trackedFiles.put(fileEntry.getKey(), new Long(newLength)); + } else + { + // if the file length has stayed the same, process the file // and stop tracking it - try - { - processFile(file); - } catch (OpenAS2Exception e) - { - e.terminate(); - try - { - IOUtilOld.handleError(file, errorDir); - } catch (OpenAS2Exception e1) - { - logger.error("Error handling file error for file: " + file.getAbsolutePath(), e1); + try + { + processFile(file); + } catch (OpenAS2Exception e) + { + e.terminate(); + try + { + IOUtilOld.handleError(file, errorDir); + } catch (OpenAS2Exception e1) + { + logger.error("Error handling file error for file: " + file.getAbsolutePath(), e1); forceStop(e1); return; } - } finally - { - trackedFiles.remove(fileEntry.getKey()); + } finally + { + trackedFiles.remove(fileEntry.getKey()); } } } @@ -218,8 +215,8 @@ protected void processFile(File file) throws OpenAS2Exception protected abstract Message createMessage(); - public Map getTrackedFiles() - { + private Map getTrackedFiles() + { if (trackedFiles == null) { trackedFiles = new HashMap(); diff --git a/Server/src/main/java/org/openas2/processor/receiver/MessageBuilderModule.java b/Server/src/main/java/org/openas2/processor/receiver/MessageBuilderModule.java index e35d00a2..f374627d 100644 --- a/Server/src/main/java/org/openas2/processor/receiver/MessageBuilderModule.java +++ b/Server/src/main/java/org/openas2/processor/receiver/MessageBuilderModule.java @@ -15,6 +15,7 @@ import javax.mail.MessagingException; import javax.mail.internet.MimeBodyPart; +import org.apache.commons.io.IOUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.openas2.OpenAS2Exception; @@ -31,7 +32,6 @@ import org.openas2.processor.resender.ResenderModule; import org.openas2.processor.sender.SenderModule; import org.openas2.util.AS2Util; -import org.openas2.util.IOUtilOld; public abstract class MessageBuilderModule extends BaseReceiverModule { @@ -53,10 +53,10 @@ public void init(Session session, Map options) throws OpenAS2Exc protected Message processDocument(InputStream ip, String filename) throws OpenAS2Exception, FileNotFoundException { - Message msg = createMessage(); - msg.setAttribute(FileAttribute.MA_FILENAME, filename); - String pendingFile = AS2Util.buildPendingFileName(msg, getSession().getProcessor(), "pendingmdn"); - msg.setAttribute(FileAttribute.MA_PENDINGFILE, pendingFile); + Message msg = buildMessageMetadata(filename); + + String pendingFile = msg.getAttribute(FileAttribute.MA_PENDINGFILE); + // Persist the file that has been passed in File doc = new File(pendingFile); FileOutputStream fo = null; try @@ -68,7 +68,7 @@ protected Message processDocument(InputStream ip, String filename) throws OpenAS } try { - IOUtilOld.copy(ip, fo); + IOUtils.copy(ip, fo); } catch (IOException e1) { fo = null; @@ -92,14 +92,11 @@ protected Message processDocument(InputStream ip, String filename) throws OpenAS e1.printStackTrace(); } fo = null; - msg.setAttribute(FileAttribute.MA_ERROR_DIR, getParameter(PARAM_ERROR_DIRECTORY, true)); - if (getParameter(PARAM_SENT_DIRECTORY, false) != null) - msg.setAttribute(FileAttribute.MA_SENT_DIR, getParameter(PARAM_SENT_DIRECTORY, false)); FileInputStream fis = new FileInputStream(doc); try { - updateMessage(msg, fis, filename); + buildMessageData(msg, fis, filename); } finally { @@ -210,8 +207,11 @@ protected Message processDocument(InputStream ip, String filename) throws OpenAS protected abstract Message createMessage(); - public void updateMessage(Message msg, InputStream ip, String filename) throws OpenAS2Exception + public Message buildMessageMetadata(String filename) throws OpenAS2Exception { + Message msg = createMessage(); + msg.setAttribute(FileAttribute.MA_FILENAME, filename); + msg.setPayloadFilename(filename); MessageParameters params = new MessageParameters(msg); // Get the parameter that should provide the link between the polled directory and an AS2 sender and recipient @@ -236,7 +236,20 @@ public void updateMessage(Message msg, InputStream ip, String filename) throws O // Set the sender and receiver in the Message object headers msg.setHeader("AS2-To", msg.getPartnership().getReceiverID(AS2Partnership.PID_AS2)); msg.setHeader("AS2-From", msg.getPartnership().getSenderID(AS2Partnership.PID_AS2)); + // Now build the filename since it is by default dependent on having sender and receiver ID + String pendingFile = AS2Util.buildPendingFileName(msg, getSession().getProcessor(), "pendingmdn"); + msg.setAttribute(FileAttribute.MA_PENDINGFILE, pendingFile); + msg.setAttribute(FileAttribute.MA_ERROR_DIR, getParameter(PARAM_ERROR_DIRECTORY, true)); + if (getParameter(PARAM_SENT_DIRECTORY, false) != null) + msg.setAttribute(FileAttribute.MA_SENT_DIR, getParameter(PARAM_SENT_DIRECTORY, false)); + return msg; + + } + + public void buildMessageData(Message msg, InputStream ip, String filename) throws OpenAS2Exception + { + MessageParameters params = new MessageParameters(msg); try { @@ -301,5 +314,4 @@ public void updateMessage(Message msg, InputStream ip, String filename) throws O } } } - } diff --git a/Server/src/main/java/org/openas2/processor/receiver/NetModule.java b/Server/src/main/java/org/openas2/processor/receiver/NetModule.java index e988d390..98f5b27b 100644 --- a/Server/src/main/java/org/openas2/processor/receiver/NetModule.java +++ b/Server/src/main/java/org/openas2/processor/receiver/NetModule.java @@ -15,12 +15,13 @@ import java.security.cert.CertificateException; import java.util.Map; +import javax.annotation.Nullable; import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLServerSocket; import javax.net.ssl.SSLServerSocketFactory; import javax.net.ssl.TrustManagerFactory; +import org.apache.commons.lang3.ClassUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.openas2.OpenAS2Exception; @@ -34,6 +35,8 @@ import org.openas2.params.MessageParameters; import org.openas2.util.IOUtilOld; +import static org.apache.commons.lang3.StringUtils.defaultIfBlank; + public abstract class NetModule extends BaseReceiverModule { public static final String PARAM_ADDRESS = "address"; @@ -44,61 +47,72 @@ public abstract class NetModule extends BaseReceiverModule { public static final String PARAM_SSL_PROTOCOL = "ssl_protocol"; public static final String PARAM_ERROR_DIRECTORY = "errordir"; public static final String PARAM_ERRORS = "errors"; - public static final String DEFAULT_ERRORS = "$date.yyyyMMddhhmmss$"; - + public static final String DEFAULT_ERRORS = "$date.yyyyMMddhhmmss$"; + private HTTPServerThread mainThread; - private Log logger = LogFactory.getLog(NetModule.class.getSimpleName()); + private Log logger = LogFactory.getLog(NetModule.class.getSimpleName()); - public void doStart() throws OpenAS2Exception { - try { - mainThread = new HTTPServerThread(this, getParameter(PARAM_ADDRESS, false), - getParameterInt(PARAM_PORT, true)); + public void doStart() throws OpenAS2Exception + { + try + { + mainThread = new HTTPServerThread(this, getParameter(PARAM_ADDRESS, false), getParameterInt(PARAM_PORT, true)); mainThread.start(); - } catch (IOException ioe) { - String host = getParameter(PARAM_ADDRESS, false); - if (host == null || host.length() < 1) host = "localhost"; - logger.error("Error in HTTP connection starting server thread on host::port: " - + host + "::" - + getParameterInt(PARAM_PORT, true), ioe); + } catch (IOException ioe) + { + String host = getParameter(PARAM_ADDRESS, false); + if (host == null || host.length() < 1) + { + host = "localhost"; + } + logger.error("Error in HTTP connection starting server thread on host::port: " + + host + "::" + + getParameterInt(PARAM_PORT, true), ioe); throw new WrappedException(ioe); } } - public void doStop() throws OpenAS2Exception { - if (mainThread != null) { + public void doStop() throws OpenAS2Exception + { + if (mainThread != null) + { mainThread.terminate(); mainThread = null; } } - public void init(Session session, Map options) throws OpenAS2Exception { + public void init(Session session, Map options) throws OpenAS2Exception + { super.init(session, options); getParameter(PARAM_PORT, true); // Override the password if it was passed as a system property String pwd = System.getProperty("org.openas2.ssl.Password"); if (pwd != null) { - setParameter(PARAM_SSL_KEYSTORE_PASSWORD, pwd);; + setParameter(PARAM_SSL_KEYSTORE_PASSWORD, pwd); + ; } } protected abstract NetModuleHandler getHandler(); - protected void handleError(Message msg, OpenAS2Exception oae) { + protected void handleError(Message msg, OpenAS2Exception oae) + { oae.addSource(OpenAS2Exception.SOURCE_MESSAGE, msg); oae.terminate(); - try { - CompositeParameters params = new CompositeParameters(false) . - add("date", new DateParameters()) . - add("msg", new MessageParameters(msg)); + try + { + CompositeParameters params = new CompositeParameters(false). + add("date", new DateParameters()). + add("msg", new MessageParameters(msg)); + + String name = params.format(getParameter(PARAM_ERRORS, DEFAULT_ERRORS)); + String directory = getParameter(PARAM_ERROR_DIRECTORY, true); - String name = params.format(getParameter(PARAM_ERRORS, DEFAULT_ERRORS)); - String directory = getParameter(PARAM_ERROR_DIRECTORY, true); - File msgFile = IOUtilOld.getUnique(IOUtilOld.getDirectoryFile(directory), - IOUtilOld.cleanFilename(name)); + IOUtilOld.cleanFilename(name)); String msgText = msg.toString(); FileOutputStream fOut = new FileOutputStream(msgFile); @@ -109,10 +123,12 @@ protected void handleError(Message msg, OpenAS2Exception oae) { InvalidMessageException im = new InvalidMessageException("Stored invalid message to " + msgFile.getAbsolutePath()); im.terminate(); - } catch (OpenAS2Exception oae2) { + } catch (OpenAS2Exception oae2) + { oae2.addSource(OpenAS2Exception.SOURCE_MESSAGE, msg); oae2.terminate(); - } catch (IOException ioe) { + } catch (IOException ioe) + { WrappedException we = new WrappedException(ioe); we.addSource(OpenAS2Exception.SOURCE_MESSAGE, msg); we.terminate(); @@ -123,33 +139,35 @@ protected class ConnectionThread extends Thread { private NetModule owner; private Socket socket; - public ConnectionThread(NetModule owner, Socket socket) { - super(); + public ConnectionThread(NetModule owner, Socket socket) + { + super(ClassUtils.getSimpleName(ConnectionThread.class) + "-Thread"); this.owner = owner; this.socket = socket; start(); } - public void setOwner(NetModule owner) { - this.owner = owner; - } - - public NetModule getOwner() { + public NetModule getOwner() + { return owner; } - public Socket getSocket() { + public Socket getSocket() + { return socket; } - public void run() { + public void run() + { Socket s = getSocket(); getOwner().getHandler().handle(getOwner(), s); - try { + try + { s.close(); - } catch (IOException sce) { + } catch (IOException sce) + { new WrappedException(sce).terminate(); } } @@ -160,163 +178,174 @@ protected class HTTPServerThread extends Thread { private ServerSocket socket; private boolean terminated; - public HTTPServerThread(NetModule owner, String address, int port) - throws IOException { - super(); + HTTPServerThread(NetModule owner, @Nullable String address, int port) + throws IOException + { + super(ClassUtils.getSimpleName(HTTPServerThread.class) + " (" + defaultIfBlank(address, "0.0.0.0") + ":" + port + ")"); this.owner = owner; String protocol = "http"; String sslProtocol = "TLS"; try - { - protocol = owner.getParameter(PARAM_PROTOCOL, "http"); - sslProtocol = owner.getParameter(PARAM_SSL_PROTOCOL, "TLS"); - } catch (InvalidParameterException e) - { - // Do nothing - } + { + protocol = owner.getParameter(PARAM_PROTOCOL, "http"); + sslProtocol = owner.getParameter(PARAM_SSL_PROTOCOL, "TLS"); + } catch (InvalidParameterException e) + { + // Do nothing + } if ("https".equalsIgnoreCase(protocol)) { - String ksName; - char [] ksPass; - try - { - ksName = owner.getParameter(PARAM_SSL_KEYSTORE, true); - ksPass = owner.getParameter(PARAM_SSL_KEYSTORE_PASSWORD, true).toCharArray(); - } catch (InvalidParameterException e) - { - logger.error("Required SSL parameter missing.", e); - throw new IOException("Failed to retireve require SSL parameters. Check config XML"); - } + String ksName; + char[] ksPass; + try + { + ksName = owner.getParameter(PARAM_SSL_KEYSTORE, true); + ksPass = owner.getParameter(PARAM_SSL_KEYSTORE_PASSWORD, true).toCharArray(); + } catch (InvalidParameterException e) + { + logger.error("Required SSL parameter missing.", e); + throw new IOException("Failed to retireve require SSL parameters. Check config XML"); + } KeyStore ks; - try - { - ks = KeyStore.getInstance("JKS"); - } catch (KeyStoreException e) - { - logger.error("Failed to initialise SSL keystore.", e); - throw new IOException("Error initialising SSL keystore"); - } try - { - ks.load(new FileInputStream(ksName), ksPass); - } catch (NoSuchAlgorithmException e) - { - logger.error("Failed to load keystore: " + ksName, e); - throw new IOException("Error loading SSL keystore"); - } catch (CertificateException e) - { - logger.error("Failed to load SSL certificate: " + ksName, e); - throw new IOException("Error loading SSL certificate"); - } + { + ks = KeyStore.getInstance("JKS"); + } catch (KeyStoreException e) + { + logger.error("Failed to initialise SSL keystore.", e); + throw new IOException("Error initialising SSL keystore"); + } + try + { + ks.load(new FileInputStream(ksName), ksPass); + } catch (NoSuchAlgorithmException e) + { + logger.error("Failed to load keystore: " + ksName, e); + throw new IOException("Error loading SSL keystore"); + } catch (CertificateException e) + { + logger.error("Failed to load SSL certificate: " + ksName, e); + throw new IOException("Error loading SSL certificate"); + } KeyManagerFactory kmf; - try - { - kmf = KeyManagerFactory.getInstance("SunX509"); - } catch (NoSuchAlgorithmException e) - { - logger.error("Failed to create key manager instance", e); - throw new IOException("Error creating SSL key manager instance"); - } try - { - kmf.init(ks, ksPass); - } catch (Exception e) - { - logger.error("Failed to initialise key manager instance", e); - throw new IOException("Error initialising SSL key manager instance"); - } + { + kmf = KeyManagerFactory.getInstance("SunX509"); + } catch (NoSuchAlgorithmException e) + { + logger.error("Failed to create key manager instance", e); + throw new IOException("Error creating SSL key manager instance"); + } + try + { + kmf.init(ks, ksPass); + } catch (Exception e) + { + logger.error("Failed to initialise key manager instance", e); + throw new IOException("Error initialising SSL key manager instance"); + } // setup the trust manager factory TrustManagerFactory tmf; try - { - tmf = TrustManagerFactory.getInstance ( "SunX509" ); - tmf.init( ks ); - } catch (Exception e1) - { - logger.error("Failed to create trust manager instance", e1); - throw new IOException("Error creating SSL trust manager instance"); - } + { + tmf = TrustManagerFactory.getInstance("SunX509"); + tmf.init(ks); + } catch (Exception e1) + { + logger.error("Failed to create trust manager instance", e1); + throw new IOException("Error creating SSL trust manager instance"); + } SSLContext sc; - try - { - sc = SSLContext.getInstance(sslProtocol); - } catch (NoSuchAlgorithmException e) - { - logger.error("Failed to create SSL context instance", e); - throw new IOException("Error creating SSL context instance"); - } try - { - sc.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); - } catch (KeyManagementException e) - { - logger.error("Failed to initialise SSL context instance", e); - throw new IOException("Error initialising SSL context instance"); - } + { + sc = SSLContext.getInstance(sslProtocol); + } catch (NoSuchAlgorithmException e) + { + logger.error("Failed to create SSL context instance", e); + throw new IOException("Error creating SSL context instance"); + } + try + { + sc.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); + } catch (KeyManagementException e) + { + logger.error("Failed to initialise SSL context instance", e); + throw new IOException("Error initialising SSL context instance"); + } SSLServerSocketFactory ssf = sc.getServerSocketFactory(); - if (address != null) { - socket = (SSLServerSocket) ssf.createServerSocket(port, 0, InetAddress.getByName(address)); + if (address != null) + { + socket = ssf.createServerSocket(port, 0, InetAddress.getByName(address)); + } else + { + socket = ssf.createServerSocket(port); } - else - socket = (SSLServerSocket) ssf.createServerSocket(port); - } - else + } else { - socket = new ServerSocket(); - if (address != null) { + socket = new ServerSocket(); + if (address != null) + { socket.bind(new InetSocketAddress(address, port)); - } else { + } else + { socket.bind(new InetSocketAddress(port)); } } } - public void setOwner(NetModule owner) { - this.owner = owner; - } - - public NetModule getOwner() { + NetModule getOwner() + { return owner; } - public ServerSocket getSocket() { + public ServerSocket getSocket() + { return socket; } - public void setTerminated(boolean terminated) { + public boolean isTerminated() + { + return terminated; + } + + public void setTerminated(boolean terminated) + { this.terminated = terminated; - if (socket != null) { - try { + if (socket != null) + { + try + { socket.close(); - } catch (IOException e) { + } catch (IOException e) + { owner.forceStop(e); } } } - public boolean isTerminated() { - return terminated; - } - - public void run() { - while (!isTerminated()) { - try { + public void run() + { + while (!isTerminated()) + { + try + { Socket conn = socket.accept(); conn.setSoLinger(true, 60); new ConnectionThread(getOwner(), conn); - } catch (IOException e) { - if (!isTerminated()) { + } catch (IOException e) + { + if (!isTerminated()) + { owner.forceStop(e); } } } - - System.out.println("NetModule terminated."); } - - public void terminate() { + + public void terminate() + { setTerminated(true); } } diff --git a/Server/src/main/java/org/openas2/processor/receiver/PollingModule.java b/Server/src/main/java/org/openas2/processor/receiver/PollingModule.java index 35b64ed0..3345f2df 100644 --- a/Server/src/main/java/org/openas2/processor/receiver/PollingModule.java +++ b/Server/src/main/java/org/openas2/processor/receiver/PollingModule.java @@ -10,58 +10,62 @@ import org.openas2.params.InvalidParameterException; -public abstract class PollingModule extends MessageBuilderModule { - public static final String PARAM_POLLING_INTERVAL = "interval"; - private Timer timer; +public abstract class PollingModule extends MessageBuilderModule { + private static final String PARAM_POLLING_INTERVAL = "interval"; + private Timer timer; private boolean busy; - - public void init(Session session, Map options) throws OpenAS2Exception { - super.init(session, options); - getParameter(PARAM_POLLING_INTERVAL, true); - } - public void setInterval(int seconds) { - setParameter(PARAM_POLLING_INTERVAL, seconds); - } - - public int getInterval() throws InvalidParameterException { - - return getParameterInt(PARAM_POLLING_INTERVAL, true); - - } + public void init(Session session, Map options) throws OpenAS2Exception + { + super.init(session, options); + getParameter(PARAM_POLLING_INTERVAL, true); + } - public abstract void poll(); + private int getInterval() throws InvalidParameterException + { + return getParameterInt(PARAM_POLLING_INTERVAL, true); + } - public void doStart() throws OpenAS2Exception { - timer = new Timer(true); - timer.scheduleAtFixedRate(new PollTask(), 0, getInterval() * 1000); - } + public abstract void poll(); - public void doStop() throws OpenAS2Exception { - if (timer != null) { - timer.cancel(); - timer = null; - } - } + public void doStart() throws OpenAS2Exception + { + timer = new Timer(getName(), false); + timer.scheduleAtFixedRate(new PollTask(), 0, getInterval() * 1000); + } - private class PollTask extends TimerTask { - public void run() { - if (!isBusy()) { - setBusy(true); - poll(); - setBusy(false); - } else { - System.out.println("Miss tick"); - } - } - } + public void doStop() throws OpenAS2Exception + { + if (timer != null) + { + timer.cancel(); + timer = null; + } + } - public boolean isBusy() { + private boolean isBusy() + { return busy; } - public void setBusy(boolean b) { + private void setBusy(boolean b) + { busy = b; } + private class PollTask extends TimerTask { + public void run() + { + if (!isBusy()) + { + setBusy(true); + poll(); + setBusy(false); + } else + { + System.out.println("Miss tick"); + } + } + } + } diff --git a/Server/src/main/java/org/openas2/processor/resender/BaseResenderModule.java b/Server/src/main/java/org/openas2/processor/resender/BaseResenderModule.java index 0d1adba3..53541d2d 100644 --- a/Server/src/main/java/org/openas2/processor/resender/BaseResenderModule.java +++ b/Server/src/main/java/org/openas2/processor/resender/BaseResenderModule.java @@ -8,26 +8,30 @@ public abstract class BaseResenderModule extends BaseActiveModule implements ResenderModule { - public static final int TICK_INTERVAL = 30 * 1000; - private Timer timer; - - public abstract void resend(); - - public void doStart() throws OpenAS2Exception { - timer = new Timer(true); - timer.scheduleAtFixedRate(new PollTask(), 0, TICK_INTERVAL); - } - - public void doStop() throws OpenAS2Exception { - if (timer != null) { - timer.cancel(); - timer = null; - } - } - - private class PollTask extends TimerTask { - public void run() { - resend(); - } - } + public static final int TICK_INTERVAL = 30 * 1000; + private Timer timer; + + public abstract void resend(); + + public void doStart() throws OpenAS2Exception + { + timer = new Timer(getName(), true); + timer.scheduleAtFixedRate(new PollTask(), 0, TICK_INTERVAL); + } + + public void doStop() throws OpenAS2Exception + { + if (timer != null) + { + timer.cancel(); + timer = null; + } + } + + private class PollTask extends TimerTask { + public void run() + { + resend(); + } + } } diff --git a/Server/src/main/java/org/openas2/processor/resender/DirectoryResenderModule.java b/Server/src/main/java/org/openas2/processor/resender/DirectoryResenderModule.java index 9f2d63a5..d6c71804 100644 --- a/Server/src/main/java/org/openas2/processor/resender/DirectoryResenderModule.java +++ b/Server/src/main/java/org/openas2/processor/resender/DirectoryResenderModule.java @@ -62,6 +62,7 @@ public void handle(String action, Message msg, Map options) { logger.trace("Message object in resender module for storage. Content-Disposition: " + msg.getContentDisposition() + "\n Content-Type : " + msg.getContentType() + + "\n Retries : " + retries + "\n HEADERS : " + AS2Util.printHeaders(msg.getData().getAllHeaders()) + "\n Content-Disposition in MSG getData() MIMEPART: " + msg.getData().getContentType() diff --git a/Server/src/main/java/org/openas2/processor/sender/AS2SenderModule.java b/Server/src/main/java/org/openas2/processor/sender/AS2SenderModule.java index b1e271a2..f5967bbb 100644 --- a/Server/src/main/java/org/openas2/processor/sender/AS2SenderModule.java +++ b/Server/src/main/java/org/openas2/processor/sender/AS2SenderModule.java @@ -15,6 +15,7 @@ import javax.mail.internet.MimeBodyPart; import javax.net.ssl.SSLHandshakeException; +import org.apache.commons.io.IOUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.openas2.OpenAS2Exception; @@ -41,328 +42,364 @@ import org.openas2.util.Profiler; import org.openas2.util.ProfilerStub; -public class AS2SenderModule extends HttpSenderModule -{ - - private Log logger = LogFactory.getLog(AS2SenderModule.class.getSimpleName()); - - public boolean canHandle(String action, Message msg, Map options) - { - if (!action.equals(SenderModule.DO_SEND)) - { - return false; - } - - return (msg instanceof AS2Message); - } - - @SuppressWarnings("unchecked") - public void handle(String action, Message msg, Map options) throws OpenAS2Exception - { - - if (logger.isInfoEnabled()) - logger.info("message sender invoked" + msg.getLogMsgID()); - boolean isResend = Message.MSG_STATUS_MSG_RESEND.equals(msg.getStatus()); - options.put("DIRECTION", "SEND"); - options.put("IS_RESEND", isResend?"Y":"N"); - if (!(msg instanceof AS2Message)) - { - throw new OpenAS2Exception("Can't send non-AS2 message"); - } - - // verify all required information is present for sending - checkRequired(msg); - // Store options on the message object - if (options != null) - msg.getOptions().putAll(options); - if (logger.isTraceEnabled()) - logger.trace("Retry count from options: " + options); - // Get the resend retry count - String retries = AS2Util.retries(options, getParameter(SenderModule.SOPT_RETRIES, false)); - - // Get any static custom headers - String customHeaders = msg.getPartnership().getAttribute(AS2Partnership.PA_CUSTOM_MIME_HEADERS); - if (customHeaders != null && customHeaders.length() > 0) - { - if (logger.isTraceEnabled()) logger.trace("Adding custom header attribute to custom headers map..." + msg.getLogMsgID()); - String[] headers = customHeaders.split("\\s*;\\s*"); - for (int i = 0; i < headers.length; i++) - { - String[] header = headers[i].split("\\s*:\\s*"); - if (logger.isTraceEnabled()) logger.trace("Adding custom header: " + headers[i] - + " :::Split count:" + header.length + msg.getLogMsgID()); - if (header.length != 2) throw new OpenAS2Exception("Invalid custom header: " + headers[i]); - msg.addCustomOuterMimeHeader(header[0].replaceAll(" ", ""), header[1]); - } - } - // encrypt and/or sign and/or compress the message if needed - MimeBodyPart securedData; - try - { - securedData = secure(msg); - //Add any additional headers since this will be the outermost Mime body part if configured - addCustomOuterMimeHeaders(msg, securedData); - - storePendingInfo((AS2Message) msg, isResend); - } catch (Exception e) - { - msg.setLogMsg(org.openas2.logging.Log.getExceptionMsg(e)); - logger.error(msg, e); - // Log significant msg state - msg.setOption("STATE", Message.MSG_STATE_SEND_EXCEPTION); - msg.trackMsgState(getSession()); - throw new OpenAS2Exception("Error setting up message for sending.", e); - } - if (logger.isTraceEnabled()) - try - { - logger.trace("Message object in sender module. Content-Disposition: " + msg.getContentDisposition() - + "\n Content-Type : " + msg.getContentType() + "\n HEADERS : " + AS2Util.printHeaders(msg.getData().getAllHeaders()) - + "\n Content-Disposition in MSG getData() MIMEPART: " + msg.getData().getContentType() - + msg.getLogMsgID()); - } catch (Exception e) - { - } - HttpURLConnection conn = null; - try - { - try - { - // Create the HTTP connection and set up headers - String url = msg.getPartnership().getAttribute(AS2Partnership.PA_AS2_URL); - conn = getConnection(url, true, true, false, "POST"); - // Log significant msg state - msg.setOption("STATE", Message.MSG_STATE_SEND_START); - msg.trackMsgState(getSession()); - - sendMessage(conn, msg, securedData, retries); - } catch (HttpResponseException hre) - { - // Will have been logged so just resend - resend(msg, hre, retries); - // Log significant msg state - msg.setOption("STATE", Message.MSG_STATE_SEND_EXCEPTION); - msg.trackMsgState(getSession()); - return; - } catch (SSLHandshakeException e) - { - msg.setLogMsg("Failed to connect to partner using SSL certificate. Please run the SSL certificate checker utility to identify the issue: " + conn.getURL()); - logger.error(msg, e); - msg.setOption("STATE", Message.MSG_STATE_SEND_FAIL); - msg.trackMsgState(getSession()); - return; - } catch (Exception e) - { - msg.setLogMsg("Unexpected error sending file: " + org.openas2.logging.Log.getExceptionMsg(e)); - logger.error(msg, e); - resend(msg, new OpenAS2Exception(org.openas2.logging.Log.getExceptionMsg(e)), retries); - // Log significant msg state - msg.setOption("STATE", Message.MSG_STATE_SEND_EXCEPTION); - msg.trackMsgState(getSession()); - return; - } - if (logger.isTraceEnabled()) logger.trace("Message sent. Checking if MDN will be returned..." + msg.getLogMsgID()); - // Receive an MDN - if (msg.isConfiguredForMDN()) - { - msg.setStatus(Message.MSG_STATUS_MDN_WAIT); - // Check if it will be an AsyncMDN - if (msg.getPartnership().getAttribute(AS2Partnership.PA_AS2_RECEIPT_OPTION) == null) - { - if (logger.isTraceEnabled()) logger.trace("Waiting for synchronous MDN response..." + msg.getLogMsgID()); - // Create a MessageMDN and copy HTTP headers - if (logger.isTraceEnabled()) - logger.trace("Awaiting sync MDN. Orig msg contains headers:" + AS2Util.printHeaders(msg.getHeaders().getAllHeaders()) + msg.getLogMsgID()); - MessageMDN mdn = new AS2MessageMDN((AS2Message) msg, false); - if (logger.isTraceEnabled()) - logger.trace("MDN msg initalised for inbound contains headers:" + AS2Util.printHeaders(mdn.getHeaders().getAllHeaders()) + msg.getLogMsgID()); - copyHttpHeaders(conn, mdn.getHeaders()); - - // Receive the MDN data - InputStream connIn = null; - try - { - connIn = conn.getInputStream(); - } catch (IOException e1) - { - msg.setLogMsg("Failed to get input stream for receiving MDN: " - + org.openas2.logging.Log.getExceptionMsg(e1)); - logger.error(msg, e1); - resend(msg, new OpenAS2Exception(org.openas2.logging.Log.getExceptionMsg(e1)), retries); - // Log significant msg state - msg.setOption("STATE", Message.MSG_STATE_MDN_RECEIVING_EXCEPTION); - msg.trackMsgState(getSession()); - } - ByteArrayOutputStream mdnStream = new ByteArrayOutputStream(); - - try - { - String cl = mdn.getHeader("Content-Length"); - // Retrieve the message content - if (cl != null) - { - try - { - int contentSize = Integer.parseInt(cl); - - IOUtilOld.copy(connIn, mdnStream, contentSize); - } catch (NumberFormatException nfe) - { - IOUtilOld.copy(connIn, mdnStream); - } - } else - { - IOUtilOld.copy(connIn, mdnStream); - } - } catch (IOException ioe) - { - msg.setLogMsg("IO exception receiving MDN: " - + org.openas2.logging.Log.getExceptionMsg(ioe)); - logger.error(msg, ioe); - // What to do??? - resend(msg, new OpenAS2Exception(org.openas2.logging.Log.getExceptionMsg(ioe)), retries); - // Log significant msg state - msg.setOption("STATE", Message.MSG_STATE_MDN_RECEIVING_EXCEPTION); - msg.trackMsgState(getSession()); - } finally - { - try - { - if (connIn != null) - connIn.close(); - } catch (IOException e) - { - } - } - - if (logger.isTraceEnabled()) logger.trace("Synchronous MDN received. Start processing..." + msg.getLogMsgID()); - msg.setStatus(Message.MSG_STATUS_MDN_PROCESS_INIT); - try - { - AS2Util.processMDN((AS2Message) msg, mdnStream.toByteArray(), null, false, getSession(), this); - // Log significant msg state - msg.setOption("STATE", Message.MSG_STATE_MSG_SENT_MDN_RECEIVED_OK); - msg.trackMsgState(getSession()); - } catch (Exception e) - { - if (Message.MSG_STATUS_MDN_PROCESS_INIT.equals(msg.getStatus()) - || Message.MSG_STATUS_MDN_PARSE.equals(msg.getStatus()) - || !(e instanceof OpenAS2Exception)) - { - /* +public class AS2SenderModule extends HttpSenderModule { + + private Log logger = LogFactory.getLog(AS2SenderModule.class.getSimpleName()); + + public boolean canHandle(String action, Message msg, Map options) + { + if (!action.equals(SenderModule.DO_SEND)) + { + return false; + } + + return (msg instanceof AS2Message); + } + + @SuppressWarnings("unchecked") + public void handle(String action, Message msg, Map options) throws OpenAS2Exception + { + + if (logger.isInfoEnabled()) + { + logger.info("message sender invoked" + msg.getLogMsgID()); + } + boolean isResend = Message.MSG_STATUS_MSG_RESEND.equals(msg.getStatus()); + options.put("DIRECTION", "SEND"); + options.put("IS_RESEND", isResend ? "Y" : "N"); + if (!(msg instanceof AS2Message)) + { + throw new OpenAS2Exception("Can't send non-AS2 message"); + } + + // verify all required information is present for sending + checkRequired(msg); + // Store options on the message object + if (options != null) + { + msg.getOptions().putAll(options); + } + if (logger.isTraceEnabled()) + { + logger.trace("Retry count from options: " + options); + } + // Get the resend retry count + String retries = AS2Util.retries(options, getParameter(SenderModule.SOPT_RETRIES, false)); + + // Get any static custom headers + String customHeaders = msg.getPartnership().getAttribute(AS2Partnership.PA_CUSTOM_MIME_HEADERS); + if (customHeaders != null && customHeaders.length() > 0) + { + if (logger.isTraceEnabled()) + { + logger.trace("Adding custom header attribute to custom headers map..." + msg.getLogMsgID()); + } + String[] headers = customHeaders.split("\\s*;\\s*"); + for (int i = 0; i < headers.length; i++) + { + String[] header = headers[i].split("\\s*:\\s*"); + if (logger.isTraceEnabled()) + { + logger.trace("Adding custom header: " + headers[i] + + " :::Split count:" + header.length + msg.getLogMsgID()); + } + if (header.length != 2) + { + throw new OpenAS2Exception("Invalid custom header: " + headers[i]); + } + msg.addCustomOuterMimeHeader(header[0].replaceAll(" ", ""), header[1]); + } + } + // encrypt and/or sign and/or compress the message if needed + MimeBodyPart securedData; + try + { + securedData = secure(msg); + //Add any additional headers since this will be the outermost Mime body part if configured + addCustomOuterMimeHeaders(msg, securedData); + + storePendingInfo((AS2Message) msg, isResend); + } catch (Exception e) + { + msg.setLogMsg(org.openas2.logging.Log.getExceptionMsg(e)); + logger.error(msg, e); + // Log significant msg state + msg.setOption("STATE", Message.MSG_STATE_SEND_EXCEPTION); + msg.trackMsgState(getSession()); + throw new OpenAS2Exception("Error setting up message for sending.", e); + } + if (logger.isTraceEnabled()) + { + try + { + logger.trace("Message object in sender module. Content-Disposition: " + msg.getContentDisposition() + + "\n Content-Type : " + msg.getContentType() + "\n HEADERS : " + AS2Util.printHeaders(msg.getData().getAllHeaders()) + + "\n Content-Disposition in MSG getData() MIMEPART: " + msg.getData().getContentType() + + msg.getLogMsgID()); + } catch (Exception e) + { + } + } + HttpURLConnection conn = null; + try + { + try + { + // Create the HTTP connection and set up headers + String url = msg.getPartnership().getAttribute(AS2Partnership.PA_AS2_URL); + conn = getConnection(url, true, true, false, "POST"); + // Log significant msg state + msg.setOption("STATE", Message.MSG_STATE_SEND_START); + msg.trackMsgState(getSession()); + + sendMessage(conn, msg, securedData, retries); + } catch (HttpResponseException hre) + { + // Will have been logged so just resend + resend(msg, hre, retries); + // Log significant msg state + msg.setOption("STATE", Message.MSG_STATE_SEND_EXCEPTION); + msg.trackMsgState(getSession()); + return; + } catch (SSLHandshakeException e) + { + msg.setLogMsg("Failed to connect to partner using SSL certificate. Please run the SSL certificate checker utility to identify the issue: " + conn.getURL()); + logger.error(msg, e); + msg.setOption("STATE", Message.MSG_STATE_SEND_FAIL); + msg.trackMsgState(getSession()); + return; + } catch (Exception e) + { + msg.setLogMsg("Unexpected error sending file: " + org.openas2.logging.Log.getExceptionMsg(e)); + logger.error(msg, e); + resend(msg, new OpenAS2Exception(org.openas2.logging.Log.getExceptionMsg(e)), retries); + // Log significant msg state + msg.setOption("STATE", Message.MSG_STATE_SEND_EXCEPTION); + msg.trackMsgState(getSession()); + return; + } + if (logger.isTraceEnabled()) + { + logger.trace("Message sent. Checking if MDN will be returned..." + msg.getLogMsgID()); + } + // Receive an MDN + if (msg.isConfiguredForMDN()) + { + msg.setStatus(Message.MSG_STATUS_MDN_WAIT); + // Check if it will be an AsyncMDN + if (msg.getPartnership().getAttribute(AS2Partnership.PA_AS2_RECEIPT_OPTION) == null) + { + if (logger.isTraceEnabled()) + { + logger.trace("Waiting for synchronous MDN response..." + msg.getLogMsgID()); + } + // Create a MessageMDN and copy HTTP headers + if (logger.isTraceEnabled()) + { + logger.trace("Awaiting sync MDN. Orig msg contains headers:" + AS2Util.printHeaders(msg.getHeaders().getAllHeaders()) + msg.getLogMsgID()); + } + MessageMDN mdn = new AS2MessageMDN((AS2Message) msg, false); + if (logger.isTraceEnabled()) + { + logger.trace("MDN msg initalised for inbound contains headers:" + AS2Util.printHeaders(mdn.getHeaders().getAllHeaders()) + msg.getLogMsgID()); + } + copyHttpHeaders(conn, mdn.getHeaders()); + + // Receive the MDN data + InputStream connIn = null; + try + { + connIn = conn.getInputStream(); + } catch (IOException e1) + { + msg.setLogMsg("Failed to get input stream for receiving MDN: " + + org.openas2.logging.Log.getExceptionMsg(e1)); + logger.error(msg, e1); + resend(msg, new OpenAS2Exception(org.openas2.logging.Log.getExceptionMsg(e1)), retries); + // Log significant msg state + msg.setOption("STATE", Message.MSG_STATE_MDN_RECEIVING_EXCEPTION); + msg.trackMsgState(getSession()); + } + ByteArrayOutputStream mdnStream = new ByteArrayOutputStream(); + + try + { + String contentLength = mdn.getHeader("Content-Length"); + // Retrieve the message content + if (contentLength != null) + { + try + { + + IOUtils.copy(connIn, mdnStream); + } catch (NumberFormatException nfe) + { + IOUtils.copy(connIn, mdnStream); + } + } else + { + IOUtils.copy(connIn, mdnStream); + } + } catch (IOException ioe) + { + msg.setLogMsg("IO exception receiving MDN: " + + org.openas2.logging.Log.getExceptionMsg(ioe)); + logger.error(msg, ioe); + // What to do??? + resend(msg, new OpenAS2Exception(org.openas2.logging.Log.getExceptionMsg(ioe)), retries); + // Log significant msg state + msg.setOption("STATE", Message.MSG_STATE_MDN_RECEIVING_EXCEPTION); + msg.trackMsgState(getSession()); + } finally + { + try + { + if (connIn != null) + { + connIn.close(); + } + } catch (IOException e) + { + } + } + + if (logger.isTraceEnabled()) + { + logger.trace("Synchronous MDN received. Start processing..." + msg.getLogMsgID()); + } + msg.setStatus(Message.MSG_STATUS_MDN_PROCESS_INIT); + try + { + AS2Util.processMDN((AS2Message) msg, mdnStream.toByteArray(), null, false, getSession(), this); + // Log significant msg state + msg.setOption("STATE", Message.MSG_STATE_MSG_SENT_MDN_RECEIVED_OK); + msg.trackMsgState(getSession()); + } catch (Exception e) + { + if (Message.MSG_STATUS_MDN_PROCESS_INIT.equals(msg.getStatus()) + || Message.MSG_STATUS_MDN_PARSE.equals(msg.getStatus()) + || !(e instanceof OpenAS2Exception)) + { + /* * Cannot identify the target if in init or parse * state so not sure what the best course of action * is apart from do nothing */ - msg.setLogMsg("Unhandled error condition receiving synchronous MDN. Message and asociated files cleanup will be attempted but may be in an unknown state."); - logger.error(msg, e); - } - /* + msg.setLogMsg("Unhandled error condition receiving synchronous MDN. Message and asociated files cleanup will be attempted but may be in an unknown state."); + logger.error(msg, e); + } + /* * Most likely a resend abort of max resend reached if * OpenAS2Exception so do not log as should have been * logged upstream ... just clean up the mess */ - else - { - // Must have received MDN successfully - msg.setLogMsg("Exception receiving synchronous MDN. Message and asociated files cleanup will be attempted but may be in an unknown state."); - logger.error(msg, e); - - } - // Log significant msg state - msg.setOption("STATE", Message.MSG_STATE_SEND_FAIL); - msg.trackMsgState(getSession()); - AS2Util.cleanupFiles(msg, true); - } - } - } - - } finally - { - if (conn != null) - conn.disconnect(); - } - } - - protected void checkRequired(Message msg) throws InvalidParameterException - { - Partnership partnership = msg.getPartnership(); - - try - { - InvalidParameterException.checkValue(msg, "ContentType", msg.getContentType()); - InvalidParameterException.checkValue(msg, "Attribute: " + AS2Partnership.PA_AS2_URL, - partnership.getAttribute(AS2Partnership.PA_AS2_URL)); - InvalidParameterException.checkValue(msg, "Receiver: " + AS2Partnership.PID_AS2, - partnership.getReceiverID(AS2Partnership.PID_AS2)); - InvalidParameterException.checkValue(msg, "Sender: " + AS2Partnership.PID_AS2, - partnership.getSenderID(AS2Partnership.PID_AS2)); - InvalidParameterException.checkValue(msg, "Subject", msg.getSubject()); - InvalidParameterException.checkValue(msg, "Sender: " + Partnership.PID_EMAIL, - partnership.getSenderID(Partnership.PID_EMAIL)); - InvalidParameterException.checkValue(msg, "Message Data", msg.getData()); - } catch (InvalidParameterException rpe) - { - rpe.addSource(OpenAS2Exception.SOURCE_MESSAGE, msg); - throw rpe; - } - } - - private void sendMessage(HttpURLConnection conn, Message msg, MimeBodyPart securedData, String retries) - throws Exception - { - - updateHttpHeaders(conn, msg, securedData); - msg.setAttribute(NetAttribute.MA_DESTINATION_IP, conn.getURL().getHost()); - msg.setAttribute(NetAttribute.MA_DESTINATION_PORT, Integer.toString(conn.getURL().getPort())); - - if (logger.isInfoEnabled()) - logger.info("Connecting to: " + conn.getURL() + msg.getLogMsgID()); - - // Note: closing this stream causes connection abort errors on some AS2 - // servers - OutputStream messageOut = conn.getOutputStream(); - - // Transfer the data - InputStream messageIn = securedData.getInputStream(); - - try - { - ProfilerStub transferStub = Profiler.startProfile(); - - int bytes = IOUtilOld.copy(messageIn, messageOut); - - Profiler.endProfile(transferStub); - if (logger.isInfoEnabled()) - logger.info("transferred " + IOUtilOld.getTransferRate(bytes, transferStub) + msg.getLogMsgID()); - } finally - { - messageIn.close(); - } - // Check the HTTP Response code - int rc = conn.getResponseCode(); - if ((rc != HttpURLConnection.HTTP_OK) && (rc != HttpURLConnection.HTTP_CREATED) - && (rc != HttpURLConnection.HTTP_ACCEPTED) && (rc != HttpURLConnection.HTTP_PARTIAL) - && (rc != HttpURLConnection.HTTP_NO_CONTENT)) - { - msg.setLogMsg("Error sending message. URL: " + conn.getURL().toString() + " ::: Response Code: " + rc - + " ::: Response Message: " + conn.getResponseMessage()); - logger.error(msg); - throw new HttpResponseException(conn.getURL().toString(), rc, conn.getResponseMessage()); - } - } - - private void resend(Message msg, OpenAS2Exception cause, String tries) throws OpenAS2Exception - { - AS2Util.resend(getSession().getProcessor(), this, SenderModule.DO_SEND, msg, cause, tries, false); - } - - // Returns a MimeBodyPart or MimeMultipart object - protected MimeBodyPart secure(Message msg) throws Exception - { - // Set up encrypt/sign variables - MimeBodyPart dataBP = msg.getData(); - /* + else + { + // Must have received MDN successfully + msg.setLogMsg("Exception receiving synchronous MDN. Message and asociated files cleanup will be attempted but may be in an unknown state."); + logger.error(msg, e); + + } + // Log significant msg state + msg.setOption("STATE", Message.MSG_STATE_SEND_FAIL); + msg.trackMsgState(getSession()); + AS2Util.cleanupFiles(msg, true); + } + } + } + + } finally + { + if (conn != null) + { + conn.disconnect(); + } + } + } + + protected void checkRequired(Message msg) throws InvalidParameterException + { + Partnership partnership = msg.getPartnership(); + + try + { + InvalidParameterException.checkValue(msg, "ContentType", msg.getContentType()); + InvalidParameterException.checkValue(msg, "Attribute: " + AS2Partnership.PA_AS2_URL, + partnership.getAttribute(AS2Partnership.PA_AS2_URL)); + InvalidParameterException.checkValue(msg, "Receiver: " + AS2Partnership.PID_AS2, + partnership.getReceiverID(AS2Partnership.PID_AS2)); + InvalidParameterException.checkValue(msg, "Sender: " + AS2Partnership.PID_AS2, + partnership.getSenderID(AS2Partnership.PID_AS2)); + InvalidParameterException.checkValue(msg, "Subject", msg.getSubject()); + InvalidParameterException.checkValue(msg, "Sender: " + Partnership.PID_EMAIL, + partnership.getSenderID(Partnership.PID_EMAIL)); + InvalidParameterException.checkValue(msg, "Message Data", msg.getData()); + } catch (InvalidParameterException rpe) + { + rpe.addSource(OpenAS2Exception.SOURCE_MESSAGE, msg); + throw rpe; + } + } + + private void sendMessage(HttpURLConnection conn, Message msg, MimeBodyPart securedData, String retries) + throws Exception + { + + updateHttpHeaders(conn, msg, securedData); + msg.setAttribute(NetAttribute.MA_DESTINATION_IP, conn.getURL().getHost()); + msg.setAttribute(NetAttribute.MA_DESTINATION_PORT, Integer.toString(conn.getURL().getPort())); + + if (logger.isInfoEnabled()) + { + logger.info("Connecting to: " + conn.getURL() + msg.getLogMsgID()); + } + + // Note: closing this stream causes connection abort errors on some AS2 + // servers + OutputStream messageOut = conn.getOutputStream(); + + // Transfer the data + InputStream messageIn = securedData.getInputStream(); + + try + { + ProfilerStub transferStub = Profiler.startProfile(); + + int bytes = IOUtils.copy(messageIn, messageOut); + + Profiler.endProfile(transferStub); + if (logger.isInfoEnabled()) + { + logger.info("transferred " + IOUtilOld.getTransferRate(bytes, transferStub) + msg.getLogMsgID()); + } + } finally + { + messageIn.close(); + } + // Check the HTTP Response code + int rc = conn.getResponseCode(); + if ((rc != HttpURLConnection.HTTP_OK) && (rc != HttpURLConnection.HTTP_CREATED) + && (rc != HttpURLConnection.HTTP_ACCEPTED) && (rc != HttpURLConnection.HTTP_PARTIAL) + && (rc != HttpURLConnection.HTTP_NO_CONTENT)) + { + msg.setLogMsg("Error sending message. URL: " + conn.getURL().toString() + " ::: Response Code: " + rc + + " ::: Response Message: " + conn.getResponseMessage()); + logger.error(msg); + throw new HttpResponseException(conn.getURL().toString(), rc, conn.getResponseMessage()); + } + } + + private void resend(Message msg, OpenAS2Exception cause, String tries) throws OpenAS2Exception + { + AS2Util.resend(getSession(), this, SenderModule.DO_SEND, msg, cause, tries, false); + } + + // Returns a MimeBodyPart or MimeMultipart object + protected MimeBodyPart secure(Message msg) throws Exception + { + // Set up encrypt/sign variables + MimeBodyPart dataBP = msg.getData(); + /* * Based on RFC4130, RFC6362 and RFC5042, the MIC is calculated as * follows: Signed message - MIME header fields and content that is to * be signed which may or may not be encrypted and/or compressed. @@ -377,306 +414,346 @@ protected MimeBodyPart secure(Message msg) throws Exception * messages (see RFC4130 section 7.3.1 for details) */ - Partnership partnership = msg.getPartnership(); - String contentTxfrEncoding = partnership.getAttribute(Partnership.PA_CONTENT_TRANSFER_ENCODING); - if (contentTxfrEncoding == null) - contentTxfrEncoding = Session.DEFAULT_CONTENT_TRANSFER_ENCODING; - - boolean encrypt = partnership.getAttribute(SecurePartnership.PA_ENCRYPT) != null; - boolean sign = partnership.getAttribute(SecurePartnership.PA_SIGN) != null; - - if (!sign) - calcAndStoreMic(msg, dataBP, (sign || encrypt)); - - // Check if compression is enabled - String compressionType = msg.getPartnership().getAttribute("compression_type"); - if (logger.isTraceEnabled()) - logger.trace("Compression type from config: " + compressionType); - boolean isCompress = false; - if (compressionType != null) - { - if (compressionType.equalsIgnoreCase(ICryptoHelper.COMPRESSION_ZLIB)) - { - isCompress = true; - } else - throw new OpenAS2Exception("Unsupported compression type: " + compressionType); - } - String compressionMode = msg.getPartnership().getAttribute("compression_mode"); - boolean isCompressBeforeSign = true; // Defaults to compressing the - // entire message before signing - // and encryption - if (compressionMode != null && compressionMode.equalsIgnoreCase("compress-after-signing")) - isCompressBeforeSign = false; - if (isCompress && isCompressBeforeSign) - { - if (logger.isTraceEnabled()) - logger.trace("Compressing outbound message before signing..."); - if (!sign && !encrypt) - { - //Add any additional headers since this will be the outermost Mime body part if configured - addCustomOuterMimeHeaders(msg, dataBP); - } - dataBP = AS2Util.getCryptoHelper().compress(msg, dataBP, compressionType, contentTxfrEncoding); - } - // Encrypt and/or sign the data if requested - CertificateFactory certFx = getSession().getCertificateFactory(); - - // Sign the data if requested - if (sign) - { - if (!encrypt && !(isCompress && !isCompressBeforeSign)) - { - //Add any additional headers since this will be the outermost Mime body part if configured - addCustomOuterMimeHeaders(msg, dataBP); - } - calcAndStoreMic(msg, dataBP, (sign || encrypt)); - X509Certificate senderCert = certFx.getCertificate(msg, Partnership.PTYPE_SENDER); - - PrivateKey senderKey = certFx.getPrivateKey(msg, senderCert); - String digest = partnership.getAttribute(SecurePartnership.PA_SIGN); - - if (logger.isDebugEnabled()) - logger.debug("Params for creating signed body part:: DATA: " + dataBP + "\n SIGN DIGEST: " + digest - + "\n CERT ALG NAME EXTRACTED: " + senderCert.getSigAlgName() - + "\n CERT PUB KEY ALG NAME EXTRACTED: " + senderCert.getPublicKey().getAlgorithm() - + msg.getLogMsgID()); + Partnership partnership = msg.getPartnership(); + String contentTxfrEncoding = partnership.getAttribute(Partnership.PA_CONTENT_TRANSFER_ENCODING); + if (contentTxfrEncoding == null) + { + contentTxfrEncoding = Session.DEFAULT_CONTENT_TRANSFER_ENCODING; + } + + boolean encrypt = partnership.getAttribute(SecurePartnership.PA_ENCRYPT) != null; + boolean sign = partnership.getAttribute(SecurePartnership.PA_SIGN) != null; + + if (!sign) + { + calcAndStoreMic(msg, dataBP, (sign || encrypt)); + } + + // Check if compression is enabled + String compressionType = msg.getPartnership().getAttribute("compression_type"); + if (logger.isTraceEnabled()) + { + logger.trace("Compression type from config: " + compressionType); + } + boolean isCompress = false; + if (compressionType != null) + { + if (compressionType.equalsIgnoreCase(ICryptoHelper.COMPRESSION_ZLIB)) + { + isCompress = true; + } else + { + throw new OpenAS2Exception("Unsupported compression type: " + compressionType); + } + } + String compressionMode = msg.getPartnership().getAttribute("compression_mode"); + boolean isCompressBeforeSign = true; // Defaults to compressing the + // entire message before signing + // and encryption + if (compressionMode != null && compressionMode.equalsIgnoreCase("compress-after-signing")) + { + isCompressBeforeSign = false; + } + if (isCompress && isCompressBeforeSign) + { + if (logger.isTraceEnabled()) + { + logger.trace("Compressing outbound message before signing..."); + } + if (!sign && !encrypt) + { + //Add any additional headers since this will be the outermost Mime body part if configured + addCustomOuterMimeHeaders(msg, dataBP); + } + dataBP = AS2Util.getCryptoHelper().compress(msg, dataBP, compressionType, contentTxfrEncoding); + } + // Encrypt and/or sign the data if requested + CertificateFactory certFx = getSession().getCertificateFactory(); + + // Sign the data if requested + if (sign) + { + if (!encrypt && !(isCompress && !isCompressBeforeSign)) + { + //Add any additional headers since this will be the outermost Mime body part if configured + addCustomOuterMimeHeaders(msg, dataBP); + } + calcAndStoreMic(msg, dataBP, (sign || encrypt)); + X509Certificate senderCert = certFx.getCertificate(msg, Partnership.PTYPE_SENDER); + + PrivateKey senderKey = certFx.getPrivateKey(msg, senderCert); + String digest = partnership.getAttribute(SecurePartnership.PA_SIGN); + + if (logger.isDebugEnabled()) + { + logger.debug("Params for creating signed body part:: DATA: " + dataBP + "\n SIGN DIGEST: " + digest + + "\n CERT ALG NAME EXTRACTED: " + senderCert.getSigAlgName() + + "\n CERT PUB KEY ALG NAME EXTRACTED: " + senderCert.getPublicKey().getAlgorithm() + + msg.getLogMsgID()); + } boolean isRemoveCmsAlgorithmProtectionAttr = "true".equalsIgnoreCase(partnership.getAttribute(Partnership.PA_REMOVE_PROTECTION_ATTRIB)); - dataBP = AS2Util.getCryptoHelper().sign(dataBP, senderCert, senderKey, digest - , contentTxfrEncoding, msg.getPartnership().isRenameDigestToOldName(), isRemoveCmsAlgorithmProtectionAttr); - - DataHistoryItem historyItem = new DataHistoryItem(dataBP.getContentType()); - // *** add one more item to msg history - msg.getHistory().getItems().add(historyItem); - - if (logger.isDebugEnabled()) - logger.debug("signed data" + msg.getLogMsgID()); - } - - if (isCompress && !isCompressBeforeSign) - { - if (!encrypt) - { - //Add any additional headers since this will be the outermost Mime body part if configured - addCustomOuterMimeHeaders(msg, dataBP); - } - if (logger.isTraceEnabled()) - logger.trace("Compressing outbound message after signing..."); - dataBP = AS2Util.getCryptoHelper().compress(msg, dataBP, compressionType, contentTxfrEncoding); - } - // Encrypt the data if requested - if (encrypt) - { - //Add any additional headers since this will be the outermost Mime body part if configured - addCustomOuterMimeHeaders(msg, dataBP); - String algorithm = partnership.getAttribute(SecurePartnership.PA_ENCRYPT); - - X509Certificate receiverCert = certFx.getCertificate(msg, Partnership.PTYPE_RECEIVER); - dataBP = AS2Util.getCryptoHelper().encrypt(dataBP, receiverCert, algorithm, contentTxfrEncoding); - - // Asynch MDN 2007-03-12 - DataHistoryItem historyItem = new DataHistoryItem(dataBP.getContentType()); - // *** add one more item to msg history - msg.getHistory().getItems().add(historyItem); - - if (logger.isDebugEnabled()) - logger.debug("encrypted data" + msg.getLogMsgID()); - } - - String t = dataBP.getEncoding(); - if ((t == null || t.length() < 1) && "true".equalsIgnoreCase(partnership.getAttribute(Partnership.PA_SET_CONTENT_TRANSFER_ENCODING_OMBP))) - { - dataBP.setHeader("Content-Transfer-Encoding", contentTxfrEncoding); - } - return dataBP; - } - - protected void addCustomOuterMimeHeaders(Message msg, MimeBodyPart dataBP) throws MessagingException - { - if (logger.isTraceEnabled()) logger.trace("Adding custom headers to outer MBP...." + msg.getLogMsgID()); - Map hdrs = msg.getCustomOuterMimeHeaders(); - if (hdrs == null) return; - for (Map.Entry entry :hdrs.entrySet()) - { - dataBP.addHeader(entry.getKey(), entry.getValue()); - if (logger.isTraceEnabled()) - logger.trace("Added custom headers to outer MBP: " + entry.getKey() + "--->" + entry.getValue() + msg.getLogMsgID()); - } - } - - protected void updateHttpHeaders(HttpURLConnection conn, Message msg, MimeBodyPart securedData) - { - Partnership partnership = msg.getPartnership(); - - conn.setRequestProperty("Connection", "close, TE"); - conn.setRequestProperty("User-Agent", msg.getAppTitle() + " (AS2Sender)"); - - conn.setRequestProperty("Date", DateUtil.formatDate("EEE, dd MMM yyyy HH:mm:ss Z")); - conn.setRequestProperty("Message-ID", msg.getMessageID()); - conn.setRequestProperty("Mime-Version", "1.0"); // make sure this is the - // encoding used in the - // msg, run TBF1 - try - { - conn.setRequestProperty("Content-type", securedData.getContentType()); - } catch (MessagingException e) - { - conn.setRequestProperty("Content-type", msg.getContentType()); - } - conn.setRequestProperty("AS2-Version", "1.1"); // RFC6017 - AS2 V1.1 supports compression - // AS2 V1.2 additionally supports EDIINT-Features - // conn.setRequestProperty("EDIINT-Features", - // "CEM,multiple-attachments"); // TODO (possibly implement???) - String cte = null; - try - { - cte = securedData.getEncoding(); - } catch (MessagingException e1) - { - e1.printStackTrace(); - } - if (cte == null) cte = Session.DEFAULT_CONTENT_TRANSFER_ENCODING; - conn.setRequestProperty("Content-Transfer-Encoding", cte); - conn.setRequestProperty("Recipient-Address", partnership.getAttribute(AS2Partnership.PA_AS2_URL)); - conn.setRequestProperty("AS2-To", partnership.getReceiverID(AS2Partnership.PID_AS2)); - conn.setRequestProperty("AS2-From", partnership.getSenderID(AS2Partnership.PID_AS2)); - conn.setRequestProperty("Subject", msg.getSubject()); - conn.setRequestProperty("From", partnership.getSenderID(Partnership.PID_EMAIL)); - - String dispTo = partnership.getAttribute(AS2Partnership.PA_AS2_MDN_TO); - - if (dispTo != null) - { - conn.setRequestProperty("Disposition-Notification-To", dispTo); - } - - String dispOptions = partnership.getAttribute(AS2Partnership.PA_AS2_MDN_OPTIONS); - - if (dispOptions != null) - { - conn.setRequestProperty("Disposition-Notification-Options", dispOptions); - } - - String receiptOption = partnership.getAttribute(AS2Partnership.PA_AS2_RECEIPT_OPTION); - if (receiptOption != null) - { - conn.setRequestProperty("Receipt-Delivery-Option", receiptOption); - } - - String contentDisp; - try - { - contentDisp = securedData.getDisposition(); - } catch (MessagingException e) - { - contentDisp = msg.getContentDisposition(); - } - if (contentDisp != null) - { - conn.setRequestProperty("Content-Disposition", contentDisp); - } - if ("true".equalsIgnoreCase((partnership.getAttribute(AS2Partnership.PA_ADD_CUSTOM_MIME_HEADERS_TO_HTTP)))) - { - if (logger.isTraceEnabled()) logger.trace("Adding custom headers to HTTP..." + msg.getLogMsgID()); - for (Map.Entry entry : msg.getCustomOuterMimeHeaders().entrySet()) - { - conn.setRequestProperty(entry.getKey(), entry.getValue()); - } - - } - - } - - /** - * @description Stores metadata into pending information file and storing - * message object from first send attempt. The message object - * is written to a separate file to avoid repeated rewrites of - * possibly very large objects since it contains the original - * file data - * @param msg - * AS2Message - * @param mic - * @throws WrappedException - */ - protected void storePendingInfo(AS2Message msg, boolean isResend) throws Exception - { - ObjectOutputStream oos = null; - - try - { - String pendingInfoFile = AS2Util.buildPendingFileName(msg, getSession().getProcessor(), "pendingmdninfo"); - String pendingFile = msg.getAttribute(FileAttribute.MA_PENDINGFILE); - msg.setAttribute(FileAttribute.MA_PENDINGFILE, pendingFile); - if (!isResend) - { - // Write the object to a file to keep a lot of the original - // static metadata intact for resends - String pendingMsgObjFile = pendingFile + ".object"; - oos = new ObjectOutputStream(new FileOutputStream(pendingMsgObjFile)); - oos.writeObject(msg); - oos.flush(); - oos.close(); - } - msg.setAttribute(FileAttribute.MA_PENDINGINFO, pendingInfoFile); - oos = new ObjectOutputStream(new FileOutputStream(pendingInfoFile)); - oos.writeObject(msg.getCalculatedMIC()); - String retries = (String) msg.getOption(ResenderModule.OPTION_RETRIES); - oos.writeObject((retries == null ? "" : retries)); - - if (logger.isInfoEnabled()) - logger.info("Save Original mic & message id information into file: " + pendingInfoFile - + msg.getLogMsgID()); - oos.writeObject(msg.getAttribute(FileAttribute.MA_FILENAME)); - oos.writeObject(pendingFile); - oos.writeObject(msg.getAttribute(FileAttribute.MA_ERROR_DIR)); - String sentDir = msg.getAttribute(FileAttribute.MA_SENT_DIR); - oos.writeObject((sentDir == null ? "" : sentDir)); - if (logger.isTraceEnabled()) - logger.trace("Pending info file written to:" + pendingInfoFile + "\n Original MIC: " - + msg.getCalculatedMIC() + "\n Retry Count: " + retries - + "\n Original file name : " + msg.getAttribute(FileAttribute.MA_FILENAME) - + "\n Pending message file : " + pendingFile + "\n Error directory: " - + msg.getAttribute(FileAttribute.MA_ERROR_DIR) + "\n Sent directory: " - + msg.getAttribute(FileAttribute.MA_SENT_DIR) + msg.getLogMsgID()); - - msg.setAttribute(FileAttribute.MA_STATUS, FileAttribute.MA_PENDING); - - } catch (Exception e) - { - msg.setLogMsg("Error setting up pending information files: " + org.openas2.logging.Log.getExceptionMsg(e)); - logger.error(msg, e); - throw new Exception("Unable to set up pending information files."); - } finally - { - if (oos != null) - try - { - oos.close(); - } catch (IOException e) - { - } - } - } - - protected void calcAndStoreMic(Message msg, MimeBodyPart mbp, boolean includeHeaders) throws Exception - { - // Calculate and get the original mic - // includeHeaders = (msg.getHistory().getItems().size() > 1); - - DispositionOptions dispOptions = new DispositionOptions(msg.getPartnership().getAttribute( - AS2Partnership.PA_AS2_MDN_OPTIONS)); - msg.setCalculatedMIC(AS2Util.getCryptoHelper().calculateMIC(mbp, dispOptions.getMicalg() - , includeHeaders, msg.getPartnership().isPreventCanonicalization())); + dataBP = AS2Util.getCryptoHelper().sign(dataBP, senderCert, senderKey, digest + , contentTxfrEncoding, msg.getPartnership().isRenameDigestToOldName(), isRemoveCmsAlgorithmProtectionAttr); + + DataHistoryItem historyItem = new DataHistoryItem(dataBP.getContentType()); + // *** add one more item to msg history + msg.getHistory().getItems().add(historyItem); + + if (logger.isDebugEnabled()) + { + logger.debug("signed data" + msg.getLogMsgID()); + } + } + + if (isCompress && !isCompressBeforeSign) + { + if (!encrypt) + { + //Add any additional headers since this will be the outermost Mime body part if configured + addCustomOuterMimeHeaders(msg, dataBP); + } + if (logger.isTraceEnabled()) + { + logger.trace("Compressing outbound message after signing..."); + } + dataBP = AS2Util.getCryptoHelper().compress(msg, dataBP, compressionType, contentTxfrEncoding); + } + // Encrypt the data if requested + if (encrypt) + { + //Add any additional headers since this will be the outermost Mime body part if configured + addCustomOuterMimeHeaders(msg, dataBP); + String algorithm = partnership.getAttribute(SecurePartnership.PA_ENCRYPT); + + X509Certificate receiverCert = certFx.getCertificate(msg, Partnership.PTYPE_RECEIVER); + dataBP = AS2Util.getCryptoHelper().encrypt(dataBP, receiverCert, algorithm, contentTxfrEncoding); + + // Asynch MDN 2007-03-12 + DataHistoryItem historyItem = new DataHistoryItem(dataBP.getContentType()); + // *** add one more item to msg history + msg.getHistory().getItems().add(historyItem); + + if (logger.isDebugEnabled()) + { + logger.debug("encrypted data" + msg.getLogMsgID()); + } + } + + String t = dataBP.getEncoding(); + if ((t == null || t.length() < 1) && "true".equalsIgnoreCase(partnership.getAttribute(Partnership.PA_SET_CONTENT_TRANSFER_ENCODING_OMBP))) + { + dataBP.setHeader("Content-Transfer-Encoding", contentTxfrEncoding); + } + return dataBP; + } + + protected void addCustomOuterMimeHeaders(Message msg, MimeBodyPart dataBP) throws MessagingException + { if (logger.isTraceEnabled()) { - // Generate some alternative MIC's to see if the partner is somehow using a different default - String tmic = AS2Util.getCryptoHelper().calculateMIC(mbp, dispOptions.getMicalg() - , includeHeaders, !msg.getPartnership().isPreventCanonicalization()); - logger.trace("MIC outbound with forced reversed prevent canocalization: " + tmic + msg.getLogMsgID()); - tmic = AS2Util.getCryptoHelper().calculateMIC(msg.getData(), dispOptions.getMicalg(), - false, msg.getPartnership().isPreventCanonicalization()); - logger.trace("MIC outbound with forced exclude headers flag: " + tmic + msg.getLogMsgID()); - + logger.trace("Adding custom headers to outer MBP...." + msg.getLogMsgID()); + } + Map hdrs = msg.getCustomOuterMimeHeaders(); + if (hdrs == null) + { + return; + } + for (Map.Entry entry : hdrs.entrySet()) + { + dataBP.addHeader(entry.getKey(), entry.getValue()); + if (logger.isTraceEnabled()) + { + logger.trace("Added custom headers to outer MBP: " + entry.getKey() + "--->" + entry.getValue() + msg.getLogMsgID()); + } + } + } + + protected void updateHttpHeaders(HttpURLConnection conn, Message msg, MimeBodyPart securedData) + { + Partnership partnership = msg.getPartnership(); + + conn.setRequestProperty("Connection", "close, TE"); + conn.setRequestProperty("User-Agent", msg.getAppTitle() + " (AS2Sender)"); + + conn.setRequestProperty("Date", DateUtil.formatDate("EEE, dd MMM yyyy HH:mm:ss Z")); + conn.setRequestProperty("Message-ID", msg.getMessageID()); + conn.setRequestProperty("Mime-Version", "1.0"); // make sure this is the + // encoding used in the + // msg, run TBF1 + try + { + conn.setRequestProperty("Content-type", securedData.getContentType()); + } catch (MessagingException e) + { + conn.setRequestProperty("Content-type", msg.getContentType()); + } + conn.setRequestProperty("AS2-Version", "1.1"); // RFC6017 - AS2 V1.1 supports compression + // AS2 V1.2 additionally supports EDIINT-Features + // conn.setRequestProperty("EDIINT-Features", + // "CEM,multiple-attachments"); // TODO (possibly implement???) + String cte = null; + try + { + cte = securedData.getEncoding(); + } catch (MessagingException e1) + { + e1.printStackTrace(); + } + if (cte == null) + { + cte = Session.DEFAULT_CONTENT_TRANSFER_ENCODING; + } + conn.setRequestProperty("Content-Transfer-Encoding", cte); + conn.setRequestProperty("Recipient-Address", partnership.getAttribute(AS2Partnership.PA_AS2_URL)); + conn.setRequestProperty("AS2-To", partnership.getReceiverID(AS2Partnership.PID_AS2)); + conn.setRequestProperty("AS2-From", partnership.getSenderID(AS2Partnership.PID_AS2)); + conn.setRequestProperty("Subject", msg.getSubject()); + conn.setRequestProperty("From", partnership.getSenderID(Partnership.PID_EMAIL)); + + String dispTo = partnership.getAttribute(AS2Partnership.PA_AS2_MDN_TO); + + if (dispTo != null) + { + conn.setRequestProperty("Disposition-Notification-To", dispTo); + } + + String dispOptions = partnership.getAttribute(AS2Partnership.PA_AS2_MDN_OPTIONS); + + if (dispOptions != null) + { + conn.setRequestProperty("Disposition-Notification-Options", dispOptions); + } + + String receiptOption = partnership.getAttribute(AS2Partnership.PA_AS2_RECEIPT_OPTION); + if (receiptOption != null) + { + conn.setRequestProperty("Receipt-Delivery-Option", receiptOption); + } + + String contentDisp; + try + { + contentDisp = securedData.getDisposition(); + } catch (MessagingException e) + { + contentDisp = msg.getContentDisposition(); + } + if (contentDisp != null) + { + conn.setRequestProperty("Content-Disposition", contentDisp); + } + if ("true".equalsIgnoreCase((partnership.getAttribute(AS2Partnership.PA_ADD_CUSTOM_MIME_HEADERS_TO_HTTP)))) + { + if (logger.isTraceEnabled()) + { + logger.trace("Adding custom headers to HTTP..." + msg.getLogMsgID()); + } + for (Map.Entry entry : msg.getCustomOuterMimeHeaders().entrySet()) + { + conn.setRequestProperty(entry.getKey(), entry.getValue()); + } + + } + + } + + /** + * @param msg AS2Message + * @param mic + * @throws WrappedException + * @description Stores metadata into pending information file and storing + * message object from first send attempt. The message object + * is written to a separate file to avoid repeated rewrites of + * possibly very large objects since it contains the original + * file data + */ + protected void storePendingInfo(AS2Message msg, boolean isResend) throws Exception + { + ObjectOutputStream oos = null; + + try + { + String pendingInfoFile = AS2Util.buildPendingFileName(msg, getSession().getProcessor(), "pendingmdninfo"); + String pendingFile = msg.getAttribute(FileAttribute.MA_PENDINGFILE); + msg.setAttribute(FileAttribute.MA_PENDINGFILE, pendingFile); + if (!isResend) + { + // Write the object to a file to keep a lot of the original + // static metadata intact for resends + String pendingMsgObjFile = pendingFile + ".object"; + oos = new ObjectOutputStream(new FileOutputStream(pendingMsgObjFile)); + oos.writeObject(msg); + oos.flush(); + oos.close(); + } + msg.setAttribute(FileAttribute.MA_PENDINGINFO, pendingInfoFile); + oos = new ObjectOutputStream(new FileOutputStream(pendingInfoFile)); + oos.writeObject(msg.getCalculatedMIC()); + String retries = (String) msg.getOption(ResenderModule.OPTION_RETRIES); + oos.writeObject((retries == null ? "" : retries)); + + if (logger.isInfoEnabled()) + { + logger.info("Save Original mic & message id information into file: " + pendingInfoFile + + msg.getLogMsgID()); + } + oos.writeObject(msg.getAttribute(FileAttribute.MA_FILENAME)); + oos.writeObject(pendingFile); + oos.writeObject(msg.getAttribute(FileAttribute.MA_ERROR_DIR)); + String sentDir = msg.getAttribute(FileAttribute.MA_SENT_DIR); + oos.writeObject((sentDir == null ? "" : sentDir)); + oos.writeObject(msg.getAttributes()); + if (logger.isTraceEnabled()) + { + logger.trace("Pending info file written to:" + pendingInfoFile + "\n\tOriginal MIC: " + + msg.getCalculatedMIC() + "\n\tRetry Count: " + retries + + "\n\tOriginal file name : " + msg.getAttribute(FileAttribute.MA_FILENAME) + + "\n\tPending message file : " + pendingFile + "\n\tError directory: " + + msg.getAttribute(FileAttribute.MA_ERROR_DIR) + "\n\tSent directory: " + + msg.getAttribute(FileAttribute.MA_SENT_DIR) + msg.getLogMsgID()); + } + + msg.setAttribute(FileAttribute.MA_STATUS, FileAttribute.MA_PENDING); + + } catch (Exception e) + { + msg.setLogMsg("Error setting up pending information files: " + org.openas2.logging.Log.getExceptionMsg(e)); + logger.error(msg, e); + throw new Exception("Unable to set up pending information files."); + } finally + { + if (oos != null) + { + try + { + oos.close(); + } catch (IOException e) + { + } + } + } + } + + protected void calcAndStoreMic(Message msg, MimeBodyPart mbp, boolean includeHeaders) throws Exception + { + // Calculate and get the original mic + // includeHeaders = (msg.getHistory().getItems().size() > 1); + + DispositionOptions dispOptions = new DispositionOptions(msg.getPartnership().getAttribute( + AS2Partnership.PA_AS2_MDN_OPTIONS)); + msg.setCalculatedMIC(AS2Util.getCryptoHelper().calculateMIC(mbp, dispOptions.getMicalg() + , includeHeaders, msg.getPartnership().isPreventCanonicalization())); + if (logger.isTraceEnabled()) + { + // Generate some alternative MIC's to see if the partner is somehow using a different default + String tmic = AS2Util.getCryptoHelper().calculateMIC(mbp, dispOptions.getMicalg() + , includeHeaders, !msg.getPartnership().isPreventCanonicalization()); + logger.trace("MIC outbound with forced reversed prevent canocalization: " + tmic + msg.getLogMsgID()); + tmic = AS2Util.getCryptoHelper().calculateMIC(msg.getData(), dispOptions.getMicalg(), + false, msg.getPartnership().isPreventCanonicalization()); + logger.trace("MIC outbound with forced exclude headers flag: " + tmic + msg.getLogMsgID()); + } - } + } } diff --git a/Server/src/main/java/org/openas2/processor/sender/AsynchMDNSenderModule.java b/Server/src/main/java/org/openas2/processor/sender/AsynchMDNSenderModule.java index 3a81fe61..2c2c73cf 100644 --- a/Server/src/main/java/org/openas2/processor/sender/AsynchMDNSenderModule.java +++ b/Server/src/main/java/org/openas2/processor/sender/AsynchMDNSenderModule.java @@ -10,6 +10,7 @@ import javax.mail.Header; +import org.apache.commons.io.IOUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.openas2.OpenAS2Exception; @@ -19,6 +20,7 @@ import org.openas2.message.MessageMDN; import org.openas2.processor.resender.ResenderModule; import org.openas2.processor.storage.StorageModule; +import org.openas2.util.AS2Util; import org.openas2.util.DateUtil; import org.openas2.util.DispositionType; import org.openas2.util.IOUtilOld; @@ -93,6 +95,7 @@ private void sendAsyncMDN(AS2Message msg, Map options) conn.setRequestProperty("Connection", "close, TE"); conn.setRequestProperty("User-Agent", msg.getAppTitle() + " (AsyncMDNSenderModule)"); // Copy all the header from mdn to the RequestProperties of conn + @SuppressWarnings("unchecked") Enumeration

headers = mdn.getHeaders().getAllHeaders(); Header header = null; while (headers.hasMoreElements()) { @@ -115,8 +118,8 @@ private void sendAsyncMDN(AS2Message msg, Map options) try { ProfilerStub transferStub = Profiler.startProfile(); - int bytes = IOUtilOld.copy(messageIn, messageOut); - Profiler.endProfile(transferStub); + int bytes = IOUtils.copy(messageIn, messageOut); + Profiler.endProfile(transferStub); if (logger.isInfoEnabled()) logger.info("transferred " + IOUtilOld.getTransferRate(bytes, transferStub) + msg.getLogMsgID()); @@ -188,9 +191,37 @@ private void sendAsyncMDN(AS2Message msg, Map options) protected void resend(Message msg, OpenAS2Exception cause) throws OpenAS2Exception { + // Get the resend retry count + Map msgOptions = msg.getOptions(); + String tries = AS2Util.retries(msgOptions, getParameter(SenderModule.SOPT_RETRIES, false)); + if (logger.isDebugEnabled()) + logger.debug("MDN resend retries: MSG - " + msgOptions.get(SenderModule.SOPT_RETRIES) + " ::: RETRIES - " + tries); + int retries = -1; + if (tries == null) tries = SenderModule.DEFAULT_RETRIES; + try { + retries = Integer.parseInt(tries); + } catch (Exception e) { + msg.setLogMsg("The retry count is not a valid integer value: " + tries); + logger.error(msg); + } + if (msgOptions.get(SenderModule.SOPT_RETRIES) == null) + msgOptions.put(SenderModule.SOPT_RETRIES, retries); + if (logger.isTraceEnabled()) logger.trace("Send MDN retry count: " + retries); + if (retries >= 0 && retries -- <= 0) + { + msg.setLogMsg("MDN response abandoned after retry limit reached."); + logger.error(msg); + // Log significant msg state + msg.setOption("STATE", Message.MSG_STATE_MSG_RXD_MDN_SENDING_FAIL); + msg.trackMsgState(getSession()); + AS2Util.cleanupFiles(msg, false); + throw new OpenAS2Exception("MDN response abandoned after retry limit reached." + msg.getLogMsgID()); + } Map options = new HashMap(); options.put(ResenderModule.OPTION_CAUSE, cause); options.put(ResenderModule.OPTION_INITIAL_SENDER, this); + options.put(ResenderModule.OPTION_RESEND_METHOD, SenderModule.DO_SENDMDN); + options.put(ResenderModule.OPTION_RETRIES, "" + retries); getSession().getProcessor().handle(ResenderModule.DO_RESEND, msg, options); } diff --git a/Server/src/main/java/org/openas2/processor/sender/HttpSenderModule.java b/Server/src/main/java/org/openas2/processor/sender/HttpSenderModule.java index 66035f6b..ad3946d2 100644 --- a/Server/src/main/java/org/openas2/processor/sender/HttpSenderModule.java +++ b/Server/src/main/java/org/openas2/processor/sender/HttpSenderModule.java @@ -4,7 +4,11 @@ import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; +import java.net.Authenticator; import java.net.HttpURLConnection; +import java.net.InetSocketAddress; +import java.net.PasswordAuthentication; +import java.net.Proxy; import java.net.URL; import java.util.Iterator; import java.util.List; @@ -25,6 +29,7 @@ import org.apache.commons.logging.LogFactory; import org.openas2.OpenAS2Exception; import org.openas2.WrappedException; +import org.openas2.util.Properties; public abstract class HttpSenderModule extends BaseSenderModule implements SenderModule { @@ -32,29 +37,40 @@ public abstract class HttpSenderModule extends BaseSenderModule implements Sende public static final String PARAM_READ_TIMEOUT = "readtimeout"; public static final String PARAM_CONNECT_TIMEOUT = "connecttimeout"; + private Log logger = LogFactory.getLog(HttpSenderModule.class.getSimpleName()); public HttpURLConnection getConnection(String url, boolean output, boolean input, boolean useCaches, String requestMethod) throws OpenAS2Exception { if (url == null) throw new OpenAS2Exception("HTTP sender module received empty URL string."); - Log logger = LogFactory.getLog(HttpSenderModule.class.getSimpleName()); try { System.setProperty("sun.net.client.defaultReadTimeout", getParameter(PARAM_READ_TIMEOUT, "60000")); System.setProperty("sun.net.client.defaultConnectTimeout", getParameter(PARAM_CONNECT_TIMEOUT, "60000")); + + initializeProxyAuthenticator(); HttpURLConnection conn; URL urlObj = new URL(url); if (urlObj.getProtocol().equalsIgnoreCase("https")) { - HttpsURLConnection connS = (HttpsURLConnection) urlObj.openConnection(); + HttpsURLConnection connS = (HttpsURLConnection) urlObj.openConnection(getProxy("https")); String selfSignedCN = System.getProperty("org.openas2.cert.TrustSelfSignedCN"); if (selfSignedCN != null) { File file = new File("jssecacerts"); if (file.isFile() == false) { - char SEP = File.separatorChar; - File dir = new File(System.getProperty("java.home") + SEP + "lib" + SEP + "security"); - file = new File(dir, "jssecacerts"); + char SEP = File.separatorChar; + File dir = new File(System.getProperty("java.home") + SEP + "lib" + SEP + "security"); + /* Check if this is a JDK home */ + if (!dir.isDirectory()) + { + dir = new File(System.getProperty("java.home") + SEP + "jre" + SEP + "lib" + SEP + "security"); + } + if (!dir.isDirectory()) { + throw new OpenAS2Exception( + "The JSSE folder could not be identified. Please check that JSSE is installed."); + } + file = new File(dir, "jssecacerts"); if (file.isFile() == false) { file = new File(dir, "cacerts"); @@ -84,7 +100,7 @@ public HttpURLConnection getConnection(String url, boolean output, boolean input conn = connS; } else { - conn = (HttpURLConnection) urlObj.openConnection(); + conn = (HttpURLConnection) urlObj.openConnection(getProxy("http")); } conn.setDoOutput(output); conn.setDoInput(input); @@ -97,7 +113,40 @@ public HttpURLConnection getConnection(String url, boolean output, boolean input throw new WrappedException(ioe); } } + + private Proxy getProxy(String protocol) throws OpenAS2Exception + { + String proxyHost =Properties.getProperty(protocol + ".proxyHost", null); + if (proxyHost == null) proxyHost = System.getProperty(protocol + ".proxyHost"); + if (logger.isDebugEnabled()) logger.debug("PROXY HOST: " + proxyHost + " (protocol=" + protocol + ")"); + if (proxyHost == null) return Proxy.NO_PROXY; + String proxyPort =Properties.getProperty(protocol + ".proxyPort", null); + if (proxyPort == null) proxyPort = System.getProperty(protocol + ".proxyPort"); + if (proxyPort == null) throw new OpenAS2Exception("Missing PROXY port since Proxy host is set"); + int port = Integer.parseInt(proxyPort); + return new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxyHost, port)); + + } + private void initializeProxyAuthenticator() { + String proxyUser1 = Properties.getProperty("http.proxyUser", null); + final String proxyUser = proxyUser1 == null?System.getProperty("http.proxyUser"):proxyUser1; + String proxyPwd1 =Properties.getProperty("http.proxyPassword", null); + final String proxyPassword = proxyPwd1 == null?System.getProperty("http.proxyPassword"):proxyPwd1; + + if (proxyUser != null && proxyPassword != null) { + Authenticator.setDefault( + new Authenticator() { + public PasswordAuthentication getPasswordAuthentication() { + return new PasswordAuthentication( + proxyUser, proxyPassword.toCharArray() + ); + } + } + ); + } + } + // Copy headers from an Http connection to an InternetHeaders object protected void copyHttpHeaders(HttpURLConnection conn, InternetHeaders headers) { Iterator>> connHeadersIt = conn.getHeaderFields().entrySet().iterator(); diff --git a/Server/src/main/java/org/openas2/processor/storage/BaseStorageModule.java b/Server/src/main/java/org/openas2/processor/storage/BaseStorageModule.java index 94bb3fc6..b00da73d 100644 --- a/Server/src/main/java/org/openas2/processor/storage/BaseStorageModule.java +++ b/Server/src/main/java/org/openas2/processor/storage/BaseStorageModule.java @@ -6,6 +6,7 @@ import java.io.InputStream; import java.util.Map; +import org.apache.commons.io.IOUtils; import org.openas2.OpenAS2Exception; import org.openas2.Session; import org.openas2.message.Message; @@ -18,17 +19,22 @@ public abstract class BaseStorageModule extends BaseProcessorModule implements S public static final String PARAM_PROTOCOL = "protocol"; public static final String PARAM_TEMPDIR = "tempdir"; - public boolean canHandle(String action, Message msg, Map options) { - try { - if (!action.equals(getModuleAction())) { + public boolean canHandle(String action, Message msg, Map options) + { + try + { + if (!action.equals(getModuleAction())) + { return false; } String modProtocol = getParameter(PARAM_PROTOCOL, false); String msgProtocol = msg.getProtocol(); - if (modProtocol != null) { - if ((msgProtocol != null) && msgProtocol.equals(modProtocol)) { + if (modProtocol != null) + { + if ((msgProtocol != null) && msgProtocol.equals(modProtocol)) + { return true; } @@ -36,79 +42,88 @@ public boolean canHandle(String action, Message msg, Map options } return true; - } catch (OpenAS2Exception oae) { + } catch (OpenAS2Exception oae) + { return false; } } - public void init(Session session, Map options) throws OpenAS2Exception { + public void init(Session session, Map options) throws OpenAS2Exception + { super.init(session, options); getParameter(PARAM_FILENAME, true); } protected abstract String getModuleAction(); - -/** - * Add one more method "getFile" to make no impact to all modules who call this method with - * only two parameter "Message msg" & "String fileParam" - */ - - + + /** + * Add one more method "getFile" to make no impact to all modules who call this method with + * only two parameter "Message msg" & "String fileParam" + */ + + protected File getFile(Message msg, String fileParam) throws IOException, - OpenAS2Exception { - return getFile(msg, fileParam, ""); - } - + OpenAS2Exception + { + return getFile(msg, fileParam, ""); + } + /** - * @since 2007-06-01 * @param msg * @param fileParam * @param action * @return * @throws IOException * @throws OpenAS2Exception + * @since 2007-06-01 */ - protected File getFile(Message msg, String fileParam, String action) throws IOException, OpenAS2Exception { - String filename = getFilename(msg, fileParam, action); - - // make sure the parent directories exist - File file = new File(filename); - File parentDir = file.getParentFile(); - parentDir.mkdirs(); - // don't overwrite existing files - return IOUtilOld.getUnique(parentDir, IOUtilOld.cleanFilename(file.getName())); - + protected File getFile(Message msg, String fileParam, String action) throws IOException, OpenAS2Exception + { + String filename = getFilename(msg, fileParam, action); + + // make sure the parent directories exist + File file = new File(filename); + File parentDir = file.getParentFile(); + parentDir.mkdirs(); + + return file; + } protected abstract String getFilename(Message msg, String fileParam, String action) throws InvalidParameterException; - protected void store(File msgFile, InputStream in) throws IOException, OpenAS2Exception { - String tempDirname = getParameter(PARAM_TEMPDIR, false); - if (tempDirname != null) { + protected void store(File msgFile, InputStream in) throws IOException, OpenAS2Exception + { + String tempDirname = getParameter(PARAM_TEMPDIR, false); + if (tempDirname != null) + { // write the data to a temporary directory first File tempDir = IOUtilOld.getDirectoryFile(tempDirname); - String tempFilename = msgFile.getName(); + String tempFilename = msgFile.getName(); File tempFile = IOUtilOld.getUnique(tempDir, tempFilename); writeStream(in, tempFile); - + // copy the temp file over to the destination IOUtilOld.moveFile(tempFile, msgFile, true, false); - } else { - writeStream(in, msgFile); + } else + { + writeStream(in, msgFile); } } - - protected void writeStream(InputStream in, File destination) throws IOException { + + protected void writeStream(InputStream in, File destination) throws IOException + { FileOutputStream out = new FileOutputStream(destination); - try { - IOUtilOld.copy(in, out); - } finally { - out.close(); - in.close(); + try + { + IOUtils.copy(in, out); + } finally + { + IOUtils.closeQuietly(in, out); } } - - + + } \ No newline at end of file diff --git a/Server/src/main/java/org/openas2/processor/storage/MDNFileModule.java b/Server/src/main/java/org/openas2/processor/storage/MDNFileModule.java index d6d670a4..36bd0eec 100644 --- a/Server/src/main/java/org/openas2/processor/storage/MDNFileModule.java +++ b/Server/src/main/java/org/openas2/processor/storage/MDNFileModule.java @@ -17,6 +17,7 @@ import org.openas2.params.InvalidParameterException; import org.openas2.params.MessageMDNParameters; import org.openas2.params.ParameterParser; +import org.openas2.params.RandomParameters; public class MDNFileModule extends BaseStorageModule { @@ -45,9 +46,10 @@ protected String getModuleAction() { */ protected String getFilename(Message msg, String fileParam, String action) throws InvalidParameterException { MessageMDN mdn = msg.getMDN(); - CompositeParameters compParams = new CompositeParameters(false) . - add ("date", new DateParameters()) . - add ("mdn", new MessageMDNParameters(mdn)); + CompositeParameters compParams = new CompositeParameters(false) + .add("date", new DateParameters()) + .add("mdn", new MessageMDNParameters(mdn)) + .add("rand", new RandomParameters()); return ParameterParser.parse(fileParam, compParams); } diff --git a/Server/src/main/java/org/openas2/processor/storage/MessageFileModule.java b/Server/src/main/java/org/openas2/processor/storage/MessageFileModule.java index cb292f96..c67f37bf 100644 --- a/Server/src/main/java/org/openas2/processor/storage/MessageFileModule.java +++ b/Server/src/main/java/org/openas2/processor/storage/MessageFileModule.java @@ -19,6 +19,7 @@ import org.openas2.params.InvalidParameterException; import org.openas2.params.MessageParameters; import org.openas2.params.ParameterParser; +import org.openas2.params.RandomParameters; import org.openas2.processor.receiver.AS2ReceiverModule; import org.openas2.util.DispositionType; @@ -63,9 +64,10 @@ protected String getModuleAction() { * @since 2007-06-01 */ protected String getFilename(Message msg, String fileParam, String action) throws InvalidParameterException { - CompositeParameters compParams = new CompositeParameters(false) . - add ("date", new DateParameters()) . - add ("msg", new MessageParameters(msg)); + CompositeParameters compParams = new CompositeParameters(false) + .add("date", new DateParameters()) + .add("msg", new MessageParameters(msg)) + .add("rand", new RandomParameters()); return ParameterParser.parse(fileParam, compParams); } diff --git a/Server/src/main/java/org/openas2/schedule/HasSchedule.java b/Server/src/main/java/org/openas2/schedule/HasSchedule.java new file mode 100644 index 00000000..771ca8ad --- /dev/null +++ b/Server/src/main/java/org/openas2/schedule/HasSchedule.java @@ -0,0 +1,16 @@ +package org.openas2.schedule; + +import java.util.concurrent.ScheduledExecutorService; + +import org.openas2.OpenAS2Exception; + +/** + * An optional extension of {@link org.openas2.Component} which allow to schedule tasks on scheduler. + * In case when a component requires to do a periodical work, e.g. directory polling, file change monitoring, + * this interface could be used instead of threads or timers. + */ +public interface HasSchedule { + + void schedule(ScheduledExecutorService executor) throws OpenAS2Exception; + +} diff --git a/Server/src/main/java/org/openas2/schedule/SchedulerComponent.java b/Server/src/main/java/org/openas2/schedule/SchedulerComponent.java new file mode 100644 index 00000000..592dc18b --- /dev/null +++ b/Server/src/main/java/org/openas2/schedule/SchedulerComponent.java @@ -0,0 +1,67 @@ +package org.openas2.schedule; + +import java.util.Map; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +import org.apache.commons.lang3.concurrent.BasicThreadFactory; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.openas2.BaseComponent; +import org.openas2.Component; +import org.openas2.OpenAS2Exception; +import org.openas2.Session; +import org.openas2.params.InvalidParameterException; + +/** + * Scheduler module for periodic tasks. + */ +public class SchedulerComponent extends BaseComponent { + + public static final String PARAMETER_THREADS = "threads"; + // + private static final int MIN_AMOUNT_OF_THREADS = 6; + private Log logger = LogFactory.getLog(SchedulerComponent.class.getSimpleName()); + + private ScheduledExecutorService executorService; + + @Override + public void init(Session session, Map parameters) throws OpenAS2Exception + { + super.init(session, parameters); + createExecutor(); + scheduleComponentsTasks(session); + } + + private void createExecutor() throws InvalidParameterException + { + int configuredAmountOfThreads = getParameterInt(PARAMETER_THREADS, false); + int amountOfThreads = configuredAmountOfThreads < MIN_AMOUNT_OF_THREADS ? MIN_AMOUNT_OF_THREADS : configuredAmountOfThreads; + BasicThreadFactory threadFactory = new BasicThreadFactory.Builder() + .namingPattern(getName() + "-Thread-%d") + .build(); + + this.executorService = Executors.newScheduledThreadPool(amountOfThreads, threadFactory); + logger.debug("Scheduler module is ready."); + } + + private void scheduleComponentsTasks(Session session) throws OpenAS2Exception + { + for (Component component : session.getComponents().values()) + { + if (HasSchedule.class.isAssignableFrom(component.getClass())) + { + HasSchedule.class.cast(component).schedule(executorService); + } + } + } + + @Override + public void destroy() throws Exception + { + //graceful shutdown + executorService.awaitTermination(3, TimeUnit.SECONDS); + executorService.shutdownNow(); + } +} \ No newline at end of file diff --git a/Server/src/main/java/org/openas2/support/FileMonitor.java b/Server/src/main/java/org/openas2/support/FileMonitor.java new file mode 100644 index 00000000..ecf3f4c6 --- /dev/null +++ b/Server/src/main/java/org/openas2/support/FileMonitor.java @@ -0,0 +1,72 @@ +package org.openas2.support; + +import java.io.File; +import java.util.Date; +import java.util.LinkedList; +import java.util.List; + +import javax.annotation.Nonnull; + +/** + * A watcher for a file. Changes are detected by {@link File#lastModified()} + */ +public class FileMonitor implements Runnable { + + private List listeners = new LinkedList(); + + @Nonnull + private Date lastModified; + @Nonnull + private File file; + + public FileMonitor(@Nonnull File file, FileMonitorListener listener) + { + if (!file.exists()) + { + throw new IllegalArgumentException("File " + file.getAbsolutePath() + " doesn't exist."); + } + + if (!file.isFile()) + { + throw new IllegalArgumentException(file.getAbsolutePath() + " isn't a file."); + } + this.file = file; + this.lastModified = getLastModifiedFromFile(); + listeners.add(listener); + } + + public void addListener(FileMonitorListener listener) + { + listeners.add(listener); + } + + private boolean isModified() + { + Date currentModified = getLastModifiedFromFile(); + return currentModified.after(lastModified); + } + + @Nonnull + private Date getLastModifiedFromFile() + { + return new Date(file.lastModified()); + } + + private void notifyListeners(int eventID) + { + for (FileMonitorListener listener : listeners) + { + listener.onFileEvent(file, eventID); + } + } + + @Override + public void run() + { + if (isModified()) + { + lastModified = getLastModifiedFromFile(); + notifyListeners(FileMonitorListener.EVENT_MODIFIED); + } + } +} \ No newline at end of file diff --git a/Server/src/main/java/org/openas2/support/FileMonitorAdapter.java b/Server/src/main/java/org/openas2/support/FileMonitorAdapter.java new file mode 100644 index 00000000..27075504 --- /dev/null +++ b/Server/src/main/java/org/openas2/support/FileMonitorAdapter.java @@ -0,0 +1,59 @@ +package org.openas2.support; + +import java.io.File; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +import org.openas2.OpenAS2Exception; + +/** + * An adapter between + */ +public abstract class FileMonitorAdapter implements FileMonitorListener { + + + public static final int MINIMAL_SCHEDULE_INTERVAL = 1; + + /** + * Schedule a watch for file changes. + * If schedule interval less then {@link #MINIMAL_SCHEDULE_INTERVAL} no tasks will be scheduled + * + * @param executor a executor + * @param file an existing file + * @param refreshInterval refresh interval + * @param unit a time unit + */ + public void scheduleIfNeed(ScheduledExecutorService executor, File file, int refreshInterval, TimeUnit unit) + { + if (refreshInterval >= MINIMAL_SCHEDULE_INTERVAL) + { + executor.scheduleAtFixedRate(new FileMonitor(file, this), refreshInterval, refreshInterval, unit); + } + + } + + @Override + public void onFileEvent(File file, int eventID) + { + switch (eventID) + { + case FileMonitorListener.EVENT_MODIFIED: + + try + { + onConfigFileChanged(); + } catch (OpenAS2Exception oae) + { + oae.terminate(); + } + break; + } + } + + /** + * A template method which is triggered once observing file is changed. + * + * @throws OpenAS2Exception + */ + public abstract void onConfigFileChanged() throws OpenAS2Exception; +} diff --git a/Server/src/main/java/org/openas2/support/FileMonitorListener.java b/Server/src/main/java/org/openas2/support/FileMonitorListener.java new file mode 100644 index 00000000..c7d7cb57 --- /dev/null +++ b/Server/src/main/java/org/openas2/support/FileMonitorListener.java @@ -0,0 +1,11 @@ +package org.openas2.support; + +import java.io.File; + + +public interface FileMonitorListener { + + int EVENT_MODIFIED = 1; + + void onFileEvent(File file, int eventID); +} diff --git a/Server/src/main/java/org/openas2/test/MimeBodyPartEncodingTest.java b/Server/src/main/java/org/openas2/test/MimeBodyPartEncodingTest.java index a4b22adc..9e048639 100644 --- a/Server/src/main/java/org/openas2/test/MimeBodyPartEncodingTest.java +++ b/Server/src/main/java/org/openas2/test/MimeBodyPartEncodingTest.java @@ -30,178 +30,178 @@ /** * @author christopher broderick - * */ -public class MimeBodyPartEncodingTest -{ - protected static OutputStream sysOut; - protected static BufferedWriter sysOutWriter; - - - public static void main(String[] args) - { - XMLSession session = null; - int exitStatus = 0; - System.setProperty("org.apache.commons.logging.Log", "org.openas2.logging.Log"); - //System.setProperty("openas2log.properties", "Server/bin/openas2log.properties"); - LogFactory.getFactory().setAttribute("level", "TRACE"); - System.out.println("Current working directory: " + System.getProperty("user.dir") + System.getProperty("line.separator")); - System.out.println("Logging prop: " + System.getProperty("org.apache.commons.logging.Log") + System.getProperty("line.separator")); - File f = new File(TestConfig.TEST_OUTPUT_FOLDER); - f.mkdirs(); - - try - { - write("Starting test..." + System.getProperty("line.separator")); - - // create the OpenAS2 Session object - // this is used by all other objects to access global configs and - // functionality - write("Loading configuration..." + System.getProperty("line.separator")); - String configFile = "Server/config/config.xml"; - if (args.length == 1) - { - configFile = args[0]; - } - else if (args.length > 1) - { - write("Current working directory: " + System.getProperty("user.dir") + System.getProperty("line.separator")); - write("Usage:" + System.getProperty("line.separator")); - write("java org.openas2.app.OpenAS2Server " + System.getProperty("line.separator")); - throw new Exception("Missing configuration file"); - } - session = new XMLSession(configFile); - // Do the deed... - write("Entering test phase...." + System.getProperty("line.separator")); - Message msg = new AS2Message(); - getPartnerInfo(msg); - // update the message's partnership with any stored information - session.getPartnershipFactory().updatePartnership(msg, true); - Map attribs = msg.getPartnership().getAttributes(); - write("Partnership attributes:\n" + msg.getPartnership().getName()); - for (String key : attribs.keySet()) - { - write("\t" + key + " ::= " + attribs.get(key) + System.getProperty("line.separator")); - } - - msg.setAttribute(FileAttribute.MA_FILEPATH, TestConfig.TEST_SOURCE_FOLDER); - msg.setAttribute(FileAttribute.MA_FILENAME, TestConfig.TEST_DEFAULT_SRC_FILE_NAME); - byte[] data = TestConfig.DEFAULT_MESSAGE_TEXT.getBytes(); - String contentType = "application/octet-stream"; - ByteArrayDataSource byteSource = new ByteArrayDataSource(data, contentType, null); - MimeBodyPart body = new MimeBodyPart(); - body.setDataHandler(new DataHandler(byteSource)); - - // below statement is not filename related, just want to make it - // consist with the parameter "mimetype="application/EDI-X12"" - // defined in config.xml 2007-06-01 - - body.setHeader("Content-Type", contentType); - - // add below statement will tell the receiver to save the filename - // as the one sent by sender. 2007-06-01 - String contentDisposition = "Attachment; filename=\"" + msg.getAttribute(FileAttribute.MA_FILENAME) + "\""; - body.setHeader("Content-Disposition", contentDisposition); - msg.setContentDisposition(contentDisposition); - - String contentTxfrEncoding = msg.getPartnership().getAttribute(Partnership.PA_CONTENT_TRANSFER_ENCODING); - if (contentTxfrEncoding == null) - contentTxfrEncoding = Session.DEFAULT_CONTENT_TRANSFER_ENCODING; - write("Using Content-Transfer-Encoding: " + contentTxfrEncoding + System.getProperty("line.separator")); - body.addHeader("Content-Transfer-Encoding", contentTxfrEncoding); - - msg.setData(body); - - // update the message's partnership with any stored information - session.getPartnershipFactory().updatePartnership(msg, true); - msg.updateMessageID(); - - CertificateFactory certFx = session.getCertificateFactory(); - - X509Certificate senderCert = certFx.getCertificate(msg, Partnership.PTYPE_SENDER); - - PrivateKey senderKey = certFx.getPrivateKey(msg, senderCert); - String digest = msg.getPartnership().getAttribute(SecurePartnership.PA_SIGN); - - System.out.println("Params for creating signed body part:: SIGN DIGEST: " + digest - + "\n CERT ALG NAME EXTRACTED: " + senderCert.getSigAlgName() - + "\n CERT PUB KEY ALG NAME EXTRACTED: " + senderCert.getPublicKey().getAlgorithm() - + msg.getLogMsgID()); - - String testFile = TestConfig.TEST_OUTPUT_FOLDER + "/" + TestConfig.TEST_DEFAULT_TGT_FILE_NAME + ".presigning"; - FileOutputStream fos = new FileOutputStream(testFile); - write(fos, body); - fos.close(); - System.out.println("MimeBodyPart written to: " + testFile); +public class MimeBodyPartEncodingTest { + protected static OutputStream sysOut; + protected static BufferedWriter sysOutWriter; + + + //todo move to tests + public static void main(String[] args) + { + XMLSession session = null; + int exitStatus = 0; + System.setProperty("org.apache.commons.logging.Log", "org.openas2.logging.Log"); + //System.setProperty("openas2log.properties", "Server/bin/openas2log.properties"); + LogFactory.getFactory().setAttribute("level", "TRACE"); + System.out.println("Current working directory: " + System.getProperty("user.dir") + System.getProperty("line.separator")); + System.out.println("Logging prop: " + System.getProperty("org.apache.commons.logging.Log") + System.getProperty("line.separator")); + File f = new File(TestConfig.TEST_OUTPUT_FOLDER); + f.mkdirs(); + + try + { + write("Starting test..." + System.getProperty("line.separator")); + + // create the OpenAS2 Session object + // this is used by all other objects to access global configs and + // functionality + write("Loading configuration..." + System.getProperty("line.separator")); + String configFile = "Server/config/config.xml"; + if (args.length == 1) + { + configFile = args[0]; + } else if (args.length > 1) + { + write("Current working directory: " + System.getProperty("user.dir") + System.getProperty("line.separator")); + write("Usage:" + System.getProperty("line.separator")); + write("java org.openas2.app.OpenAS2Server " + System.getProperty("line.separator")); + throw new Exception("Missing configuration file"); + } + session = new XMLSession(configFile); + // Do the deed... + write("Entering test phase...." + System.getProperty("line.separator")); + Message msg = new AS2Message(); + getPartnerInfo(msg); + // update the message's partnership with any stored information + session.getPartnershipFactory().updatePartnership(msg, true); + Map attribs = msg.getPartnership().getAttributes(); + write("Partnership attributes:\n" + msg.getPartnership().getName()); + for (String key : attribs.keySet()) + { + write("\t" + key + " ::= " + attribs.get(key) + System.getProperty("line.separator")); + } + + msg.setAttribute(FileAttribute.MA_FILEPATH, TestConfig.TEST_SOURCE_FOLDER); + msg.setAttribute(FileAttribute.MA_FILENAME, TestConfig.TEST_DEFAULT_SRC_FILE_NAME); + byte[] data = TestConfig.DEFAULT_MESSAGE_TEXT.getBytes(); + String contentType = "application/octet-stream"; + ByteArrayDataSource byteSource = new ByteArrayDataSource(data, contentType, null); + MimeBodyPart body = new MimeBodyPart(); + body.setDataHandler(new DataHandler(byteSource)); + + // below statement is not filename related, just want to make it + // consist with the parameter "mimetype="application/EDI-X12"" + // defined in config.xml 2007-06-01 + + body.setHeader("Content-Type", contentType); + + // add below statement will tell the receiver to save the filename + // as the one sent by sender. 2007-06-01 + String contentDisposition = "Attachment; filename=\"" + msg.getAttribute(FileAttribute.MA_FILENAME) + "\""; + body.setHeader("Content-Disposition", contentDisposition); + msg.setContentDisposition(contentDisposition); + + String contentTxfrEncoding = msg.getPartnership().getAttribute(Partnership.PA_CONTENT_TRANSFER_ENCODING); + if (contentTxfrEncoding == null) + { + contentTxfrEncoding = Session.DEFAULT_CONTENT_TRANSFER_ENCODING; + } + write("Using Content-Transfer-Encoding: " + contentTxfrEncoding + System.getProperty("line.separator")); + body.addHeader("Content-Transfer-Encoding", contentTxfrEncoding); + + msg.setData(body); + + // update the message's partnership with any stored information + session.getPartnershipFactory().updatePartnership(msg, true); + msg.updateMessageID(); + + CertificateFactory certFx = session.getCertificateFactory(); + + X509Certificate senderCert = certFx.getCertificate(msg, Partnership.PTYPE_SENDER); + + PrivateKey senderKey = certFx.getPrivateKey(msg, senderCert); + String digest = msg.getPartnership().getAttribute(SecurePartnership.PA_SIGN); + + System.out.println("Params for creating signed body part:: SIGN DIGEST: " + digest + + "\n CERT ALG NAME EXTRACTED: " + senderCert.getSigAlgName() + + "\n CERT PUB KEY ALG NAME EXTRACTED: " + senderCert.getPublicKey().getAlgorithm() + + msg.getLogMsgID()); + + String testFile = TestConfig.TEST_OUTPUT_FOLDER + "/" + TestConfig.TEST_DEFAULT_TGT_FILE_NAME + ".presigning"; + FileOutputStream fos = new FileOutputStream(testFile); + write(fos, body); + fos.close(); + System.out.println("MimeBodyPart written to: " + testFile); boolean isRemoveCmsAlgorithmProtectionAttr = "true".equalsIgnoreCase(msg.getPartnership().getAttribute(Partnership.PA_REMOVE_PROTECTION_ATTRIB)); - MimeBodyPart signedMbp = AS2Util.getCryptoHelper().sign(body, senderCert, senderKey, digest, contentTxfrEncoding - , msg.getPartnership().isRenameDigestToOldName(), isRemoveCmsAlgorithmProtectionAttr); - testFile = TestConfig.TEST_OUTPUT_FOLDER + "/" + TestConfig.TEST_DEFAULT_TGT_FILE_NAME + ".signed"; - fos = new FileOutputStream(testFile); - write(fos, signedMbp); - fos.close(); - System.out.println("MimeBodyPart written to: " + testFile); - - String algorithm = msg.getPartnership().getAttribute(SecurePartnership.PA_ENCRYPT); - X509Certificate receiverCert = certFx.getCertificate(msg, Partnership.PTYPE_RECEIVER); - signedMbp = AS2Util.getCryptoHelper().encrypt(signedMbp, receiverCert, algorithm, contentTxfrEncoding); - testFile = TestConfig.TEST_OUTPUT_FOLDER + "/" + TestConfig.TEST_DEFAULT_TGT_FILE_NAME + ".encrypted"; - fos = new FileOutputStream(testFile); - write(fos, signedMbp); - fos.close(); - System.out.println("MimeBodyPart written to: " + testFile); - - - } catch (Exception e) - { - exitStatus = -1; - e.printStackTrace(); - } catch (Error err) - { - exitStatus = -1; - err.printStackTrace(); - } finally - { - - write("OpenAS2 test has shut down." + System.getProperty("line.separator")); - - System.exit(exitStatus); - } - } - - public static void write(OutputStream os, MimeBodyPart mbp) throws MessagingException, IOException - { - os.write((System.getProperty("line.separator") + "========BEGIN MIMEBODYPART=========" + System.getProperty("line.separator")).getBytes()); - mbp.writeTo(os); - os.write((System.getProperty("line.separator") + "========END MIMEBODYPART=========" + System.getProperty("line.separator")).getBytes()); - } - - public static void write(String msg) - { - if (sysOutWriter == null) - { - sysOutWriter = new BufferedWriter(new OutputStreamWriter(System.out)); - } - - try - { - sysOutWriter.write(msg); - sysOutWriter.flush(); - } catch (java.io.IOException e) - { - e.printStackTrace(); - } - } - - public static void getPartnerInfo(Message msg) throws InvalidParameterException - { - MessageParameters params = new MessageParameters(msg); - - // Get the parameter that should provide the link between the polled - // directory and an AS2 sender and recipient - String defaults = System.getProperty("partnership.defaults",TestConfig.DEFAULT_PARTNER_INFO); - // Link the file to an AS2 sender and recipient via the Message object - // associated with the file - params.setParameters(defaults); - } + MimeBodyPart signedMbp = AS2Util.getCryptoHelper().sign(body, senderCert, senderKey, digest, contentTxfrEncoding + , msg.getPartnership().isRenameDigestToOldName(), isRemoveCmsAlgorithmProtectionAttr); + testFile = TestConfig.TEST_OUTPUT_FOLDER + "/" + TestConfig.TEST_DEFAULT_TGT_FILE_NAME + ".signed"; + fos = new FileOutputStream(testFile); + write(fos, signedMbp); + fos.close(); + System.out.println("MimeBodyPart written to: " + testFile); + + String algorithm = msg.getPartnership().getAttribute(SecurePartnership.PA_ENCRYPT); + X509Certificate receiverCert = certFx.getCertificate(msg, Partnership.PTYPE_RECEIVER); + signedMbp = AS2Util.getCryptoHelper().encrypt(signedMbp, receiverCert, algorithm, contentTxfrEncoding); + testFile = TestConfig.TEST_OUTPUT_FOLDER + "/" + TestConfig.TEST_DEFAULT_TGT_FILE_NAME + ".encrypted"; + fos = new FileOutputStream(testFile); + write(fos, signedMbp); + fos.close(); + System.out.println("MimeBodyPart written to: " + testFile); + + + } catch (Exception e) + { + exitStatus = -1; + e.printStackTrace(); + } catch (Error err) + { + exitStatus = -1; + err.printStackTrace(); + } finally + { + + write("OpenAS2 test has shut down." + System.getProperty("line.separator")); + + System.exit(exitStatus); + } + } + + public static void write(OutputStream os, MimeBodyPart mbp) throws MessagingException, IOException + { + os.write((System.getProperty("line.separator") + "========BEGIN MIMEBODYPART=========" + System.getProperty("line.separator")).getBytes()); + mbp.writeTo(os); + os.write((System.getProperty("line.separator") + "========END MIMEBODYPART=========" + System.getProperty("line.separator")).getBytes()); + } + + public static void write(String msg) + { + if (sysOutWriter == null) + { + sysOutWriter = new BufferedWriter(new OutputStreamWriter(System.out)); + } + + try + { + sysOutWriter.write(msg); + sysOutWriter.flush(); + } catch (java.io.IOException e) + { + e.printStackTrace(); + } + } + + public static void getPartnerInfo(Message msg) throws InvalidParameterException + { + MessageParameters params = new MessageParameters(msg); + + // Get the parameter that should provide the link between the polled + // directory and an AS2 sender and recipient + String defaults = System.getProperty("partnership.defaults", TestConfig.DEFAULT_PARTNER_INFO); + // Link the file to an AS2 sender and recipient via the Message object + // associated with the file + params.setParameters(defaults); + } } diff --git a/Server/src/main/java/org/openas2/util/AS2Util.java b/Server/src/main/java/org/openas2/util/AS2Util.java index a9c3c7bd..55fb0e1f 100644 --- a/Server/src/main/java/org/openas2/util/AS2Util.java +++ b/Server/src/main/java/org/openas2/util/AS2Util.java @@ -12,7 +12,6 @@ import java.util.Enumeration; import java.util.HashMap; import java.util.Map; -import java.util.UUID; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -41,12 +40,17 @@ import org.openas2.message.Message; import org.openas2.message.MessageMDN; import org.openas2.message.NetAttribute; +import org.openas2.params.CompositeParameters; +import org.openas2.params.DateParameters; +import org.openas2.params.InvalidParameterException; import org.openas2.params.MessageParameters; import org.openas2.params.ParameterParser; +import org.openas2.params.RandomParameters; import org.openas2.partner.AS2Partnership; import org.openas2.partner.ASXPartnership; import org.openas2.partner.Partnership; import org.openas2.processor.Processor; +import org.openas2.processor.msgtracking.BaseMsgTrackingModule; import org.openas2.processor.resender.ResenderModule; import org.openas2.processor.sender.SenderModule; import org.openas2.processor.storage.StorageModule; @@ -62,6 +66,25 @@ public static ICryptoHelper getCryptoHelper() throws Exception { return ch; } + + public static String generateMessageID(Message msg) throws InvalidParameterException + { + CompositeParameters params = + new CompositeParameters(false). + add("date", new DateParameters()). + add("msg", new MessageParameters(msg)). + add("rand", new RandomParameters()); + + String idFormat = msg.getPartnership().getAttribute(AS2Partnership.PA_MESSAGEID); + if (idFormat == null) + { + idFormat = Properties.getProperty("as2_message_id_format" + , "OPENAS2-$date.ddMMyyyyHHmmssZ$-$rand.UUID$@$msg.sender.as2_id$_$msg.receiver.as2_id$"); + } + return ParameterParser.parse(idFormat, params); + } + + public static MessageMDN createMDN(Session session, AS2Message msg, String mic, DispositionType disposition, String text) throws Exception { @@ -410,11 +433,11 @@ public static String retries(Map options, String fallbackRetries) return left; } - /* @description Attempts to check if a resend should go ahead and if o decrements the resend count + /* @description Attempts to check if a resend should go ahead and if so decrements the resend count * and stores the decremented retry count in the options map. If the passed in retry count is null or invalid * it will fall back to a system default */ - public static boolean resend(Processor processor, Object sourceClass, String how, Message msg, OpenAS2Exception cause + public static boolean resend(Session session, Object sourceClass, String how, Message msg, OpenAS2Exception cause , String tries, boolean useOriginalMsgObject) throws OpenAS2Exception { Log logger = LogFactory.getLog(AS2Util.class.getSimpleName()); if (logger.isDebugEnabled()) logger.debug("RESEND requested.... retries to go: " + tries @@ -433,6 +456,9 @@ public static boolean resend(Processor processor, Object sourceClass, String how { msg.setLogMsg("Message abandoned after retry limit reached."); logger.error(msg); + // Log significant msg state + msg.setOption("STATE", Message.MSG_STATE_SEND_FAIL); + msg.trackMsgState(session); throw new OpenAS2Exception("Message abandoned after retry limit reached." + msg.getLogMsgID()); } @@ -490,14 +516,19 @@ public static boolean resend(Processor processor, Object sourceClass, String how msg = originalMsg; } + // Update the message state for the failed message as it will no longer be using the same message ID + msg.setOption("STATE", Message.MSG_STATE_SEND_FAIL_RESEND_QUEUED); + msg.trackMsgState(session); + // Resend requires a new Message-Id and we need to update the pendinginfo file name to match.... // The actual file that is pending can remain the same name since it is pointed to by line in pendinginfo file String oldMsgId = msg.getMessageID(); + msg.setAttribute(BaseMsgTrackingModule.FIELDS.PRIOR_MSG_ID, oldMsgId); String oldPendingInfoFileName = msg.getAttribute(FileAttribute.MA_PENDINGINFO); String newMsgId = ((AS2Message)msg).generateMessageID(); // Set new Id in Message object so we can generate new file name msg.setMessageID(newMsgId); - String newPendingInfoFileName = buildPendingFileName(msg, processor, "pendingmdninfo"); + String newPendingInfoFileName = buildPendingFileName(msg, session.getProcessor(), "pendingmdninfo"); if (logger.isDebugEnabled()) logger.debug("" + "\n Old Msg Id: " + oldMsgId @@ -530,7 +561,7 @@ public static boolean resend(Processor processor, Object sourceClass, String how options.put(ResenderModule.OPTION_INITIAL_SENDER, sourceClass); options.put(ResenderModule.OPTION_RESEND_METHOD, how); options.put(ResenderModule.OPTION_RETRIES, "" + retries); - processor.handle(ResenderModule.DO_RESEND, msg, options); + session.getProcessor().handle(ResenderModule.DO_RESEND, msg, options); return true; } @@ -580,7 +611,8 @@ public static void processMDN(AS2Message msg, byte[] data, OutputStream out, boo String retries = (String) msg.getOption(ResenderModule.OPTION_RETRIES); msg.setStatus(Message.MSG_STATUS_MDN_VERIFY); - if (logger.isTraceEnabled()) logger.trace("MDN parsed. Checking MDN report..." + msg.getLogMsgID()); + if (logger.isTraceEnabled()) logger.trace("MDN parsed. \n\tPayload file name: " + msg.getPayloadFilename() + + "\n\tChecking MDN report..." + msg.getLogMsgID()); try { AS2Util.checkMDN(msg); @@ -605,7 +637,7 @@ public static void processMDN(AS2Message msg, byte[] data, OutputStream out, boo if (logger.isErrorEnabled()) logger.error("Disposition exception processing MDN ..." + msg.getLogMsgID(), de); // Hmmmm... Error may require manual intervention but keep // trying.... possibly change retry count to 1 or just fail???? - AS2Util.resend(session.getProcessor(), sourceClass, SenderModule.DO_SEND, msg, de, retries, true); + AS2Util.resend(session, sourceClass, SenderModule.DO_SEND, msg, de, retries, true); msg.setOption("STATE", Message.MSG_STATE_MSG_SENT_MDN_RECEIVED_ERROR); msg.trackMsgState(session); return; @@ -620,7 +652,7 @@ public static void processMDN(AS2Message msg, byte[] data, OutputStream out, boo oae2.initCause(oae); oae2.addSource(OpenAS2Exception.SOURCE_MESSAGE, msg); oae2.terminate(); - AS2Util.resend(session.getProcessor(), sourceClass, SenderModule.DO_SEND, msg, oae2, retries, true); + AS2Util.resend(session, sourceClass, SenderModule.DO_SEND, msg, oae2, retries, true); msg.setOption("STATE", Message.MSG_STATE_MSG_SENT_MDN_RECEIVED_ERROR); msg.trackMsgState(session); return; @@ -628,6 +660,9 @@ public static void processMDN(AS2Message msg, byte[] data, OutputStream out, boo msg.setOption("STATE", Message.MSG_STATE_MSG_SENT_MDN_RECEIVED_OK); msg.trackMsgState(session); + if (logger.isTraceEnabled()) logger.trace("MDN processed. \n\tPayload file name: " + + msg.getPayloadFilename() + "\n\tPersisting MDN report..." + msg.getLogMsgID()); + session.getProcessor().handle(StorageModule.DO_STOREMDN, msg, null); msg.setStatus(Message.MSG_STATUS_MSG_CLEANUP); // To support extended reporting via logging log info passing Message object @@ -649,7 +684,7 @@ public static String buildPendingFileName(Message msg, Processor processor, Stri if (msgId == null || msgId.length() < 1) { // No ID set yet so generate a random string for uniqueness - msgId = msg.getAttribute(FileAttribute.MA_FILENAME) + "." + UUID.randomUUID(); + msgId = AS2Util.generateMessageID(msg); } return (dir + "/" + msgId); } @@ -658,7 +693,8 @@ public static String buildPendingFileName(Message msg, Processor processor, Stri * the sender module * @param msg - the Message object containing enough information to build the pending info file name */ - public static void getMetaData(AS2Message msg, Session session) throws OpenAS2Exception + + public static void getMetaData(AS2Message msg, Session session) throws OpenAS2Exception { Log logger = LogFactory.getLog(AS2Util.class.getSimpleName()); // use original message ID to open the pending information file from pendinginfo folder. @@ -689,11 +725,14 @@ public static void getMetaData(AS2Message msg, Session session) throws OpenAS2Ex if (logger.isTraceEnabled()) logger.trace("RETRY COUNT from pending info file: " + retries); msg.setOption(ResenderModule.OPTION_RETRIES, retries); // Get the original source file name from the 3rd line of pending information file - msg.setAttribute(FileAttribute.MA_FILENAME, (String)pifois.readObject()); + String origFileName = (String)pifois.readObject(); + msg.setAttribute(FileAttribute.MA_FILENAME, origFileName); + msg.setPayloadFilename(origFileName); // Get the original pending file from the 4th line of pending information file msg.setAttribute(FileAttribute.MA_PENDINGFILE, (String)pifois.readObject()); msg.setAttribute(FileAttribute.MA_ERROR_DIR, (String)pifois.readObject()); msg.setAttribute(FileAttribute.MA_SENT_DIR, (String)pifois.readObject()); + msg.getAttributes().putAll((Map)pifois.readObject()); msg.setAttribute(FileAttribute.MA_PENDINGINFO, pendinginfofile); if (logger.isTraceEnabled()) @@ -705,6 +744,7 @@ public static void getMetaData(AS2Message msg, Session session) throws OpenAS2Ex + "\n Pending message file : " + msg.getAttribute(FileAttribute.MA_PENDINGFILE) + "\n Error directory: " + msg.getAttribute(FileAttribute.MA_ERROR_DIR) + "\n Sent directory: " + msg.getAttribute(FileAttribute.MA_SENT_DIR) + + "\n Attributes: " + msg.getAttributes() + msg.getLogMsgID() ); } catch (IOException e) diff --git a/Server/src/main/java/org/openas2/util/DateUtil.java b/Server/src/main/java/org/openas2/util/DateUtil.java index 5ad41c94..5c6a637a 100644 --- a/Server/src/main/java/org/openas2/util/DateUtil.java +++ b/Server/src/main/java/org/openas2/util/DateUtil.java @@ -1,54 +1,42 @@ package org.openas2.util; import java.text.ParseException; -import java.text.SimpleDateFormat; import java.util.Date; -import java.util.HashMap; -import java.util.Map; + +import org.apache.commons.lang3.time.FastDateFormat; public class DateUtil { - private static final Map formatters = new HashMap(); - - public static synchronized String formatDate(String format, Date value) { - SimpleDateFormat df = getDateFormat(format); - return df.format(value); + + public static String formatDate(String format, Date value) + { + return FastDateFormat.getInstance(format).format(value); } - - public static synchronized String formatDate(String format) { + + public static String formatDate(String format) + { return formatDate(format, new Date()); } - - public static synchronized Date parseDate(String format, String value) throws ParseException { - SimpleDateFormat df = getDateFormat(format); - return df.parse(value); - } - private static SimpleDateFormat getDateFormat(String format) { - SimpleDateFormat df = (SimpleDateFormat) formatters.get(format); - if (df == null) { - df = new SimpleDateFormat(format); - formatters.put(format, df); - } - return df; - } - - public static synchronized String getSqlTimestamp() + public static Date parseDate(String format, String value) throws ParseException { + return FastDateFormat.getInstance(format).parse(value); + } + + public static String getSqlTimestamp() + { return getSqlTimestamp(new Date()); } - // ========================================================= - /** - * @return - */ - // ========================================================= - public static synchronized String getSqlTimestamp(Date date) + private static String getSqlTimestamp(Date date) { if (date == null) + { return ""; - return formatDate(Properties.getProperty("sql_timestamp_format", "yyyy-MM-dd HH:mm:ss.SSS"), date); + } else + { + //todo this property belongs to DbTracking module, it should be removed from here + return formatDate(Properties.getProperty("sql_timestamp_format", "yyyy-MM-dd HH:mm:ss.SSS"), date); + } } - - } diff --git a/Server/src/main/java/org/openas2/util/DispositionDataContentHandler.java b/Server/src/main/java/org/openas2/util/DispositionDataContentHandler.java deleted file mode 100644 index 33351b47..00000000 --- a/Server/src/main/java/org/openas2/util/DispositionDataContentHandler.java +++ /dev/null @@ -1,81 +0,0 @@ -package org.openas2.util; - -import java.awt.datatransfer.DataFlavor; -import java.io.BufferedInputStream; -import java.io.BufferedOutputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.OutputStream; - -import javax.activation.ActivationDataFlavor; -import javax.activation.DataContentHandler; -import javax.activation.DataSource; -import javax.mail.MessagingException; -import javax.mail.internet.MimeBodyPart; -import javax.mail.internet.MimeMultipart; - -public class DispositionDataContentHandler implements DataContentHandler { - private static final ActivationDataFlavor ADF1; - private static final DataFlavor[] ADFs; - - static { - ADF1 = new ActivationDataFlavor(MimeBodyPart.class, "message/disposition-notification", - "Disposition Notification"); - ADFs = new DataFlavor[] { ADF1 }; - } - - public DispositionDataContentHandler() { - super(); - } - - public Object getContent(DataSource ds) throws IOException { - byte[] buf = new byte[4096]; - BufferedInputStream bIn = new BufferedInputStream(ds.getInputStream()); - ByteArrayOutputStream baOut = new ByteArrayOutputStream(); - BufferedOutputStream bOut = new BufferedOutputStream(baOut); - int count = bIn.read(buf); - - while (count > -1) { - bOut.write(buf, 0, count); - count = bIn.read(buf); - } - - bIn.close(); - bOut.close(); - - return baOut.toByteArray(); - } - - public Object getTransferData(DataFlavor df, DataSource ds) throws IOException { - if (ADF1.equals(df)) { - return getContent(ds); - } - return null; - } - - public DataFlavor[] getTransferDataFlavors() { - return ADFs; - } - - public void writeTo(Object obj, String mimeType, OutputStream os) throws IOException { - if (obj instanceof MimeBodyPart) { - try { - ((MimeBodyPart) obj).writeTo(os); - } catch (MessagingException me) { - throw new IOException(me.getMessage()); - } - } else if (obj instanceof MimeMultipart) { - try { - ((MimeMultipart) obj).writeTo(os); - } catch (MessagingException me) { - throw new IOException(me.getMessage()); - } - } else if (obj instanceof byte[]) { - os.write((byte[]) obj); - } else if (obj instanceof String) { - os.write(((String) obj).getBytes()); - } else { - throw new IOException("Unknown object type: " + obj.getClass().getName()); - } - } -} \ No newline at end of file diff --git a/Server/src/main/java/org/openas2/util/FileMonitor.java b/Server/src/main/java/org/openas2/util/FileMonitor.java deleted file mode 100644 index de72388c..00000000 --- a/Server/src/main/java/org/openas2/util/FileMonitor.java +++ /dev/null @@ -1,150 +0,0 @@ -package org.openas2.util; - -import java.io.File; -import java.util.ArrayList; -import java.util.Date; -import java.util.Iterator; -import java.util.List; -import java.util.Timer; -import java.util.TimerTask; - -public class FileMonitor { - public List listeners; - private Date lastModified; - private File file; - private Timer timer; - private boolean busy; - private int interval; - - public FileMonitor(File file, int interval) { - super(); - this.file = file; - this.interval = interval; - start(); - } - - public void setBusy(boolean busy) { - this.busy = busy; - } - - public boolean isBusy() { - return busy; - } - - public void setFile(File file) { - this.file = file; - } - - public File getFile() { - return file; - } - - public String getFilename() { - if (getFile() != null) { - return getFile().getAbsolutePath(); - } - - return null; - } - - public void setInterval(int interval) { - this.interval = interval; - restart(); - } - - public int getInterval() { - return interval; - } - - public void setLastModified(Date lastModified) { - this.lastModified = lastModified; - } - - public Date getLastModified() { - return lastModified; - } - - public void setListeners(List listeners) { - this.listeners = listeners; - } - - public List getListeners() { - if (listeners == null) { - listeners = new ArrayList(); - } - - return listeners; - } - - public void addListener(FileMonitorListener listener) { - getListeners().add(listener); - } - - public void restart() { - stop(); - start(); - } - - public void start() { - timer = getTimer(); - timer.scheduleAtFixedRate(new TimerTick(), 0, getInterval() * 1000); - } - - public void stop() { - if (getTimer() != null) { - getTimer().cancel(); - } - } - - protected boolean isModified() { - Date lastModified = getLastModified(); - - if (lastModified != null) { - Date currentModified = new Date(getFile().lastModified()); - - return !currentModified.equals(getLastModified()); - } - updateModified(); - return false; - } - - protected Timer getTimer() { - if (timer == null) { - timer = new Timer(true); - } - - return timer; - } - - protected void updateListeners() { - if (isModified()) { - updateModified(); - updateListeners(FileMonitorListener.EVENT_MODIFIED); - } - } - - protected void updateListeners(int eventID) { - List listeners = getListeners(); - - Iterator iterator = (Iterator) listeners.iterator(); - for (Iterator it = iterator; it.hasNext();) { - (it.next()).handle(this, getFile(), eventID); - } - } - - protected void updateModified() { - setLastModified(new Date(getFile().lastModified())); - } - - private class TimerTick extends TimerTask { - public void run() { - if (!isBusy()) { - setBusy(true); - updateListeners(); - setBusy(false); - } else { - updateListeners(FileMonitorListener.EVENT_MISSED_TICK); - } - } - } -} \ No newline at end of file diff --git a/Server/src/main/java/org/openas2/util/FileMonitorListener.java b/Server/src/main/java/org/openas2/util/FileMonitorListener.java deleted file mode 100644 index 0550a36f..00000000 --- a/Server/src/main/java/org/openas2/util/FileMonitorListener.java +++ /dev/null @@ -1,12 +0,0 @@ -package org.openas2.util; - -import java.io.File; - - -public interface FileMonitorListener { - public static int EVENT_UNDEFINED = 0; - public static int EVENT_MODIFIED = 1; - public static int EVENT_MISSED_TICK = -1; - - public void handle(FileMonitor monitor, File file, int eventID); -} diff --git a/Server/src/main/java/org/openas2/util/HTTPUtil.java b/Server/src/main/java/org/openas2/util/HTTPUtil.java index 0bcc46e0..8ca2259b 100644 --- a/Server/src/main/java/org/openas2/util/HTTPUtil.java +++ b/Server/src/main/java/org/openas2/util/HTTPUtil.java @@ -7,7 +7,6 @@ import java.io.OutputStream; import java.net.HttpURLConnection; import java.util.StringTokenizer; - import javax.mail.MessagingException; import javax.mail.internet.InternetHeaders; @@ -278,8 +277,8 @@ else if (ch >= '0' && ch <= '9') { // And now the CRLF after the chunk; while (dataIn.readByte() != '\n'); } - msg.setHeader("Content-Length", new Integer(length).toString()); - } + msg.setHeader("Content-Length", Integer.toString(length)); + } else { if (outStream != null) HTTPUtil.sendHTTPResponse(outStream, HttpURLConnection.HTTP_LENGTH_REQUIRED, false); diff --git a/Server/src/main/java/org/openas2/util/IOUtilOld.java b/Server/src/main/java/org/openas2/util/IOUtilOld.java index 2c14df37..a07e604f 100644 --- a/Server/src/main/java/org/openas2/util/IOUtilOld.java +++ b/Server/src/main/java/org/openas2/util/IOUtilOld.java @@ -1,105 +1,96 @@ package org.openas2.util; -import java.io.ByteArrayOutputStream; import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; import java.io.FilenameFilter; import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; import java.util.UUID; +import org.apache.commons.io.FileUtils; import org.openas2.OpenAS2Exception; import org.openas2.message.InvalidMessageException; public class IOUtilOld { - public static final String VALID_FILENAME_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.@-"; + private static final String VALID_FILENAME_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.@-"; public static File getDirectoryFile(String directory) - throws IOException { + throws IOException + { File directoryFile = new File(directory); - if (!directoryFile.exists()) { - if (!directoryFile.mkdirs()) { + if (!directoryFile.exists()) + { + if (!directoryFile.mkdirs()) + { throw new IOException("Could not create directory: " + directory); } } - if (!directoryFile.isDirectory()) { + if (!directoryFile.isDirectory()) + { throw new IOException("Invalid directory: " + directory); } return directoryFile; } - public static byte[] getFileBytes(File file) throws IOException { - FileInputStream fis = null; - try - { - fis = new FileInputStream(file); - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - byte[] buf = new byte[4096]; - int byteCount; - - while ((byteCount = fis.read(buf)) >= 0) { - baos.write(buf, 0, byteCount); - } - return baos.toByteArray(); - } - finally - { - if (fis != null) fis.close(); - } - } - - public static String getTransferRate(int bytes, ProfilerStub stub) { + public static String getTransferRate(int bytes, ProfilerStub stub) + { StringBuffer buf = new StringBuffer(); buf.append(bytes).append(" bytes in "); buf.append(stub.getCombined()).append(" at "); long time = stub.getDifference(); - if (time != 0) { + if (time != 0) + { double stime = time / 1000.0; long rate = Math.round(bytes / stime); buf.append(getTransferRate(rate)); - } else { + } else + { buf.append(getTransferRate(bytes)); } return buf.toString(); } - public static String getTransferRate(long bytesPerSecond) { + private static String getTransferRate(long bytesPerSecond) + { StringBuffer buf = new StringBuffer(); long kbytesPerSecond = bytesPerSecond / 1024; - if (bytesPerSecond < 1024) { + if (bytesPerSecond < 1024) + { buf.append(bytesPerSecond).append(" Bps"); - } else if (kbytesPerSecond < 1024) { + } else if (kbytesPerSecond < 1024) + { buf.append(kbytesPerSecond).append(".").append(bytesPerSecond % 1024).append(" KBps"); - } else { + } else + { buf.append(kbytesPerSecond / 1024).append(".").append(kbytesPerSecond % 1024).append(" MBps"); } return buf.toString(); } - public static File getUnique(File dir, String filename) { + public static File getUnique(File dir, String filename) + { filename = cleanFilename(filename); return new File(dir, filename + "." + UUID.randomUUID()); } - public static String cleanFilename(String filename) { + public static String cleanFilename(String filename) + { byte[] fnBytes = filename.getBytes(); byte c; - for (int i = 0; i < fnBytes.length; i++) { + for (int i = 0; i < fnBytes.length; i++) + { c = fnBytes[i]; - if (VALID_FILENAME_CHARS.indexOf(c) == -1) { + if (VALID_FILENAME_CHARS.indexOf(c) == -1) + { fnBytes[i] = '_'; } } @@ -107,85 +98,22 @@ public static String cleanFilename(String filename) { return new String(fnBytes); } - public static int copy(InputStream in, OutputStream out) - throws IOException { - int totalCount = 0; - byte[] buf = new byte[4096]; - int count = 0; - - while (count >= 0) { - count = in.read(buf); - totalCount += count; - - if (count > 0) { - out.write(buf, 0, count); - } - } - - return totalCount; - } - - public static int copy(InputStream in, OutputStream out, int contentSize) - throws IOException { - int totalCount = 0; - byte[] buf = new byte[4096]; - int bytesIn = 0; - - /*Timer timer = new Timer(180); // 3 minute timeout - - while (bytesIn >= 0 && totalCount < contentSize) { - if (in.available() > 0) { - bytesIn = in.read(buf); - totalCount += bytesIn; - - if (bytesIn > 0) { - out.write(buf, 0, bytesIn); - } - timer.reset(); - } - if (timer.isTimedOut()) { - throw new IOException("Copy time-out after " + Integer.toString(totalCount) + " bytes"); - } - }*/ - while ((bytesIn >= 0) && (totalCount < contentSize)) { - bytesIn = in.read(buf); - totalCount += bytesIn; - - if (bytesIn > 0) { - out.write(buf, 0, bytesIn); - } - } - - return totalCount; - } - - public static void copyFile(File src, File dest) throws IOException { - FileInputStream fIn = null; - FileOutputStream fOut = null; - - try { - fIn = new FileInputStream(src); - fOut = new FileOutputStream(dest); - copy(fIn, fOut); - } finally { - if (fIn != null) fIn.close(); - if (fOut != null) fOut.close(); - } - } - // move the file to an error directory public static void handleError(File file, String errorDirectory) - throws OpenAS2Exception { + throws OpenAS2Exception + { File destFile = null; - try { + try + { File errorDir = IOUtilOld.getDirectoryFile(errorDirectory); destFile = new File(errorDir, file.getName()); // move the file destFile = IOUtilOld.moveFile(file, destFile, false, true); - } catch (IOException ioe) { + } catch (IOException ioe) + { InvalidMessageException im = new InvalidMessageException("Failed to move " + file.getAbsolutePath() + " to error directory " + destFile.getAbsolutePath()); im.initCause(ioe); @@ -198,30 +126,28 @@ public static void handleError(File file, String errorDirectory) imMoved.terminate(); } - public static void moveFile(File src, File dest) throws IOException { - copyFile(src, dest); - - //if (!new File(file.getAbsolutePath()).delete()) { // do this if file deletion always fails, may help - if (!src.delete()) { - dest.delete(); - throw new IOException("Move failed, unable to delete " + src); - } - } public static File moveFile(File src, File dest, boolean overwrite, boolean rename) - throws IOException { - if (!overwrite && dest.exists()) { - if (rename) { + throws IOException + { + if (!overwrite && dest.exists()) + { + if (rename) + { dest = getUnique(dest.getAbsoluteFile().getParentFile(), dest.getName()); - } else { + } else + { throw new IOException("File already exists: " + dest); } } - copyFile(src, dest); + //copyFile(src, dest); //todo why copy?? + FileUtils.copyFile(src, dest); // + //if (!new File(file.getAbsolutePath()).delete()) { // do this if file deletion always fails, may help - if (!src.delete()) { + if (!src.delete()) + { dest.delete(); throw new IOException("Move failed, unable to delete " + src); } @@ -229,45 +155,39 @@ public static File moveFile(File src, File dest, boolean overwrite, boolean rena return dest; } - public static void deleteFile(File f) throws IOException + public static void deleteFile(File f) throws IOException { - if (!f.exists()) return; - // Delete the file using java.nio.file.Files.delete() instead of java.io.File.delete() - // if possible (requires Java 7 or better) so exception can be used for debugging cause of fail - boolean canUseNio = true; - try - { - Class.forName("java.nio.file.Path"); - // it exists on the classpath - } catch(ClassNotFoundException e) { - // it does not exist on the classpath - canUseNio = false; - } - if (canUseNio) - { - java.nio.file.Path fp = f.toPath(); - java.nio.file.Files.delete(fp); - } - else if (!f.delete()) throw new IOException("Failed to delete file: " + f.getName()); - } - - public static byte[] toArray(InputStream in) throws IOException { - ByteArrayOutputStream bOut = new ByteArrayOutputStream(); - byte[] buf = new byte[4096]; - int count = in.read(buf); - - while (count >= 0) { - bOut.write(buf, 0, count); - count = in.read(buf); + if (!f.exists()) + { + return; + } + // Delete the file using java.nio.file.Files.delete() instead of java.io.File.delete() + // if possible (requires Java 7 or better) so exception can be used for debugging cause of fail + boolean canUseNio = true; + try + { + Class.forName("java.nio.file.Path"); + // it exists on the classpath + } catch (ClassNotFoundException e) + { + // it does not exist on the classpath + canUseNio = false; + } + if (canUseNio) + { + java.nio.file.Path fp = f.toPath(); + java.nio.file.Files.delete(fp); + } else if (!f.delete()) + { + throw new IOException("Failed to delete file: " + f.getName()); } - - return bOut.toByteArray(); } - - public static File[] getFiles(File dir, final String extensionFilter) { - return dir.listFiles(new FilenameFilter() - { - public boolean accept(File dir, String name) { + + public static File[] getFiles(File dir, final String extensionFilter) + { + return dir.listFiles(new FilenameFilter() { + public boolean accept(File dir, String name) + { return name.toLowerCase().endsWith("." + extensionFilter); } }); diff --git a/Server/src/main/java/org/openas2/util/Properties.java b/Server/src/main/java/org/openas2/util/Properties.java index e8da0ba0..7e2c2b2e 100644 --- a/Server/src/main/java/org/openas2/util/Properties.java +++ b/Server/src/main/java/org/openas2/util/Properties.java @@ -5,6 +5,9 @@ public class Properties { + public static String APP_VERSION_PROP = "app.version"; + public static String APP_TITLE_PROP = "app.title"; + private static Map _properties = new HashMap(); public static void setProperties(Map map) @@ -22,7 +25,6 @@ public static String getProperty(String key, String fallback) String val = _properties.get(key); if (val == null) { - _properties.put(key, fallback); return fallback; } return val; diff --git a/Server/src/main/java/org/openas2/util/Timer.java b/Server/src/main/java/org/openas2/util/Timer.java deleted file mode 100644 index 8ddc18bb..00000000 --- a/Server/src/main/java/org/openas2/util/Timer.java +++ /dev/null @@ -1,40 +0,0 @@ -package org.openas2.util; - -import java.util.Date; - - -public class Timer { - private Date startTime; - private long timeOut; - - public Timer(long timeOut) { - this.timeOut = timeOut; - startTime = new Date(); - } - - public void setStartTime(Date start) { - startTime = start; - } - - public Date getStartTime() { - return startTime; - } - - public void setTimeOut(long timeOut) { - this.timeOut = timeOut; - } - - public long getTimeOut() { - return timeOut; - } - - public void reset() { - startTime = new Date(); - } - - public boolean isTimedOut() { - Date timeOutDate = new Date(startTime.getTime() + (timeOut * 1000)); - - return timeOutDate.before(new Date()); - } -} diff --git a/Server/src/main/java/org/openas2/util/URLParser.java b/Server/src/main/java/org/openas2/util/URLParser.java deleted file mode 100644 index 0efaa56e..00000000 --- a/Server/src/main/java/org/openas2/util/URLParser.java +++ /dev/null @@ -1,50 +0,0 @@ -package org.openas2.util; -/** - * @author James Wang (wangwenpin@users.sourceforge.net) - */ - -import org.openas2.lib.OpenAS2Exception; - -public class URLParser { - String url = ""; - String host = "?"; - int port = 80; - String resourcename = "?"; - public URLParser(String url) throws OpenAS2Exception { - if (!url.toUpperCase().startsWith("HTTP://")) { - throw new OpenAS2Exception("bad url for URLParser, see "+url); - } - url = url.substring(7); - int pos1 = url.indexOf(":"); - int pos2 = url.indexOf("/"); - if (pos1 < 0) { - port = 80; - host = url.substring(0, pos2); - } else { - port = Integer.parseInt(url.substring(pos1 + 1, pos2)); - host = url.substring(0, pos1); - } - resourcename = url.substring(pos2); } - public String getHOST() { - return host; - } - public int getPORT() { - return port; - } - public String getRESOURCE() { - return resourcename; - } - - /** - * @param args - * @throws OpenAS2Exception - */ - public static void main(String[] args) throws OpenAS2Exception { - - URLParser UP = new URLParser("http://www.google.com/search?ie=UTF-8&oe=UTF-8&sourceid=navclient&gfns=1&q=urlparser+java"); - - System.out.println("host : " + UP.getHOST()); - System.out.println("port : " + UP.getPORT()); - System.out.println("resource : " + UP.getRESOURCE()); - } -} \ No newline at end of file diff --git a/Server/src/main/java/org/openas2/util/XMLUtil.java b/Server/src/main/java/org/openas2/util/XMLUtil.java index 31a8f9ef..ebd4fdf9 100644 --- a/Server/src/main/java/org/openas2/util/XMLUtil.java +++ b/Server/src/main/java/org/openas2/util/XMLUtil.java @@ -9,7 +9,6 @@ import org.openas2.OpenAS2Exception; import org.openas2.Session; import org.openas2.WrappedException; -import org.openas2.XMLSession; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.w3c.dom.NodeList; @@ -17,48 +16,54 @@ public class XMLUtil { public static Component getComponent(Node node, Session session) - throws OpenAS2Exception { + throws OpenAS2Exception + { Node classNameNode = node.getAttributes().getNamedItem("classname"); - if (classNameNode == null) { + if (classNameNode == null) + { throw new OpenAS2Exception("Missing classname"); } String className = classNameNode.getNodeValue(); - - try { + + try + { Class objClass = Class.forName(className); - if (!Component.class.isAssignableFrom(objClass)) { + if (!Component.class.isAssignableFrom(objClass)) + { throw new OpenAS2Exception("Class " + className + " must implement " + - Component.class.getName()); + Component.class.getName()); } - + Component obj = (Component) objClass.newInstance(); Map parameters = XMLUtil.mapAttributes(node); - if (session instanceof XMLSession) { - updateDirectories(((XMLSession) session).getBaseDirectory(), parameters); - } + updateDirectories(session.getBaseDirectory(), parameters); obj.init(session, parameters); return obj; - } catch (Exception e) { - throw new WrappedException("Error creating component: " + className, e); - } + } catch (Exception e) + { + throw new WrappedException("Error creating component: " + className, e); + } } - public static Node findChildNode(Node parent, String childName) { + public static Node findChildNode(Node parent, String childName) + { NodeList childNodes = parent.getChildNodes(); int childCount = childNodes.getLength(); Node child; - for (int i = 0; i < childCount; i++) { + for (int i = 0; i < childCount; i++) + { child = childNodes.item(i); - if (child.getNodeName().equals(childName)) { + if (child.getNodeName().equals(childName)) + { return child; } } @@ -67,7 +72,8 @@ public static Node findChildNode(Node parent, String childName) { } public static Map mapAttributeNodes(NodeList nodes, String nodeName, String nodeKeyName, - String nodeValueName) throws OpenAS2Exception { + String nodeValueName) throws OpenAS2Exception + { Map attributes = new HashMap(); int nodeCount = nodes.getLength(); Node attrNode; @@ -76,24 +82,28 @@ public static Map mapAttributeNodes(NodeList nodes, String nodeN String attrName; String attrValue; - for (int i = 0; i < nodeCount; i++) { + for (int i = 0; i < nodeCount; i++) + { attrNode = nodes.item(i); - if (attrNode.getNodeName().equals(nodeName)) { + if (attrNode.getNodeName().equals(nodeName)) + { nodeAttributes = attrNode.getAttributes(); tmpNode = nodeAttributes.getNamedItem(nodeKeyName); - if (tmpNode == null) { + if (tmpNode == null) + { throw new OpenAS2Exception(attrNode.toString() + - " does not have key attribute: " + nodeKeyName); + " does not have key attribute: " + nodeKeyName); } attrName = tmpNode.getNodeValue(); tmpNode = nodeAttributes.getNamedItem(nodeValueName); - if (tmpNode == null) { + if (tmpNode == null) + { throw new OpenAS2Exception(attrNode.toString() + - " does not have value attribute: " + nodeValueName); + " does not have value attribute: " + nodeValueName); } attrValue = tmpNode.getNodeValue(); @@ -104,52 +114,68 @@ public static Map mapAttributeNodes(NodeList nodes, String nodeN return attributes; } - public static Map mapAttributes(Node node) { - Map attrMap = new HashMap(); + public static Map mapAttributes(Node node, boolean keyToLowerCase) + { + Map attrMap = new HashMap(); NamedNodeMap attrNodes = node.getAttributes(); int attrCount = attrNodes.getLength(); Node attribute; - for (int i = 0; i < attrCount; i++) { + for (int i = 0; i < attrCount; i++) + { attribute = attrNodes.item(i); - attrMap.put(attribute.getNodeName().toLowerCase(), attribute.getNodeValue()); + String key = attribute.getNodeName(); + if (keyToLowerCase) key = key.toLowerCase(); + attrMap.put(key, attribute.getNodeValue()); } return attrMap; } - public static Map mapAttributes(Node node, String[] requiredAttributes) - throws OpenAS2Exception { - Map attributes = mapAttributes(node); + public static Map mapAttributes(Node node) + { + return mapAttributes(node, true); + } + public static Map mapAttributes(Node node, String[] requiredAttributes) + throws OpenAS2Exception + { + Map attributes = mapAttributes(node); String attrName; - for (int i = 0; i < requiredAttributes.length; i++) { - attrName = requiredAttributes[i]; + for (String requiredAttribute : requiredAttributes) + { + attrName = requiredAttribute; - if (attributes.get(attrName) == null) { + if (attributes.get(attrName) == null) + { throw new OpenAS2Exception(node.toString() + " is missing required attribute: " + - attrName); + attrName); } } return attributes; } - public static void updateDirectories(String baseDirectory, Map attributes) - throws OpenAS2Exception { + private static void updateDirectories(String baseDirectory, Map attributes) + throws OpenAS2Exception + { Iterator> attrIt = attributes.entrySet().iterator(); - Map.Entry attrEntry; + Map.Entry attrEntry; String value; - while (attrIt.hasNext()) { + while (attrIt.hasNext()) + { attrEntry = attrIt.next(); - value = (String) attrEntry.getValue(); + value = attrEntry.getValue(); - if (value.startsWith("%home%")) { - if (baseDirectory != null) { + if (value.startsWith("%home%")) + { + if (baseDirectory != null) + { value = baseDirectory + value.substring(6); attributes.put(attrEntry.getKey(), value); - } else { + } else + { throw new OpenAS2Exception("Base directory isn't set"); } } diff --git a/Server/src/resources/db/build.properties b/Server/src/resources/db/build.properties new file mode 100644 index 00000000..c2294089 --- /dev/null +++ b/Server/src/resources/db/build.properties @@ -0,0 +1,2 @@ +lib.dir=ddlutils-lib + diff --git a/Server/src/resources/db/build.xml b/Server/src/resources/db/build.xml new file mode 100644 index 00000000..ac3a2fe9 --- /dev/null +++ b/Server/src/resources/db/build.xml @@ -0,0 +1,195 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Server/src/resources/db/create_db_table.sh b/Server/src/resources/db/create_db_table.sh new file mode 100644 index 00000000..aa68947e --- /dev/null +++ b/Server/src/resources/db/create_db_table.sh @@ -0,0 +1,3 @@ +#!/bin/sh +#ant -Djdbc.properties.file=jdbc.properties.postgresql writeSchemaToDb +ant -Djdbc.properties.file=jdbc.properties.h2 writeSchemaToDb diff --git a/Server/src/resources/db/ddlutils-lib/commons-beanutils-1.7.0.jar b/Server/src/resources/db/ddlutils-lib/commons-beanutils-1.7.0.jar new file mode 100644 index 00000000..b1b89c9c Binary files /dev/null and b/Server/src/resources/db/ddlutils-lib/commons-beanutils-1.7.0.jar differ diff --git a/Server/src/resources/db/ddlutils-lib/commons-codec-1.3.jar b/Server/src/resources/db/ddlutils-lib/commons-codec-1.3.jar new file mode 100644 index 00000000..957b6752 Binary files /dev/null and b/Server/src/resources/db/ddlutils-lib/commons-codec-1.3.jar differ diff --git a/Server/src/resources/db/ddlutils-lib/commons-collections-3.1.jar b/Server/src/resources/db/ddlutils-lib/commons-collections-3.1.jar new file mode 100644 index 00000000..41e230fe Binary files /dev/null and b/Server/src/resources/db/ddlutils-lib/commons-collections-3.1.jar differ diff --git a/Server/src/resources/db/ddlutils-lib/commons-dbcp-1.2.1.jar b/Server/src/resources/db/ddlutils-lib/commons-dbcp-1.2.1.jar new file mode 100644 index 00000000..08440c02 Binary files /dev/null and b/Server/src/resources/db/ddlutils-lib/commons-dbcp-1.2.1.jar differ diff --git a/Server/src/resources/db/ddlutils-lib/commons-digester-1.7.jar b/Server/src/resources/db/ddlutils-lib/commons-digester-1.7.jar new file mode 100644 index 00000000..1783dbea Binary files /dev/null and b/Server/src/resources/db/ddlutils-lib/commons-digester-1.7.jar differ diff --git a/Server/src/resources/db/ddlutils-lib/commons-lang-2.1.jar b/Server/src/resources/db/ddlutils-lib/commons-lang-2.1.jar new file mode 100644 index 00000000..87b80ab5 Binary files /dev/null and b/Server/src/resources/db/ddlutils-lib/commons-lang-2.1.jar differ diff --git a/Server/src/resources/db/ddlutils-lib/commons-logging-1.0.4.jar b/Server/src/resources/db/ddlutils-lib/commons-logging-1.0.4.jar new file mode 100644 index 00000000..b73a80fa Binary files /dev/null and b/Server/src/resources/db/ddlutils-lib/commons-logging-1.0.4.jar differ diff --git a/Server/src/resources/db/ddlutils-lib/commons-pool-1.2.jar b/Server/src/resources/db/ddlutils-lib/commons-pool-1.2.jar new file mode 100644 index 00000000..4ba534c9 Binary files /dev/null and b/Server/src/resources/db/ddlutils-lib/commons-pool-1.2.jar differ diff --git a/Server/src/resources/db/ddlutils-lib/dom4j-1.4.jar b/Server/src/resources/db/ddlutils-lib/dom4j-1.4.jar new file mode 100644 index 00000000..2a587563 Binary files /dev/null and b/Server/src/resources/db/ddlutils-lib/dom4j-1.4.jar differ diff --git a/Server/src/resources/db/ddlutils-lib/log4j-1.2.8.jar b/Server/src/resources/db/ddlutils-lib/log4j-1.2.8.jar new file mode 100644 index 00000000..493a3ccc Binary files /dev/null and b/Server/src/resources/db/ddlutils-lib/log4j-1.2.8.jar differ diff --git a/Server/src/resources/db/ddlutils-lib/stax-api-1.0.1.jar b/Server/src/resources/db/ddlutils-lib/stax-api-1.0.1.jar new file mode 100644 index 00000000..d9a16651 Binary files /dev/null and b/Server/src/resources/db/ddlutils-lib/stax-api-1.0.1.jar differ diff --git a/Server/src/resources/db/ddlutils-lib/wstx-asl-3.0.2.jar b/Server/src/resources/db/ddlutils-lib/wstx-asl-3.0.2.jar new file mode 100644 index 00000000..eb5e3ced Binary files /dev/null and b/Server/src/resources/db/ddlutils-lib/wstx-asl-3.0.2.jar differ diff --git a/Server/src/resources/db/jdbc.properties.h2 b/Server/src/resources/db/jdbc.properties.h2 new file mode 100644 index 00000000..1f044cd4 --- /dev/null +++ b/Server/src/resources/db/jdbc.properties.h2 @@ -0,0 +1,52 @@ +# JDBC properties for PostgreSQL + +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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. + +# Note: Properties starting with "datasource." will be fed into the datasource instance of the +# class configured via the datasource.class property + +# Use this property if ddlutils does not recognize the platform from the settings +#ddlutils.platform=PostgreSQL + +# +# Using the plain DBCP datasource +# + +datasource.class=org.apache.commons.dbcp.BasicDataSource +datasource.driverClassName=org.h2.Driver +datasource.url=jdbc:h2:tcp://localhost:9092/openas2 +datasource.username=sa +datasource.password=OpenAS2 + +# +# PostgreSQL comes with its own DataSource implementations which can be configured like this: +# + +#datasource.class=org.postgresql.ds.PGSimpleDataSource +# or this one for pooling: +#datasource.class=org.postgresql.ds.PGPoolingDataSource + +#datasource.serverName=localhost +#datasource.databaseName=ddlutils +#datasource.user= +#datasource.password= + +# additional properties for the pooling datasource: +#datasource.dataSourceName=pooling datasource +#datasource.initialConnections=5 +#datasource.maxConnections=20 diff --git a/Server/src/resources/db/jdbc.properties.postgresql b/Server/src/resources/db/jdbc.properties.postgresql new file mode 100644 index 00000000..685c25d2 --- /dev/null +++ b/Server/src/resources/db/jdbc.properties.postgresql @@ -0,0 +1,52 @@ +# JDBC properties for PostgreSQL + +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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. + +# Note: Properties starting with "datasource." will be fed into the datasource instance of the +# class configured via the datasource.class property + +# Use this property if ddlutils does not recognize the platform from the settings +#ddlutils.platform=PostgreSQL + +# +# Using the plain DBCP datasource +# + +datasource.class=org.apache.commons.dbcp.BasicDataSource +datasource.driverClassName=org.postgresql.Driver +datasource.url=jdbc:postgresql://localhost/openas2 +datasource.username=sa +datasource.password=OpenAS2 + +# +# PostgreSQL comes with its own DataSource implementations which can be configured like this: +# + +#datasource.class=org.postgresql.ds.PGSimpleDataSource +# or this one for pooling: +#datasource.class=org.postgresql.ds.PGPoolingDataSource + +#datasource.serverName=localhost +#datasource.databaseName=ddlutils +#datasource.user= +#datasource.password= + +# additional properties for the pooling datasource: +#datasource.dataSourceName=pooling datasource +#datasource.initialConnections=5 +#datasource.maxConnections=20 diff --git a/Server/src/resources/db/lib/ddlutils-1.1.jar b/Server/src/resources/db/lib/ddlutils-1.1.jar new file mode 100644 index 00000000..528ab71c Binary files /dev/null and b/Server/src/resources/db/lib/ddlutils-1.1.jar differ diff --git a/Server/lib/h2-1.4.192.jar b/Server/src/resources/db/lib/h2-1.4.193.jar old mode 100755 new mode 100644 similarity index 50% rename from Server/lib/h2-1.4.192.jar rename to Server/src/resources/db/lib/h2-1.4.193.jar index 8936686e..6d7baf58 Binary files a/Server/lib/h2-1.4.192.jar and b/Server/src/resources/db/lib/h2-1.4.193.jar differ diff --git a/Server/src/resources/db/lib/postgresql-9.4.1212.jre6.jar b/Server/src/resources/db/lib/postgresql-9.4.1212.jre6.jar new file mode 100644 index 00000000..153dd8fe Binary files /dev/null and b/Server/src/resources/db/lib/postgresql-9.4.1212.jre6.jar differ diff --git a/Server/src/test/java/org/openas2/TestResource.java b/Server/src/test/java/org/openas2/TestResource.java new file mode 100644 index 00000000..a934dfa0 --- /dev/null +++ b/Server/src/test/java/org/openas2/TestResource.java @@ -0,0 +1,54 @@ +package org.openas2; + +import java.io.File; +import java.io.FileNotFoundException; +import java.net.URISyntaxException; +import java.net.URL; + +import org.apache.commons.lang3.StringUtils; + +/** + * A helper class for locating files and directories on the file systems. + * The main reason is to categorize static files used in tests. + */ +public class TestResource { + + /** + * An absolute path to a test specific resource. + */ + private final String pathPrefix; + + private TestResource(String clazzSimpleName) + { + try + { + URL resource = Thread.currentThread().getContextClassLoader().getResource("."); + this.pathPrefix = new File(resource.toURI()).getAbsolutePath() + File.separator + clazzSimpleName; + } catch (URISyntaxException e) + { + throw new RuntimeException(e); + } + } + + public static TestResource forClass(Class clazz) + { + return new TestResource(clazz.getSimpleName()); + } + + /** + * Get a file or directory within {@link #pathPrefix} + * + * @param fileName a file or directory name + * @param child a children name + * @return a file + */ + public File get(String fileName, String... child) throws FileNotFoundException + { + File file = new File(pathPrefix + File.separator + fileName + File.separator + StringUtils.join(child, File.separator)); + if (!file.exists()) + { + throw new FileNotFoundException(file.getAbsolutePath()); + } + return file; + } +} diff --git a/Server/src/test/java/org/openas2/TestUtils.java b/Server/src/test/java/org/openas2/TestUtils.java new file mode 100644 index 00000000..d9dee4fc --- /dev/null +++ b/Server/src/test/java/org/openas2/TestUtils.java @@ -0,0 +1,60 @@ +package org.openas2; + +import java.io.File; +import java.io.FileNotFoundException; +import java.util.Collection; +import java.util.concurrent.TimeUnit; + +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.filefilter.IOFileFilter; +import org.apache.commons.io.filefilter.TrueFileFilter; + +/** + * Utilities for tests + */ +public class TestUtils { + + /** + * Wait till a file will occur on the file system. + * + * @param parent a directory where file will be. + * @param fileFilter a filter to scan the parent dir. + * @param timeout an amount of time units to wait + * @param unit a time unit + * @return a file + * @throws FileNotFoundException + */ + public static File waitForFile(File parent, IOFileFilter fileFilter, int timeout, TimeUnit unit) throws FileNotFoundException + { + long finishAt = System.currentTimeMillis() + unit.toMillis(timeout); + waitForFile(parent, timeout, unit); + while (finishAt - System.currentTimeMillis() > 0) + { + Collection files = FileUtils.listFiles(parent, fileFilter, TrueFileFilter.INSTANCE); + if (!files.isEmpty()) + { + if (files.size() > 1) + { + throw new IllegalStateException("Result is not unique."); + } else + { + return files.iterator().next(); + } + } + } + throw new FileNotFoundException(fileFilter.toString()); + } + + /** + * Wait till file will occur on the file system. + * + * @param file a file + * @param timeout an amount of time units to wait + * @param unit a time unit + */ + public static void waitForFile(File file, int timeout, TimeUnit unit) + { + FileUtils.waitFor(file, Long.valueOf(unit.toSeconds(timeout)).intValue()); + } + +} diff --git a/Server/src/test/java/org/openas2/app/OpenAS2ServerTest.java b/Server/src/test/java/org/openas2/app/OpenAS2ServerTest.java new file mode 100644 index 00000000..8d4659cf --- /dev/null +++ b/Server/src/test/java/org/openas2/app/OpenAS2ServerTest.java @@ -0,0 +1,141 @@ +package org.openas2.app; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; + +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.filefilter.PrefixFileFilter; +import org.apache.commons.lang.RandomStringUtils; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.openas2.TestResource; +import org.openas2.util.DateUtil; + +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertThat; +import static org.openas2.TestUtils.waitForFile; + +public class OpenAS2ServerTest { + + private static final TestResource RESOURCE = TestResource.forClass(OpenAS2ServerTest.class); + private static File openAS2AHome; + private static File openAS2AOutbox; + private static File openAS2AMDNs; + private static File openAS2BHome; + private static File openAS2BInbox; + private static File openAS2BMDNs; + private static OpenAS2Server openAS2A; + private static OpenAS2Server openAS2B; + private static ExecutorService executorService; + @Rule + public TemporaryFolder tmp = new TemporaryFolder(); + + @BeforeClass + public static void startServers() throws Exception + { + executorService = Executors.newFixedThreadPool(20); + System.setProperty("org.apache.commons.logging.Log", "org.openas2.logging.Log"); + + openAS2AHome = RESOURCE.get("OpenAS2A"); + OpenAS2ServerTest.openAS2A = new OpenAS2Server.Builder().run(RESOURCE.get("OpenAS2A", "config", "config.xml").getAbsolutePath()); + + openAS2AOutbox = FileUtils.getFile(openAS2AHome, "data", "toOpenAS2B"); + openAS2AMDNs = FileUtils.getFile(openAS2AHome, "data", "OpenAS2A_OID-OpenAS2B_OID", "mdn", DateUtil.formatDate("yyyy-MM-dd")); + + openAS2B = new OpenAS2Server.Builder().run(RESOURCE.get("OpenAS2B", "config", "config.xml").getAbsolutePath()); + openAS2BHome = RESOURCE.get("OpenAS2B"); + openAS2BInbox = FileUtils.getFile(openAS2BHome, "data", "OpenAS2A_OID-OpenAS2B_OID", "inbox"); + openAS2BMDNs = FileUtils.getFile(openAS2BHome, "data", "OpenAS2A_OID-OpenAS2B_OID", "mdn", DateUtil.formatDate("yyyy-MM-dd")); + } + + @AfterClass + public static void tearDown() throws Exception + { + openAS2A.shutdown(); + openAS2B.shutdown(); + executorService.shutdown(); + } + + @Test + public void shouldSendMessages() throws Exception + { + int amountOfMessages = 100; + List> callers = new ArrayList>(amountOfMessages); + + // prepare messages + for (int i = 0; i < amountOfMessages; i++) + { + callers.add(new Callable() { + @Override + public TestMessage call() throws Exception + { + return sendMessage(); + } + }); + } + + //send and verify all messages in parallel + for (Future result : executorService.invokeAll(callers)) + { + verifyMessageDelivery(result.get()); + } + } + + private TestMessage sendMessage() throws IOException + { + String outgoingMsgFileName = RandomStringUtils.randomAlphanumeric(10) + ".txt"; + String outgoingMsgBody = RandomStringUtils.randomAlphanumeric(1024); + File outgoingMsg = tmp.newFile(outgoingMsgFileName); + FileUtils.write(outgoingMsg, outgoingMsgBody, "UTF-8"); + + FileUtils.copyFileToDirectory(outgoingMsg, openAS2AOutbox); + + return new TestMessage(outgoingMsgFileName, outgoingMsgBody); + + } + + + private void verifyMessageDelivery(TestMessage testMessage) throws IOException + { + + // wait till delivery occurs + File deliveredMsg = waitForFile(openAS2BInbox, new PrefixFileFilter(testMessage.fileName), 20, TimeUnit.SECONDS); + + { + String deliveredMsgBody = FileUtils.readFileToString(deliveredMsg, "UTF-8"); + assertThat("Verify content of delivered message", deliveredMsgBody, is(testMessage.body)); + } + + { + File deliveryConfirmationMDN = waitForFile(openAS2AMDNs, new PrefixFileFilter(testMessage.fileName), 1, TimeUnit.SECONDS); + assertThat("Verify MDN was received by OpenAS2A", deliveryConfirmationMDN.exists(), is(true)); + } + + { + File deliveryConfirmationMDN = waitForFile(openAS2BMDNs, new PrefixFileFilter(testMessage.fileName), 1, TimeUnit.SECONDS); + assertThat("Verify MDN was stored by OpenAB2B", deliveryConfirmationMDN.exists(), is(true)); + } + } + + private static class TestMessage { + private final String fileName; + private final String body; + + private TestMessage(String fileName, String body) + { + this.fileName = fileName; + this.body = body; + } + } + +} \ No newline at end of file diff --git a/Server/src/test/java/org/openas2/message/AS2MessageMDNTest.java b/Server/src/test/java/org/openas2/message/AS2MessageMDNTest.java new file mode 100644 index 00000000..b1de6082 --- /dev/null +++ b/Server/src/test/java/org/openas2/message/AS2MessageMDNTest.java @@ -0,0 +1,40 @@ +package org.openas2.message; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; +import org.openas2.partner.AS2Partnership; +import org.openas2.partner.Partnership; + +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertThat; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.when; + +@RunWith(MockitoJUnitRunner.class) +public class AS2MessageMDNTest { + + @Mock + private AS2Message message; + @Mock + private Partnership partnership; + + @Before + public void setUp() throws Exception + { + when(message.getPartnership()).thenReturn(partnership); + when(partnership.getReceiverID(eq(AS2Partnership.PID_AS2))).thenReturn("receiverId"); + when(partnership.getSenderID(eq(AS2Partnership.PID_AS2))).thenReturn("senderId"); + when(message.getPartnership().getAttribute(eq(AS2Partnership.PA_MESSAGEID))) + .thenReturn("OPENAS2-$date.ddMMyyyyHHmmssZ$-$rand.1234$@$msg.sender.as2_id$_$msg.receiver.as2_id$"); + } + + @Test + public void shouldGenerateMessageId() throws Exception + { + String messageId = new AS2MessageMDN(message, false).generateMessageID(); + assertThat("Check " + messageId, messageId.matches("^OPENAS2-[0-9]{14}\\+[0-9]{4}-[0-9]{4}@senderId_receiverId"), is(true)); + } +} \ No newline at end of file diff --git a/Server/src/test/java/org/openas2/support/config/FileMonitorAdapterTest.java b/Server/src/test/java/org/openas2/support/config/FileMonitorAdapterTest.java new file mode 100644 index 00000000..c6d566bc --- /dev/null +++ b/Server/src/test/java/org/openas2/support/config/FileMonitorAdapterTest.java @@ -0,0 +1,80 @@ +package org.openas2.support.config; + +import java.io.File; +import java.util.Date; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +import org.apache.commons.lang3.RandomUtils; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; +import org.openas2.OpenAS2Exception; +import org.openas2.support.FileMonitorAdapter; + +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; + +/** + * Created by nick on 08.04.17. + */ +@RunWith(MockitoJUnitRunner.class) +public class FileMonitorAdapterTest { + + @Rule + public TemporaryFolder tmp = new TemporaryFolder(); + @Captor + ArgumentCaptor monitorJob; + @Mock + private ScheduledExecutorService executorService; + private File configFile; + + private FileMonitorAdapter adapter; + + @Before + public void setUp() throws Exception + { + configFile = spy(tmp.newFile()); + adapter = spy(new FileMonitorAdapter() { + @Override + public void onConfigFileChanged() throws OpenAS2Exception + { + + } + }); + + } + + @Test + public void shouldNotScheduleRefreshWhenIntervalNotConfigured() throws Exception + { + adapter.scheduleIfNeed(executorService, configFile, 0, TimeUnit.SECONDS); + + verifyZeroInteractions(executorService); + } + + @Test + public void shouldScheduleConfigRefresh() throws Exception + { + + int refreshInterval = RandomUtils.nextInt(1, 10); + + adapter.scheduleIfNeed(executorService, configFile, refreshInterval, TimeUnit.SECONDS); + + doReturn(new Date().getTime() + 10).when(configFile).lastModified(); + verify(executorService).scheduleAtFixedRate(monitorJob.capture(), eq((long) refreshInterval), eq((long) refreshInterval), eq(TimeUnit.SECONDS)); + + monitorJob.getValue().run(); + + verify(adapter).onConfigFileChanged(); + } +} \ No newline at end of file diff --git a/Server/src/test/java/org/openas2/support/config/FileMonitorTest.java b/Server/src/test/java/org/openas2/support/config/FileMonitorTest.java new file mode 100644 index 00000000..85492c71 --- /dev/null +++ b/Server/src/test/java/org/openas2/support/config/FileMonitorTest.java @@ -0,0 +1,57 @@ +package org.openas2.support.config; + +import java.io.File; +import java.util.Date; + +import org.apache.commons.io.FileUtils; +import org.apache.commons.lang.RandomStringUtils; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.runners.MockitoJUnitRunner; +import org.openas2.support.FileMonitor; +import org.openas2.support.FileMonitorListener; + +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; + +/** + * Created by nick on 08.04.17. + */ +@RunWith(MockitoJUnitRunner.class) +public class FileMonitorTest { + + @Rule + public TemporaryFolder temp = new TemporaryFolder(); + @Mock + private FileMonitorListener listener; + + @Test + public void shouldTriggerListenersWhenFileChanged() throws Exception + { + File fileToObserve = Mockito.spy(temp.newFile()); + + FileMonitor fileMonitor = new FileMonitor(fileToObserve, listener); + verifyZeroInteractions(listener); + + fileMonitor.run(); + verifyZeroInteractions(listener); + + FileUtils.write(fileToObserve, RandomStringUtils.randomAlphanumeric(1024), "UTF-8"); + doReturn(new Date().getTime() + 3).when(fileToObserve).lastModified(); + fileMonitor.run(); + + verify(listener).onFileEvent(eq(fileToObserve), eq(FileMonitorListener.EVENT_MODIFIED)); + reset(listener); + + fileMonitor.run(); + verifyZeroInteractions(listener); + + } +} \ No newline at end of file diff --git a/Server/config/DB/openas2.mv.db b/Server/src/test/resources/OpenAS2ServerTest/OpenAS2A/config/DB/openas2.mv.db similarity index 100% rename from Server/config/DB/openas2.mv.db rename to Server/src/test/resources/OpenAS2ServerTest/OpenAS2A/config/DB/openas2.mv.db diff --git a/Server/src/test/resources/OpenAS2ServerTest/OpenAS2A/config/as2_certs.p12 b/Server/src/test/resources/OpenAS2ServerTest/OpenAS2A/config/as2_certs.p12 new file mode 100644 index 00000000..0fecc3bf Binary files /dev/null and b/Server/src/test/resources/OpenAS2ServerTest/OpenAS2A/config/as2_certs.p12 differ diff --git a/Server/src/test/resources/OpenAS2ServerTest/OpenAS2A/config/commands.xml b/Server/src/test/resources/OpenAS2ServerTest/OpenAS2A/config/commands.xml new file mode 100644 index 00000000..6f095991 --- /dev/null +++ b/Server/src/test/resources/OpenAS2ServerTest/OpenAS2A/config/commands.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Server/src/test/resources/OpenAS2ServerTest/OpenAS2A/config/config.xml b/Server/src/test/resources/OpenAS2ServerTest/OpenAS2A/config/config.xml new file mode 100644 index 00000000..f6d5e70a --- /dev/null +++ b/Server/src/test/resources/OpenAS2ServerTest/OpenAS2A/config/config.xml @@ -0,0 +1,145 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Server/config/db_ddl.sql b/Server/src/test/resources/OpenAS2ServerTest/OpenAS2A/config/db_ddl.sql similarity index 100% rename from Server/config/db_ddl.sql rename to Server/src/test/resources/OpenAS2ServerTest/OpenAS2A/config/db_ddl.sql diff --git a/Server/src/test/resources/OpenAS2ServerTest/OpenAS2A/config/partnerships.xml b/Server/src/test/resources/OpenAS2ServerTest/OpenAS2A/config/partnerships.xml new file mode 100644 index 00000000..bda31169 --- /dev/null +++ b/Server/src/test/resources/OpenAS2ServerTest/OpenAS2A/config/partnerships.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Server/src/test/resources/OpenAS2ServerTest/OpenAS2A/config/ssl_certs.jks b/Server/src/test/resources/OpenAS2ServerTest/OpenAS2A/config/ssl_certs.jks new file mode 100644 index 00000000..52b4e7bd Binary files /dev/null and b/Server/src/test/resources/OpenAS2ServerTest/OpenAS2A/config/ssl_certs.jks differ diff --git a/Server/src/test/resources/OpenAS2ServerTest/OpenAS2B/config/DB/openas2.mv.db b/Server/src/test/resources/OpenAS2ServerTest/OpenAS2B/config/DB/openas2.mv.db new file mode 100644 index 00000000..f7fe6aae Binary files /dev/null and b/Server/src/test/resources/OpenAS2ServerTest/OpenAS2B/config/DB/openas2.mv.db differ diff --git a/Server/src/test/resources/OpenAS2ServerTest/OpenAS2B/config/as2_certs.p12 b/Server/src/test/resources/OpenAS2ServerTest/OpenAS2B/config/as2_certs.p12 new file mode 100644 index 00000000..0fecc3bf Binary files /dev/null and b/Server/src/test/resources/OpenAS2ServerTest/OpenAS2B/config/as2_certs.p12 differ diff --git a/Server/src/test/resources/OpenAS2ServerTest/OpenAS2B/config/commands.xml b/Server/src/test/resources/OpenAS2ServerTest/OpenAS2B/config/commands.xml new file mode 100644 index 00000000..6f095991 --- /dev/null +++ b/Server/src/test/resources/OpenAS2ServerTest/OpenAS2B/config/commands.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Server/src/test/resources/OpenAS2ServerTest/OpenAS2B/config/config.xml b/Server/src/test/resources/OpenAS2ServerTest/OpenAS2B/config/config.xml new file mode 100644 index 00000000..0fac1cb3 --- /dev/null +++ b/Server/src/test/resources/OpenAS2ServerTest/OpenAS2B/config/config.xml @@ -0,0 +1,144 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Server/src/test/resources/OpenAS2ServerTest/OpenAS2B/config/db_ddl.sql b/Server/src/test/resources/OpenAS2ServerTest/OpenAS2B/config/db_ddl.sql new file mode 100644 index 00000000..80fab0a4 --- /dev/null +++ b/Server/src/test/resources/OpenAS2ServerTest/OpenAS2B/config/db_ddl.sql @@ -0,0 +1,29 @@ +set IGNORECASE TRUE; +DROP TABLE msg_metadata IF EXISTS; +CREATE TABLE msg_metadata ( + ID INTEGER default 0 NOT NULL AUTO_INCREMENT, + MSG_ID LONGVARCHAR NOT NULL, + MDN_ID LONGVARCHAR, + DIRECTION VARCHAR(25) , + IS_RESEND VARCHAR(1) DEFAULT 'N', + RESEND_COUNT INTEGER DEFAULT 0, + SENDER_ID VARCHAR(255) NOT NULL, + RECEIVER_ID VARCHAR(255) NOT NULL, + STATUS VARCHAR(255), + STATE VARCHAR(255), + SIGNATURE_ALGORITHM VARCHAR(255), + ENCRYPTION_ALGORITHM VARCHAR(255), + COMPRESSION VARCHAR(255), + FILE_NAME VARCHAR(255), + CONTENT_TYPE VARCHAR(255), + CONTENT_TRANSFER_ENCODING VARCHAR(255), + MDN_MODE VARCHAR(255), + MDN_RESPONSE LONGVARCHAR, + STATE_MSG LONGVARCHAR, + CREATE_DT TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + UPDATE_DT TIMESTAMP, + + PRIMARY KEY (ID) ); + +ALTER TABLE msg_metadata ADD CONSTRAINT MSG_ID_UNIQUE UNIQUE (MSG_ID); + diff --git a/Server/src/test/resources/OpenAS2ServerTest/OpenAS2B/config/partnerships.xml b/Server/src/test/resources/OpenAS2ServerTest/OpenAS2B/config/partnerships.xml new file mode 100644 index 00000000..2ad4b41e --- /dev/null +++ b/Server/src/test/resources/OpenAS2ServerTest/OpenAS2B/config/partnerships.xml @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Server/src/test/resources/OpenAS2ServerTest/OpenAS2B/config/ssl_certs.jks b/Server/src/test/resources/OpenAS2ServerTest/OpenAS2B/config/ssl_certs.jks new file mode 100644 index 00000000..52b4e7bd Binary files /dev/null and b/Server/src/test/resources/OpenAS2ServerTest/OpenAS2B/config/ssl_certs.jks differ diff --git a/build.properties b/build.properties deleted file mode 100644 index b1aea1ef..00000000 --- a/build.properties +++ /dev/null @@ -1,14 +0,0 @@ -source.openas2-server-osgi.jar = Bundle/src/main/java/,\ - Server/src/main/java/ -bin.includes = META-INF/,\ - Server/lib/commons-lang3-3.4.jar,\ - Server/lib/commons-logging-1.2.jar,\ - Server/lib/javax.mail.jar,\ - Server/lib/bcmail-jdk15on-154.jar,\ - Server/lib/bcpg-jdk15on-154.jar,\ - Server/lib/bcpkix-jdk15on-154.jar,\ - Server/lib/bcprov-jdk15on-154.jar,\ - Server/lib/h2-1.4.192.jar,\ - Server/lib/dom4j-2.0.0.jar -src.excludes = Remote/src/main/java/ -jre.compilation.profile = JavaSE-1.6 diff --git a/changes.txt b/changes.txt index d545d305..6dca6000 100644 --- a/changes.txt +++ b/changes.txt @@ -1,3 +1,21 @@ +Version 2.3.0 - 2017-05-04 +This is an enhancement and minor bugfix release: + IMPORTANT NOTE: Please review ALL release notes for intermediate versions if you are upgrading + + 1. Enhance message state tracking to correctly handle resends + 2. Support external databases that have JDBC drivers for message state tracking via configuration. + 3. Allow AS2 Message ID format to be configurable globally and overridden on a per partnership basis + 4. Persist message attributes for the full lifecycle of the message including resends. + 5. Provide support for proxy server authentication. + 6. Fix resending ASYNC MDN messages + 7. Document all of the enhancements and imporve existing documentation. + +Version 2.2.3 - 2017-04-07 +This is a minor bugfix release: + IMPORTANT NOTE: Please review ALL release notes for intermediate versions if you are upgrading + + 1. Remove appending a UUID to the received AS2 filename allowing complete user control. To add one us $rand.UUID$ in the "filename" parameter + Version 2.2.1 - 2017-03-03 This is a minor bugfix release: IMPORTANT NOTE: Please review ALL release notes for intermediate versions if you are upgrading diff --git a/docs/DeveloperGuide.odt b/docs/DeveloperGuide.odt index 08ceb826..cda2df10 100644 Binary files a/docs/DeveloperGuide.odt and b/docs/DeveloperGuide.odt differ diff --git a/docs/OpenAS2HowTo.odt b/docs/OpenAS2HowTo.odt index 78a6aed6..90e2c670 100644 Binary files a/docs/OpenAS2HowTo.odt and b/docs/OpenAS2HowTo.odt differ diff --git a/docs/OpenAS2HowTo.pdf b/docs/OpenAS2HowTo.pdf index 5c08c6f3..cf869588 100644 Binary files a/docs/OpenAS2HowTo.pdf and b/docs/OpenAS2HowTo.pdf differ diff --git a/docs/index.html b/docs/index.html new file mode 100644 index 00000000..02312707 --- /dev/null +++ b/docs/index.html @@ -0,0 +1,6859 @@ + + + + + + + - no title specified + + + + + + + + + + + + + +

OpenAS2 Server Application

+

 

+ + +

Table of Contents

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

1. Introduction

2. Glossary

3. Installing OpenAS2

3.1. System Requirements

3.2. Installing Application

3.3. Tuning Java

4. Configuration

4.1. Application Configuration

4.1.1. Overriding Certificate Store Password +

4.1.2. Resend Retry Configuration

4.2. Partner Configuration

4.2.1. Partner Definition

4.2.2. Partnership Definition

4.2.3. Transfer Encoding

4.2.4.

4.2.5.

4.2.6.

4.2.6.1. Static Header Values

4.2.6.2.

Delimiter Mode

Regular Expression Mode

4.2.6.3. Adding Custom Headers To HTTP

+

4.2.7.

4.3. Certificate Configuration

4.3.1. Creating And Importing

4.3.2. Supporting Multiple Private + Certificates

4.4. Logging System

4.4.1. Log Level Configuration

4.4.2. Email Logging Configuration

4.5. MDN Configuration

4.5.1. Asynchronous MDN Receiver Configuration +

4.5.2. Asynchronous MDN Sender Configuration

+

4.6. Configuring HTTPS Transport

4.6.1. Inbound Transfers

4.6.2. Outbound Transfers

4.7. Message State Tracking

5. Running OpenAS2

5.1. Starting OpenAS2

5.2. Command Entry

5.3. Automated Launching As UNIX Daemon

+

5.3.1. INIT.D Service

5.3.2. SYSTEMD Service

6. Testing OpenAS2 Transfers

6.1. Using HTTPS Transport

7. Troubleshooting OpenAS2

7.1. Canonicalization For MIC Algorithm

+

7.2. Binary Encoding

7.3. HTTP Restricted Headers

7.4. CMS Algorithm Protection

7.5. SSL Certificate Exceptions

7.6. Java Versions Prior To 1.6

7.7. Mime Body Part Logging

7.8. TLSv1.2

8. Partner AS2 Compatibility Settings

+

9. Remote Control

10. Dynamic Variables

11. Appendix: config.xml file structure

12. Appendix: partnership.xml file structure

+

13. Appendix: command.xml file structure

+

 

+

1. Introduction

+

The OpenAS2 application enables you to transmit and + receive AS2 messages with EDI-X12, EDIFACT, XML, or binary payloads between trading partners. The AS2 implementation conforms with RFC4130.

+

This document describes how to install, configure and use OpenAS2. In this document a partner can be either your own company or a company you will be exchanging data with using AS2. +

+

The sample configurations in this document are based on Unix type OS but in + general the only significant difference is that it may be necessary to use “\” instead of + “/” for folder name separators on Windows based machines but because the application is Java it should work fine + leaving the “/” for the most part as Java will do the conversion if necessary.

+

This document is valid for version 2.1.0 and up.

+

2. Glossary

+

EDI – Electronic Data Interchange

+

MDN - Message Disposition Notification

+

3. Installing OpenAS2

+

3.1. System + Requirements

+

To be able to run the OpenAS2, you will need:

+
    +
  1. 1.Java™ + installed on the machine you intend to run the OpenAS2 server on – this document +  uses Java 1.6. 

  2. +
  3. 2.The OpenAS2 package version you wish to use. The downloadable packages can be found here: https://sourceforge.net/projects/openas2/files 

    +
  4. +
  5. 3.Java + Cryptography Extension (JCE) policy files - you can download the correct version from the Java website. Search “Java Cryptography Extension Unlimited Strength“ to find the right cryptography extension for your version of Java. The current link for Java8 is here. 

  6. +
+

3.2. Installing + Application

+

The following steps will provide an installed app on a target + machine:

+
    +
  1. 1.Unzip + the downloaded OpenAS2 package into a suitable location, which we will call <install_dir>. +  

    +

    NOTE: Typical values for  <install_dir> locations  are /opt/OpenAS2 under Linux®/Unix or C:\OpenAS2 under Microsoft® Windows®.
     

  2. +
  3. 2.For + the encryption and certificate management to work correctly, you must have the proper JCE policy files installed + in your version of Java (see system requirements above). The downloaded zip archive + contains the two files local_policy.jar and US_export_policy.jar. + Install them into your Java location under <JAVA_HOME>/lib/security. + Back up the existing files before installing these new ones. There are numerous detailed articles on the web for installing these files if you need more information. 

  4. +
+

The file structure will look something like the figure below without the data and logs folders which are + created automatically by the server when it starts based on configuration if they do not exist.

+ +
+
+
+
+
 
+

3.3. Tuning Java

+

The default settings for the Java virtual machine in the startup script (start_openas2.sh or start_openas2.bat) will work for installations on most machines for low volume/small file size transfers. However, if your system will be transferring large files you will need to increase memory allocation. If you expect to support very high AS2 traffic you will need to increase memory allocation and possibly tune the garbage collector to get reasonable performance. +

+

How much you can increase memory allocation to Java will depend on how much RAM is installed on the system running OpenAS2 and how many other processes will be running concurrently that will also require memory.  Most systems deploy with at least 8GB RAM these days so increasing memory allocation from the default amount in the startup script should not cause adverse affects to the system. +

+

+

To increase memory allocation you need to increase the heap space. This is set using the -Xmx option. You could increase this from the 384m (m = MB) default setting to 1g or 2g to get good performance for large files or busy systems and for very large files given enough RAM you can set it to 6g or 8g. Search for “-Xmx” in the startup script and adjust accordingly.

+

For garbage collection you may want to allocate a more appropriate garbage collector than the default parallel collector that is the default in Java. In Java 7 and up, the G1 collector is ideal if you use large heap space allocation. To enable it add this to the command line parameter: +

+

 –XX:+UseG1GC  

+

4. Configuration

+

This section explains the details of the configuration files and how they link together.

+

The OpenAS2 server uses four files to configure and execute:

+
    +
  1. 1.config.xml – configures the application 

  2. +
  3. 2.partnerships.xml – configures the partners 

  4. +
  5. 3.as2_certs.p12 – stores the PKCS12 certificates for all partners 

  6. +
  7. 4.commands.xml – stores the commands that the application will support. This file should not be modified 

  8. +
+

IMPORTANT: A restart of the application is required to load any configuration changes. +

+

The folder containing the config.xml file defines the home configuration parameter that can be used to reference other files on the file system relative to a known base folder in the app. This is done by encapsulating home in percentage signs (%home%). All files can be referenced relative to this parameter and it is how the default config.xml file defines the location of other configuration and data file locations used by the OpenAS2 application. +

+

4.1. Application + Configuration

+

The file named “config.xml” configures the modules that will be activated by the AS2 server when it starts up. This file can be located anywhere within the disk subsystem on which the OpenAS2 application runs as it is passed into the application as a startup parameter. +

+

Some of the key configuration settings in the config.xml file are:

+
    +
  • define + the modules to be activated in the OpenAS2 application 

  • +
  • override + module default classes in the AS2 code base 

  • +
  • enhance + or change behaviour of modules and the inputs and outputs of the modules. 

  • +
  • define the location of the certificates keystore and password 

  • +
  • define the location of the partnerships configuration file 

    +
  • +
  • specify + the listening ports 

  • +
+

See appendices for a detailed definition of the config.xml file structure.

+

There are 2 listening ports for inbound connections (see partnerships.xml config for outbound + connections) used for:

+
    +
  1. 1.receiving + messages and synchronous MDN's – default port number 10080 

  2. +
  3. 2.receiving + asynchronous MDN's - default port number 10081  

  4. +
+

The port numbers are arbitrary and defaulted to a number above 1024 that does not require root access to listen on (normally on Unix type systems any port below 1024 requires root access). The + port values are important to the partner you will be communicating with if they will be sending AS2 messages to your system. + For outbound only systems, it is only necessary to have a listener for asynchronous MDN's if using that mechanism for MDN's. +

+

Each module has a number of attributes that can be configured on the module element to control and change + how the module behaves.

+

4.1.1. Overriding Certificate Store Password

+

The certificate store password is stored as an XML attribute “password” on the <certificates> + element. This can be overridden using the system property “org.openas2.cert.Password”. For + improved security, it may not be desired to store the password in the XML file.

+

This can be passsed into the application by adding the following to the java command:

+
    +
  • -Dorg.openas2.cert.Password=myCertificateStorePassword 

  • +
+

This can be set by using an additional parameter to the batch script file so that it can be set as part of invoking the script. The UNIX shell script will support the password as a parameter. + The Windows bat file will need to be enhanced.

+

4.1.2. Resend Retry Configuration

+

When failures occur transferring a message to a trading partner, the system will automatically try to + resend the message. By default the system will retry indefinitely.

+

Restricting the retry attempts can be done at the processor level (applies to all partnerships configured + on the server) and at the partnership level. Partnership configuration will override processor settings.

+

To define the processor level retry count, set the “resend_max_retries” + attribute on the processor element to a valid integer.

+

Example snippet:

+

 <processor classname="org.openas2.processor.DefaultProcessor"

+

     pendingMDN="%home%/../data/pendingMDN3"

+

     pendingMDNinfo="%home%/../data/pendinginfoMDN3"

+

            resend_max_retries="10" >

+

To define the partnership level retry count, set an attribute element on the partnership with name attribute value as “resend_max_retries” and a value attribute element to a valid integer.

+

Example snippet:

+

 <partnership name="OpenAS2A-to-OpenAS2B">

+

  <attribute name="resend_max_retries" value="3"/>

+

  <sender name="OpenAS2A"/>

+

 

+

4.2. Partner Configuration

+

The file named partnerships.xml configures all the information relating to the partners you will be + exchanging data with. See the appendix for information on the structure of this file.

+

It is important to keep in mind that the word partner refers to any entity specified as a recipient or sender of AS2 messages and includes your own company that you might be configuring the application for. +

+

 

+

Each partner will require the following entries in the file:

+
    +
  • a <partner> element – key information defining the partner 

  • +
  • a <partnership> element  - key information for defining a partnership between 2 partners
    Separate
    <partnership> elements are required for inbound and outbound data for a specific partner pairing.
    NOTE:It is necessary to have 2 elements even if data transfer is unidirectional.
     

  • +
+

4.2.1. Partner + Definition

+

The <partner> element requires 3 attributes to + enable AS2 partner identification:

+
    +
  1. 1.partner + name – this is the key to connect partnerships to a partner definition  

  2. +
  3. 2.AS2 + identifier – this is the key for identifying the target/source partner and is included in AS2 message headers to + allow the receiving partner to identify the source of the message and verify the target partner for the AS2 + message. It is also used by the general directory polling module to look up the partner names and hence the + partnership definition where the as2_id of the sender and receiver are part of the transferred file name. 

  4. +
  5. 3.X.509 + certificate alias – identifies the alis of the certificates for this partner in the keystore. The encryption and + decryption of messages requires the partners public or private key as appropriate 

    +
  6. +
+

4.2.2. Partnership + Definition

+

The <partnership> element identifies a specific direction of AS2 message transfer from one partner to another. The “name” attribute on the <partnership> element is not important but should be used to clearly identify the intended use of the partnership definition. It is suggested the name value uses the names of the source and destination partners something like xxx-to-yyy. +

+

The <partnership> element encapsulates a number of child elements that are necessary to properly + configure a partnership:

+
    +
  • <sender name=”xxx”> - identifies the sending partner definition such that xxx must match the “name” attribute of a <partner> element  +

  • +
  • <receiver name=”yyy”> - identifies the receiving partner definition such that yyy must match the “name” attribute of a <partner> element  +

  • +
  • <as2_url> - a fully qualified URI that provides the connection string to the remote partner for sending AS2 messages. If sending to another OpenAS2 server then the port number must match the value configured in the config.xml file of the remote OpenAS2 server. 

  • +
  • <as2_mdn_to> - neccesary if an MDN response is required and can be any random string + but is most commonly configured with an email address 

    +
  • +
+

4.2.3. Transfer + Encoding

+

As of version 1.3.7, the default content transfer encoding uses “binaryif not explicitly overwritten in the configuration. The default can be changed using the “content_transfer_encoding” attribute in the partnership.xml file. If you experience issues with failing to verify a partners AS2 inbound message because the message contains CR/LF data in it then you should switch to using “binary” for the transfer encoding. The sample partnership file sets the transfer encoding to “binary” for both partners. +

+

4.2.4. Supported Encoding Algorithms

+

The currently supported encoding algorithms are: +

+
    +
  • MD5 

  • +
  • SHA1 

  • +
  • SHA224 

  • +
  • SHA256 

  • +
  • SHA384 

  • +
  • SHA512 

  • +
  • CAST5 

  • +
  • 3DES 

  • +
  • IDEA 

  • +
  • RC2_CBC 

  • +
  • AES128 (CBC mode) 

  • +
  • AES192 (CBC mode) 

  • +
  • AES256 (CBC mode) 

  • +
  • AES256_WRAP 

  • +
+

4.2.5. Message Compression +

+

The application supports inbound compression automatically. There is no configuration for this option. To enable outbound compression requires setting “compression_type” attribute on the partnership definition for the outbound configuration.  The only supported compression/decompression at this time is “ZLIB”. The default is no compression of sent messages. +

+

By default compression will occur on the message body part prior to signing. The compression can be configured to occur after signing using the “compression_mode” attribute on the partnership definition for the outbound configuration. Set the attribute to “compress-after-signing” to enable this.

+

See partnership.xml appendix for configuration details. +

+

4.2.6. Custom Mime Headers

+

Mime headers can be added to the outermost Mime body part for outbound messages and additionally added to the HTPP headers. The outermost Mime body part will depend on configuration of the partnership and could be the compressed, signed or encrypted part. In the case of the encrypted part being the outermost mime body part, the HTTP headers will not be visible until after decryption of the body part since encryption protects the content and the headers.

+

4.2.6.1. Static + Header Values

+

Custom headers can be added as statically defined name/value pairs in a partnership attribute where the name and the value are separated by a colon. Multiple static headers are added using a semi-colon separated list between each name/value pair. The attribute name for this is “custom_mime_headers” and a sample entry of 2 static headers is shown below: +

+

<attribute name="custom_mime_headers" value="X-CustomRoute: X1Z34Y ; X-CustomShape:oblong"/>

+

        Note that spaces before or after the “;” and “:” separators will be excluded. +

4.2.6.2. Dynamic Header Values From File Name

+

Dynamic headers require 2 attributes to configure their behaviour and there are 2 different modes of +  operation for extracting the value(s) for the defined header(s) from the file name:

+
    +
  1. 1.delimiter + mode 

  2. +
  3. 2.regular + expression mode 

  4. +
+

 Delimiter mode is relatively simple and does not require any special knowledge but regular expression + mode may require someone with regular expression skills. Regular expression mode provides far greater flexibility + for extracting the value(s) from a file name where specific character sequences or character counts are + required.

+

Both modes use an attribute named “custom_mime_header_names_from_filename” to enter the list of header names but the format for the two are slightly different. The second attribute required has a different name for each of the modes, custom_mime_header_name_delimiters_in_filename” for delimiter mode and “custom_mime_header_names_regex_on_filename” for regular expression mode.

+

IMPORTANT: if both delimiter mode and regular expression mode attributes are + entered into a partnership then delimiter mode will be chosen irrespective.

Delimiter Mode
+

In delimiter mode, the values in the file name are separated by specifiying one or more delimiters and the entire file name is parsed into a list of values using + the delimiter(s) defined. In order to accommodate file names that have more than just the values required for the + custom headers, the list of header names are defined with a prefix that designates if the value in the list will be + used as a header value or not. For an entry to be added as a header it must have the prefix “header.”. Any other prefix will cause that entry to be ignored. There must be as many header names defined as there are string sequences that would result from splitting the file name string by the delimiter(s) otherwise the system will throw an error. +

+

Below is an example of a delimiter based configuration.

+

<attribute name="custom_mime_header_names_from_filename"

+

        value="header.X-Header1,header.Y-Header2, junk.extraStuff"/>

+

        <attribute name="custom_mime_header_name_delimiters_in_filename" value="-_"/>

+

Using this configuration, given a file name ABC-123-INVOICES.csv there would be 2 headers added as:
X-Header1   value ABC
Y-Header2   value 123

+

If the file name was ABC-123-H4FT_INVOICES.csv the system would throw an error as there would be 4 string sequences extracted so you could fix this by appending junk.moreStuff to the “custom_mime_headers_from_filename” attribute. +

+

Another example of delimiter mode in the partnership:

+

<attribute name="custom_mime_header_names_from_filename"

+

        value="header.X-Header1, other.string1,header.Y-Header2"/>

+

        <attribute name="custom_mime_header_name_delimiters_in_filename" value="-"/>

+

Using this configuration, given a file name ABC-123_TEST-INVOICES.csv there would be 2 headers added as:
X-Header1   value ABC
Y-Header2   value
INVOICES +

+

 

Regular Expression Mode
+

Regular expression based mode uses Java regular expressions and requires that the regular expression is + constructed in grouping mode where the number of groups in the regular expression exactly matches the number of + header names in the “custom_mime_header_names_from_filename” attribute. The regular + expression will be used to parse the file name to extract the values for the defined names in the attribute named “custom_mime_header_names_regex_on_filename”. + Regular expressions can become extremely complex and this document will show some simple examples but there are many + sites that provide regular expression tutorials if you need a complicated soultion.

+

An example for a regular expression mode configuration is shown below:

+

<attribute name="custom_mime_header_names_from_filename" value="X-Header1,Y-Header2"/> +

+

        <attribute name="custom_mime_header_names_regex_on_filename" value="([^-]*)-([^.]*).csv"/>

+

Using this configuration, given a file name ABC-123-INVOICES.csv there would be 2 headers added as:
X-Header1   value ABC
Y-Header2   value 123-INVOICES
+

+

If the file name was ABC-123-H4FT_INVOICES.csv there would be 2 headers added as:
X-Header1   + value ABC
Y-Header2   value 123—HFT_INVOICES

+

If the file name was ABC-123-H4FT_INVOICES.txt or ABC_123.csv the system would throw an error since there would be no match. +

+

 

+

Another example for a regular expression mode configuration is shown below:

+

<attribute name="custom_mime_header_names_from_filename" value="X-Header1,Y-Header2"/> +

+

        <attribute name="custom_mime_header_names_regex_on_filename" value="([^-]*)-([^.]*).csv"/>

+

Using this configuration, given a file name ABC-123-INVOICES.csv there would be 2 headers added as:
X-Header1   value ABC
Y-Header2   value 123-INVOICES
+

+

 

4.2.6.3. Adding Custom Headers To HTTP

+

The following attribute set to  value of “true” will additionally add the headers to the HTTP headers + for both static and dynamic header mechanisms:

+

<attribute name="add_custom_mime_headers_to_http" value="true"/>

+

4.2.7. Setting Dynamic Attributes From File Name +

+

Partnership attributes can be added to the partnership definition based on parsing the file name of the document to be sent  using a regular expression. Dynamic + attributes require 2 partnership attributes to configure their behaviour for extracting the value(s) for the defined attribute(s) from the file name.

+
    +
  1. 1.attribute_names_from_filename”  - when added to a partnership it must contain a list of comma separated attribute names 

  2. +
  3. 2.attribute_names_regex_on_filename” - defines the regular expression  

  4. +
+

The extracted name/value pairs can then be referenced in config using the format:

+

$attributes.<attribute name>$

+

 

+

Regular expressions uses Java regular expressions and requires that the + regular expression is constructed in grouping mode where the number of groups in the regular expression exactly + matches the number of attribute names in the “attribute_names_from_filename” attribute. +  Regular expressions can become extremely complex and this document will show some simple examples but there are + many sites that provide regular expression tutorials if you need a complicated solution. +

+

 

+

An example for a regular expression mode configuration is shown below:

+

<attribute name="attribute_names_from_filename" value="X-attribute1,Y-attribute2"/>

+

        <attribute name="attribute_names_regex_on_filename" value="([^-]*)-([^.]*).csv"/>

+

Using this configuration, given a file name ABC-123-INVOICES.csv there would be 2 attributes added as:

+

X-attribute1   value ABC
Y-attribute2   value 123-INVOICES

+

 

+

If the file name was ABC-123-H4FT_INVOICES.csv there would be 2 attributes added as:

+

X-attribute1   value ABC
Y-attribute2   value 123—HFT_INVOICES

+

If the file name was ABC-123-H4FT_INVOICES.txt or ABC_123.csv the system would throw an error since there would be no match. +

+

 

+

Another example for a regular expression mode configuration is shown below:

+

<attribute name="attribute_names_from_filename" value="X-attribute1,Y-attribute2"/>

+

        <attribute name="attribute_names_regex_on_filename" value="([^-]*)-([^.]*).csv"/>

+

Using this configuration, given a file name ABC-123-INVOICES.csv there would be 2 attributes added as:

+

X-attribute1   value ABC
Y-attribute2   value 123-INVOICES

+

 

+

The above attributes could be referenced in config to set a more dynamic subject using something like + this:

+

        <attribute name="subject" value="Target product: $attributes.X-attribute1$ Sequence Count: $attributes.Y-attribute2$"/>

+

This would produce a subject looking like this:

+

 Target product: ABC Sequence Count: 123-INVOICES

+

4.3. Certificate + Configuration

+

NOTE: SHA1 certificates are no longer supported and are rapidly being phased out so you should use + SHA256 for all partners that do support SHA256 certificates.

+

4.3.1. Creating And Importing

+

The certificate store used by default is a PKCS12 key store and stores all X.509 certificates for all trading partners. The location and name of the certificate keystore is defined in “filename” attribute of the certificates element in the config.xml file. The default deployment uses the file <installDir>/config/as2_certs.p12

+

 

+

The key store must contain the private key of your own X.509 certificate and the public key for each of + your trading partners X.509 certificates.

+

The certificates must be stored with the matching alias as specified in the partner definition of each + partner in the partnership.xml file.

+

 

+

There is a shell file to help generating certificates: bin/gen_p12_key_par.sh

+

An excellent open source visual keystore manager that will run on any OS and will allow importing and managing certificates in your keystore can + be found here: http://portecle.sourceforge.net/

+

 

+

The following steps will create an X509 self signed certificate using OpenSSL:
openssl req -x509 -newkey rsa:4096 -keyout priv.key -out selfcert.crt -days 3650 -sha256

+

The generated certificate (selfcert.crt in the above command) can then be + imported into the PKCS12 keystore using a command like this in the OpenAS2 command mode that is the default when starting openas2 from the command line or via the OpenAS2 remote app: +

+

cert import <alias> <path+filename>

+

To create the public key for sending to the partner you can use this:

+

openssl x509 -pubkey -noout -in selfcert.crt > pubkey.cer

+

The file containing the public key will be pubkey.cer +

+

If you wish to import the public and private key then you create the PKCS12 + key store from the certificates:
openssl pkcs12 -export -in selfcert.crt -inkey priv.key -out certs.p12 -name my_new_partner_alias +

+

The PKCS12 keystore now contains both public and private keys and can be imported into the keystore via the command interface or simply by replacing the existing keystore with the new one and setting the appropriate attributes in the config.xml file. It + is important to use the ".p12" extension when importing certificates from a PKCS12 keystore as the importer requires the “.p12” extension to detect that you are not + importing a certificate directly but rather the certificates in a PKCS12 keystore.

+

The OpenAS2 command processor (or remote OpenAS2 app) import command for a + PKCS12 keystore would be in this format:

+

cert import <alias> <path+filename> <keystore password> +

+

4.3.2. Supporting Multiple Private Certificates

+

In the case where you need to support multiple certificates such as when one partner needs SHA1 and + another needs SHA256 or when you want to set up different certificates per partner, follow these steps below.

+

The key to supporting multiple certificates is ensuring you use a separate as2_id and x509_alias attribute.

+

In the partnership.xml you would add another partner element pointing to a different certificate. +

+

If for example you have a <partner> element definition for your company as below: +

+

<partner name="MyCompany" as2_id="MyCompany_OID" x509_alias="MyCompanyCert" + email="me@MyCompany.com"/>

+

For each additional certificate you support, you then add another <partner> element. If for instance you have SHA1 already deployed and working with existing partners and you create a SHA256 certificate to support a new partner, you add a new <partner> element something + like this:

+

<partner name="MyCompany256" as2_id="MyCompany2_OID" x509_alias="MyCompanyCert256" email="me@MyCompany.com"/>

+

In your partnership definition for the partners using the SHA256 certificate + you set the "sender" and "receiver" + attribute as appropriate to point to the correct partner definition ("MyCompany256" per the + example above) along with changing the SHA1 to SHA256 in the other relevant attributes as shown in the snippet below. +

+

   <partnership name="MyCompany256-to-MyPartner256">

+

        <sender name="MyCompany256"/>

+

        <receiver name="MyPartner256"/>

+

        <attribute name="protocol" value="as2"/>

+

        ...

+

    </partnership>

+

 

+

Import the new certificate into the existing + p12 keystore using the alias as defined in the x509_alias attribute above ( "MyCompany2Cert256" ) and send the partner the matching public key for the new certificate along with the + as2_id "MyCompany256_OID" that they will need to use so you can + differentiate your target definition in the partnership file containing the SHA1 certificate from the SHA256 certificate.  See the previous section for importing certificates into your existing keystore. +

+

4.4. Logging System +

+

Logging supports 6 levels that can be controlled by configuration. The logging output can be directed to + to multiple destinations including:

+
    +
  • System + console 

  • +
  • Local + log files 

  • +
  • Email + – log messages are emailed to a configured email address. 

  • +
  • Socket + – log messages are writeen to a socket supporting remote logging 

  • +
+

All log classes can be overridden or custom logger classes can be coded and included via + confioguration

+

 

+

4.4.1. Log Level Configuration

+

The logging system supports the use of either or both the commons-logging.properties file or a file named openas2log.properties to control the logging level. Properties in openas2log.properties will override commons-logging.properties entries. There is a commons-logging.properties file in the bin directory which is part of the classpath specified in the script file described in the section on running the application. +

+

 

+

The properties in the openas2log.properties file should be prefixed by “org.openas2.logging.

+

The following are the logging levels supported by the application in order of lowest(finest) to + highest:

+

"TRACE", "DEBUG", "INFO", "WARN", "ERROR", "FATAL"

+

The logging levels are turned off by specifying the level you want on and all + other levels higher than that level will also be turned on.

+

The default level is INFO and therefore WARN, ERROR and FATAL are also turned on by default.

+

By adding a property level=DEBUG in the common-logging.properties file will result in DEBUG logging being + enabled along with INFO, WARN, ERROR and FATAL

+

The same can be achieved by adding org.openas2.logging.openas2log.level=DEBUG in the openas2log.properties + file.

+

4.4.2. Email Logging Configuration

+

The email logger uses the javax mail API to send ERROR level log messages. Some + of the basic email configuration parameters are supported via config in the config.properties file as indicated in + the appendix. The rest of the mail properties as listed in the Javamail API  can be set by passing them as system properties on the command line by modifying the start-openas2.sh or start-openas2.bat file as appropriate or using the javax.mail.properties.file attribute on the email logger element.

+

 

+

The configuration values can overwrite each other depending on the source of the configurtion value. The + order of priority is as follows:

+
    +
  1. 1.values + set in the logger element attributes  

  2. +
  3. 2.entries in the file identified by javax.mail.properties.file  

  4. +
  5. 3.entries + using system properties 

  6. +
+

 

+

For example, to pass the port for connection you could add this to the command + line: -Dmail.smtp.port=529

+

 

+

To point to a properties file containing all the relevant information you would add something like + this:

+

                <logger classname="org.openas2.logging.EmailLogger"

+

                        javax.mail.properties.file="%home%/java.mail.properties" +

+

                        from="openas2"

+

                       

+

 

+

4.5. MDN + Configuration

+

MDN's can be sent synchronously or asynchronously. By default the system will use synchronous MDN mechanism. Per the AS2 specification, an MDN will only be sent on receipt of an AS2 message if the “Disposition-Notification-Toheader is present in the received message with a non-empty value. Although this value is specified to be configured with an email address, it is not utilized for any purpose in the AS2 protocol other than to indicate an MDN is required so can in fact be any random string. To set the  “Disposition-Notification-To“ header in an outbound message, the “as2_mdn_to” attribute must be set on the partnership. +

+

The other attribute that must be set is the “as2_mdn_options”. This defines the encryption algorithm and other MDN settings as specified by the AS2 protocol and the value entered for this attribute will be sent in the “Disposition-Notification-Options” header of the AS2 message. Generally changing the encryption algorithm to suit the trading partner should be sufficient on this attribute. +

+

4.5.1. Asynchronous MDN Receiver + Configuration

+

In order to specify an asynchronous MDN response from a partner requires setting the following attribute + on the partnership element in the partnership configuration:

+
+

Receiving an asynchronous MDN  requires the “AS2MDNReceiverModule”  module. This module declaration requires a port parameter in addition to the class and can be entered as a child member of the processor node in the config file as shown below: +

+

<module classname="org.openas2.processor.receiver.AS2MDNReceiverModule" port=”10081” /> +

+

4.5.2. Asynchronous MDN Sender + Configuration

+

Sending an asynchronous MDN  requires the “AsynchMDNSenderModule”  module. This module declaration does not require any parameters other than the class and can be entered as shown below as a child member of the processor node in the config file:

+

<module classname="org.openas2.processor.sender.AsynchMDNSenderModule" /> +

+

4.6. Configuring HTTPS Transport

+

HTTPS transport using SSL is configured separately for inbound and outbound connectivity.

+

4.6.1. Inbound + Transfers

+

Configuration for inbound is in the config.xml file. The requirements for receiving AS2 files using HTTPS + are:

+
    +
  • JKS + keystore containing the SSL certificate 

  • +
  • an + appropriately configured As2ReceiverModule module element 

  • +
+

The key attributes that configure HTTPS are:

+
    +
  • protocol="https" 

  • +
  • ssl_keystore="%home%/ssl_certs.jks" + – points to the JKS certificate keystore 

  • +
  • ssl_keystore_password="<passwordforkeystorefile" 

  • +
  • ssl_protocol="TLS"  

    +
  • +
+

See the appendix for details on the attributes.

+

4.6.2. Outbound Transfers

+

The partnership definition for the connection URL will also have to be set to + the appropriate host name.

+

The key attributes that configure HTTPS are:

+
    +
  • as2_url 

  • +
  • as2_mdn_to + (only if MDN is required) 

  • +
+

 

+

If asynchronous MDN is in use and requires HTTPS then a As2MDNReceiverModule + module needs to be configured in the same way as for the As2ReceiverModule class above. +

+

If the target system being connected to uses self signed certificates, the + following system property will have to be passed to the application in the java command line with a comma separated list (no spaces before or after comma) of the “Common Name” (CN) in the self signed certificate that will be returned by the target system: +

+

-Dorg.openas2.cert.TrustSelfSignedCN=<Common.Name1>,<Common.Name2>,…

+

4.7. Message + State Tracking

+

As of version 2.1.0 the system will track key events in the message + transmission and reception process and write them to an embedded H2 database. The database default location is:

+

<install_dir>/config/DB +

+

The database can be accessed by a JDBC client whilst the OpenAS2 app is running using the parameters shown in the table below.

+ + + + + + + + + + + + + + + + + + + + + + + + + +

Item

Default Value

Database name

openas2

Database user name

sa

Database password

OpenAS2

JDBC connect string

jdbc:h2:

+

+

There is a file named db_ddl.sql file located in the config folder that can be used to create the necessary table structure if your DB becomes corrupted. The simplest way to recreate the database table is using this command whilst OpenAS2 is running:

+

java -cp [path to OpenAS2 install]/lib/h2-1.4.192.jar org.h2.tools.RunScript -user sa -password OpenAS2 -url jdbc:h2:tcp://localhost:9092/openas2 -script [path to OpenAS2 install]/config/db_ddl.sql

+

The above is for the version of H2 deployed with OpenAS2 version 2.1.0. If you use a different version of H2 then change the jar name to reflect this. +

+

5. Running OpenAS2 +

+

OpenAS2 can be started from the command line in any operating system that supports Java or can be + configured to run as a daemon using the appropriate mechanisms for the operating system.

+

The default deployment for OpenAS2 has a console logger enabled which means that all logging will be + visible in the command line window that OpenAS2 is started from. The server can also be configured from the command + line once the application is running by simply typing in commands once it has started. Because the logging will + appear in the window it may make command entry difficult if there are active transfers at the time you try to enter + commands and it may be desirable to switch off the console logger if you have no need for it.

+

5.1. Starting + OpenAS2

+

The default install of the application is as in the figure below from a windows PC.

+
+
+
+
+
 
+

There are 2 executable script files in the bin folder + of the AS2 application root as indicated in the screenshot above:

+
    +
  1. 1.start-openas2.sh + – for UNIX based systems 

  2. +
  3. 2.start-openas2.bat + – for Microsoft Windows based system 

  4. +
+

It is not necessary to modify these files for the default install to work. If you choose to put the + config.xml file in a different location than the default then you will need to edit the appropriate script file and + set the path to the config.xml file appropriately.

+

Simply execute the script file and an AS2 server will start up. It will create the following folders along with sub folders when it starts assuming no change to the default config: +

+
    +
  • logs + – contains the norml program logging   

  • +
  • data + – contains all the transferred files and any AS2 specific headers associated with AS2 transfers. This folder will have a number of sub folders for outbound and inbound files for different partners 

  • +
+

In Microsoft Windows you should be able to double click the start-openas2.bat file and a command window will open as below. +

+
+
+
+
+
 
+

For Unix based systems such as Linux and OSX, open a terminal window and change directory to the “bin” + folder of the install. The start_openas2.sh file should have execute permissions in which case simply type the name + and press enter. If no execute permissions are set, either set the execute permission as needed or use “sh” to run + the script:

+

/opt/OpenAS2:>sh opensas2.sh

+

The output in a Unix based system will be identical to that in a Windows based + system.

+

5.2. Command + Entry

+

After startup of the OpenAS2 application, no command prompt is shown in the command line window initially but you can enter a command or just press <ENTER> + to get a visible prompt. Typing ? Will show possible commands. Each command will list sub commands they require if + you try to enter them without the appropriate parameters.

+

 A screenshot showing command entry is shown below.

+

Command Entry

+
+
+
+
+
 
+

5.3. Automated Launching As UNIX Daemon

+

Although the application will launch as a daemon without any change to the default config, it is recommended that the following configuration changes are made to reduce unnecessary processing by modules that are redundant in this mode and filling the system logs with unwanted logging: +

+
    +
  1. 1.Remove + the console logger – remove the element in the <loggers> element as shown below
    <logger classname="org.openas2.logging.ConsoleLogger"/> 

  2. +
  3. 2.Remove + the stream command processor in the <commandProcessors> element as shown below
    <commandProcessor classname="org.openas2.cmd.processor.StreamCommandProcessor"/>
     

  4. +
+

 

+

5.3.1. INIT.D + Service

+

A sample “openas2.d” is provided in the bin + directory of the install package. It provides support for starting and stopping the + OpenAS2 application as a daemon using the init.d mechanism. Use the appropriate tool for the NIX operating system you are using to install the script in the /etc/init.d folder and create the soft links to launch the OpenAS2 application when the system starts. +

+

First modify the openas2.d file to reflect the path where you have installed + OpenAS2 then follow one of the options below.

+

On Redhat based systems as root:

+

$ cp <srcDir>/bin/openas2.d /etc/init.d/

+

$ chkconfig --add openas2.d

+

$ chkconfig --level 2345 openas2.d on

+

On Debian/Ubuntu based systems as root:

+

$ cp <srcDir>/bin/openas2.d /etc/init.d/

+

$ chmod 750 /etc/init.d/openas2.d

+

$ update-rc openas2.d defaults

+

+

5.3.2. SYSTEMD + Service

+

A sample file openas2.service is provided in the bin folder of the install package. +

+

First modify the openas2.d file to reflect the path where you have installed OpenAS2 then follow the steps below. +

+

$ cp <srcDir>/bin/openas2.service /etc/systemd/system/ +

+

$ systemctl daemon-reload +

+

$ systemctl enable openas2.service

+

+

Test that it works using the below commands:

+

 
$ systemctl enable openas2.service
$ systemctl start openas2.service
$ systemctl stop openas2.service

+

 

+

6. Testing + OpenAS2 Transfers

+

The default configuration of the OpenAS2 configuration is set up for two partners named “OpenAS2A” and “OpenAS2B”. The system will effectively send messages to itself between the 2 configured partners. You can simply start the OpenAS2 server without any changes and then copy a file into the appropriate outbox as defined by the relevant module using the  org.openas2.processor.receiver.AS2DirectoryPollingModule classes “outboxdir” attribute to send  the file to the desired partner.

+

The default configuration provides for 2 partners OPENAS2A and OPENAS2B and will create outbox folders + <installDir>/data/toOpenAS2A and <installDir>/data/toOpenAS2B for explicitly targeting a partner for any file + dropped in one of those folders.

+

If you wish to run 2 OpenAS2 servers on the same machine then the ports on the 2nd instance of OpenAS2 as configured in the config.xml must be different to those configured on the first instance (see Application Configuration above). If using asynchronous MDN, the URL entry for the attribute “as2_receipt_option” in the partnerships.xml file for the 2nd instance must match the values configured in the 1st instances config.xml for hist name and port and vice-versa.

+

6.1. Using + HTTPS Transport

+

To test on a local machine using the supplied sample self signed SSL certificate (config/ssl_certs.jks) + you should create a localhost DNS entry. The sample certificate was generated for “www.openas2.localhost”.

+

This site will help in how to set up a local DNS: http://www.selfsignedcertificate.com/development_tips.php +

+

The As2ReceiverModule module element should be configured correctly. The key attributes that will work + with the supplied sample certificate are already in the sample config file and should just be uncommented:

+
    +
  • protocol="https" 

  • +
  • ssl_keystore="%home%/ssl_certs.jks" 

  • +
  • ssl_keystore_password="testas2" 

  • +
  • ssl_protocol="TLS"  

    +
  • +
+

The partnership definition for the connection URL will also have to be set to + the appropriate host name and use “https” instead of “http”:

+

        <attribute name="as2_url" value="https://www.openas2.localhost:10080"/>

+

If asynchronous MDN is used then the as2_receipt_option attribute must be configured for SSL as + well:

+

        <attribute name="as2_receipt_option" value="https://www.openas2.localhost.com:10081"/>

+

 

+

The following system property will have to be passed to the application in the java command line:

+

-Dorg.openas2.cert.TrustSelfSignedCN=www.openas2.localhost

+

 

+

If you experience problems with SSL, try adding this to the startup command in the script file: -Djavax.net.debug=SSL

+

7. Troubleshooting OpenAS2 +

+

This section provides some help in identifying issues with AS2 transfers or configuration and execution of the OpenAS2 application. Experience + has shown that not all systems properly implement the AS2 specification or have an interpretation of the + specification that is different to the OpenAS2 default implementation. To accommodate these differences, the OpenAS2 application has some configuration parameters to change the default behaviour on a per partnership basis + that may help to accommodate the implementation anomalies for various other AS2 systems. +

+

7.1. Canonicalization For MIC Algorithm

+

Some systems (including OpenAS2 prior to V1.3.7) do not canonicalize the MimeBodyPart as specified in the + RFC when content transfer encoding is not “binary” (the OpenAS2 default is “binary” but can be set to other values using the “content_transfer_encoding” attribute on the prtnership). This + manifests as errors that cause signature authentication failure that may specifically mention a mismatched MIC. To cater for this set the following + attribute on the partnership:

+

<attribute name="prevent_canonicalization_for_mic" value="true"/> +

+

7.2. Binary + Encoding

+

If using a content transfer encoding algorithm other than “binary” results in authentication failures, try setting the attribute on the partnership:

+

<attribute name="content_transfer_encoding" value="binary"/>

+

7.3. HTTP Restricted Headers

+

Depending on the version of Java you are running, the HTTP class handling sending AS2 messages over HTTP that is part of the core Java distribution will automatically remove any restricted HTTP headers (see here for a discussion: http://stackoverflow.com/questions/11147330/httpurlconnection-wont-let-me-set-via-header).

+

This should not be a problem for modern AS2 implementations that OpenAS2 communicates with but there are reports that some systems respond with an HTTP 400 error code and reject the message if the “Content-Transfer-Encoding” + header that is a restricted header (see section 19.4.5 here: https://www.w3.org/Protocols/rfc2616/rfc2616-sec19.html), is not present in the HTTP headers (it is present in the mime body part headers of the AS2 message). +

+

To solve this uncomment the line in the startup script file containing this entry

+

-Dsun.net.http.allowRestrictedHeaders=true

+

 

+

IMPORTANT NOTE: This change cannot be made partner specific due to the way it is implemented in Java so + all configured partners will then receive restricted HTTP headers so they may fail as a result of this change. Your + only way around this is to run separate OpenAS2 instances.

+

7.4. CMS Algorithm Protection

+

Some AS2 systems do not support RFC6211.

+

The partner system will most likely not provide detailed information that this OID is the issue unless + you request detailed logging from the partner but will manifest as authentication failures of some sort. Currently + known systems that do not support this are IBM Sterling Integrator.

+

To disable the OID from being sent add this attribute to the partnership (from a security point of view to include it wherever possible as it plugs a security issue in CMS signed messages): +

+

<attribute name="remove_cms_algorithm_protection_attrib" value="true"/>

+

7.5. SSL Certificate Exceptions

+

Sometimes a partner uses a certificate that has intermediate providers not registered in your Java + security keystore. Generally this will be manifested by an exception something like this:

+

javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building + failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to + requested target

+

 at sun.security.ssl.Alerts.getSSLException(Alerts.java:192)

+

 at sun.security.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1917)

+

 at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:301)

+

 at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:295)

+

 at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1369)

+

In this case you will need to set up a local trusted certificate provider keystore containing the root + or chained (intermediate) certificates that are missing.

+

Steps:

+
    +
  1. 1.Run + the class embedded in the OpenAS2 library jar:

    java -cp <pathToOpenAS2LibFolder>/openas2-server.jar CheckCertificate + <host name>[:port] <localKeystoreFile> [passphrase] 

    +

    "<host + name>[:port]" should be the same as what you have in the partnerships "as2_url" attribute EXCLUDING the + "https://" 

  2. +
+

 

+
    +
  1. "<localKeystoreFile>" is the name you want to give to your local keystore (e.g + jssechaincerts)

    +

    "[passphrase]" is the password for the keystore - it will default to "changeit" if you do not + provide one`

    +

    NOTE: If there is no existing keystore you want to add it to then leave out the password otherwise it will throw an error. You can use the keytool utility that comes with java to change the keystore password if you wish but since it does not contain any private keys there is little point in changing the password but if you do then you will have to pass the new password in to the OpenAS2 app using the javax.net.ssl.trustStorePassword property.

    If the class only receives a single certificate as response from the remote host it generslly indicates that the root certificate is not trusted and will need installing into a keystore for use by the OpenAS2 application.
    The + output from the class should make it clear it was unable to successfully complete an SSL handshake and it will + import the certificate (root or chain  as necessary) into the keystore.

  2. +
  3. 2.Add + the local cert store to the OpenAS2 startup by adding this to the startup command in the relevant batch file you + are using to start OpenAS2:
    -Djavax.net.ssl.trustStore=<pathToKeystore>/<localKeystoreFile> 

  4. +
+

+

 

+

7.6. Java Versions Prior To 1.6

+ +

7.7. Mime + Body Part Logging

+

Sometimes it may be necessary to see what is actually in the mime body parts received from a partner. + OpenAS2 provides a mechanism to enable logging of either received message mime body parts or receieved MDN mime body + parts. These are enabled using OpenAS2 startup variables in the startup script in combination with TRACE level + logging. Both the DOS and Unix scripts provide these variables but are commented out near the top of the batch file + and you can simply uncomment and start the application.

+

IMPORTANT: this could produce large log files so use sparingly and disable as soon as possible.

+

The startup variables are:
logRxdMsgMimeBodyParts=true
logRxdMdnMimeBodyParts=true
+

+

7.8. TLSv1.2

+

It appears that although Java7 does support TLSv1.2 it is not enabled by default (refer here: https://blogs.oracle.com/java-platform-group/entry/diagnosing_tls_ssl_and_https) +

+

If you need to use the protocol, add the following to the top of the batch + shell script that starts OpenAS2:

+

Windows: set EXTRA_PARMS=%EXTRA_PARMS% -Dhttps.protocols=TLSv1.2 +

+

Linux/Unix/OSX: EXTRA_PARMS=$EXTRA_PARMS -Dhttps.protocols=TLSv1.2

+

8. Partner AS2 Compatibility Settings

+

The below table provides configuration settings for other AS2 systems that are known to work based on user feedback.

+

PLEASE FEEL FREE TO PROVIDE SETTINGS FOR ANY SYSTEMS THAT REQUIRE A CHANGE FROM THE DEFAULT PROVIDED WITH THE OPENAS2 INSTALL PACKAGE TO COMMUNICATE WITH OTHER AS2 SYSTEMS. +  

+

Where the field is left blank, the setting is unknown and the default that comes with OpenAS2 will + probably work.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

AS2 System

Allow Restricted Headers
(startup script property: sun.net.http.allowRestrictedHeaders)

Prevent Canonicalization For + MIC

+

(partner attribute: prevent_cononicalization_for_mic)

Remove CMS + Algorithm Protection
(partner attribute:  remove_cms_algorithm_protection_attrib)

IBM Sterling +

false

 

true

IBM Datapower

false

 

true

Mendelson

false

true

 

Seeburger

?

?

?

Oracle Integration B2B

false

false

false

+

 

+

9. Remote Control +

+

By default the OpenAS2 server application will start up a command processor as a socket listener allowing + remote connection to the OpenAS2 server to execute commands. The OpenAS2 remote application is part of the + application package but is not necessary to use it if you have no remote access requirement and should be disabled + in the config.xml file if not using it by removing or commenting out the <commandProcessor> element with classname value org.openas2.cmd.processor.SocketCommandProcessor +  

+

You can set the port that the command processor listens on using the portId + parameter.  

+

        <commandProcessor classname="org.openas2.cmd.processor.SocketCommandProcessor"  portId="14321"  userid="userID" password="pWd"/> +

+

The remote control application will need to connect to the specified port with the specified user ID and + password.

+

The connection uses an anonymous secure socket cipher and may require changing this if your Java implementation does not support the default cipher which is TLS_DH_anon_WITH_AES_256_CBC_SHA + for the latest release. This cipher is not available in older Java versions and it may be necessary to switch to SSL_DH_anon_WITH_RC4_128_MD5 +

+

To switch cipher you will need to start the OpenAS2 server and the remote command client passing the cipher name as a system property using the -D switch that can be added to the batch script that starts the application. The property must be named  “CmdProcessorSocketCipher”.

+

e.g java -DCmdProcessorSocketCipher=SSL_DH_anon_WITH_RC4_128_MD5 … +

+

10. Dynamic Variables

+

Variables can be used in configuration files for real time replacement of strings. Some variables are specific to certain processor modules. The + variables used in the configuration files are as follows:

+
    +
  • $date.xxx$ ::: for date parameters

    +

    where xxx is any valid character formatting string defined in java.text.SimpleDateFormat
    for + example: $date.YYYY$ gets the four digit year

    +

    $msg.xxx.yyy$, accesses various information about the incoming message, used by MessageFileModule. + The available options for this format of dynamic variable are:

  • +
+
    +
  1. +
      +
    1. 1.$msg.sender.as2_id$ + - retrieves the AS2 ID of the sender of the message 

    2. +
    3. 2.$msg.receiver.as2_id$ + - retrieves the AS2 ID of the receiver of the message 

    4. +
    5. 3.$msg.attributes.yyy$ - used to access any attribute on the message where the attribute identifier + is used in place of “yyy
      for example  

    6. +
    7. 4.$msg.headers.yyy$ - used to access any header on the message where the header identifier is + used in place of “yyy 

    8. +
    9. 5.$msg.content-disposition.yyy$ - used to access any content-disposition attribute in the message content disposition where + the attribute identifier is used in place of “yyy
      Some attriutes commonly found in an AS2 message content disposition + include 

    10. +
    +
  2. +
+
    +
  • +
      +
    • +
        +
      • filename + – the original name of the file that was sent 

      • +
      •  

      • +
      +
    • +
    +
  • +
+

 

+
    +
  • $mdn.zzz$ for message + mdn parameters, used by EmailLogger and MDNFileModule

    +

    where zzz can be any of the following values to get

    +
      +
    • •.msg + – requires “zzz” to be in the form “xxx.yyy” and can access data points as defined for $msg.xxx.yyy$ format dynamic variables above 

    • +
    • •.sender + – gets the as2_id of the sender 

    • +
    • •.receiver +  – gets the as2_id of the receiver 

    • +
    • •.text + - gets the text portion of the MDN 

    • +
    • •.attributes +  – requires “zzz” to be in the form “xxx.yyy” and can access data points as defined for $msg.xxx.yyy$ format dynamic variables above 

    • +
    • •.headers + – requires “zzz” to be in the form “xxx.yyy” and can access data points as defined for $msg.xxx.yyy$ format dynamic variables above 

    • +
    +


    for example: $mdn.text$ gets the text portion of the MDN

    +

    $rand.zzz$ can be used on any string parsed for parameters + to produce a random UUID or a 0 padded random number of a defined number of digits +

    +

    where zzz can be any string of any number of characters +

    +
      +
    • •.if + the string is “UUID” or “uuid” (e.g $rand.UUID) then it produces a random UUID 

    • +
    • •.for + any other string of characters other than UUID, the number of characters in the string determines the + number of digits in the random number that is generated and will be zero padded  +

    • +
    • •.e.g + $rand.1234 - creates a random number between 0000 and 9999 

    • +
    • •.e.g $rand.ax1fg4c5 - creates a random number between 00000000 and 99999999 

    • +
    +

     

    +

    $exception.xxx$, used by EmailLogger

    +

    where xxx can be any of the follow ing values to get

    +
      +
    • •.name +  

    • +
    • •.message +  

    • +
    • •.trace +  

    • +
    • •.terminated +  

    • +
    +


    for example: $exception.trace$ gets the trace log of the + exception

  • +
+

11. Appendix: config.xml file structure

+

 

+
+

+
    +
  • +
      +
    • +
        +
      • •.Node: + module  

        +
          +

        • Attributes

          +

          classname

          +

          describes the Java class to rehandle messages +
          for example: org.openas2.processor.resender.DirectoryResenderModule +

          +

          resenddir

          +

          defines the directory to find message to resend
          for example: %home%/resend

          +

          errordir

          +

          defines the director to store resend messages that are in error.
          for + example: %home%/resend/error

          +

          resenddelay

          +

          defines the wait time between resends. Defined in seconds. Default is + 60.
          for example: 600

        • +
        +
      • +
      +
    • +
    +
  • +
+

12. Appendix: partnership.xml file structure

+

This file describes your company and your trading partners. This file requires modification to work with your + application

+
    +
  • •.Node: + partnerships
    The root node.  

    +
      +
    • •.Node: + partner
      partner definition
      Attributes  

      +

      name +  

      +

      partner + name as defined in OpenAS2 configuration file.
      OpenAS2A  

      +

      as2_id +  

      +

      partner + name as defined in partnership node
      OpenAS2A  

      +

      x509_alias +  

      +

      Alias + as defined in certificate file
      openas2a  

      +

      email +  

      +

      E-mail + address of partner
      as2a@MySillyMailerServer.com 

      +

       

    • +
    • •.Node: + partnership
      defines partner + relationships between sender and receiver  

      +
        +
      • •.Node: + partnership
        Attributes  

        +

        name +  

        +

        Unique + name of partnership relation. See filename parsing above.
        OpenAS2A-OpenAS2B  

      • +
      • •.Node: + sender
        Attributes  

        +

        name +  

        +

        Unique + name of Sender
        OpenAS2A  

      • +
      • •.Node: + receiver
        Attributes  

        +

        name +  

        +

        Unique + name of receiver
        OpenAS2B  

        +

        The following is a list of nodes that use the node name of attribute. The subnodes of attribute use a name/value node naming pair structure. +  

      • +
      • •.Node: + attribute
        name is + protocol defines the protocol to use with this partner.
        value is as2
        name="protocol" value="as2"  

      • +
      • •.Node: + attribute
        name is + subject defines text used in E-mail subject line
        value
        name="subject" value="From OpenAS2A to OpenAS2B"  

      • +
      • •.Node: + attribute
        name is + as2_url defines partners AS2 server's URL
        value
        name="as2_url" value="http://www.MyPartnerAS2Machine.com:10080"/>  

      • +
      • •.Node: + attribute
        name is + as2_mdn_to when set this specifies that an MDN response is required and defines + value of the “"Disposition-Notification-To" + header in the AS2 message sent to the partner. It is normally an email address but can be any string that is meaningful
        value
        name="as2_mdn_to" value="datamanager@mypartner.com"  

      • +
      • •.Node: + attribute
        name is + as2_receipt_option defines asynchronous MDN server's URL
        value
        name="as2_receipt_option" value="http://www.MyAS2Machine.com:10081"  

      • +
      • •.Node: + attribute
        name is + as2_mdn_options defines MDN option values for E-mail header
        value
        name="as2_mdn_options" value="signed-receipt-protocol=optional, pkcs7-signature; signed-receipt-micalg=optional, sha1"  

      • +
      • •.Node: + attribute
        name is + encrypt defines encrypting algorithm name for E-mail header
        value
        name="encrypt" value="3des"  

      • +
      • •.Node: attribute (optional)
        name is content_transfer_encoding defines what the header field should display
        value 8bit (default), binary, ...
        name="content_transfer_encoding" value="binary"
         

      • +
      • •.Node: attribute (optional)
        name is compression_type if defined it determines what the type of compression to use. Leave this attribute out if no compression is required
        value ZLIB (default) – no other supported options
        name="
        compression_type" value="ZLIB"  

        +
      • +
      • •.Node: attribute (optional)
        name is compression_mode if defined it determines when compression occurs. If this attribute is not specified then compression occurs before signing.
        value – “compress-after-signing”
        name="
        compression_mode" value="compress-after-signing"  

      • +
      +
    • +
    +
  • +
+

13. Appendix: command.xml file structure

+

List of commands available to the OpenAS2 server Application.

+
    +
  • •.Node: + commands the root node  

    +
      +
    • •.Node: + multicommand  

      +

      attribute +  

      +

      name +  

      +

      value + "cert|part", certificate commands or partnership commands  

      +

      description +  

      +

      value + is some useful text  

      +
        +
      • •.Node: + command  

        +

        attribute +  

        +

        classname +  

        +

        value + is a OpenAS2 classname that will process a command  

      • +
      +
    • +
    +
  • +
+ + \ No newline at end of file diff --git a/mvnw b/mvnw new file mode 100755 index 00000000..fc7efd17 --- /dev/null +++ b/mvnw @@ -0,0 +1,234 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Maven2 Start Up Batch script +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# M2_HOME - location of maven2's installed home dir +# MAVEN_OPTS - parameters passed to the Java VM when running Maven +# e.g. to debug Maven itself, use +# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ] ; then + + if [ -f /etc/mavenrc ] ; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ] ; then + . "$HOME/.mavenrc" + fi + +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false; +darwin=false; +mingw=false +case "`uname`" in + CYGWIN*) cygwin=true ;; + MINGW*) mingw=true;; + Darwin*) darwin=true + # + # Look for the Apple JDKs first to preserve the existing behaviour, and then look + # for the new JDKs provided by Oracle. + # + if [ -z "$JAVA_HOME" ] && [ -L /System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK ] ; then + # + # Apple JDKs + # + export JAVA_HOME=/System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK/Home + fi + + if [ -z "$JAVA_HOME" ] && [ -L /System/Library/Java/JavaVirtualMachines/CurrentJDK ] ; then + # + # Apple JDKs + # + export JAVA_HOME=/System/Library/Java/JavaVirtualMachines/CurrentJDK/Contents/Home + fi + + if [ -z "$JAVA_HOME" ] && [ -L "/Library/Java/JavaVirtualMachines/CurrentJDK" ] ; then + # + # Oracle JDKs + # + export JAVA_HOME=/Library/Java/JavaVirtualMachines/CurrentJDK/Contents/Home + fi + + if [ -z "$JAVA_HOME" ] && [ -x "/usr/libexec/java_home" ]; then + # + # Apple JDKs + # + export JAVA_HOME=`/usr/libexec/java_home` + fi + ;; +esac + +if [ -z "$JAVA_HOME" ] ; then + if [ -r /etc/gentoo-release ] ; then + JAVA_HOME=`java-config --jre-home` + fi +fi + +if [ -z "$M2_HOME" ] ; then + ## resolve links - $0 may be a link to maven's home + 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 + + saveddir=`pwd` + + M2_HOME=`dirname "$PRG"`/.. + + # make it fully qualified + M2_HOME=`cd "$M2_HOME" && pwd` + + cd "$saveddir" + # echo Using m2 at $M2_HOME +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin ; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --unix "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --unix "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --unix "$CLASSPATH"` +fi + +# For Migwn, ensure paths are in UNIX format before anything is touched +if $mingw ; then + [ -n "$M2_HOME" ] && + M2_HOME="`(cd "$M2_HOME"; pwd)`" + [ -n "$JAVA_HOME" ] && + JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" + # TODO classpath? +fi + +if [ -z "$JAVA_HOME" ]; then + javaExecutable="`which javac`" + if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then + # readlink(1) is not available as standard on Solaris 10. + readLink=`which readlink` + if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then + if $darwin ; then + javaHome="`dirname \"$javaExecutable\"`" + javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" + else + javaExecutable="`readlink -f \"$javaExecutable\"`" + fi + javaHome="`dirname \"$javaExecutable\"`" + javaHome=`expr "$javaHome" : '\(.*\)/bin'` + JAVA_HOME="$javaHome" + export JAVA_HOME + fi + fi +fi + +if [ -z "$JAVACMD" ] ; then + 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 + else + JAVACMD="`which java`" + fi +fi + +if [ ! -x "$JAVACMD" ] ; then + echo "Error: JAVA_HOME is not defined correctly." >&2 + echo " We cannot execute $JAVACMD" >&2 + exit 1 +fi + +if [ -z "$JAVA_HOME" ] ; then + echo "Warning: JAVA_HOME environment variable is not set." +fi + +CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher + +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --path --windows "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --windows "$CLASSPATH"` +fi + +# traverses directory structure from process work directory to filesystem root +# first directory with .mvn subdirectory is considered project base directory +find_maven_basedir() { + local basedir=$(pwd) + local wdir=$(pwd) + while [ "$wdir" != '/' ] ; do + if [ -d "$wdir"/.mvn ] ; then + basedir=$wdir + break + fi + wdir=$(cd "$wdir/.."; pwd) + done + echo "${basedir}" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + echo "$(tr -s '\n' ' ' < "$1")" + fi +} + +export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-$(find_maven_basedir)} +MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" + +# Provide a "standardized" way to retrieve the CLI args that will +# work with both Windows and non-Windows executions. +MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" +export MAVEN_CMD_LINE_ARGS + +WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +exec "$JAVACMD" \ + $MAVEN_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} $MAVEN_CMD_LINE_ARGS + diff --git a/mvnw.cmd b/mvnw.cmd new file mode 100644 index 00000000..00104808 --- /dev/null +++ b/mvnw.cmd @@ -0,0 +1,145 @@ +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Maven2 Start Up Batch script +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM M2_HOME - location of maven2's installed home dir +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" +if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +set MAVEN_CMD_LINE_ARGS=%MAVEN_CONFIG% %* + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" + +set WRAPPER_JAR=""%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CMD_LINE_ARGS% +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" +if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%" == "on" pause + +if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% + +exit /B %ERROR_CODE% diff --git a/pom.xml b/pom.xml index 93844c20..aff47f4b 100644 --- a/pom.xml +++ b/pom.xml @@ -1,19 +1,181 @@ - - 4.0.0 - org.openas2 - OpenAS2 - 2.2.0 - OpenAS2 - pom - - This is the base Maven build file for the OpenAS2 project. - The project comprises a server ,osgi bundle and a remote client component. - There is a pom.xml for each of the components to build that component. - - - Server - Remote - Bundle + + 4.0.0 + org.openas2 + OpenAS2 + 2.3.0 + OpenAS2 + pom + + + This is the base Maven build file for the OpenAS2 project. + The project comprises a server ,osgi bundle and a remote client component. + There is a pom.xml for each of the components to build that component. + + + https://sourceforge.net/projects/openas2 + + + Server + Remote + Bundle - \ No newline at end of file + + + 1.6 + + + + + + org.osgi + org.osgi.core + 4.3.1 + + + org.dom4j + dom4j + 2.0.0 + + + org.bouncycastle + bcmail-jdk15on + 1.54 + + + org.bouncycastle + bcpkix-jdk15on + 1.54 + + + org.bouncycastle + bcprov-jdk15on + 1.54 + + + org.apache.commons + commons-lang3 + 3.4 + + + commons-logging + commons-logging + 1.2 + + + javax.mail + mail + 1.4.7 + + + com.h2database + h2 + 1.4.193 + + + org.bouncycastle + bcpg-jdk15on + 1.54 + + + com.google.code.findbugs + findbugs + 3.0.1 + provided + + + junit + junit + 4.12 + test + + + org.hamcrest + hamcrest-all + 1.3 + test + + + org.mockito + mockito-all + 1.10.19 + test + + + commons-io + commons-io + 2.5 + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.6.1 + + ${java.version} + ${java.version} + + + + org.apache.maven.plugins + maven-jar-plugin + 3.0.2 + + + true + + true + ${mainClass} + true + true + + + + + + org.apache.maven.plugins + maven-dependency-plugin + 3.0.0 + + + copy-dependencies + package + + copy-dependencies + + + ${project.build.directory}/dist/lib + false + false + true + runtime + + + + + + org.apache.maven.plugins + maven-antrun-plugin + 1.8 + + + org.apache.maven.plugins + maven-site-plugin + 3.6 + + + org.apache.maven.wagon + wagon-ssh + 2.12 + + + + + + + diff --git a/tests/data/test.msg b/tests/data/test.msg deleted file mode 100644 index 5db405c8..00000000 --- a/tests/data/test.msg +++ /dev/null @@ -1 +0,0 @@ -This is a short AS2 message