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
107 changes: 93 additions & 14 deletions Server/src/main/java/org/openas2/partner/XMLPartnershipFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -110,35 +110,57 @@ void loadPartnershipsFile() throws OpenAS2Exception {
}

void refreshConfig() throws OpenAS2Exception {
getSession().destroyPartnershipPollers(Session.PARTNERSHIP_POLLER);
Map<String, Object> newPartners = new HashMap<String, Object>();
List<Partnership> newPartnerships = new ArrayList<Partnership>();
/* Parse the whole file before touching the running pollers. Anything that goes wrong
* parsing then leaves the currently running configuration untouched rather than killing
* the pollers and aborting part way through the reload.
*/
List<Node> pollerNodes = new ArrayList<Node>();
List<Partnership> pollerPartnerships = new ArrayList<Partnership>();
try {
Element root = getPartnershipsXml().getDocumentElement();
NodeList rootNodes = root.getChildNodes();
Node rootNode;
String nodeName;

Map<String, Object> newPartners = new HashMap<String, Object>();
List<Partnership> newPartnerships = new ArrayList<Partnership>();

for (int i = 0; i < rootNodes.getLength(); i++) {
rootNode = rootNodes.item(i);

nodeName = rootNode.getNodeName();

if (nodeName.equals("partner")) {
loadPartner(newPartners, rootNode);
loadPartner(newPartners, rootNode, false);
} else if (nodeName.equals("partnership")) {
loadPartnership(newPartners, newPartnerships, rootNode);
Partnership partnership = loadPartnership(newPartners, newPartnerships, rootNode, false);
if (partnership != null) {
pollerNodes.add(rootNode);
pollerPartnerships.add(partnership);
}
}
}

synchronized (this) {
setPartners(newPartners);
setPartnerships(newPartnerships);
}
} catch (Exception e) {
throw new WrappedException(e);
}

synchronized (this) {
setPartners(newPartners);
setPartnerships(newPartnerships);
}

// The parsed config is good so now swap the pollers over to the new configuration
getSession().destroyPartnershipPollers(Session.PARTNERSHIP_POLLER);
for (int i = 0; i < pollerNodes.size(); i++) {
Partnership partnership = pollerPartnerships.get(i);
try {
setupPartnershipPoller(pollerNodes.get(i), partnership);
} catch (Exception e) {
/* A poller that cannot be configured must not prevent the rest of the partnerships
* from having their pollers started so report it and keep going.
*/
logger.error("Failed to configure the directory poller for partnership " + partnership.getName() + ": " + org.openas2.util.Logging.getExceptionMsg(e), e);
}
}
}

private void loadAttributes(Node node, Partnership partnership) throws OpenAS2Exception {
Expand All @@ -149,13 +171,31 @@ private void loadAttributes(Node node, Partnership partnership) throws OpenAS2Ex
}

public void loadPartner(Map<String, Object> partners, Node node) throws OpenAS2Exception {
loadPartner(partners, node, true);
}

/**
* Load a partner definition into the passed map of partners.
*
* @param partners - the map of partners the loaded partner is added to
* @param node - the XML node containing the partner definition
* @param failOnDuplicate - if true a partner that is already defined causes an exception,
* otherwise the duplicate is logged as an error and ignored so that
* the first definition found remains in effect
* @throws OpenAS2Exception - the partner definition could not be loaded
*/
public void loadPartner(Map<String, Object> partners, Node node, boolean failOnDuplicate) throws OpenAS2Exception {
String[] requiredAttributes = {Partnership.PID_NAME};

Map<String, String> newPartner = XMLUtil.mapAttributes(node, requiredAttributes);
String name = newPartner.get(Partnership.PID_NAME);

if (partners.get(name) != null) {
throw new OpenAS2Exception("Partner is defined more than once: " + name);
if (failOnDuplicate) {
throw new OpenAS2Exception("Partner is defined more than once: " + name);
}
logger.error("Partner is defined more than once in the partnerships file so the duplicate definition is ignored and the first one found is used: " + name);
return;
}

partners.put(name, newPartner);
Expand Down Expand Up @@ -191,14 +231,39 @@ private void loadPartnerIDs(Map<String, Object> partners, String partnershipName
}

public void loadPartnership(Map<String, Object> partners, List<Partnership> partnerships, Node node) throws OpenAS2Exception {
Partnership partnership = loadPartnership(partners, partnerships, node, true);
if (partnership != null) {
setupPartnershipPoller(node, partnership);
}
}

/**
* Load a partnership definition into the passed list of partnerships. This only parses the
* partnership. Use {@link #setupPartnershipPoller(Node, Partnership)} to activate any directory
* poller configured for it.
*
* @param partners - the map of partners the partnership sender and receiver are looked up in
* @param partnerships - the list of partnerships the loaded partnership is added to
* @param node - the XML node containing the partnership definition
* @param failOnDuplicate - if true a partnership that is already defined causes an exception,
* otherwise the duplicate is logged as an error and ignored so that
* the first definition found remains in effect
* @return the loaded partnership or null if it was ignored as a duplicate
* @throws OpenAS2Exception - the partnership definition could not be loaded
*/
public Partnership loadPartnership(Map<String, Object> partners, List<Partnership> partnerships, Node node, boolean failOnDuplicate) throws OpenAS2Exception {
Partnership partnership = new Partnership();
String[] requiredAttributes = {"name"};

Map<String, String> psAttributes = XMLUtil.mapAttributes(node, requiredAttributes);
String name = psAttributes.get("name");

if (getPartnership(partnerships, name) != null) {
throw new OpenAS2Exception("Partnership is defined more than once: " + name);
if (failOnDuplicate) {
throw new OpenAS2Exception("Partnership is defined more than once: " + name);
}
logger.error("Partnership is defined more than once in the partnerships file so the duplicate definition is ignored and the first one found is used: " + name);
return null;
}

partnership.setName(name);
Expand All @@ -220,7 +285,21 @@ public void loadPartnership(Map<String, Object> partners, List<Partnership> part
}
// add the partnership to the list of available partnerships
partnerships.add(partnership);


return partnership;
}

/**
* Activate the directory polling module configured for a partnership, if there is one.
* Any poller currently running for the partnership must have been destroyed before calling
* this otherwise the polled directory will be rejected as already in use.
*
* @param node - the XML node containing the partnership definition
* @param partnership - the partnership the poller belongs to
* @throws OpenAS2Exception - the poller could not be configured
*/
public void setupPartnershipPoller(Node node, Partnership partnership) throws OpenAS2Exception {
String name = partnership.getName();
// Now check if we need to add a directory polling module
Node pollerCfgNode = XMLUtil.findChildNode(node, Partnership.PCFG_POLLER);
if (pollerCfgNode != null) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
package org.openas2.partner;

import org.junit.jupiter.api.Test;
import org.openas2.OpenAS2Exception;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

import javax.xml.parsers.DocumentBuilderFactory;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;

/**
* Verifies how duplicate definitions in the partnerships file are handled. A duplicate must not
* abort loading the file since that leaves the system unable to send anything, but the commands
* that add a partner or partnership must still reject one that already exists.
*/
public class XMLPartnershipFactoryTest {

private final Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();

public XMLPartnershipFactoryTest() throws Exception {
}

private Element partnerNode(String name, String as2Id) {
Element partner = doc.createElement("partner");
partner.setAttribute(Partnership.PID_NAME, name);
partner.setAttribute(Partnership.PID_AS2, as2Id);
return partner;
}

private Element partnershipNode(String name, String senderName, String receiverName, String url) {
Element partnership = doc.createElement("partnership");
partnership.setAttribute("name", name);
Element sender = doc.createElement(Partnership.PTYPE_SENDER);
sender.setAttribute(Partnership.PID_NAME, senderName);
partnership.appendChild(sender);
Element receiver = doc.createElement(Partnership.PTYPE_RECEIVER);
receiver.setAttribute(Partnership.PID_NAME, receiverName);
partnership.appendChild(receiver);
Element attribute = doc.createElement("attribute");
attribute.setAttribute("name", Partnership.PA_AS2_URL);
attribute.setAttribute("value", url);
partnership.appendChild(attribute);
return partnership;
}

private Map<String, Object> partners() throws OpenAS2Exception {
XMLPartnershipFactory factory = new XMLPartnershipFactory();
Map<String, Object> partners = new HashMap<String, Object>();
factory.loadPartner(partners, partnerNode("MyCompany", "MyCompany_OID"), false);
factory.loadPartner(partners, partnerNode("PartnerA", "PartnerA_OID"), false);
return partners;
}

@Test
public void duplicatePartnershipIsIgnoredAndTheFirstOneIsUsed() throws Exception {
XMLPartnershipFactory factory = new XMLPartnershipFactory();
Map<String, Object> partners = partners();
List<Partnership> partnerships = new ArrayList<Partnership>();

assertNotNull(factory.loadPartnership(partners, partnerships,
partnershipNode("MyCompany-to-PartnerA", "MyCompany", "PartnerA", "http://first:10080"), false));
assertNull(factory.loadPartnership(partners, partnerships,
partnershipNode("MyCompany-to-PartnerA", "MyCompany", "PartnerA", "http://second:10080"), false),
"the duplicate partnership must be reported as skipped");

assertEquals(1, partnerships.size(), "the duplicate must not be added to the partnership list");
assertEquals("http://first:10080", partnerships.get(0).getAttribute(Partnership.PA_AS2_URL),
"the first definition found must remain in effect");
}

@Test
public void duplicatePartnerIsIgnoredAndTheFirstOneIsUsed() throws Exception {
XMLPartnershipFactory factory = new XMLPartnershipFactory();
Map<String, Object> partners = new HashMap<String, Object>();

factory.loadPartner(partners, partnerNode("PartnerA", "PartnerA_OID"), false);
factory.loadPartner(partners, partnerNode("PartnerA", "SomeOtherOID"), false);

assertEquals(1, partners.size());
@SuppressWarnings("unchecked")
Map<String, Object> partner = (Map<String, Object>) partners.get("PartnerA");
assertEquals("PartnerA_OID", partner.get(Partnership.PID_AS2),
"the first definition found must remain in effect");
}

@Test
public void addingAnAlreadyDefinedPartnershipStillFails() throws Exception {
XMLPartnershipFactory factory = new XMLPartnershipFactory();
Map<String, Object> partners = partners();
List<Partnership> partnerships = new ArrayList<Partnership>();

factory.loadPartnership(partners, partnerships,
partnershipNode("MyCompany-to-PartnerA", "MyCompany", "PartnerA", "http://first:10080"));

assertThrows(OpenAS2Exception.class, () -> factory.loadPartnership(partners, partnerships,
partnershipNode("MyCompany-to-PartnerA", "MyCompany", "PartnerA", "http://second:10080")));
}

@Test
public void addingAnAlreadyDefinedPartnerStillFails() throws Exception {
XMLPartnershipFactory factory = new XMLPartnershipFactory();
Map<String, Object> partners = new HashMap<String, Object>();

factory.loadPartner(partners, partnerNode("PartnerA", "PartnerA_OID"));

assertThrows(OpenAS2Exception.class,
() -> factory.loadPartner(partners, partnerNode("PartnerA", "SomeOtherOID")));
}
}
Loading