Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions RELEASE-NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ This is a minor enhancement and bugfix release.
6. Fix start-openas2.bat for logging directory setting.
7. Add poller configuration to API command for partnership.
8. Change the IOUtil moveFile method to a more intelligent algorithm for non-homogeneous moves.
9. Add DbPartnershipFactory: partnerships can optionally be stored in a database (Azure SQL, PostgreSQL, MySQL, Oracle or the embedded H2) instead of the partnerships XML file. See the commented example in config.xml. The required tables are included in db_ddl.sql and openas2-schema.xml and are only needed when using the database partnership store.

## Upgrade Notes
See the openAS2HowTo appendix for the general process on upgrading OpenAS2.
Expand Down
14 changes: 14 additions & 0 deletions Server/src/config/config.xml
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,20 @@
<partnerships classname="org.openas2.partner.XMLPartnershipFactory"
filename="$properties.partnership_file$"
interval="$properties.partnerships.polling.interval$"/>
<!-- Alternatively partnerships can be loaded (read-only) from a database using the
partner/partner_attribute/partnership/partnership_attribute tables
(see the db_ddl.sql and resources/db/openas2-schema.xml files for the table definitions).
Any database with a JDBC driver is supported using the same connection parameters as the
DbTrackingModule. Partnerships are reloaded by the "refresh" console command and, if
"interval" is set to a value in seconds, on a fixed schedule. Example for Azure SQL:
<partnerships classname="org.openas2.partner.DbPartnershipFactory"
db_user="openas2"
db_pwd="OpenAS2"
jdbc_connect_string="jdbc:sqlserver://myserver.database.windows.net:1433;database=openas2;encrypt=true"
jdbc_driver="com.microsoft.sqlserver.jdbc.SQLServerDriver"
force_load_jdbc_driver="true"
interval="600"/>
-->

<messages enabled="$properties.messages.enabled$"
classname="org.openas2.message.XMLMessageFactory"
Expand Down
61 changes: 61 additions & 0 deletions Server/src/config/db_ddl.sql
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,64 @@ CREATE TABLE msg_metadata
);

CREATE UNIQUE INDEX MSG_ID_UNIQUE ON msg_metadata (MSG_ID);

-- -----------------------------------------------------------------------
-- partner / partnership tables used by org.openas2.partner.DbPartnershipFactory
-- to load partnerships from a database instead of the partnerships XML file.
-- partnership_attribute.CATEGORY is 'attribute' for regular partnership
-- attributes, 'sender'/'receiver' for per-partnership overrides applied on
-- top of the linked partner's attributes or 'pollerConfig' for the attributes
-- of a partnership specific directory poller.
-- -----------------------------------------------------------------------

DROP TABLE partnership_attribute IF EXISTS;
DROP TABLE partnership IF EXISTS;
DROP TABLE partner_attribute IF EXISTS;
DROP TABLE partner IF EXISTS;

CREATE TABLE partner
(
ID INTEGER NOT NULL AUTO_INCREMENT,
NAME VARCHAR(255) NOT NULL,
PRIMARY KEY (ID)
);

CREATE UNIQUE INDEX PARTNER_NAME_UNIQUE ON partner (NAME);

CREATE TABLE partner_attribute
(
ID INTEGER NOT NULL AUTO_INCREMENT,
PARTNER_ID INTEGER NOT NULL,
ATTRIBUTE_NAME VARCHAR(255) NOT NULL,
ATTRIBUTE_VALUE VARCHAR(4000),
PRIMARY KEY (ID),
FOREIGN KEY (PARTNER_ID) REFERENCES partner (ID)
);

CREATE UNIQUE INDEX PARTNER_ATTRIBUTE_UNIQUE ON partner_attribute (PARTNER_ID, ATTRIBUTE_NAME);

CREATE TABLE partnership
(
ID INTEGER NOT NULL AUTO_INCREMENT,
NAME VARCHAR(255) NOT NULL,
SENDER_PARTNER_ID INTEGER NOT NULL,
RECEIVER_PARTNER_ID INTEGER NOT NULL,
PRIMARY KEY (ID),
FOREIGN KEY (SENDER_PARTNER_ID) REFERENCES partner (ID),
FOREIGN KEY (RECEIVER_PARTNER_ID) REFERENCES partner (ID)
);

CREATE UNIQUE INDEX PARTNERSHIP_NAME_UNIQUE ON partnership (NAME);

CREATE TABLE partnership_attribute
(
ID INTEGER NOT NULL AUTO_INCREMENT,
PARTNERSHIP_ID INTEGER NOT NULL,
CATEGORY VARCHAR(20) DEFAULT 'attribute' NOT NULL,
ATTRIBUTE_NAME VARCHAR(255) NOT NULL,
ATTRIBUTE_VALUE VARCHAR(4000),
PRIMARY KEY (ID),
FOREIGN KEY (PARTNERSHIP_ID) REFERENCES partnership (ID)
);

CREATE UNIQUE INDEX PARTNERSHIP_ATTRIBUTE_UNIQUE ON partnership_attribute (PARTNERSHIP_ID, CATEGORY, ATTRIBUTE_NAME);
43 changes: 28 additions & 15 deletions Server/src/main/java/org/openas2/app/partner/AddPartnerCommand.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import org.openas2.OpenAS2Exception;
import org.openas2.cmd.CommandResult;
import org.openas2.partner.DbPartnershipFactory;
import org.openas2.partner.PartnershipFactory;
import org.openas2.partner.XMLPartnershipFactory;
import org.w3c.dom.Document;
Expand All @@ -12,6 +13,9 @@
import javax.xml.parsers.FactoryConfigurationError;
import javax.xml.parsers.ParserConfigurationException;

import java.util.LinkedHashMap;
import java.util.Map;

/**
* adds a new partner entry in partnership store
*
Expand All @@ -36,6 +40,28 @@ public CommandResult execute(PartnershipFactory partFx, Object[] params) throws
}

synchronized (partFx) {
Map<String, String> attributes = new LinkedHashMap<String, String>();
for (int i = 0; i < params.length; i++) {
String param = (String) params[i];
int pos = param.indexOf('=');
if (i == 0) {
attributes.put("name", param);
} else if (pos == 0) {
return new CommandResult(CommandResult.TYPE_ERROR, "incoming parameter missing name");
} else if (pos > 0) {
attributes.put(param.substring(0, pos), param.substring(pos + 1));
} else {
return new CommandResult(CommandResult.TYPE_ERROR, "incoming parameter missing value");
}
}

if (partFx instanceof DbPartnershipFactory) {
((DbPartnershipFactory) partFx).addPartner(attributes);
return new CommandResult(CommandResult.TYPE_OK);
}
if (!(partFx instanceof XMLPartnershipFactory)) {
return new CommandResult(CommandResult.TYPE_COMMAND_NOT_SUPPORTED, "Not supported by current partnership store");
}

DocumentBuilder db = null;
try {
Expand All @@ -50,21 +76,8 @@ public CommandResult execute(PartnershipFactory partFx, Object[] params) throws

Element partnerRoot = doc.createElement("partner");
doc.appendChild(partnerRoot);

for (int i = 0; i < params.length; i++) {
String param = (String) params[i];
int pos = param.indexOf('=');
if (i == 0) {
partnerRoot.setAttribute("name", param);
} else if (pos == 0) {
return new CommandResult(CommandResult.TYPE_ERROR, "incoming parameter missing name");
} else if (pos > 0) {
partnerRoot.setAttribute(param.substring(0, pos), param.substring(pos + 1));

} else {
return new CommandResult(CommandResult.TYPE_ERROR, "incoming parameter missing value");
}

for (Map.Entry<String, String> attribute : attributes.entrySet()) {
partnerRoot.setAttribute(attribute.getKey(), attribute.getValue());
}

((XMLPartnershipFactory) partFx).loadPartner(partFx.getPartners(), partnerRoot);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import org.slf4j.LoggerFactory;
import org.openas2.OpenAS2Exception;
import org.openas2.cmd.CommandResult;
import org.openas2.partner.DbPartnershipFactory;
import org.openas2.partner.Partnership;
import org.openas2.partner.PartnershipFactory;
import org.openas2.partner.XMLPartnershipFactory;
Expand All @@ -12,6 +13,8 @@
import org.w3c.dom.Document;
import org.w3c.dom.Element;

import java.util.LinkedHashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

Expand Down Expand Up @@ -43,6 +46,29 @@ public CommandResult execute(PartnershipFactory partFx, Object[] params) throws
}

synchronized (partFx) {
if (partFx instanceof DbPartnershipFactory) {
Map<String, String> attributes = new LinkedHashMap<String, String>();
Map<String, String> pollerConfig = new LinkedHashMap<String, String>();
for (int i = 3; i < params.length; i++) {
String param = (String) params[i];
int equalsPos = param.indexOf('=');
if (equalsPos == 0) {
return new CommandResult(CommandResult.TYPE_ERROR, "incoming parameter missing name");
} else if (equalsPos < 0) {
return new CommandResult(CommandResult.TYPE_ERROR, "incoming parameter missing value");
}
if (param.startsWith("pollerConfig.")) {
pollerConfig.put(param.substring("pollerConfig.".length(), equalsPos), param.substring(equalsPos + 1));
} else {
attributes.put(param.substring(0, equalsPos), param.substring(equalsPos + 1));
}
}
((DbPartnershipFactory) partFx).addPartnership((String) params[0], (String) params[1], (String) params[2], attributes, pollerConfig.isEmpty() ? null : pollerConfig);
return new CommandResult(CommandResult.TYPE_OK);
}
if (!(partFx instanceof XMLPartnershipFactory)) {
return new CommandResult(CommandResult.TYPE_COMMAND_NOT_SUPPORTED, "Not supported by current partnership store");
}
Document doc;
try {
doc = XMLUtil.createDoc(null);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import org.openas2.OpenAS2Exception;
import org.openas2.cmd.CommandResult;
import org.openas2.partner.DbPartnershipFactory;
import org.openas2.partner.Partnership;
import org.openas2.partner.PartnershipFactory;
import org.openas2.partner.XMLPartnershipFactory;
Expand Down Expand Up @@ -34,6 +35,16 @@ public CommandResult execute(PartnershipFactory partFx, Object[] params) throws
synchronized (partFx) {

String name = params[0].toString();

if (partFx instanceof DbPartnershipFactory) {
try {
((DbPartnershipFactory) partFx).deletePartner(name);
} catch (OpenAS2Exception e) {
return new CommandResult(CommandResult.TYPE_ERROR, e.getMessage());
}
return new CommandResult(CommandResult.TYPE_OK);
}

Iterator<String> parts = partFx.getPartners().keySet().iterator();

boolean found = false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import org.openas2.OpenAS2Exception;
import org.openas2.cmd.CommandResult;
import org.openas2.partner.DbPartnershipFactory;
import org.openas2.partner.Partnership;
import org.openas2.partner.PartnershipFactory;
import org.openas2.partner.XMLPartnershipFactory;
Expand Down Expand Up @@ -34,6 +35,16 @@ public CommandResult execute(PartnershipFactory partFx, Object[] params) throws
synchronized (partFx) {

String name = params[0].toString();

if (partFx instanceof DbPartnershipFactory) {
try {
((DbPartnershipFactory) partFx).deletePartnership(name);
} catch (OpenAS2Exception e) {
return new CommandResult(CommandResult.TYPE_ERROR, e.getMessage());
}
return new CommandResult(CommandResult.TYPE_OK);
}

Iterator<Partnership> parts = partFx.getPartnerships().iterator();

while (parts.hasNext()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import org.openas2.OpenAS2Exception;
import org.openas2.cmd.BaseCommand;
import org.openas2.cmd.CommandResult;
import org.openas2.partner.DbPartnershipFactory;
import org.openas2.partner.PartnershipFactory;
import org.openas2.partner.XMLPartnershipFactory;

Expand Down Expand Up @@ -35,6 +36,9 @@ public CommandResult execute(Object[] params) {

return new CommandResult(CommandResult.TYPE_OK, "Stored partnerships");
}
if (partnerFx instanceof DbPartnershipFactory) {
return new CommandResult(CommandResult.TYPE_OK, "The database partnership store persists changes immediately so there is nothing to do");
}
return new CommandResult(CommandResult.TYPE_COMMAND_NOT_SUPPORTED, "Not supported by current partnership store, must be XML");
}
} catch (OpenAS2Exception oae) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,23 @@
package org.openas2.partner;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.openas2.BaseComponent;
import org.openas2.OpenAS2Exception;
import org.openas2.message.FileAttribute;
import org.openas2.message.Message;
import org.openas2.message.MessageMDN;
import org.openas2.params.MessageParameters;
import org.openas2.params.ParameterParser;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;

import java.util.ArrayList;
import java.util.Iterator;
Expand All @@ -18,6 +29,8 @@
public abstract class BasePartnershipFactory extends BaseComponent implements PartnershipFactory {
private List<Partnership> partnerships;

private Logger baseLogger = LoggerFactory.getLogger(BasePartnershipFactory.class);

public Partnership getPartnership(Partnership p, boolean reverseLookup) throws OpenAS2Exception {
Partnership ps = (p.getName() == null) ? null : getPartnership(p.getName());

Expand Down Expand Up @@ -144,6 +157,77 @@ protected Partnership getPartnership(String name) throws OpenAS2Exception {
return getPartnership(getPartnerships(), name);
}

public void replacePartnershipPlaceHolders(Document doc, Partnership partnership) throws OpenAS2Exception {
String xpathExpression = "//*[@*[contains(.,'$partnership.')]]/@*";
// Create XPathFactory object
XPathFactory xpathFactory = XPathFactory.newInstance();
// Create XPath object
XPath xpath = xpathFactory.newXPath();
try {
// Create XPathExpression object
XPathExpression expr = xpath.compile(xpathExpression);
// Evaluate expression result on XML document
NodeList nodes = (NodeList) expr.evaluate(doc, XPathConstants.NODESET);
Pattern PATTERN = Pattern.compile("\\$partnership\\.([^\\$]++)\\$");

for (int i = 0; i < nodes.getLength(); i++) {
Node node = nodes.item(i);
String val = node.getNodeValue();
//logger.debug("Partnership place holder NODE processing: " + val);
StringBuffer strBuf = new StringBuffer();
Matcher matcher = PATTERN.matcher(val);
boolean hasChanged = false;
while (matcher.find()) {
String value = null;
String[] keys = matcher.group(1).split("\\.");
if (keys.length == 1) {
switch (keys[0]) {
case "name":
value = partnership.getName();
break;
default:
throw new OpenAS2Exception(
"The partnership placeholder cannot be resolved: " + keys[0] + " in " + val);
}
} else if (keys.length == 2) {
switch (keys[0]) {
case "receiver":
value = partnership.getReceiverID(keys[1]);
break;
case "sender":
value = partnership.getSenderID(keys[1]);
break;
default:
throw new OpenAS2Exception(
"The partnership placeholder cannot be resolved: " + keys[0] + " in " + val);
}
} else {
// don't know how to handle this
throw new OpenAS2Exception(
"The partnership placeholder is invalid and cannot be parsed: " + val);
}
if (value == null) {
throw new OpenAS2Exception(
"Missing attribute value for replacement: " + matcher.group() + " in " + val);
} else {
hasChanged = true;
matcher.appendReplacement(strBuf, Matcher.quoteReplacement(value));
if (baseLogger.isTraceEnabled()) {
baseLogger.trace("Partnership place holder replaced: " + keys + " :: Replaced with: " + value);
}
}
}
if (hasChanged) {
matcher.appendTail(strBuf);
node.setNodeValue(strBuf.toString());
}
}

} catch (XPathExpressionException e) {
e.printStackTrace();
}
}

// returns true if all values in searchIds match values in partnerIds
protected boolean compareMap(Map<String, Object> searchIds, Map<String, Object> partnerIds) {
Iterator<Map.Entry<String, Object>> searchIt = searchIds.entrySet().iterator();
Expand Down
Loading
Loading