diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md
index 14678d69..679a3fb2 100644
--- a/RELEASE-NOTES.md
+++ b/RELEASE-NOTES.md
@@ -1,21 +1,19 @@
# OpenAS2 Server
-# Version 3.7.0
+# Version 3.8.0
# RELEASE NOTES
-----
-The OpenAS2 project is pleased to announce the release of OpenAS2 3.7.0
+The OpenAS2 project is pleased to announce the release of OpenAS2 3.8.0
-The release download file is: OpenAS2Server-3.7.0.zip
+The release download file is: OpenAS2Server-3.8.0.zip
The zip file contains a PDF document (OpenAS2HowTo.pdf) providing information on installing and using the application.
## NOTE: Testing covers Java 8 to 17. The application should work for older versions down to Java 7 but they are not tested as part of the CI/CD pipeline.
-Version 3.7.0 - 2023-09-12
-This is an enhancement and bugfix release:
+Version 3.8.0 - 2023-11-07
+This is an enhancement release:
**IMPORTANT NOTE**: Please review upgrade notes below if you are upgrading
- 1. Support parallel mode processing for the directory polling configuration to achieve high volume throughput.
- 2. Enhance error handling when chacking for files that never received an DMN response.
- 3. Added logging to indicate reading a fixed byte count message from HTTP stream to aid debugging.
+ 1. Support for configurable dynamic Content-Type based on the file extension. See documentation section 7.5 "Setting Content Type"
##Upgrade Notes
See the openAS2HowTo appendix for the general process on upgrading OpenAS2.
diff --git a/Remote/pom.xml b/Remote/pom.xml
index cddb1ad2..f9511e55 100644
--- a/Remote/pom.xml
+++ b/Remote/pom.xml
@@ -4,7 +4,7 @@
net.sf.openas2
OpenAS2
- 3.7.0
+ 3.8.0
4.0.0
diff --git a/Server/pom.xml b/Server/pom.xml
index 8b576221..0de6aa6b 100644
--- a/Server/pom.xml
+++ b/Server/pom.xml
@@ -7,7 +7,7 @@
net.sf.openas2
OpenAS2
- 3.7.0
+ 3.8.0
../pom.xml
diff --git a/Server/src/config/content_type_mappings.properties b/Server/src/config/content_type_mappings.properties
new file mode 100644
index 00000000..556d6d8a
--- /dev/null
+++ b/Server/src/config/content_type_mappings.properties
@@ -0,0 +1,3 @@
+xml=application/xml
+edi=application/edifact
+txt=text/plain
\ No newline at end of file
diff --git a/Server/src/main/java/org/openas2/XMLSession.java b/Server/src/main/java/org/openas2/XMLSession.java
index b7a164f1..9399b30a 100644
--- a/Server/src/main/java/org/openas2/XMLSession.java
+++ b/Server/src/main/java/org/openas2/XMLSession.java
@@ -12,11 +12,13 @@
import org.openas2.params.InvalidParameterException;
import org.openas2.params.ParameterParser;
import org.openas2.message.MessageFactory;
+import org.openas2.partner.Partnership;
import org.openas2.partner.PartnershipFactory;
import org.openas2.processor.Processor;
import org.openas2.processor.ProcessorModule;
import org.openas2.processor.receiver.PollingModule;
import org.openas2.schedule.SchedulerComponent;
+import org.openas2.util.FileUtil;
import org.openas2.util.Properties;
import org.openas2.util.XMLUtil;
import org.w3c.dom.Document;
@@ -141,8 +143,9 @@ protected void load(InputStream in) throws Exception {
*
* @param propNode - the "properties" element of the configuration file containing property values
* @throws InvalidParameterException
+ * @throws IOException
*/
- private void loadProperties(Node propNode) throws InvalidParameterException {
+ private void loadProperties(Node propNode) throws InvalidParameterException, IOException {
LOGGER.info("Loading properties...");
Map properties = XMLUtil.mapAttributes(propNode, false);
@@ -151,7 +154,7 @@ private void loadProperties(Node propNode) throws InvalidParameterException {
properties.put(Properties.APP_TITLE_PROP, getAppTitle());
properties.put(Properties.APP_VERSION_PROP, getAppVersion());
Properties.setProperties(properties);
- String appPropsFile = System.getProperty("openas2.properties.file");
+ String appPropsFile = System.getProperty(Properties.OPENAS2_PROPERTIES_FILE_PROP);
if (appPropsFile != null && appPropsFile.length() > 1) {
java.util.Properties appProps = new java.util.Properties();
FileInputStream fis = null;
@@ -210,6 +213,11 @@ private void loadProperties(Node propNode) throws InvalidParameterException {
Properties.setProperty(key, entry.getValue());
}
}
+ // Now check if we need to load Content-Type mappings
+ String contentTypeMapFilename = Properties.getProperty(Partnership.PA_CONTENT_TYPE_MAPPING_FILE, null);
+ if (contentTypeMapFilename != null) {
+ Properties.setContentTypeMap(FileUtil.loadProperties(contentTypeMapFilename));
+ }
}
private void loadCertificates(Node rootNode) throws OpenAS2Exception {
diff --git a/Server/src/main/java/org/openas2/app/partner/AddPartnershipCommand.java b/Server/src/main/java/org/openas2/app/partner/AddPartnershipCommand.java
index 3b211f5f..30b96ed8 100644
--- a/Server/src/main/java/org/openas2/app/partner/AddPartnershipCommand.java
+++ b/Server/src/main/java/org/openas2/app/partner/AddPartnershipCommand.java
@@ -16,7 +16,7 @@
import java.util.regex.Pattern;
/**
- * adds a new partnership entry in partneship store
+ * adds a new partnership entry in partnership store
*
* @author joseph mcverry
*/
@@ -56,7 +56,7 @@ public CommandResult execute(PartnershipFactory partFx, Object[] params) throws
for (int i = 0; i < params.length; i++) {
String param = (String) params[i];
- int pos = param.indexOf('=');
+ int equalsPos = param.indexOf('=');
if (i == 0) {
partnershipRoot.setAttribute("name", param);
} else if (i == 1) {
@@ -67,9 +67,9 @@ public CommandResult execute(PartnershipFactory partFx, Object[] params) throws
Element elem = doc.createElement(Partnership.PCFG_RECEIVER);
elem.setAttribute("name", param);
partnershipRoot.appendChild(elem);
- } else if (pos == 0) {
+ } else if (equalsPos == 0) {
return new CommandResult(CommandResult.TYPE_ERROR, "incoming parameter missing name");
- } else if (pos > 0) {
+ } else if (equalsPos > 0) {
if (param.startsWith("pollerConfig.")) {
// Add a pollerConfig element
String regex = "^pollerConfig.([^=]*)=((?:[^\"']+)|'(?:[^']*)'|\"(?:[^\"]*)\")";
@@ -86,8 +86,8 @@ public CommandResult execute(PartnershipFactory partFx, Object[] params) throws
pollerConfigElem.setAttribute(name, val);
} else {
Element elem = doc.createElement("attribute");
- elem.setAttribute("name", param.substring(0, pos));
- elem.setAttribute("value", param.substring(pos + 1));
+ elem.setAttribute("name", param.substring(0, equalsPos));
+ elem.setAttribute("value", param.substring(equalsPos + 1));
partnershipRoot.appendChild(elem);
}
} else {
diff --git a/Server/src/main/java/org/openas2/message/FileAttribute.java b/Server/src/main/java/org/openas2/message/FileAttribute.java
index 8dd9893b..1e122e63 100644
--- a/Server/src/main/java/org/openas2/message/FileAttribute.java
+++ b/Server/src/main/java/org/openas2/message/FileAttribute.java
@@ -11,4 +11,5 @@ public interface FileAttribute {
String MA_ERROR_FILENAME = "errorfilename";
String MA_SENT_DIR = "sentdir";
String MA_SENT_FILENAME = "sentfilename";
+ String MA_FILENAME_EXTENSION = "filename_extension";
}
diff --git a/Server/src/main/java/org/openas2/partner/Partnership.java b/Server/src/main/java/org/openas2/partner/Partnership.java
index 09dedc43..98e23504 100644
--- a/Server/src/main/java/org/openas2/partner/Partnership.java
+++ b/Server/src/main/java/org/openas2/partner/Partnership.java
@@ -2,8 +2,10 @@
import org.openas2.OpenAS2Exception;
import org.openas2.cert.CertificateNotFoundException;
+import org.openas2.util.FileUtil;
import org.openas2.util.Properties;
+import java.io.IOException;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Iterator;
@@ -37,11 +39,13 @@ public class Partnership implements Serializable {
/* partnership definition attributes */
public static final String PA_SUBJECT = "subject"; // Subject sent in messages
public static final String PA_CONTENT_TYPE = "content_type"; // optional content type for mime parts
+ public static final String PA_USE_DYNAMIC_CONTENT_TYPE_MAPPING = "use_dynamic_content_type_mapping"; // use file extension to Content-Type mapping
+ public static final String PA_CONTENT_TYPE_MAPPING_FILE = "content_type_mapping_file"; // file containing file extension to Content-Type mapping
public static final String PA_CONTENT_TRANSFER_ENCODING = "content_transfer_encoding"; // optional content transfer enc value
public static final String PA_SET_CONTENT_TRANSFER_ENCODING_HTTP = "set_content_transfer_encoding_http_header"; // See as an HTTP header
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
- public static final String PA_RESEND_REQUIRES_NEW_MESSAGE_ID = "resend_requires_new_message_id"; // list of nme/value pairs for setting custom mime headers
+ public static final String PA_RESEND_REQUIRES_NEW_MESSAGE_ID = "resend_requires_new_message_id"; // list of name/value pairs for setting custom mime headers
public static final String PA_COMPRESSION_TYPE = "compression";
public static final String PA_SIGNATURE_ALGORITHM = "sign";
public static final String PA_ENCRYPTION_ALGORITHM = "encrypt";
@@ -80,6 +84,9 @@ public class Partnership implements Serializable {
private Map receiverIDs;
private Map senderIDs;
private String name;
+ private java.util.Properties overrideContentTypeFromFileExtensionMap = null;
+ private java.util.Properties contentTypeFromFileExtensionMap = null;
+ private boolean useDynamicContentTypeLookup = false;
public String getName() {
return name;
@@ -173,7 +180,59 @@ public boolean matches(Partnership partnership) {
}
- public String getAlias(String partnershipType) throws OpenAS2Exception {
+ public boolean isUseDynamicContentTypeLookup() {
+ return useDynamicContentTypeLookup;
+ }
+
+ /** This method is called if the partnership is configured to use dynamic mappings.
+ * It will check that there are either system or partnership specific mappings available
+ * load them into a partnership mapping cache.
+ * @param useDynamicContentTypeLookup - if true then enable dynamic mapping
+ * @throws OpenAS2Exception
+ * @throws IOException
+ */
+ public void setUseDynamicContentTypeLookup(boolean useDynamicContentTypeLookup) throws OpenAS2Exception, IOException {
+ if (useDynamicContentTypeLookup) {
+ // Make sure there is a lookup available
+ // If there is a partnership specific override then make the partnership use
+ // that otherwise point it at the system mapping if available
+ String contentTypeMapFilename = getAttribute(Partnership.PA_CONTENT_TYPE_MAPPING_FILE);
+ if (contentTypeMapFilename != null) {
+ if (Properties.getContentTypeMap() != null) {
+ // Copy the system level mapping in first then override/add the custom mappings
+ overrideContentTypeFromFileExtensionMap = new java.util.Properties();
+ overrideContentTypeFromFileExtensionMap.putAll(Properties.getContentTypeMap());
+ overrideContentTypeFromFileExtensionMap.putAll(FileUtil.loadProperties(contentTypeMapFilename));
+ } else {
+ // Get the override map
+ setOverrideContentTypeFromFileExtension(FileUtil.loadProperties(contentTypeMapFilename));
+ }
+ // Configure this partnership to use the override lookup
+ contentTypeFromFileExtensionMap = overrideContentTypeFromFileExtensionMap;
+ } else {
+ // Set the partnership to use the global map
+ contentTypeFromFileExtensionMap = Properties.getContentTypeMap();
+ }
+ // If there is no map to do the lookup throw an excpetion
+ if (this.contentTypeFromFileExtensionMap == null) {
+ throw new OpenAS2Exception("Trying to use Content-Type mapping functionality but no mappings loaded.");
+ }
+ }
+ this.useDynamicContentTypeLookup = useDynamicContentTypeLookup;
+ }
+
+ public String getContentTypeFromFileExtension(String key) {
+ if (contentTypeFromFileExtensionMap == null) {
+ return null;
+ }
+ return (String) contentTypeFromFileExtensionMap.get(key);
+ }
+
+ public void setOverrideContentTypeFromFileExtension(java.util.Properties contentTypeFromFileExtension) {
+ this.overrideContentTypeFromFileExtensionMap = contentTypeFromFileExtension;
+ }
+
+ public String getAlias(String partnershipType) throws OpenAS2Exception {
String alias = null;
if (partnershipType == PTYPE_RECEIVER) {
diff --git a/Server/src/main/java/org/openas2/partner/XMLPartnershipFactory.java b/Server/src/main/java/org/openas2/partner/XMLPartnershipFactory.java
index 7f5d21a4..bbcd7453 100644
--- a/Server/src/main/java/org/openas2/partner/XMLPartnershipFactory.java
+++ b/Server/src/main/java/org/openas2/partner/XMLPartnershipFactory.java
@@ -209,7 +209,15 @@ public void loadPartnership(Map partners, List part
// read in the partnership attributes
loadAttributes(node, partnership);
-
+ // Now check if we need to enable Content-Type mappings for this partnership
+ if ("true".equalsIgnoreCase(partnership.getAttributeOrProperty(Partnership.PA_USE_DYNAMIC_CONTENT_TYPE_MAPPING, "false"))) {
+ try {
+ partnership.setUseDynamicContentTypeLookup(true);
+ } catch (IOException e) {
+ logger.error("Error setting up dynamic Content-Type lookup: " + e.getMessage(), e);
+ throw new OpenAS2Exception("Partnership failed to be set up correctly for dynamic Content-Type lookup: " + getName());
+ }
+ }
// add the partnership to the list of available partnerships
partnerships.add(partnership);
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 915cc1d1..4b01c668 100644
--- a/Server/src/main/java/org/openas2/processor/receiver/MessageBuilderModule.java
+++ b/Server/src/main/java/org/openas2/processor/receiver/MessageBuilderModule.java
@@ -20,6 +20,7 @@
import org.openas2.processor.resender.ResenderModule;
import org.openas2.processor.sender.SenderModule;
import org.openas2.util.AS2Util;
+import org.openas2.util.FileUtil;
import org.openas2.util.IOUtil;
import org.openas2.util.Properties;
@@ -280,6 +281,8 @@ public Message buildBaseMessage(String filename) throws OpenAS2Exception {
public void addMessageMetadata(Message msg, String filename) throws OpenAS2Exception {
msg.setAttribute(FileAttribute.MA_FILENAME, filename);
msg.setPayloadFilename(filename);
+ // Set the filename extension if it has one
+ msg.setAttribute(FileAttribute.MA_FILENAME_EXTENSION, FileUtil.getFilenameExtension(filename));
// Set a new message ID
msg.updateMessageID();
// Set the sender and receiver in the Message object headers
@@ -352,24 +355,35 @@ public void buildMessageData(Message msg, DataSource dataSource, String contentT
msg.setData(body);
}
- private String getMessageContentType(Message msg) throws OpenAS2Exception {
+ public String getMessageContentType(Message msg) throws OpenAS2Exception {
MessageParameters params = new MessageParameters(msg);
- // Allow Content-Type to be overridden at partnership level or as property
- String contentType = msg.getPartnership().getAttributeOrProperty(Partnership.PA_CONTENT_TYPE, null);
- if (contentType == null) {
- contentType = getParameter(PARAM_MIMETYPE, false);
- }
- if (contentType == null) {
- contentType = "application/octet-stream";
- } else {
- try {
- contentType = ParameterParser.parse(contentType, params);
- } catch (InvalidParameterException e) {
- throw new OpenAS2Exception("Bad content-type" + contentType, e);
+ // Allow Content-Type to be overridden at partnership level or as property
+ String contentType = msg.getPartnership().getAttributeOrProperty(Partnership.PA_CONTENT_TYPE, null);
+ // The content type could be determined dynamically based on filename extension
+ if (msg.getPartnership().isUseDynamicContentTypeLookup()) {
+ String fileExtension = msg.getAttribute(FileAttribute.MA_FILENAME_EXTENSION);
+ if (fileExtension != null) {
+ String dynamicContentType = msg.getPartnership().getContentTypeFromFileExtension(fileExtension);
+ if (dynamicContentType != null) {
+ // Dynamic override found so use it
+ contentType = dynamicContentType;
}
}
- return contentType;
+ }
+ if (contentType == null) {
+ contentType = getParameter(PARAM_MIMETYPE, false);
+ }
+ if (contentType == null) {
+ contentType = "application/octet-stream";
+ } else {
+ try {
+ contentType = ParameterParser.parse(contentType, params);
+ } catch (InvalidParameterException e) {
+ throw new OpenAS2Exception("Bad content-type" + contentType, e);
+ }
+ }
+ return contentType;
}
private void setAdditionalMetaData(Message msg, MimeBodyPart mimeBodyPart) throws OpenAS2Exception {
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 61e5ef62..377993cf 100644
--- a/Server/src/main/java/org/openas2/processor/receiver/PollingModule.java
+++ b/Server/src/main/java/org/openas2/processor/receiver/PollingModule.java
@@ -10,7 +10,7 @@
public abstract class PollingModule extends MessageBuilderModule {
- private static final String PARAM_POLLING_INTERVAL = "interval";
+ protected final String PARAM_POLLING_INTERVAL = "interval";
private Timer timer;
private boolean busy;
private String outboxDir;
diff --git a/Server/src/main/java/org/openas2/util/FileUtil.java b/Server/src/main/java/org/openas2/util/FileUtil.java
index 9d3bc2a6..dab44161 100644
--- a/Server/src/main/java/org/openas2/util/FileUtil.java
+++ b/Server/src/main/java/org/openas2/util/FileUtil.java
@@ -5,16 +5,46 @@
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.File;
+import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
+import java.util.Properties;
public class FileUtil {
//private static final Log logger = LogFactory.getLog(FileUtil.class.getSimpleName());
+ public static Properties loadProperties(String filename) throws IOException {
+ Properties fileProps = new java.util.Properties();
+ FileInputStream fis = null;
+ fis = new FileInputStream(filename);
+ try {
+ fileProps.load(fis);
+ } finally {
+ if (fis != null) {
+ fis.close();
+ }
+ }
+ return fileProps;
+ }
+
+ /** Attempts to extract the filename extension by searching for the last occurrence
+ * of a period and returning all characters following that period.
+ * If no period is found then it returns null.
+ * @param filename - the full name of the file including extension
+ * @return the extension of the filename excluding the period
+ */
+ public static String getFilenameExtension(String filename) {
+ int period_index = filename.lastIndexOf(".");
+ if (period_index == -1) {
+ return null;
+ }
+ return filename.substring(filename.lastIndexOf(".") + 1);
+ }
+
public static void splitLineBasedFile(File sourceFile, String outputDir, long maxFileSize, boolean containsHeaderRow, String newFileBaseName, String filenamePrefix) throws OpenAS2Exception {
FileReader fileReader;
try {
diff --git a/Server/src/main/java/org/openas2/util/Properties.java b/Server/src/main/java/org/openas2/util/Properties.java
index bce69332..941a84a5 100644
--- a/Server/src/main/java/org/openas2/util/Properties.java
+++ b/Server/src/main/java/org/openas2/util/Properties.java
@@ -8,6 +8,7 @@ public class Properties {
public static final String APP_TITLE_PROP = "app.title";
public static final String APP_BASE_DIR_PROP = "app.base.dir";
public static final String HTTP_USER_AGENT_PROP = "http.user.agent";
+ public static final String OPENAS2_PROPERTIES_FILE_PROP = "openas2.properties.file";
public static final String AS2_MESSAGE_ID_FORMAT = "as2_message_id_format";
public static final String AS2_MDN_MESSAGE_ID_FORMAT = "as2_mdn_message_id_format";
@@ -20,6 +21,19 @@ public class Properties {
private static final Map _properties = new HashMap();
+ private static final java.util.Properties contentTypeMap = new java.util.Properties();
+
+ public static java.util.Properties getContentTypeMap() {
+ if (contentTypeMap.isEmpty()) {
+ return null;
+ }
+ return contentTypeMap;
+ }
+
+ public static void setContentTypeMap(java.util.Properties contentTypeMappings) {
+ contentTypeMap.putAll(contentTypeMappings);
+ }
+
public static void setProperties(Map map) {
_properties.putAll(map);
}
diff --git a/Server/src/test/java/org/openas2/app/BaserServerSetup.java b/Server/src/test/java/org/openas2/app/BaserServerSetup.java
new file mode 100644
index 00000000..cf01f76e
--- /dev/null
+++ b/Server/src/test/java/org/openas2/app/BaserServerSetup.java
@@ -0,0 +1,68 @@
+package org.openas2.app;
+
+import java.io.File;
+import java.nio.file.Files;
+
+import org.apache.commons.lang3.exception.ExceptionUtils;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.io.TempDir;
+import org.openas2.TestResource;
+import org.openas2.XMLSession;
+import org.openas2.message.AS2Message;
+import org.openas2.message.Message;
+import org.openas2.partner.Partnership;
+import org.openas2.util.Properties;
+
+
+public class BaserServerSetup {
+ private static final TestResource RESOURCE = TestResource.forGroup("SingleServerTest");
+ static String myCompanyOid = "MyCompany_OID";
+ static String myPartnerOid = "PartnerA_OID";
+
+ // private static File openAS2AHome;
+ protected static XMLSession session;
+ protected static Message msg;
+
+ @TempDir
+ public static File tmpDir;
+ public File openAS2PropertiesFile;
+
+ public void refresh() throws Exception {
+ session.stop();
+ setup();
+ }
+
+ public void createFileSystemResources() throws Exception {
+ tmpDir = Files.createTempDirectory("testResources").toFile();
+ openAS2PropertiesFile = new File(tmpDir, "test.openas2.properties");
+ }
+
+ public void setup() throws Exception {
+ try {
+ System.setProperty("org.apache.commons.logging.Log", "org.openas2.logging.Log");
+ //System.setProperty("org.openas2.logging.defaultlog", "TRACE");
+ if (openAS2PropertiesFile.exists()) {
+ System.setProperty(Properties.OPENAS2_PROPERTIES_FILE_PROP, openAS2PropertiesFile.getAbsolutePath());
+ }
+ session = new XMLSession(RESOURCE.get("MyCompany", "config", "config.xml").getAbsolutePath());
+ msg = new AS2Message();
+ Partnership myPartnership = msg.getPartnership();
+ myPartnership.setSenderID(Partnership.PID_AS2, myCompanyOid);
+ myPartnership.setReceiverID(Partnership.PID_AS2, myPartnerOid);
+ myPartnership.setSenderID(Partnership.PID_AS2, myCompanyOid);
+ session.getPartnershipFactory().updatePartnership(msg, true);
+ } catch (Throwable e) {
+ // aid for debugging JUnit tests
+ System.err.println("ERROR occurred: " + ExceptionUtils.getStackTrace(e));
+ throw new Exception(e);
+ }
+ }
+
+ @AfterAll
+ public void tearDown() throws Exception {
+ session.stop();
+ openAS2PropertiesFile.delete();
+ System.clearProperty(Properties.OPENAS2_PROPERTIES_FILE_PROP);
+ }
+
+}
diff --git a/Server/src/test/java/org/openas2/app/FilenameParsingTest.java b/Server/src/test/java/org/openas2/app/FilenameParsingTest.java
index 64a06a35..3f629301 100644
--- a/Server/src/test/java/org/openas2/app/FilenameParsingTest.java
+++ b/Server/src/test/java/org/openas2/app/FilenameParsingTest.java
@@ -4,24 +4,20 @@
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestInstance;
+import org.junit.jupiter.api.TestInstance.Lifecycle;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.junit.jupiter.MockitoExtension;
-import org.openas2.TestResource;
-import org.openas2.XMLSession;
import org.openas2.message.AS2Message;
import org.openas2.message.FileAttribute;
-import org.openas2.message.Message;
import org.openas2.partner.Partnership;
import org.openas2.partner.PartnershipFactory;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.MatcherAssert.assertThat;
@ExtendWith(MockitoExtension.class)
-
-public class FilenameParsingTest {
- private static final TestResource RESOURCE = TestResource.forGroup("SingleServerTest");
- private static String myCompanyOid = "MyCompany_OID";
- private static String myPartnerOid = "PartnerA_OID";
+@TestInstance(Lifecycle.PER_CLASS)
+public class FilenameParsingTest extends BaserServerSetup {
private static String testFileNamePart1 = "abc";
private static String testFileNamePart2 = "123";
private static String testFileName = testFileNamePart1 + "-" + testFileNamePart2 + ".txt";
@@ -32,23 +28,19 @@ public class FilenameParsingTest {
private static String subjectAttrib = "First part filename: $attributes." + attribNamesFromFileName1 + "$ Second part filename: $attributes." + attribNamesFromFileName2 + "$";
private static String expectedSubject = "First part filename: " + testFileNamePart1 + " Second part filename: " + testFileNamePart2;
- // private static File openAS2AHome;
- private static XMLSession session;
- private static Message msg;
@BeforeAll
- public static void setup() throws Exception {
+ public void setup() throws Exception {
+ super.createFileSystemResources();
+ super.setup();
try {
- System.setProperty("org.apache.commons.logging.Log", "org.openas2.logging.Log");
- //System.setProperty("org.openas2.logging.defaultlog", "TRACE");
- FilenameParsingTest.session = new XMLSession(RESOURCE.get("MyCompany", "config", "config.xml").getAbsolutePath());
msg = new AS2Message();
msg.setAttribute(FileAttribute.MA_FILENAME, testFileName);
PartnershipFactory pf = session.getPartnershipFactory();
Partnership myPartnership = msg.getPartnership();
- myPartnership.setSenderID(Partnership.PID_AS2, myCompanyOid);
- myPartnership.setReceiverID(Partnership.PID_AS2, myPartnerOid);
- myPartnership.setSenderID(Partnership.PID_AS2, myCompanyOid);
+ myPartnership.setSenderID(Partnership.PID_AS2, BaserServerSetup.myCompanyOid);
+ myPartnership.setReceiverID(Partnership.PID_AS2, BaserServerSetup.myPartnerOid);
+ myPartnership.setSenderID(Partnership.PID_AS2, BaserServerSetup.myCompanyOid);
Partnership configuredPartnership = pf.getPartnership(myPartnership, false);
@@ -66,8 +58,8 @@ public static void setup() throws Exception {
}
@AfterAll
- public static void tearDown() throws Exception {
- session = null;
+ public void tearDown() throws Exception {
+ super.tearDown();;
}
@Test
diff --git a/Server/src/test/java/org/openas2/app/OpenAS2ServerTest.java b/Server/src/test/java/org/openas2/app/OpenAS2ServerTest.java
index 94287281..8e62f117 100644
--- a/Server/src/test/java/org/openas2/app/OpenAS2ServerTest.java
+++ b/Server/src/test/java/org/openas2/app/OpenAS2ServerTest.java
@@ -54,7 +54,7 @@ public class OpenAS2ServerTest {
@BeforeAll
public static void startServers() throws Exception {
- tmp = Files.createTempDirectory("testResources").toFile();
+ tmp = Files.createTempDirectory("testResources").toFile();
//System.setProperty("org.openas2.logging.defaultlog", "TRACE");
System.setProperty("org.apache.commons.logging.Log", "org.openas2.logging.Log");
try {
@@ -111,7 +111,7 @@ public void sendMessages(TestPartner sender, TestPartner receiver) throws Except
// write messages to outbox and build callables with test message objects
for (int i = 0; i < msgCnt; i++) {
- TestMessage testMsg = sendMessage(sender, receiver);
+ TestMessage testMsg = sendMessage(sender, receiver);
callers.add(new Callable() {
@Override
public TestMessage call() throws Exception {
@@ -121,7 +121,7 @@ public TestMessage call() throws Exception {
}
// send and verify all messages in parallel
for (Future result : executorService.invokeAll(callers)) {
- verifyMessageDelivery(result.get());
+ verifyMessageDelivery(result.get());
}
}
@@ -135,12 +135,12 @@ public static void tearDown() throws Exception {
// NOTE: For debugging "missing" files it is best to comment this out
for (int i = 0; i < dataFolders.length; i++) {
try {
- FileUtils.deleteDirectory(new File(dataFolders[i]));
- } catch (IOException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
+ FileUtils.deleteDirectory(new File(dataFolders[i]));
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
}
private TestMessage sendMessage(TestPartner fromPartner, TestPartner toPartner) throws IOException {
@@ -150,7 +150,7 @@ private TestMessage sendMessage(TestPartner fromPartner, TestPartner toPartner)
FileUtils.write(outgoingMsg, outgoingMsgBody, "UTF-8");
System.out.println("Copying a file to send to:" + fromPartner.getOutbox());
FileUtils.copyFileToDirectory(outgoingMsg, fromPartner.getOutbox());
- //System.out.println("**** **** FILE COPIED: " + fromPartner.getOutbox() + "/" + outgoingMsg.getName());
+ //System.out.println("**** **** FILE COPIED: " + fromPartner.getOutbox() + "/" + outgoingMsg.getName());
return new TestMessage(outgoingMsgFileName, outgoingMsgBody, fromPartner, toPartner);
@@ -158,12 +158,12 @@ private TestMessage sendMessage(TestPartner fromPartner, TestPartner toPartner)
private TestMessage getDeliveredMessage(TestMessage testMessage) throws IOException {
// Wait a while - will depend on the sender poller interval how long it takes to arrive
- testMessage.deliveredMsg = waitForFile(testMessage.toPartner.getInbox(), new PrefixFileFilter(testMessage.fileName), 20, TimeUnit.SECONDS);
- return testMessage;
+ testMessage.deliveredMsg = waitForFile(testMessage.toPartner.getInbox(), new PrefixFileFilter(testMessage.fileName), 20, TimeUnit.SECONDS);
+ return testMessage;
}
private void verifyMessageDelivery(TestMessage testMessage) throws IOException {
- assertThat("A file was received by " + testMessage.toPartner.getName() + " from " + testMessage.fromPartner.getName(), testMessage.deliveredMsg != null, is(true));
+ assertThat("A file was received by " + testMessage.toPartner.getName() + " from " + testMessage.fromPartner.getName(), testMessage.deliveredMsg != null, is(true));
String deliveredMsgBody = FileUtils.readFileToString(testMessage.deliveredMsg, "UTF-8");
assertThat("Verify content of delivered message", deliveredMsgBody, is(testMessage.body));
@@ -181,27 +181,27 @@ private void verifyMessageDelivery(TestMessage testMessage) throws IOException {
* @throws Exception
*/
private static TestPartner getFromFirstSendingPartnership(OpenAS2Server server) throws Exception {
- PartnershipFactory pf = server.getSession().getPartnershipFactory();
+ PartnershipFactory pf = server.getSession().getPartnershipFactory();
List partnerships = pf.getPartnerships();
for (Iterator iterator = partnerships.iterator(); iterator.hasNext();) {
- Partnership partnership = (Partnership) iterator.next();
+ Partnership partnership = (Partnership) iterator.next();
DirectoryPollingModule pollerModule = getPollingModule((XMLSession) server.getSession(), partnership);
if (pollerModule != null) {
- return new TestPartner(server, partnership, pollerModule);
+ return new TestPartner(server, partnership, pollerModule);
}
}
return null;
}
private static TestPartner getFromPartnerIds(OpenAS2Server server, String senderAs2Id, String receiverAs2Id) throws Exception {
- PartnershipFactory pf = server.getSession().getPartnershipFactory();
+ PartnershipFactory pf = server.getSession().getPartnershipFactory();
List partnerships = pf.getPartnerships();
for (Iterator iterator = partnerships.iterator(); iterator.hasNext();) {
- Partnership partnership = (Partnership) iterator.next();
- if (senderAs2Id.equals(partnership.getSenderID(Partnership.PID_AS2)) && receiverAs2Id.equals(partnership.getReceiverID(Partnership.PID_AS2))) {
- DirectoryPollingModule pollerModule = getPollingModule((XMLSession) server.getSession(), partnership);
- return new TestPartner(server, partnership, pollerModule);
- }
+ Partnership partnership = (Partnership) iterator.next();
+ if (senderAs2Id.equals(partnership.getSenderID(Partnership.PID_AS2)) && receiverAs2Id.equals(partnership.getReceiverID(Partnership.PID_AS2))) {
+ DirectoryPollingModule pollerModule = getPollingModule((XMLSession) server.getSession(), partnership);
+ return new TestPartner(server, partnership, pollerModule);
+ }
}
return null;
}
@@ -209,10 +209,10 @@ private static TestPartner getFromPartnerIds(OpenAS2Server server, String sender
private static DirectoryPollingModule getPollingModule(XMLSession session, Partnership partnership) throws ComponentNotFoundException {
DirectoryPollingModule dirPollMod = session.getPartnershipPoller(partnership.getName());
if (dirPollMod != null) {
- return dirPollMod;
+ return dirPollMod;
}
- // Try to find a module defined poller since there is no matching poller by name. (config.xml defined pollers do not have the correct partnership name in the poller cache)
- return session.getPartnershipPoller(partnership.getSenderID(Partnership.PID_AS2), partnership.getReceiverID(Partnership.PID_AS2));
+ // Try to find a module defined poller since there is no matching poller by name. (config.xml defined pollers do not have the correct partnership name in the poller cache)
+ return session.getPartnershipPoller(partnership.getSenderID(Partnership.PID_AS2), partnership.getReceiverID(Partnership.PID_AS2));
}
@SuppressWarnings("unused")
@@ -229,7 +229,7 @@ private static class TestMessage {
private final String body;
private final TestPartner fromPartner, toPartner;
@SuppressWarnings("unused")
- public File deliveredMsg = null;
+ public File deliveredMsg = null;
private TestMessage(String fileName, String body, TestPartner fromPartner, TestPartner toPartner) {
this.fileName = fileName;
diff --git a/Server/src/test/java/org/openas2/app/RestApiTest.java b/Server/src/test/java/org/openas2/app/RestApiTest.java
index c3e2e591..054555b2 100644
--- a/Server/src/test/java/org/openas2/app/RestApiTest.java
+++ b/Server/src/test/java/org/openas2/app/RestApiTest.java
@@ -50,7 +50,7 @@ public class RestApiTest {
// private static File openAS2AHome;
private static OpenAS2Server serverInstance;
private static String TEST_PARTNER_NAME = "partnerX";
- private static String TEST_PARTNERSHIP_NAME = "partnerX-partnerA";
+ private static String TEST_PARTNERSHIP_NAME = TEST_PARTNER_NAME + "-partnerA";
@TempDir
private static Path scratchpad;
private static CloseableHttpClient httpclient;
@@ -111,7 +111,6 @@ protected CredentialsProvider getCredentials() {
}
protected String doGet(String uriSuffix, boolean withAuth) throws IOException {
- String buffer = "";
HttpGet request = new HttpGet(baseUrl + uriSuffix);
HttpClientBuilder httpClientBuilder = HttpClientBuilder.create();
if (withAuth) {
diff --git a/Server/src/test/java/org/openas2/message/DynamicContentTypeTest.java b/Server/src/test/java/org/openas2/message/DynamicContentTypeTest.java
new file mode 100644
index 00000000..513fac00
--- /dev/null
+++ b/Server/src/test/java/org/openas2/message/DynamicContentTypeTest.java
@@ -0,0 +1,163 @@
+package org.openas2.message;
+
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.openas2.app.BaserServerSetup;
+import org.openas2.partner.Partnership;
+import org.openas2.processor.receiver.DirectoryPollingModule;
+import org.openas2.util.Properties;
+
+import static org.hamcrest.Matchers.is;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.MethodOrderer;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestInstance;
+import org.junit.jupiter.api.TestMethodOrder;
+import org.junit.jupiter.api.TestInstance.Lifecycle;
+import org.junit.jupiter.api.extension.ExtendWith;
+
+@ExtendWith(MockitoExtension.class)
+@TestInstance(Lifecycle.PER_CLASS)
+@TestMethodOrder(MethodOrderer.MethodName.class)
+public class DynamicContentTypeTest extends BaserServerSetup {
+ private DirectoryPollingModule poller;
+
+ public static File systemContentTypesMappingFile;
+ public static File partnershipContentTypesMappingFile;
+
+ private final static String unmappedFileExtension = "data";
+ private final static String xmlFileExtension = "xml";
+ private final static String ediFileExtension = "edi";
+ java.util.Properties contentTypeMap = Properties.getContentTypeMap();
+ private Map systemMappedContentTypes = new HashMap();
+ private Map partnershipMappedContentTypes = new HashMap();
+
+
+ @BeforeAll
+ public void setUp() throws Exception {
+ super.createFileSystemResources();
+ // Set up the system level mappings
+ systemContentTypesMappingFile = new File(tmpDir, "content_type_map.properties");
+ systemMappedContentTypes.put(xmlFileExtension, "application/xml");
+ systemMappedContentTypes.put(ediFileExtension, "application/edifact");
+ systemMappedContentTypes.put("txt", "text/plain");
+ BufferedWriter writer = new BufferedWriter(new FileWriter(systemContentTypesMappingFile));
+ for (Map.Entry entry : systemMappedContentTypes.entrySet()) {
+ writer.write(entry.getKey() + "=" + entry.getValue() + "\n");
+ }
+ writer.close();
+ // Set up the partnership override mappings
+ partnershipContentTypesMappingFile = new File(tmpDir, "override_content_type_map.properties");
+ partnershipMappedContentTypes.put(xmlFileExtension, "application/xml-custom");
+ BufferedWriter writer2 = new BufferedWriter(new FileWriter(partnershipContentTypesMappingFile));
+ for (Map.Entry entry : partnershipMappedContentTypes.entrySet()) {
+ writer2.write(entry.getKey() + "=" + entry.getValue() + "\n");
+ }
+ writer2.close();
+ super.setup();
+ this.poller = session.getPartnershipPoller(msg.getPartnership().getName());
+ }
+
+ @AfterAll
+ public void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ @Test
+ public void a1_shouldHaveNoMappingEnabled() throws Exception {
+ // The default is to not have dynamic mapping so check
+ Partnership myPartnership = msg.getPartnership();
+ assertFalse(myPartnership.isUseDynamicContentTypeLookup(), "Check default is mapping off.");
+ }
+
+
+ @Test
+ public void a2_shouldFailNoMapping() throws Exception {
+ // Make sure that there is an error if no mapping file defined but trying to use mapping
+ Partnership myPartnership = msg.getPartnership();
+ assertThrows(Exception.class, () -> { myPartnership.setUseDynamicContentTypeLookup(true);}, "No config for Content-Type mapping should throw exception.");
+ }
+
+
+ @Test
+ public void b_shouldGetDefaultContentType() throws Exception {
+ // Partnership not set for dynamic mapping so should return system poller default
+ String testFilename = "random." + ediFileExtension;
+ poller.addMessageMetadata(msg, testFilename);
+ assertThat("Check default Content-Type returned when no mapping.", poller.getMessageContentType(msg).matches(Properties.getProperty("pollerConfigBase.mimetype", "FakeValue")), is(true));
+ }
+
+ @Test
+ public void c_shouldGetPartnershipMappedContentTypeWhenNoSystemMapping() throws Exception {
+ // Set the partnership to have dynamic mapping file
+ msg.getPartnership().setAttribute(Partnership.PA_CONTENT_TYPE_MAPPING_FILE, partnershipContentTypesMappingFile.getAbsolutePath());
+ // Force load the partnership mapping
+ msg.getPartnership().setUseDynamicContentTypeLookup(true);
+ // Now check that we get the system property when no override and override when set
+ String testFilename = "random." + ediFileExtension;
+ poller.addMessageMetadata(msg, testFilename);
+ assertThat("Check system default Content-Type returned when not overridden.", poller.getMessageContentType(msg).matches(Properties.getProperty("pollerConfigBase.mimetype", "FakeValue")), is(true));
+ testFilename = "random." + xmlFileExtension;
+ poller.addMessageMetadata(msg, testFilename);
+ assertThat("Check partnership mapped Content-Type returned when partnership mapping setup.", poller.getMessageContentType(msg).matches(partnershipMappedContentTypes.get(xmlFileExtension)), is(true));
+ testFilename = "random." + unmappedFileExtension;
+ poller.addMessageMetadata(msg, testFilename);
+ assertThat("Check system mapped default Content-Type returned when no system or partnership mapping defined.", poller.getMessageContentType(msg).matches(Properties.getProperty("pollerConfigBase.mimetype", "FakeValue")), is(true));
+ }
+
+ @Test
+ public void d_shouldGetSystemMappedContentType() throws Exception {
+ // Append the mapping file property to custom load properties
+ BufferedWriter propsWriter = new BufferedWriter(new FileWriter(super.openAS2PropertiesFile, true));
+ String propVal = systemContentTypesMappingFile.getAbsolutePath().replace("\\", "\\\\");
+ propsWriter.write("\n" + Partnership.PA_CONTENT_TYPE_MAPPING_FILE + "=" + propVal);
+ propsWriter.close();
+ // Now reload the session to get new properties file that then loads system mapping
+ super.refresh();
+ // Force the partnership to have dynamic mapping enabled
+ msg.getPartnership().setUseDynamicContentTypeLookup(true);
+ String testFilename = "random." + ediFileExtension;
+ poller.addMessageMetadata(msg, testFilename);
+ assertThat("Check system mapped Content-Type returned when system mapping setup.", poller.getMessageContentType(msg).matches(systemMappedContentTypes.get(ediFileExtension)), is(true));
+ testFilename = "random." + xmlFileExtension;
+ poller.addMessageMetadata(msg, testFilename);
+ assertThat("Check system mapped Content-Type returned when system mapping setup.", poller.getMessageContentType(msg).matches(systemMappedContentTypes.get(xmlFileExtension)), is(true));
+ testFilename = "random." + unmappedFileExtension;
+ poller.addMessageMetadata(msg, testFilename);
+ assertThat("Check system mapped Content-Type returned when system mapping setup.", poller.getMessageContentType(msg).matches(Properties.getProperty("pollerConfigBase.mimetype", "FakeValue")), is(true));
+ }
+
+ @Test
+ public void e_shouldGetPartnershipOverrideMappedContentType() throws Exception {
+ // Append the property to globally enable dynamic mapping in custom load properties
+ BufferedWriter propsWriter = new BufferedWriter(new FileWriter(super.openAS2PropertiesFile, true));
+ propsWriter.write("\n" + Partnership.PA_USE_DYNAMIC_CONTENT_TYPE_MAPPING + "=true");
+ propsWriter.close();
+ // Now reload the session to get new properties file that then loads system mapping
+ super.refresh();
+ // Set the partnership to have dynamic mapping file
+ msg.getPartnership().setAttribute(Partnership.PA_CONTENT_TYPE_MAPPING_FILE, partnershipContentTypesMappingFile.getAbsolutePath());
+ // Force load the override
+ msg.getPartnership().setUseDynamicContentTypeLookup(true);
+ // Now check that we get the system property when no override and override when set
+ String testFilename = "random." + ediFileExtension;
+ poller.addMessageMetadata(msg, testFilename);
+ assertThat("Check system mapped Content-Type returned when not overridden.", poller.getMessageContentType(msg).matches(systemMappedContentTypes.get(ediFileExtension)), is(true));
+ testFilename = "random." + xmlFileExtension;
+ poller.addMessageMetadata(msg, testFilename);
+ assertThat("Check partnership mapped Content-Type returned when partnership mapping setup.", poller.getMessageContentType(msg).matches(partnershipMappedContentTypes.get(xmlFileExtension)), is(true));
+ testFilename = "random." + unmappedFileExtension;
+ poller.addMessageMetadata(msg, testFilename);
+ assertThat("Check system mapped default Content-Type returned when no system or partnership mapping defined.", poller.getMessageContentType(msg).matches(Properties.getProperty("pollerConfigBase.mimetype", "FakeValue")), is(true));
+ }
+}
diff --git a/Server/src/test/java/org/openas2/util/IOUtilTest.java b/Server/src/test/java/org/openas2/util/IOUtilTest.java
index 62e0a489..d128c8a4 100644
--- a/Server/src/test/java/org/openas2/util/IOUtilTest.java
+++ b/Server/src/test/java/org/openas2/util/IOUtilTest.java
@@ -56,7 +56,7 @@ public Record(String filename, String format, String delimiters, boolean mergeEx
};
/* Test records for polling filters
- * Format is , , "excluded extensions list>, , , "excluded extensions list>,
+
+
diff --git a/Server/src/test/resources/SingleServerTest/MyCompany/config/config.xml b/Server/src/test/resources/SingleServerTest/MyCompany/config/config.xml
index ed9927a8..baa92e51 100644
--- a/Server/src/test/resources/SingleServerTest/MyCompany/config/config.xml
+++ b/Server/src/test/resources/SingleServerTest/MyCompany/config/config.xml
@@ -28,8 +28,8 @@
module.AS2MDNReceiverModule.https.enabled="false"
module.HealthCheckModule.enabled="false"
partnership_file="%home%/partnerships.xml"
- pollerConfigBase.outboxdir="$properties.storageBaseDir$/outbox/$partnership.receiver.as2_id$"
- pollerConfigBase.errordir="$properties.storageBaseDir$/outbox/error/$date.YYYY$-$date.MM$-$date.dd$/$partnership.receiver.as2_id$"
+ pollerConfigBase.outboxdir="$properties.storageBaseDir$/outbox/$partnership.sender.as2_id$/$partnership.receiver.as2_id$"
+ pollerConfigBase.errordir="$properties.storageBaseDir$/outbox/$partnership.sender.as2_id$/error/$date.YYYY$-$date.MM$-$date.dd$/$partnership.receiver.as2_id$"
pollerConfigBase.interval="5"
pollerConfigBase.defaults="sender.as2_id=$partnership.sender.as2_id$, receiver.as2_id=$partnership.receiver.as2_id$"
pollerConfigBase.sendfilename="true"
@@ -104,18 +104,7 @@
sendfilename="true"
format="sender.as2_id, receiver.as2_id, attributes.filename"
mimetype="application/EDI-X12"/>
-
-
-
+
+
diff --git a/changes.txt b/changes.txt
index 51658195..cea151fb 100644
--- a/changes.txt
+++ b/changes.txt
@@ -1,3 +1,9 @@
+Version 3.8.0 - 2023-11-07
+This is an enhancement and minor bugfix release:
+ **IMPORTANT NOTE**: Please review upgrade notes in the RELEASE-NOTES.md if you are upgrading
+
+ 1. Support for configurable dynamic Content-Type based on the file extension. See documentation section 7.5 "Setting Content Type"
+
Version 3.7.0 - 2023-09-12
This is an enhancement and minor bugfix release:
**IMPORTANT NOTE**: Please review upgrade notes in the RELEASE-NOTES.md if you are upgrading
diff --git a/docs/OpenAS2HowTo.odt b/docs/OpenAS2HowTo.odt
index dc20f5cd..113d69f9 100644
Binary files a/docs/OpenAS2HowTo.odt and b/docs/OpenAS2HowTo.odt differ
diff --git a/docs/OpenAS2HowTo.pdf b/docs/OpenAS2HowTo.pdf
index be39fe55..186ef574 100644
Binary files a/docs/OpenAS2HowTo.pdf and b/docs/OpenAS2HowTo.pdf differ
diff --git a/pom.xml b/pom.xml
index 937eac2a..4958892b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
4.0.0
net.sf.openas2
OpenAS2
- 3.7.0
+ 3.8.0
OpenAS2
pom
@@ -82,7 +82,7 @@
commons-cli
commons-cli
- 1.5.0
+ 1.6.0
commons-logging
@@ -97,7 +97,7 @@
com.h2database
h2
- 2.2.222
+ 2.2.224
@@ -148,7 +148,7 @@
commons-io
commons-io
- 2.13.0
+ 2.15.0
@@ -171,13 +171,13 @@
com.fasterxml.jackson.core
jackson-databind
- 2.15.2
+ 2.15.3
jar
com.fasterxml.jackson.module
jackson-module-jaxb-annotations
- 2.15.2
+ 2.15.3
org.glassfish.jersey.media
@@ -199,17 +199,17 @@
com.sun.xml.bind
jaxb-core
- 4.0.3
+ 4.0.4
com.sun.xml.bind
jaxb-impl
- 4.0.3
+ 4.0.4
io.sentry
sentry
- 6.28.0
+ 6.33.0