/** * Copyright (c) 2009-2010 Misys Open Source Solutions (MOSS) and others * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. See the License for the specific language governing * permissions and limitations under the License. * * Contributors: * Misys Open Source Solutions - initial API and implementation * - */ package org.openhealthtools.openxds.xca; import gov.nist.registry.common2.exception.XdsException; import gov.nist.registry.common2.exception.XdsInternalException; import gov.nist.registry.common2.registry.MetadataSupport; import gov.nist.registry.common2.registry.RegistryErrorList; import gov.nist.registry.common2.registry.Response; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.axiom.om.OMElement; import org.apache.axiom.om.xpath.AXIOMXPath; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jaxen.JaxenException; import org.openhealthtools.openexchange.config.PropertyFacade; import org.openhealthtools.openxds.log.LogMessage; import org.openhealthtools.openxds.log.LoggerException; /** * * @author <a href="mailto:wenzhi.li@misys.com">Wenzhi Li</a> */ public abstract class Aggregator { private static final Log log = LogFactory.getLog( Aggregator.class ); /**The home IDs whose response is still pending*/ protected Collection<String> pendingHomeIds = new ArrayList<String>(); /** The total number of requests associated with this Aggregator */ protected int totalNumber; /** The number of requests whose results are available */ protected int availableNumber; /** The number of requests whose results are failed to retrieve */ protected int failureNumber; /**The max wait time in millisecond before aggregation*/ private static int timeout = PropertyFacade.getInteger("ig.timeout", 15000); /**The LogMessage*/ protected LogMessage logMessage; /**The map to store the results from each home community Map<String(homeId), OMElement(result)> */ protected Map<String, OMElement> results = Collections.synchronizedMap(new HashMap<String, OMElement>()); /**RegistryErrorList object to store errors*/ protected RegistryErrorList rel; /**The final response */ protected Response response; /** * @param requestHomeIds the collection of request home ids */ Aggregator(final Collection<String> requestHomeIds, LogMessage logMessage) throws XdsException { for (String homeId : requestHomeIds) { this.pendingHomeIds.add(homeId); } this.totalNumber = requestHomeIds.size(); this.availableNumber = 0; this.failureNumber = 0; this.logMessage = logMessage; rel = new RegistryErrorList(RegistryErrorList.version_3, true /* log */); } public synchronized void waitForAll() throws XdsInternalException, LoggerException { long start = System.currentTimeMillis(); while( this.availableNumber + this.failureNumber < this.totalNumber ){ if (log.isDebugEnabled()) { log.debug("available/failed/total - " + this.availableNumber + "/" + this.failureNumber + "/" + this.totalNumber + ", put to sleep"); } long now = System.currentTimeMillis(); if ((now-start) > timeout ) { for (String homeId : pendingHomeIds){ String msg = "Request stoped after waiting for "+ timeout / 1000 +" seconds. Home Community "+ homeId +" did not respond."; response.add_error( MetadataSupport.XDSUnavailableCommunity, msg, homeId, logMessage); } break; //stop after for maxWait. } try { this.wait(3000); //by default, check when interrupted or every 3 seconds } catch (InterruptedException e) {} } aggregate(); } private OMElement aggregate() throws XdsInternalException, LoggerException { for (String homeId : results.keySet()) { OMElement result = results.get(homeId); logMessage.addOtherParam("Response from " + homeId, result.toString()) ; String error = checkNullResult( result ); if (error != null) { rel.add_error(MetadataSupport.XDSRegistryError, error + " from Responding Gateway " + homeId , homeId, logMessage); continue; } OMElement responseElemWithStatus = getResponseElementWithStatus(result); String status = responseElemWithStatus.getAttributeValue(MetadataSupport.status_qname); //Add success result if ( !status.equals(MetadataSupport.response_status_type_namespace + "Failure")) { response.setHasSuccess(); addResult(result, homeId); } //Add error and/or warning OMElement registry_error_list = MetadataSupport.firstChildWithLocalName(responseElemWithStatus, "RegistryErrorList"); if (registry_error_list != null) { //Add the location attribute setHomeAsLocation(registry_error_list, homeId); rel.addRegistryErrorList(registry_error_list, logMessage); } }//for return response.getResponse(); } private void setHomeAsLocation(OMElement registryErrorList, String homeId) { String reXPath = "//*[local-name()='RegistryError']"; try { AXIOMXPath xpathExpression = new AXIOMXPath (reXPath); List<?> nodes = xpathExpression.selectNodes(registryErrorList); for (OMElement node : (List<OMElement>) nodes) { node.addAttribute("location", homeId, null); } } catch (JaxenException e) { log.error("Failed to add the location" + e.getMessage(), e); } } abstract protected String checkNullResult(OMElement result); abstract protected OMElement getResponseElementWithStatus(OMElement result); abstract protected void addResult(OMElement result, String homeId) throws XdsInternalException; public Response getResponse() { return this.response; } //Store the retrieved data from a responding gateway synchronized void store( String homeId, OMElement result ) { if (log.isDebugEnabled()) { log.debug("store result from homeId=" + homeId + ", result=" + result ); } results.put(homeId, result); pendingHomeIds.remove(homeId); notifyAvailable(); } synchronized void notifyAvailable(){ this.availableNumber++; assert invariant(); notify(); } synchronized void notifyFailure(){ this.failureNumber++; assert invariant(); notify(); } private boolean invariant(){ return this.availableNumber + this.failureNumber <= this.totalNumber; } }