// // Copyright (c)1998-2011 Pearson Education, Inc. or its affiliate(s). // All rights reserved. // package openadk.library.impl; import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import openadk.library.ADK; import openadk.library.ADKException; import openadk.library.ADKFlags; import openadk.library.ADKTransportException; import openadk.library.ADKZoneNotConnectedException; import openadk.library.Agent; import openadk.library.AgentMessagingMode; import openadk.library.AgentProperties; import openadk.library.AgentProvisioningMode; import openadk.library.ElementDef; import openadk.library.Event; import openadk.library.EventAction; import openadk.library.IProtocolHandler; import openadk.library.MessagingListener; import openadk.library.ProvisioningOptions; import openadk.library.Publisher; import openadk.library.PublishingOptions; import openadk.library.Query; import openadk.library.QueryResults; import openadk.library.QueryResultsOptions; import openadk.library.ReportPublisher; import openadk.library.ReportPublishingOptions; import openadk.library.RequestInfo; import openadk.library.SIFContext; import openadk.library.SIFDataObject; import openadk.library.SIFElement; import openadk.library.SIFErrorCategory; import openadk.library.SIFErrorCodes; import openadk.library.SIFException; import openadk.library.SIFMessageInfo; import openadk.library.SIFParser; import openadk.library.SIFStatusCodes; import openadk.library.SIFVersion; import openadk.library.ServiceEvent; import openadk.library.Subscriber; import openadk.library.SubscriptionOptions; import openadk.library.Topic; import openadk.library.TopicFactory; import openadk.library.UndeliverableMessageHandler; import openadk.library.Zone; import openadk.library.infra.InfraDTD; import openadk.library.infra.LogLevel; import openadk.library.infra.SIF_Ack; import openadk.library.infra.SIF_AgentACL; import openadk.library.infra.SIF_Context; import openadk.library.infra.SIF_Contexts; import openadk.library.infra.SIF_Data; import openadk.library.infra.SIF_Object; import openadk.library.infra.SIF_ProvideObjects; import openadk.library.infra.SIF_Provider; import openadk.library.infra.SIF_Providers; import openadk.library.infra.SIF_PublishAddObjects; import openadk.library.infra.SIF_PublishChangeObjects; import openadk.library.infra.SIF_PublishDeleteObjects; import openadk.library.infra.SIF_Request; import openadk.library.infra.SIF_RequestObjects; import openadk.library.infra.SIF_RespondObjects; import openadk.library.infra.SIF_ServiceInput; import openadk.library.infra.SIF_Status; import openadk.library.infra.SIF_SubscribeObjects; import openadk.library.infra.SIF_ZoneStatus; import openadk.library.log.ServerLog; import openadk.library.policy.ADKDefaultPolicy; import openadk.library.policy.PolicyFactory; import openadk.library.reporting.ReportingDTD; import openadk.library.services.SIFZoneService; import openadk.library.services.SIFZoneServiceProxy; import openadk.library.services.ServiceRequestInfo; import openadk.library.services.impl.ServiceResponseDelivery; import org.apache.log4j.Logger; /** * Implementation of the Zone interface. * * @author Eric Petersen * @version ADK 1.0 */ public class ZoneImpl implements Zone { // Values for the fState variable protected static final int UNINIT = 0x00000000, // connect() not yet called CONNECTED = 0x00000001, // connect() called successfully CLOSED = 0x00000002, // disconnect() called; ZoneImpl object now invalid SHUTDOWN = 0x00000004, // shutdown() called SLEEPING = 0x00000020, // Zone is in sleep mode GETZONESTATUS = 0x00000040; // getZoneStatus is blocking on results /** * Log4j logging category for this zone */ public Logger log; /** * The Agent that owns this zone */ protected Agent fAgent; /** * The unique string identifier for this zone */ protected String fZoneId; /** * The URL of the Zone Integration Server that manages this zone */ protected URL fZoneUrl; /** * Zone properties */ private AgentProperties fProps; /** * Connection state */ protected int fState = UNINIT; /** * The ProtocolHandler for this zone */ protected IProtocolHandler fProtocolHandler; /** * The MessageDispatcher for this zone */ private MessageDispatcher fDispatcher; /** * ResponseDelivery thread for this zone that handles sending SIF_Response packets * that have been stored on the local file system as a result of calling * the Publisher.onQuery message handler. */ private ResponseDelivery fResponseDelivery; /** * ResponseDelivery thread for this zone that handles sending SIF_Response packets * that have been stored on the local file system as a result of calling * the Publisher.onQuery message handler. */ private ServiceResponseDelivery fServiceResponseDelivery; /** * ResponseDelivery thread for this zone that handles sending SIF_Response packets * for SIF_ReportObject requests stored on the local file system as a result of * calling the ReportPublisher.onQuery message handler method. */ private ResponseDelivery frptResponseDelivery; /** * The IAgentQueue for this zone */ protected IAgentQueue fQueue; /** * Thread group for all threads associated with this zone */ protected ThreadGroup threadgrp; /** * Manages the MessagingListeners */ protected List<MessagingListener> fMessagingListeners = new ArrayList<MessagingListener>(0); /** * The UndeliverableMessageHandler for this zone */ protected UndeliverableMessageHandler fErrHandler; /** * The matrix of all Publishers, Subsriber, etc. for each SIF Context running in this */ private ProvisioningMatrix fProvMatrix = new ProvisioningMatrix(); /** * If a SIF Error is received in response to a SIF_Provide or SIF_Subscribe * message sent by the provision() method, it is added to this list and * returned when <code>getConnectWarnings</code> is called */ protected List<SIFException> fProvWarnings = new ArrayList<SIFException>(); /** * Reference to the ISIFPrimitives object to use for SIF messaging */ protected ISIFPrimitives fPrimitives; /** * The last SIF_ZoneStatus object received by MessageDispatcher. When * getZoneStatus is called, it blocks on fZSLock until setZoneStatus() * is called by MessageDispatcher or the timeout period specified by the * caller has elapsed. */ protected SIF_ZoneStatus fZoneStatus = null; // Objects used as semaphores private Object fZSLock = new Object(); private Object fConnLock = new Object(); private boolean fZoneIsConfigured = false; /** * Semaphore to ensure that MessageDispatcher does not attempt to process a * SIF_Response message before the Zone.onQuery has completed. Zone.onQuery * synchronizes on this object; MessageDispatcher must also synchronize on * this object when it receives a SIF_Response. * * @see #waitForRequestsInProgress */ private Object fReqLock = new Object(); /** * Optional user data set with the setUserData method */ protected Object fUserData; /** * Whenever SIF_ZoneStatus is requested, whatever is received will be cached here. */ private SIF_ZoneStatus lastReceivedZoneStatus = null; public SIF_ZoneStatus getLastReceivedSIF_ZoneStatus(boolean requestIfMissing) { if (lastReceivedZoneStatus == null && requestIfMissing) { try { this.getZoneStatus(); } catch (ADKException e) { log.error("Unexpected failure requesting SIF_ZoneStatus: " + e, e); } } return lastReceivedZoneStatus; } @SuppressWarnings("unused") private ZoneImpl() { } /** * Constructs a Zone instance * @param zoneId The name of the zone * @param zoneUrl The URL of the Zone Integration Server that manages this zone * @param agent The Agent object. Any ADK object such as a Topic can always * find a reference to the Agent by asking a Zone * @param props Zone properties */ protected ZoneImpl( String zoneId, String zoneUrl, Agent agent, AgentProperties props ) throws ADKTransportException { if( zoneId == null || zoneId.trim().length() < 1 ) ADKUtils._throw( new IllegalArgumentException("Zone name cannot be null or blank"),log ); log = Logger.getLogger( Agent.LOG_IDENTIFIER + "$" + zoneId ); if( zoneUrl == null || zoneUrl.trim().length() < 1 ) ADKUtils._throw( new IllegalArgumentException("Zone URL cannot be null or blank"),log ); if( agent == null ) ADKUtils._throw( new IllegalArgumentException("Agent cannot be null"),log ); // Create a ThreadGroup to organize this zone's threads (e.g. the // MessageDispatcher, the ProtocolHandlers, etc.) threadgrp = new ThreadGroup("Zone-"+zoneId); fAgent = agent; fZoneId = zoneId; setProperties(props); try { fZoneUrl = new URL(zoneUrl); } catch( MalformedURLException mue ) { throw new ADKTransportException("Zone URL is malformed: " + zoneUrl, this ); } } /** * Shutdown the zone. * @throws ADKException */ public void shutdown() throws ADKException { if( fProtocolHandler == null ) return; if( ( ADK.debug & ADK.DBG_LIFECYCLE ) != 0 ) log.info( "Shutting down zone..." ); fState |= SHUTDOWN; if( getFDispatcher() != null ) { try { if( ( ADK.debug & ADK.DBG_LIFECYCLE ) != 0 ) log.info( "Shutting down Message Dispatcher" ); getFDispatcher().shutdown(); } catch( Throwable ignored ) { log.error("Error shutting down Message Dispatcher: "+ignored, ignored); } } setFDispatcher(null); if( fProtocolHandler != null ) { try { if( ( ADK.debug & ADK.DBG_LIFECYCLE ) != 0 ){ log.info( "Shutting down Protocol Handler" ); } fProtocolHandler.shutdown(); } catch( Throwable ignored ) { log.error("Error shutting down Protocol Handler: "+ignored, ignored); } } fProtocolHandler = null; if( ( ADK.debug & ADK.DBG_LIFECYCLE ) != 0 ){ log.info( "Zone shutdown complete" ); } } /** * Gets the ZoneProperties for this zone<p> * @return The ZoneProperties object for this zone */ public AgentProperties getProperties() { return fProps; } /** * Sets the ZoneProperties for this zone<p> * @param props A ZoneProperties object */ public void setProperties( AgentProperties props ) { fProps = props == null ? new AgentProperties( fAgent.getProperties() ) : props; } /** * Gets the Agent object */ public Agent getAgent() { return fAgent; } /** * Gets the zone name * @returns The name of the zone */ public String getZoneId() { return fZoneId; } /** * Gets the URL of the Zone Integration Server that manages this zone * @returns The URL to the ZIS in whatever format is expected by the ZIS * (e.g. "https://host:port/opensif/zis/zone-name" for the OpenSIF ZIS, * "https://host:port/zone-name" for the SIFWorks ZIS, etc.) */ public URL getZoneUrl() { return fZoneUrl; } public void configure() throws ADKException { synchronized( fConnLock ) { if( isConnected() ){ ADKUtils._throw( new IllegalStateException("Zone already connected"),log ); } fZoneIsConfigured = true; fPrimitives = ADK.getPrimitives(); fState = 0; // // Initialize the Agent Local Queue if supported and enabled // /* if( ADK.supportsALQ() && !fProps.getDisableQueue() ) { String clazz = fProps.getProperty("adk.queue.impl","openadk.library.impl.JDBCQueue"); try { queue = (IAgentQueue)Class.forName(clazz).newInstance(); } catch( ClassNotFoundException cnfe ) { ADKUtils._throw( new ADKQueueException("Agent Local Queue is not supported ("+cnfe+")", this ), log); } catch( Exception ex ) { ADKUtils._throw( new ADKQueueException("Failed to create Agent Local Queue implementation class: "+ex, this ),log ); } queue.initialize(this,fProps); } */ // Initialize the MessageDispatcher if( getFDispatcher() == null ){ setFDispatcher(new MessageDispatcher(this)); } try { // Start the IProtocolHandler for this zone. The protocol handler // is transport-specific so it is created by the Transport object // associated with the zone. Starting the handler typically // establishes a socket to the ZIS and starts a thread to pull // messages when the zone is running is Pull mode. fProtocolHandler = ((TransportManagerImpl)fAgent.getTransportManager()).activate( this ); fProtocolHandler.open( this ); } catch( ADKException adke ) { ADKUtils._throw(adke,log); fZoneIsConfigured = false; } catch( Exception ex ) { ADKUtils._throw( new ADKException("Failed to start transport protocol: "+ex, this ),log ); fZoneIsConfigured = false; } // When running in Push mode, make sure protocol handler is // started right away because the ZIS may send a message to us // immediately after registration. The SIF Compliance test harness // does this, and if the protocol handler is not started a 404 // error is returned (which causes the agent to fail one of the // certification tests.) In Pull mode, the handler is started // later in this function since starting it invokes the Pull // thread (which should not be done until after SIF Register). // try { if( getProperties().getMessagingMode() == AgentMessagingMode.PUSH ) { fProtocolHandler.start(); } } catch( ADKException adke ) { ADKUtils._throw(adke,log); fZoneIsConfigured = false; } catch( Exception ex ) { ADKUtils._throw( new ADKException("Failed to start transport protocol: "+ex, this ),log ); fZoneIsConfigured = false; } } } public void connect( int provOptions ) throws ADKException { if(!fZoneIsConfigured) { configure(); } // // If any exception is thrown from this point on, catch-rethrow but // set the state to Disconnected. // boolean _connectFailed = true; // Sleep on connect will cause any messages received by the dispatcher // to be turned away with a status code 8 ("Receiver sleeping") if( ( provOptions & ADKFlags.SLEEP_ON_CONNECT ) != 0 ) fState |= SLEEPING; SIF_AgentACL acl = null; try { // Send SIF_Register if( ( ( provOptions & ADKFlags.PROV_REGISTER ) != 0 ) && getProperties().getProvisioningMode() == AgentProvisioningMode.ADK ) { try { SIF_Ack ack = fPrimitives.sifRegister(this); if( ack.hasStatusCode(8) ) fState |= SLEEPING; if( !ack.hasStatusCode(0) ) { fState &= ~CONNECTED; ADKUtils._throw( new SIFException(ack,this),log ); } else if( ( ADK.debug & ADK.DBG_PROVISIONING ) != 0 ){ log.info("SIF_Register successful"); } SIF_Data payload = ack.getSIF_Status().getSIF_Data(); if( payload != null && payload.getChildCount() > 0 ){ SIFElement child = payload.getChildList().get( 0 ); if( child.getElementDef() == InfraDTD.SIF_AGENTACL ){ acl = ( SIF_AgentACL)child; } } } catch (Throwable e) { log.error("Could not register zone", e); try { throw e; } catch (Throwable e1) { // TODO Auto-generated catch block e1.printStackTrace(); } } } // We're now connected fState |= CONNECTED; // Send pending SIF_Responses if any leftover from prior session if( ResponseDelivery.hasPendingPackets( ResponseDelivery.SRC_GENERIC, this ) ) { final ResponseDelivery _rd = getResponseDelivery(); Runnable task = new Runnable() { public void run() { try { _rd.process(); } catch( ADKException adke ) { log.debug( "Failed to send SIF_Response packets from a previous session", adke ); } } }; new Thread(threadgrp,task){}.start(); } // Send pending SIF_Responses if any leftover from prior session if( ResponseDelivery.hasPendingPackets( ResponseDelivery.SRC_SIFREPORTOBJECT, this ) ) { if( frptResponseDelivery == null ) frptResponseDelivery = new ResponseDelivery( this, ResponseDelivery.SRC_SIFREPORTOBJECT ); final ResponseDelivery _rd2 = frptResponseDelivery; Runnable task = new Runnable() { public void run() { try { _rd2.process(); } catch( ADKException adke ) { log.debug( "Failed to send SIF_ReportObject response packets from a previous session", adke ); } } }; new Thread(threadgrp,task){}.start(); } if( ( provOptions & ADKFlags.SLEEP_ON_CONNECT ) != 0 ) { try { // Sleep immediately if requested by the caller sleep(); fState |= SLEEPING; } catch( Exception ignored ) { } } else { // SIF spec recommends sending SIF_Wakeup on startup; no need // to send this when SIF_Register is sent because it is implicit // in that case SIF_Ack ack = null; if( ( provOptions & ADKFlags.PROV_REGISTER ) == 0 ) { if( getProperties().getProvisioningMode() == AgentProvisioningMode.ADK ) wakeup(); } // Calling sifPing will determine the sleep mode of the zone // from the server's perspective ack = sifPing(); if( ack.hasError() ) throw new SIFException( ack, this ); } // Send SIF_Provide and SIF_Subscribe // JEN ADKFlags.PROV_NONE if (provOptions != ADKFlags.PROV_NONE) provision( acl ); try { // Protocol handler can start now that the zone is connected. If // running in Push mode, this was done earlier in this function. if( getProperties().getMessagingMode() == AgentMessagingMode.PULL ){ fProtocolHandler.start(); fState |= CONNECTED; } // Success! _connectFailed = false; //Need to call configure again before a new connect is attempted. fZoneIsConfigured = false; } catch( Exception ex ) { throw new ADKException( "Failed to start " + fProtocolHandler.getName() + " protocol handler: " + ex.getMessage(), this ); } } finally { try { // When running in Push mode, stop the protocol handler if // SIF_Register failed if( ! ( ( fState & CONNECTED ) != 0 ) ) { if( getProperties().getMessagingMode() == AgentMessagingMode.PUSH ) fProtocolHandler.shutdown(); } } catch( Exception ex ) { log.error( "Failed to stop transport protocol: " + ex ); } if( _connectFailed ) { // Put the agent to sleep and set the zone connection state to // Disconnected. The client should try and reconnect the zone again. try { sleep(); } catch( Exception ignored ) { } fState &= ~CONNECTED; } } } /** * Gets the ResponseDelivery thread for generic SIF_Response processing * on this zone, creating it if not already initialized. This method is * normally called by the DataObjectOutputStream class to notify the * delivery thread that new packets are waiting for delivery to the ZIS. * @return The ResponseDelivery thread used for SIF_Response processing * @throws ADKException */ public synchronized ResponseDelivery getResponseDelivery() throws ADKException { if( fResponseDelivery == null ) fResponseDelivery = new ResponseDelivery( this, ResponseDelivery.SRC_GENERIC ); return fResponseDelivery; } /** * Gets the ResponseDelivery thread for generic SIF_Response processing * on this zone, creating it if not already initialized. This method is * normally called by the DataObjectOutputStream class to notify the * delivery thread that new packets are waiting for delivery to the ZIS. * @return The ResponseDelivery thread used for SIF_Response processing * @throws ADKException */ public synchronized ServiceResponseDelivery getServiceResponseDelivery() throws ADKException { if( fServiceResponseDelivery == null ) fServiceResponseDelivery = new ServiceResponseDelivery( this, ResponseDelivery.SRC_GENERIC ); return fServiceResponseDelivery; } /** * Gets the ResponseDelivery thread for SIF_ReportObject SIF_Response * processing on this zone, creating it if not already initialized. This * method is normally called by the ReportObjectOutputStream class to * notify the delivery thread that new packets are waiting for delivery * to the ZIS. * @return The ResponseDelivery thread used for SIF_Response processing * @throws ADKException */ public synchronized ResponseDelivery getReportResponseDelivery() throws ADKException { if( frptResponseDelivery == null ) frptResponseDelivery = new ResponseDelivery( this, ResponseDelivery.SRC_SIFREPORTOBJECT ); return frptResponseDelivery; } protected void provision() throws ADKException { provision( null ); } /** * Provisions the agent with the zone * @param acl The SIF_AgentACL object to use for provisioning, if available, otherwise null */ protected void provision( SIF_AgentACL acl ) throws ADKException { if( !isConnected() ) return; if( getProperties().getProvisioningMode() != AgentProvisioningMode.ADK ) { if( ( ADK.debug & ADK.DBG_PROVISIONING ) != 0 ) log.info( "Zone not sending provisioning messages because ADK-managed provisioning is not in effect" ); return; } fProvWarnings.clear(); SIFVersion effectiveZisVersion = getHighestEffectiveZISVersion(); if( ( effectiveZisVersion.compareTo( SIFVersion.SIF20 ) ) < 0 || getProperties().getProvisionInLegacyMode() ){ provisionLegacy(); } else { provisionSIF20( acl ); } } /** * Send SIF_Subscribe and SIF_Provide provisioning messages. */ private void provisionLegacy() throws ADKException { // - Visit each Topic to which this zone is joined and send a message // if the Topic has a publisher and/or subscriber and no message has // yet been sent for the Topic's data object type. // // - Send a message for each data object type for which a subscriber // and/or publisher is registered with this zone (and no message has // yet been sent for that object type.) // List<String> subSent = new ArrayList<String>(); List<String> pubSent = new ArrayList<String>(); // Send provisioning messages for all topics to which this zone is // currently joined... for( Topic topic : fAgent.getTopicFactory().getAllTopics( SIFContext.DEFAULT ) ) { TopicImpl t = (TopicImpl)topic; if( t.fZones.contains( this ) ) { // This zone is joined to the topic. Send a SIF_Subscribe and/or // SIF_Provide for the topic's data type. String objType = t.getObjectType(); if( t.fSub != null && ( t.fSubOpts == null || t.fSubOpts.getSendSIFSubscribe() ) ){ subSent.add( objType ); } if( t.fPub != null && ( t.fPubOpts == null || t.fPubOpts.getSendSIFProvide() ) ){ pubSent.add( objType ); } else if ( t.fReportPub != null && ( t.fReportPubOpts == null || t.fReportPubOpts.getSendSIFProvide() ) ){ pubSent.add( objType ); } } } // Add subscribers for( ProvisionedObject<Subscriber, SubscriptionOptions> subOption : fProvMatrix.getAllSubscribers( true ) ){ SubscriptionOptions subOptions = subOption.getProvisioningOptions(); if( subSent.contains( subOption.getObjectType().name() ) || !subOptions.getSendSIFSubscribe() ){ continue; } // For legacy provisioning, only add subscribers in the default context for( SIFContext context : subOptions.getSupportedContexts() ){ if( context.equals( SIFContext.DEFAULT ) ){ subSent.add( subOption.getObjectType().name() ); } else { log.debug( String.format( "SIF_Subscribe will not be sent in legacy mode for %s in SIF Context %s", subOption.getObjectType().name(), context.getName() ) ); } } } // Add publishers for( ProvisionedObject<Publisher, PublishingOptions> pubOption : fProvMatrix.getAllPublishers( true ) ){ PublishingOptions pubOptions = pubOption.getProvisioningOptions(); if( pubSent.contains( pubOption.getObjectType().name() ) || !pubOptions.getSendSIFProvide() ){ continue; } // For legacy provisioning, only add subscribers in the default context for( SIFContext context : pubOptions.getSupportedContexts() ){ if( context.equals( SIFContext.DEFAULT ) ){ pubSent.add( pubOption.getObjectType().name() ); } else { log.debug( String.format( "SIF_Provide will not be sent in legacy mode for %s in SIF Context %s", pubOption.getObjectType().name(), context.getName() ) ); } } } // Add report publishers for( ProvisionedObject<ReportPublisher, ReportPublishingOptions> pubOption : fProvMatrix.getAllReportPublishers() ){ ReportPublishingOptions pubOptions = pubOption.getProvisioningOptions(); if( !pubOptions.getSendSIFProvide() ){ continue; } // For legacy provisioning, only add subscribers in the default context for( SIFContext context : pubOptions.getSupportedContexts() ){ if( context.equals( SIFContext.DEFAULT ) ){ pubSent.add( pubOption.getObjectType().name() ); } else { log.debug( String.format( "SIF_Provide will not be sent in legacy mode for %s in SIF Context %s", pubOption.getObjectType().name(), context.getName() ) ); } } } if( subSent.size() > 0 ) { if( getProperties().isBatchProvisioning() ) { // Send a single SIF_Provide for the set of objects identified above String[] subTypes = new String[ subSent.size() ]; subSent.toArray( subTypes ); SIF_Ack ack = fPrimitives.sifSubscribe( this, subTypes ); if( !ack.hasStatusCode( SIFStatusCodes.ALREADY_SUBSCRIBED_5 ) && ack.hasError() ) { if( !getProperties().getIgnoreProvisioningErrors() ) { SIFException se = new SIFException( ack, this ); if( se.getSIFErrorCategory() == SIFErrorCategory.ACCESS_PERMISSIONS ) { fProvWarnings.add( se ); } else { ADKUtils._throw( se, null ); } } else { if( ( ADK.debug & ADK.DBG_PROVISIONING ) != 0 ){ log.info("SIF_Subscribe errors ignored for " + arrayToStr(subTypes)); } } } else { if( ( ADK.debug & ADK.DBG_PROVISIONING ) != 0 ) { log.info("SIF_Subscribe successful for "+arrayToStr(subTypes)); } } } else { // Send individual SIF_Provide messages for the set of objects identified above. String[] subTypes = new String[ subSent.size() ]; subSent.toArray( subTypes ); for( int i = 0; i < subTypes.length; i++ ) { SIF_Ack ack = fPrimitives.sifSubscribe( this, new String[] { subTypes[i] } ); if( !ack.hasStatusCode( SIFStatusCodes.ALREADY_SUBSCRIBED_5 ) && ack.hasError() ) { if( !getProperties().getIgnoreProvisioningErrors() ) { SIFException se = new SIFException( ack, this ); if( se.getSIFErrorCategory() == SIFErrorCategory.ACCESS_PERMISSIONS ) fProvWarnings.add( se ); else ADKUtils._throw( se, null ); } else if( ( ADK.debug & ADK.DBG_PROVISIONING ) != 0 ) log.info("SIF_Subscribe errors ignored for " + subTypes[ i ] ); } else if( ( ADK.debug & ADK.DBG_PROVISIONING ) != 0 ) log.info("SIF_Subscribe successful for " + subTypes[ i ] ); } } } if( pubSent.size() > 0 ) { if( getProperties().isBatchProvisioning() ) { // Send a single SIF_Provide for the set of objects identified above String[] pubTypes = new String[ pubSent.size() ]; pubSent.toArray( pubTypes ); SIF_Ack ack = fPrimitives.sifProvide( this, pubTypes ); if( !ack.hasStatusCode( SIFStatusCodes.ALREADY_PROVIDER_6 ) && ack.hasError() ) { if( !getProperties().getIgnoreProvisioningErrors() ) { SIFException se = new SIFException( ack, this ); if( se.getSIFErrorCategory() == SIFErrorCategory.ACCESS_PERMISSIONS ) fProvWarnings.add( se ); else ADKUtils._throw( se, null ); } else if( ( ADK.debug & ADK.DBG_PROVISIONING ) != 0 ) log.info("SIF_Provide errors ignored for "+arrayToStr(pubTypes)); } else if( ( ADK.debug & ADK.DBG_PROVISIONING ) != 0 ) log.info("SIF_Provide successful for "+arrayToStr(pubTypes)); } else { // Send individual SIF_Provide messages for the set of objects identified above. String[] pubTypes = new String[ pubSent.size() ]; pubSent.toArray( pubTypes ); for( int i = 0; i < pubTypes.length; i++ ) { SIF_Ack ack = fPrimitives.sifProvide( this, new String[] { pubTypes[i] } ); if( !ack.hasStatusCode( SIFStatusCodes.ALREADY_PROVIDER_6 ) && ack.hasError() ) { if( !getProperties().getIgnoreProvisioningErrors() ) { SIFException se = new SIFException( ack, this ); if( se.getSIFErrorCategory() == SIFErrorCategory.ACCESS_PERMISSIONS ) fProvWarnings.add( se ); else ADKUtils._throw( se, null ); } else if( ( ADK.debug & ADK.DBG_PROVISIONING ) != 0 ) log.info("SIF_Provide errors ignored for " + pubTypes[ i ] ); } else if( ( ADK.debug & ADK.DBG_PROVISIONING ) != 0 ) log.info("SIF_Provide successful for " + pubTypes[ i ] ); } } } } /** * Provisions the agent with the zone using the SIF_Provision message * @param acl The SIF_AgentACL object, if received from SIF_Register, or null * @throws ADKException */ private void provisionSIF20( SIF_AgentACL acl ) throws ADKException { SIF_SubscribeObjects subscribeObjects = new SIF_SubscribeObjects();; SIF_RequestObjects requestObjects = new SIF_RequestObjects(); SIF_ProvideObjects provideObjects = new SIF_ProvideObjects(); SIF_RespondObjects respondObjects = new SIF_RespondObjects(); // // Add subscribers // provisionHandlers( subscribeObjects, fProvMatrix.getAllSubscribers( true ) ); // // Add requestors // provisionHandlers( requestObjects, fProvMatrix.getAllQueryResults( true ) ); // // Add publishers // List<ProvisionedObject<Publisher,PublishingOptions>> publishers = fProvMatrix.getAllPublishers( true ); provisionPublisher( provideObjects, publishers ); provisionPublisher( respondObjects, publishers ); // // Add report publishers // List<ProvisionedObject<ReportPublisher, ReportPublishingOptions>> reportPublishers = fProvMatrix.getAllReportPublishers() ; provisionPublisher( provideObjects, reportPublishers ); provisionPublisher( respondObjects, reportPublishers ); // Send provisioning messages for all topics to which this zone is // currently joined... TopicFactory tFactory = fAgent.getTopicFactory(); for( SIFContext context : tFactory.getAllSupportedContexts() ) { for( Topic topic : fAgent.getTopicFactory().getAllTopics( context ) ) { TopicImpl t = (TopicImpl)topic; if( t.fZones.contains( this ) ) { // This zone is joined to the topic. Send a SIF_Subscribe and/or // SIF_Provide for the topic's data type. String objType = t.getObjectType(); if( t.fSub != null && ( t.fSubOpts == null || t.fSubOpts.getSendSIFSubscribe() ) ){ addProvisionedObject( subscribeObjects, objType, context, false ); } if( t.fQueryResults != null ){ addProvisionedObject( requestObjects, objType, context, t.fQueryResultsOptions.getSupportsExtendedQuery() ); } if( t.fPub != null && ( t.fPubOpts == null || t.fPubOpts.getSendSIFProvide() ) ){ addProvisionedObject( provideObjects, objType, context, t.fPubOpts.getSupportsExtendedQuery() ); addProvisionedObject( respondObjects, objType, context, t.fPubOpts.getSupportsExtendedQuery() ); } else if ( t.fReportPub != null && ( t.fReportPubOpts == null || t.fReportPubOpts.getSendSIFProvide() ) ){ addProvisionedObject( provideObjects, objType, context, t.fReportPubOpts.getSupportsExtendedQuery() ); addProvisionedObject( respondObjects, objType, context, t.fReportPubOpts.getSupportsExtendedQuery() ); } } } } // Get the SIF_ZoneStatus and SIF_AgentACL objects. Compare that matrix // with the list of provisioned objects in the agent. If there is a different provider // for an object, or an ACL restricts this agent from participating, log the anomaly and // remove the object from the list // The zone status check only needs to be done if this agent provides any objects if( provideObjects.getChildCount() > 0 ) { SIF_ZoneStatus zoneStatus = this.getZoneStatus(); if( zoneStatus == null ) { // should never be null log.warn( "Unable to obtain SIF_ZoneStatus for provisioning." ); } else { // Remove any provided objects if they are provided by any other agent SIF_Providers zoneProviders = zoneStatus.getSIF_Providers(); if( zoneProviders != null ){ for( SIF_Provider zoneProvider : zoneProviders.getSIF_Providers() ){ if( !zoneProvider.getSourceId().equals( getAgent().getId() ) && zoneProvider.getSIF_ObjectList() != null ){ // determine if any objects being provided match ones this agent wants // to provide for( SIF_Object zoneProvidedObject : zoneProvider.getSIF_ObjectList().getSIF_Objects() ){ SIF_Object po = provideObjects.getSIF_Object( zoneProvidedObject.getObjectName() ); if( po != null ){ SIF_Contexts publishContexts = po.getSIF_Contexts(); // Remove any contexts supported by this provider SIF_Contexts zoneObjectContexts = zoneProvidedObject.getSIF_Contexts(); if( zoneObjectContexts == null ){ publishContexts.removeSIF_Context( SIFContext.DEFAULT.getName() ); if( ( ADK.debug & ADK.DBG_PROVISIONING ) != 0 ){ log.info( "Unable to provide " + po.getObjectName() + " in SIF Context " + SIFContext.DEFAULT.getName() + " because it is being provided by " + zoneProvider.getSourceId() ); } } else { for( SIF_Context context : zoneObjectContexts.getSIF_Contexts() ) { publishContexts.removeSIF_Context( context.getValue() ); if( ( ADK.debug & ADK.DBG_PROVISIONING ) != 0 ){ log.info( "Unable to provide " + po.getObjectName() + " in SIF Context " + context.getValue() + " because it is being provided by " + zoneProvider.getSourceId() ); } } } // If there are not remaining contexts left, remove the // support for publishing this object if( publishContexts.getChildCount() == 0 ){ provideObjects.removeChild( po ); } } } } } } } } // Now check Agent ACL permissions if( acl == null ){ acl = this.getAgentACL(); } if( acl == null ) { // Should never be null log.warn( "Unable to obtain SIF_AgentACL for provisioning." ); } else { compareToACL( acl.getSIF_ProvideAccess(), provideObjects, "Publish" ); compareToACL( acl.getSIF_RespondAccess(), respondObjects, "Respond to" ); compareToACL( acl.getSIF_SubscribeAccess(), subscribeObjects, "Subscribe to" ); compareToACL( acl.getSIF_RequestAccess(), requestObjects, "Request" ); } SIF_Ack ack = fPrimitives.sifProvision( this, provideObjects, subscribeObjects, new SIF_PublishAddObjects(), new SIF_PublishChangeObjects(), new SIF_PublishDeleteObjects(), requestObjects, respondObjects ); if( ack.hasError() ){ SIFException se = new SIFException( ack, this ); ADKUtils._throw( se, null ); } else { if( ( ADK.debug & ADK.DBG_PROVISIONING ) != 0 ) { log.info( "SIF_Provision successful" ); } } } /** * Checks to see if the list of Published objects already contains the instance. If so, a check * is done to ensure that the specified context is included. * @param provObjects * @param provObject */ private void addProvisionedObject( SIFElement provObjects, String objectName, SIFContext context, boolean supportsSIFExtendedQuery ) { SIF_Object lookedUp = (SIF_Object)provObjects.getChild( InfraDTD.SIF_OBJECT.name(), objectName ); if( lookedUp == null ){ lookedUp = new SIF_Object( objectName ); lookedUp.setSIF_Contexts( new SIF_Contexts() ); provObjects.addChild( lookedUp ); } if( supportsSIFExtendedQuery ){ lookedUp.setSIF_ExtendedQuerySupport( true ); } SIF_Contexts contexts = lookedUp.getSIF_Contexts(); SIF_Context ctxt = contexts.getSIF_Context( context.getName() ); if( ctxt == null ){ contexts.addSIF_Context( context.getName() ); } } /** * Looks at all of the object actions currently desired to be provisioned by the agent. If the * corresponding ACL is not found in the ACL list, a warning is written to the log and the * corresponding provisioning option is removed from the desiredProvisionedObjects list * @param aclPermissions A SIFElement instance from SIF_AgentACL * @param desiredProvisionedObjects a ProvisionedObjects or PublishedObjects instance for * the corresponding provisioning action */ private void compareToACL( SIFElement aclPermissions, SIFElement desiredProvisionedObjects, String actionString ) { SIFElement[] allProvisionedObjects = desiredProvisionedObjects.getChildren(); for( SIFElement provisionedObject : allProvisionedObjects ) { // TODO: It would be nice to be able to better support inheritance. This method is called // with both ProvisionedObjects and PublishedObjects as its second parameter. They are similar, // but PublishedObjects has an additional element child String objectName = provisionedObject.getFieldValue( InfraDTD.SIF_OBJECT_OBJECTNAME ); SIFElement aclObject = null; if( aclPermissions != null ){ aclObject = aclPermissions.getChild( InfraDTD.SIF_OBJECT, objectName ); } SIF_Contexts aclContexts = null; if( aclObject != null ){ aclContexts = (SIF_Contexts)aclObject.getChild( InfraDTD.SIF_CONTEXTS.name() ); } if( aclContexts == null ) { desiredProvisionedObjects.removeChild( provisionedObject ); if( ( ADK.debug & ADK.DBG_PROVISIONING ) != 0 ){ log.info( "Unable to " + actionString + " " + objectName + " because the agent does not have ACL permission to do so." ); } } else { SIF_Contexts provContexts = (SIF_Contexts)provisionedObject.getChild( InfraDTD.SIF_CONTEXTS.name()); for( SIF_Context desiredContext : provContexts.getSIF_Contexts() ){ SIF_Context aclContext = aclContexts.getSIF_Context( desiredContext.getValue() ); if( aclContext == null ){ provContexts.removeChild( desiredContext ); if( ( ADK.debug & ADK.DBG_PROVISIONING ) != 0 ){ log.info( "Unable to " + actionString + " " + objectName + " in Context \"" + desiredContext.getValue() + "\" because the agent does not have ACL permission to do so." ); } } } if( provContexts.getChildCount() == 0 ){ desiredProvisionedObjects.removeChild( provisionedObject ); } } } } /** * Takes a SIFElement such as SIF_SubscribeObjects or SIF_RequestObjects and adds an additional * child nodes describing support for each of the objects in the specified provisioning list * @param addTo A SIF_SubscribeObjects or SIF_RequestObjects element * @param list A list of objects and associated publishing options related to them */ @SuppressWarnings("rawtypes") private <V extends Object, Y extends ProvisioningOptions> // NOTE: the V parameter is unused, but Java is not allowing it to be wildcarded. Strange void provisionHandlers( SIFElement addTo, List<ProvisionedObject<V,Y>> list) { for( ProvisionedObject subOption : list ){ ProvisioningOptions opts = subOption.getProvisioningOptions(); SIF_Object qr = new SIF_Object( subOption.getObjectType().name() ); SIF_Contexts qContexts = new SIF_Contexts(); qr.setSIF_Contexts( qContexts ); if( opts != null ){ // Add the list of supported contexts for this object for( SIFContext context : opts.getSupportedContexts() ){ qContexts.addSIF_Context( context.getName() ); } } addTo.addChild( qr ); } } /** * Takes a SIFElement such as SIF_ProvideObjects or SIF_RespondObjects and adds an additional * child node describing support for each of the objects in the specified provisioning list * @param addTo A SIF_ProvideObjects or SIF_RespondObjects element * @param list A list of objects and associated publishing options related to them */ private <V extends Object, Y extends PublishingOptions> // NOTE: the V parameter is unused, but Java is not allowing it to be wildcarded. Strange void provisionPublisher( SIFElement addTo, List<ProvisionedObject<V,Y>> list) { for( ProvisionedObject<V,Y> pubOption : list ){ PublishingOptions pubOptions = pubOption.getProvisioningOptions(); SIF_Object published = new SIF_Object( pubOption.getObjectType().name() ); SIF_Contexts pContexts = new SIF_Contexts(); published.addChild( pContexts ); if( pubOptions != null ){ published.setSIF_ExtendedQuerySupport( pubOptions.getSupportsExtendedQuery() ); // Add the list of supported contexts for this object for( SIFContext context : pubOptions.getSupportedContexts() ){ pContexts.addSIF_Context( context.getName() ); } } addTo.addChild( published ); } } protected String arrayToStr( String[] arr ) { StringBuilder b = new StringBuilder(); for( int i = 0; i < arr.length; i++ ) { if( i != 0 ) b.append( ", " ); b.append( arr[i] ); } return b.toString(); } /** * Gets a read-only list of any SIF Errors that resulted from sending SIF_Provide and * SIF_Subscribe provisioning messages to the zone. Currently, only access * control errors (Category 4) are treated as warnings rather than errors. * All other SIF Errors result in an exception thrown by the * <code>connect</code> method.<p> * * @return An array of SIFExceptions */ public List<SIFException> getConnectWarnings() { synchronized( fConnLock ) { return Collections.unmodifiableList( fProvWarnings ); } } /** * Disconnects the agent from this zone */ public void disconnect( int provOptions ) throws ADKException { synchronized( fConnLock ) { if( !isConnected() ) return; SIFException exc = null; try { if( fQueue != null ) { fQueue.shutdown(); fQueue = null; } if( ( provOptions & ADKFlags.PROV_UNREGISTER ) != 0 && getProperties().getProvisioningMode() == AgentProvisioningMode.ADK ) { fPrimitives.sifUnregister(this); } else { if( getProperties().getSleepOnDisconnect() ) { try { sleep(); } catch( SIFException se ){ // Ignore Category 4, Code 9 error (invalid SourceId, which // with SIFWorks would be the case if the admin manually // unregistered the agent but the agent still thinks it is // connected. // if( !( se.getSIFErrorCategory() == SIFErrorCategory.ACCESS_PERMISSIONS && se.getErrorCode() == SIFErrorCodes.ACCESS_UNKNOWN_SOURCEID_9 ) ) { exc = se; } } } } // Shutdown the message dispatcher and transport protocol shutdown(); } finally { fState |= CLOSED; fState &= ~CONNECTED; } if( exc != null ){ throw exc; } } } /** * Gets the connection state of this Zone<p> * @return true if the connect method has been called but the disconnect * method has not; false if the connect method has not yet been called * or the disconnect method has been called */ public boolean isConnected() { return( fState & CONNECTED ) == CONNECTED; } /** * Determines if this zone is shutting down<p> * @return true if the shutdown method has been called */ public boolean isShutdown() { return( fState & SHUTDOWN ) == SHUTDOWN; } /** * Puts this zone into sleep mode.<p> * * A SIF_Sleep message is sent to the Zone Integration Server to request * that this agent's queue be put into sleep mode. If successful, the ZIS * should not deliver further messages to this agent until it is receives * a SIF_Register or SIF_Wakeup message from the agent. Note the ADK keeps * an internal sleep flag for each zone, which is initialized when the * <code>connect</code> method is called by sending a SIF_Ping to the ZIS. * This flag is set so that the ADK will return a Status Code 8 ("Receiver * is sleeping") in response to any message received by the ZIS for the * duration of the session. * <p> * * If the SIF_Sleep message is not successful, an exception is thrown and * the ADK's internal sleep flag for this zone is not changed. * <p> * * @exception ADKException thrown if the SIF_Sleep message is unsuccessful */ public void sleep() throws ADKException { _checkConnect(); fState |= SLEEPING; SIF_Ack ack = sifSleep(); if( ack.hasError() ){ ADKUtils._throw( new SIFException(ack,this), log ); } } /** * Execute a SIF_Sleep received from the ZIS */ public void execSleep() { // TODO: Notify the zone's ResponseDelivery thread to pause. fState |= SLEEPING; } /** * Wakes up this zone if currently in sleep mode.<p> */ public void wakeup() throws ADKException { _checkConnect(); fState &= ~SLEEPING; SIF_Ack ack = sifWakeup(); if( ack.hasError() ) ADKUtils._throw( new SIFException( ack, this ), log ); } /** * Execute a SIF_Wakeup received from the ZIS */ public void execWakeup() { // TODO: Notify the zone's ResponseDelivery thread to continue // sending any SIF_Responses that are pending delivery but were // interrupted due to a SIF_Sleep ( see execSleep() ) fState &= ~SLEEPING; } /** * Determines if the agent's queue for this zone is in sleep mode.<p> * * @param flags When ADKFlags.LOCAL_QUEUE is specified, returns true if the * Agent Local Queue is currently in sleep mode. False is returned if * the Agent Local Queue is disabled. When ADKFlags.SERVER_QUEUE is * specified, queries the sleep mode of the Zone Integration Server * by sending a SIF_Ping message. */ public boolean isSleeping( int flags ) throws ADKException { _checkConnect(); if( ( flags & ADKFlags.QUEUE_SERVER ) != 0 ) { // Determine if agent's queue on server is sleeping SIF_Ack ack = sifPing(); if( ack.hasError() ) ADKUtils._throw( new SIFException( ack, this ), log ); return( ack.hasStatusCode( SIFStatusCodes.SLEEPING_8 ) ); } else if( ( flags & ADKFlags.QUEUE_LOCAL ) != 0 ) { // TODO: Move state into ALQ object when it exists return( ( fState & SLEEPING ) != 0 ); } else throw new IllegalArgumentException("Invalid flags (specify SERVER_QUEUE or LOCAL_QUEUE)"); } /** * Determines if this zone is awaiting a SIF_ZoneStatus result */ protected boolean awaitingZoneStatus() { return( fState & GETZONESTATUS ) != 0; } /* (non-Javadoc) * @see openadk.library.Zone#reportEvent(openadk.library.SIFDataObject, openadk.library.EventAction) */ public void reportEvent( SIFDataObject obj, EventAction actionCode ) throws ADKException { reportEvent( new Event( obj, actionCode ), null, null ); } /* (non-Javadoc) * @see openadk.library.Zone#reportEvent(openadk.library.SIFDataObject, openadk.library.EventAction, openadk.library.SIFContext[]) */ public void reportEvent( SIFDataObject obj, EventAction actionCode, SIFContext... contexts ) throws ADKException { reportEvent( new Event( obj, actionCode, contexts ), null, null ); } /* (non-Javadoc) * @see openadk.library.Zone#reportEvent(openadk.library.SIFDataObject, openadk.library.EventAction, java.lang.String) */ public void reportEvent( SIFDataObject obj, EventAction actionCode, String destinationId ) throws ADKException { reportEvent( new Event( obj, actionCode ), destinationId, null ); } /* (non-Javadoc) * @see openadk.library.Zone#reportEvent(openadk.library.Event) */ public void reportEvent( Event event ) throws ADKException { reportEvent( event, null, null ); } /** * Reports an event with a specific destination ID specified * @param event The event to report * @param destinationId the destination id for this event * @throws ADKException */ public void reportEvent( Event event, String destinationId ) throws ADKException { reportEvent( event, destinationId, null ); } /** * Reports an event with a specific destination ID and SIF message Id specified * @param event The event to report * @param destinationId the destination id for this event * @param sifMsgId The SIF message ID to use * @throws ADKException */ public void reportEvent( Event event, String destinationId, String sifMsgId ) throws ADKException { _checkConnect(); SIF_Ack ack = fPrimitives.sifEvent( this, event, destinationId, sifMsgId ); if( ack.hasError() ) ADKUtils._throw( new SIFException(ack,this),log ); } /** * Report an informative message to the zone in the form of a SIF_LogEntry object.<p> * * @param desc A textual description * @throws ADKException */ public void reportInfoLogEntry( String desc ) throws ADKException { getServerLog().log( desc ); } /** * Report an informative message to the zone in the form of a SIF_LogEntry object.<p> * * @param desc A textual description * @param extDesc An optional extended description, or null if not applicable * @param appCode An optional application-defined code * @param objects An optional array of one or more SIFDataObjects to be * included with the SIF_LogEntry, or null if not applicable * @throws ADKException */ public void reportInfoLogEntry( String desc, String extDesc, String appCode, SIFDataObject... objects ) throws ADKException { getServerLog().log( LogLevel.INFO, desc, extDesc, appCode, null, objects ); } /** * Report a warning message to the zone in the form of a SIF_LogEntry object.<p> * * @param desc A textual description * @param extDesc An optional extended description, or null if not applicable * @param category The SIF_LogEntry category * @param code The SIF_LogEntry code * @param appCode An optional application-defined code * @param objects An optional array or sequence of one or more SIFDataObjects to be * included with the SIF_LogEntry, or null if not applicable * @throws ADKException */ public void reportWarningLogEntry( String desc, String extDesc, int category, int code, String appCode, SIFDataObject... objects ) throws ADKException { getServerLog().log( LogLevel.WARNING, desc, extDesc, appCode, category, code, null, objects ); } /** * Report a warning message to the zone in the form of a SIF_LogEntry object. * Use this form of the <code>reportWarningLogEntry</code> method if the warning * references a SIF_Message received by the agent.<p> * * @param msgInfo A SIFMessageInfo instance describing the SIF_Message that * to which this warning relates. A SIFMessageInfo value is passed to all * message handler functions by the ADK. * @param desc A textual description * @param extDesc An optional extended description, or null if not applicable * @param category The SIF_LogEntry category * @param code The SIF_LogEntry code * @param appCode An optional application-defined code * @param objects An optional array or sequence of one or more SIFDataObjects to be * included with the SIF_LogEntry, or null if not applicable * @throws ADKException */ public void reportWarningLogEntry( SIFMessageInfo msgInfo, String desc, String extDesc, int category, int code, String appCode, SIFDataObject... objects ) throws ADKException { getServerLog().log( LogLevel.WARNING, desc, extDesc, appCode, category, code, msgInfo, objects ); } /** * Report an error message to the zone in the form of a SIF_LogEntry object.<p> * * @param desc A textual description * @param extDesc An optional extended description, or null if not applicable * @param category The SIF_LogEntry category * @param code The SIF_LogEntry code * @param appCode An optional application-defined code * @param objects An optional array or sequence (varargs) of one or more SIFDataObjects to be * included with the SIF_LogEntry, or null if not applicable * @throws ADKException */ public void reportErrorLogEntry( String desc, String extDesc, int category, int code, String appCode, SIFDataObject... objects ) throws ADKException { getServerLog().log( LogLevel.ERROR, desc, extDesc, appCode, category, code, null, objects ); } /** * Report an error message to the zone in the form of a SIF_LogEntry object. * Use this form of the <code>reportErrorLogEntry</code> method if the error * references a SIF_Message received by the agent.<p> * * @param msgInfo A SIFMessageInfo instance describing the SIF_Message that * to which this error relates. A SIFMessageInfo value is passed to all * message handler functions by the ADK. * @param desc A textual description * @param extDesc An optional extended description, or null if not applicable * @param category The SIF_LogEntry category * @param code The SIF_LogEntry code * @param appCode An optional application-defined code * @param objects An optional array or sequence (varargs) of one or more * SIFDataObjects to be included with the SIF_LogEntry, or null if not applicable * @throws ADKException */ public void reportErrorLogEntry( SIFMessageInfo msgInfo, String desc, String extDesc, int category, int code, String appCode, SIFDataObject... objects ) throws ADKException { getServerLog().log( LogLevel.ERROR, desc, extDesc, appCode, category, code, msgInfo, objects ); } /* (non-Javadoc) * @see openadk.library.Provisioner#setPublisher(openadk.library.Publisher) */ public void setPublisher( Publisher publisher ) throws ADKException { fProvMatrix.setPublisher( publisher ); } /* (non-Javadoc) * @see openadk.library.Provisioner#setPublisher(openadk.library.Publisher, openadk.library.ElementDef) */ public void setPublisher(Publisher publisher, ElementDef objectType) throws ADKException { fProvMatrix.setPublisher(publisher, objectType ); provision(); } /* (non-Javadoc) * @see openadk.library.Provisioner#setPublisher(openadk.library.Publisher, openadk.library.ElementDef, openadk.library.PublishingOptions) */ public void setPublisher( Publisher publisher, ElementDef objectType, PublishingOptions options ) throws ADKException { fProvMatrix.setPublisher( publisher, objectType, options ); provision(); } /* (non-Javadoc) * @see openadk.library.Provisioner#setReportPublisher(openadk.library.ReportPublisher) */ public void setReportPublisher(ReportPublisher publisher) throws ADKException { fProvMatrix.setReportPublisher( publisher ); } /* (non-Javadoc) * @see openadk.library.Provisioner#setReportPublisher(openadk.library.ReportPublisher, openadk.library.ReportPublishingOptions) */ public void setReportPublisher( ReportPublisher publisher, ReportPublishingOptions options ) throws ADKException { fProvMatrix.setReportPublisher( publisher, options ); provision(); } /* (non-Javadoc) * @see openadk.library.Provisioner#setSubscriber(openadk.library.Subscriber, openadk.library.ElementDef) */ public void setSubscriber(Subscriber subscriber, ElementDef objectType) throws ADKException { fProvMatrix.setSubscriber(subscriber, objectType ); provision(); } /* (non-Javadoc) * @see openadk.library.Provisioner#setSubscriber(openadk.library.Subscriber, openadk.library.ElementDef, openadk.library.SubscriptionOptions) */ public void setSubscriber( Subscriber subscriber, ElementDef objectType, SubscriptionOptions options ) throws ADKException { fProvMatrix.setSubscriber( subscriber, objectType, options ); provision(); } /* (non-Javadoc) * @see openadk.library.Provisioner#setQueryResults(openadk.library.QueryResults) */ public void setQueryResults( QueryResults queryResults ) throws ADKException { fProvMatrix.setQueryResults(queryResults ); } /* (non-Javadoc) * @see openadk.library.Provisioner#setQueryResults(openadk.library.QueryResults, openadk.library.ElementDef) */ public void setQueryResults(QueryResults queryResults, ElementDef objectType) throws ADKException { fProvMatrix.setQueryResults( queryResults, objectType ); } /* (non-Javadoc) * @see openadk.library.Provisioner#setQueryResults(openadk.library.QueryResults, openadk.library.ElementDef, openadk.library.QueryResultsOptions) */ public void setQueryResults( QueryResults queryResults, ElementDef objectType, QueryResultsOptions flags ) throws ADKException { fProvMatrix.setQueryResults( queryResults, objectType, flags ); } /** * Gets the Publisher for a SIF object type * @param context The SIFContext to obtain the publisher for * @param objectType A SIFDTD constant identifying a SIF Data Object type * (e.g. <code>SIFDTD.STUDENTPERSONAL</code>) * @return The Publisher registered for this object type by the agent when * it called the setPublisher method, or null if no Publisher has been * registered */ protected Publisher getPublisher( SIFContext context, ElementDef objectType ) { if( objectType == ReportingDTD.SIF_REPORTOBJECT ) throw new IllegalArgumentException( "You must call getReportPublisher to obtain the Publisher message handler for SIF_ReportObject"); return fProvMatrix.lookupPublisher( context, objectType ); } /** * Gets the ReportPublisher for the SIF_ReportObject object type.<p> * @return The ReportPublisher registered by the agent when it called the * setReportPublisher method, or null if no ReportPublisher has been * registered */ protected ReportPublisher getReportPublisher( SIFContext context ) { return fProvMatrix.lookupReportPublisher( context ); } protected ReportPublisher getServicePublisher( SIFContext context ) { return fProvMatrix.lookupServicePublisher( context ); } /** * Gets the Subscriber for a SIF object type * @param context The SIF Context to look for the subscriber in * @param objectType A SIFDTD constant identifying a SIF Data Object type * (e.g. <code>SIFDTD.STUDENTPERSONAL</code>) * @return The Subscriber registered for this object type by the agent when * it called the setSubscriber method, or null if no Subscriber has * been registered */ protected Subscriber getSubscriber(SIFContext context, ElementDef objectType ) { return fProvMatrix.lookupSubscriber( context, objectType ); } /** * Gets the QueryResults object for a SIF object type * @param context The SIF Context to use for looking up the handler * @param objectType A SIFDTD constant identifying a SIF Data Object type * (e.g. <code>SIFDTD.STUDENTPERSONAL</code>) * @return The QueryResults object registered for this object type by the * agent when it called the setQueryResults method, or null if no * QueryResults object has been registered */ protected QueryResults getQueryResults(SIFContext context, ElementDef objectType ) { return fProvMatrix.lookupQueryResults( context, objectType ); } /* (non-Javadoc) * @see openadk.library.Zone#query(openadk.library.Query) */ public String query( Query query ) throws ADKException { return query(query,null,null,0,null); } /* (non-Javadoc) * @see openadk.library.Zone#query(openadk.library.Query, openadk.library.MessagingListener) */ public String query( Query query, MessagingListener listener ) throws ADKException { return query(query,listener,null,0,null); } /* (non-Javadoc) * @see openadk.library.Zone#query(openadk.library.Query, int) */ public String query( Query query, int queryOptions ) throws ADKException { return query(query,null,null,queryOptions,null); } /* (non-Javadoc) * @see openadk.library.Zone#query(openadk.library.Query, openadk.library.MessagingListener, int) */ public String query( Query query, MessagingListener listener, int queryOptions ) throws ADKException { return query(query,listener,null,queryOptions,null); } /* (non-Javadoc) * @see openadk.library.Zone#query(openadk.library.Query, java.lang.String, int) */ public String query( Query query, String destinationId, int queryOptions ) throws ADKException { return query(query,null,destinationId,queryOptions,null); } /* (non-Javadoc) * @see openadk.library.Zone#query(openadk.library.Query, openadk.library.MessagingListener, java.lang.String, int) */ public String query( Query query, MessagingListener listener, String destinationId, int queryOptions ) throws ADKException { return query( query, listener, destinationId, queryOptions, null ); } /** * Undocumented version of the <code>query</code> method that allows the caller to * specify the SIF_MsgId that will be used in the SIF_Message/SIF_Header.<p> * * This method is not part of the public Zone interface. It is intended to be used * by agents that must decide upon the SIF_MsgId of a query before the ADK is called, * or that want to choose the SIF_MsgId that will be used for a query for debugging * purposes or to satisfy some other special requirement. (SIFWorks Student Locator * agent is an example of an agent that requires this method.) * * @param query A Query object describing the parameters of the query, * including optional conditions and field restrictions * @param listener A MessagingListener that will be notified when the * SIF_Request message is sent to the zone. Any other MessagingListeners * registered with the zone will also be called. * @param destinationId The SourceId of the agent to which the SIF Request * will be routed by the zone integration server * @param queryOptions Reserved for future use * @param sifMsgId The value to assign to the SIF_Message/SIF_Header/SIF_MsgId * element, or <code>null</code> to let the framework assign its own value * * @return The SIF_MsgId of the SIF_Request that was sent to the zone. * @throws ADKException If the query cannot be sent * * @since ADK 1.5 */ public String query( Query query, MessagingListener listener, String destinationId, int queryOptions, String sifMsgId ) throws ADKException { // Synchronized to prevent MessageDispatcher from processing a SIF_Response (on the // zone thread) while this thread is issuing a SIF_Request. This is required to ensure // that the query() method completes before any SIF_Response processing is performed. // It can lead to situations where the RequestCache is not updated in time, and/or // the QueryResults.onQueryResults method is called before the onQueryPending method // is called to notify the agent that a request was issued. // synchronized( fReqLock ) { _checkConnect(); SIF_Request req = null; try { SIF_Ack ack = fPrimitives.sifRequest(this,query,destinationId,sifMsgId); if( ack.hasError() ) ADKUtils._throw( new SIFException(ack,this),log ); req = (SIF_Request)ack.message; // Record the request in the agent's RequestCache RequestInfo ri = getFDispatcher().fRequestCache.storeRequestInfo( req, query, this ); // Call the onQueryResults.onQueryPending method... QueryResults target = getFDispatcher().getQueryResultsTarget(null,req,null,query,this); if( target != null ) { SIFMessageInfo inf = new SIFMessageInfo(ack.message,this); inf.setSIFRequestMsgId( inf.getMsgId() ); inf.setSIFRequestVersion( query.getSIFVersions() ); inf.setSIFRequestObjectType( query.getObjectType() ); inf.setSIFRequestInfo( ri ); try { if( BuildOptions.PROFILED ) ProfilerUtils.profileStart( String.valueOf( openadk.profiler.api.OIDs.ADK_SIFREQUEST_REQUESTOR_MESSAGING ), query.getObjectType(), inf.getMsgId() ); target.onQueryPending( inf,this ); } finally { if( BuildOptions.PROFILED ) ProfilerUtils.profileStop(); } } } catch( ADKException adke ) { throw adke; } // New to 1.5: Return the SIF_Request/SIF_MsgId to the caller return req.getHeader().getSIF_MsgId(); } } /** * Blocks the calling thread until Zone.onQuery has completed. * @see #query(Query) */ protected void waitForRequestsToComplete() { synchronized( fReqLock ) { // do nothing - synchronization on fReqLock hidden within this method // so we can shield the caller from knowledge of our protected data // members and have the room to expand on this functionality later on. } } private SIF_ZoneStatus cacheSIF_ZoneStatus(SIF_ZoneStatus potentialZoneStatus) { if (potentialZoneStatus != null) { lastReceivedZoneStatus = potentialZoneStatus; } return potentialZoneStatus; } /** * Gets the SIF_ZoneStatus object from the ZIS managing this zone. The * method blocks until a result is received. */ public SIF_ZoneStatus getZoneStatus() throws ADKException { return getZoneStatus( getProperties().getDefaultTimeout() ); } /** * Gets the SIF_ZoneStatus object from the ZIS managing this zone. The * method blocks for the specified timeout period.<p> * * <b>Note</b> getZoneStatus cannot be called from the Subscriber.onEvent * method when the agent is operating in Pull mode or a deadlock situation * may arise. If you must obtain a SIF_ZoneStatus while processing a * SIF Event, use the TrackQueryResults object to first issue a query for * the object, then call this method to wait for the reply to be * received. TrackQueryObjects is the only safe way to perform queries * while processing SIF Events because it can invoke Selective Message * Blocking if necessary.<p> * * @param timeout The amount of time to wait for a SIF_ZoneStatus object to * be received by the agent (or 0 to wait infinitely) * @returns The SIF_ZoneStatus object, or null if no response was received * within the specified timeout period * * @exception ADKException is thrown if an error occurs */ public SIF_ZoneStatus getZoneStatus( int timeout ) throws ADKException { _checkConnect(); synchronized( fZSLock ) { try { fState |= GETZONESTATUS; if( getProperties().getUseZoneStatusSystemControl() )//&& ADK.getSIFVersion().compareTo( SIFVersion.SIF15 ) >= 0 ) { // // SIF 1.5 EXPERIMENTAL / SIF 2.0 Official - Use a SIF_SystemControl message to // immediately retrieve the SIF_ZoneStatus // SIF_Ack ack = fPrimitives.sifZoneStatus(this); if( ack.hasError() ) throw new SIFException( ack, this ); // The SIF_ZoneStatus object will be in the SIF_Ack/SIF_Data // The SIF_AgentACL object will be in the SIF_Ack/SIF_Data SIF_Status status = ack.getSIF_Status(); if (status != null) { SIF_Data data = status.getSIF_Data(); if (data != null) { List<SIFElement> childList = data.getChildList( InfraDTD.SIF_ZONESTATUS ); if (childList != null && childList.size() > 0) { return cacheSIF_ZoneStatus((SIF_ZoneStatus)childList.get(0)); } } } return null; } else { fZoneStatus = null; int pullState = 0; SIF_Ack ack = fPrimitives.sifRequest(this,new Query( InfraDTD.SIF_ZONESTATUS ),null,null); if( ack.hasError() ) throw new SIFException(ack,this); // ** When in pull mode: force a pull immediately regardless of // the usual pull frequency (e.g. if it is 22 seconds until the // next pull we don't want to sit and wait here for the // SIF_ZoneStatus to be returned in 22 seconds.) ** boolean isPull = getProperties().getMessagingMode() == AgentMessagingMode.PULL; if( isPull ) { do { pullState = getFDispatcher().pull(); } while (fZoneStatus == null && pullState == 1); } // Wait for MessageDispatcher to hand us the response if( fZoneStatus == null ) { try { if( ( ADK.debug & ADK.DBG_MESSAGING ) != 0 ) log.debug("Waiting "+timeout+"ms for SIF_ZoneStatus reply..."); fZSLock.wait(timeout); if( ( ADK.debug & ADK.DBG_MESSAGING ) != 0 ) log.debug("Received SIF_ZoneStatus reply..."); } catch( InterruptedException ie ) { fZoneStatus = null; } } } } finally { fState &= ~GETZONESTATUS; } } return cacheSIF_ZoneStatus(fZoneStatus); } /** * Called by MessageDispatcher to inform this Zone of a SIF_Response * received for the SIF_ZoneStatus object type. */ protected void setZoneStatus( SIF_ZoneStatus stat ) { synchronized( fZSLock ) { fZoneStatus = stat; fZSLock.notifyAll(); } } /** * Gets the latest version of SIF that should be used for messaging in this zone. * * <p>The implementation checks the AgentProperties-ZisVersion property and the * ADK-SIFVersion property. The lowest version between the two properties is returned.</p> * * <p>For example, if the ADK-SIFVersion property is set to 1.5r1, SIFVersion.15r1 will * be returned. If, however, the ADK-SIFVersion property is set to 2.1, but the * AgentProperties-ZisVersion property is set to "1.1", SIFVersion.11 will be returned.</p> * * This behavior allows the ADK to be configured in one of two ways to support older versions * of SIF * <ol> * <li> Support an older version across the board. If this is the desired behavior, setting * the ADK-SIFVersion property is the proper way to achieve this.</li> * * <li> Run using the latest version of SIF, but connect to specific zones using an older * version of SIF. In this case, setting the AgentProperties-ZisVersion is the proper way * to achieve this.</li> * </ol> * * <p><b>NOTE:</b> This property is primarily for internal use only and is meant for use with * messages that are meant to be sent directly to the ZIS, which include the provisioning * messages (SIF_Register, SIF_Subscribe, SIF_Provide, etc.), and the system messages * (SIF_SystemControl).</p> * * <p>To override behavior of messages that are sent to another agent (SIF_Event, SIF_Request), * the ADK supports a more granular approach for dealing with versions down to the object * level. To accomplish this, use ADK object versioning policy</p> * * @see PolicyFactory * @see ADKDefaultPolicy * * @return The latest SIFVersion that should be used for messaging in this zone. */ public SIFVersion getHighestEffectiveZISVersion() { SIFVersion zisVersion = SIFVersion.LATEST; try { zisVersion = SIFVersion.parse( fProps.getZisVersion() ); } catch ( IllegalArgumentException iae ){ // Unable to parse the ZIS Version from props getLog().warn( "Unable to parse adk.provisioning.zisVersion value:'" + fProps.getZisVersion() + "' into a SIFVersion instance. Using " + SIFVersion.LATEST.toString(), iae ); } SIFVersion adkVersion = ADK.getSIFVersion(); if( zisVersion.compareTo( adkVersion ) < 0 ){ return zisVersion; } else { return adkVersion; } } /** * Gets the Agent ACL list from the zone. This method invokes the zone * synchronously before returning. * @return the SIF_AgentACL object returned from the ZIS * @throws ADKException */ public SIF_AgentACL getAgentACL() throws ADKException { _checkConnect(); SIF_Ack ack = fPrimitives.sifGetAgentACL(this); if( ack.hasError() ){ throw new SIFException( ack, this ); } // The SIF_AgentACL object will be in the SIF_Ack/SIF_Data SIF_Status status = ack.getSIF_Status(); if (status != null) { SIF_Data data = status.getSIF_Data(); if (data != null) { List<SIFElement> childList = data.getChildList( InfraDTD.SIF_AGENTACL ); if (childList != null && childList.size() > 0) { return (SIF_AgentACL)childList.get(0); } } } return null; } /** * Register a <i>MessagingListener</i> to listen to messages received by the * message handlers of this class.<p> * * NOTE: Agents may register a MessagingListener with the Agent or Zone * classes. When a listener is registered with both classes, it will be * called twice. Consequently, it is recommended that most implementations * choose to register MessagingListeners with only one of these classes * depending on whether the agent is interested in receiving global * notifications or notifications on only a subset of zones.<p> * * @param listener a MessagingListener implementation */ public void addMessagingListener( MessagingListener listener ) { fMessagingListeners.add( listener ); } /** * Remove a <i>MessagingListener</i> previously registered with the * <code>addMessagingListener</code> method.<p> * * @param listener a MessagingListener implementation */ public void removeMessagingListener( MessagingListener listener ) { fMessagingListeners.remove( listener ); } public void setErrorHandler( UndeliverableMessageHandler handler ) { fErrHandler = handler; } public UndeliverableMessageHandler getErrorHandler() { if( fErrHandler != null ) return fErrHandler; return fAgent.getErrorHandler(); } public SIF_Ack sifSend( String xml ) throws ADKException { _checkConnect(); SIF_Ack ack = null; try { if( ( ADK.debug & ADK.DBG_MESSAGE_CONTENT ) != 0 ) { log.debug( "Sending user message ("+xml.length()+" bytes): " ); log.debug( xml ); } String ackStr = fProtocolHandler.send( xml ); // Parse the results into a SIF_Ack SIFParser parser = SIFParser.newInstance(); ack = (SIF_Ack)parser.parse(ackStr,this,0); if( ack != null ) { ack.message = null; if( ( ADK.debug & ADK.DBG_MESSAGING_PULL ) != 0 ) { ack.LogRecv(log); } } } catch( Exception e ) { throw new ADKException( e.toString(), this ); } return ack; } public SIF_Ack sifCancelRequests(String destinationId, String[] sif_MsgIds) throws ADKException { _checkConnect(); SIF_Ack ack = null; try { if( ( ADK.debug & ADK.DBG_MESSAGE_CONTENT ) != 0 ) { log.debug( "Sending Cancel Requests message : " ); log.debug( sif_MsgIds.toString() ); } _checkConnect(); ack = fPrimitives.sifCancelRequests(this,destinationId,sif_MsgIds); } catch( Exception e ) { throw new ADKException( e.toString(), this ); } return ack; } /** * Sends a SIF_Register message to the ZIS. This method can be called by * agents that have chosen to use Agent-managed provisioning. If ZIS-managed * or ADK-managed provisioning is enabled for this zone, the method has no * effect.<p> */ public SIF_Ack sifRegister() throws ADKException { _checkConnect(); if( getProperties().getProvisioningMode() == AgentProvisioningMode.AGENT ){ return fPrimitives.sifRegister(this); } return null; } /** * Sends a SIF_Unregister message to the ZIS. This method can be called by * agents that have chosen to use Agent-managed provisioning. If ZIS-managed * or ADK-managed provisioning is enabled for this zone, the method has no * effect.<p> */ public SIF_Ack sifUnregister() throws ADKException { _checkConnect(); if( getProperties().getProvisioningMode() == AgentProvisioningMode.AGENT ){ return fPrimitives.sifUnregister(this); } return null; } /** * Sends a SIF_Subscribe message to the ZIS. This method can be called by * agents that have chosen to use Agent-managed provisioning. If ZIS-managed * or ADK-managed provisioning is enabled for this zone, the method has no * effect. */ public SIF_Ack sifSubscribe( String[] objectType ) throws ADKException { _checkConnect(); if( getProperties().getProvisioningMode() == AgentProvisioningMode.AGENT ){ return fPrimitives.sifSubscribe(this,objectType); } return null; } /** * Sends a SIF_Unsubscribe message to the ZIS. */ public SIF_Ack sifUnsubscribe( String[] objectType ) throws ADKException { _checkConnect(); if( getProperties().getProvisioningMode() != AgentProvisioningMode.ZIS ){ return fPrimitives.sifUnsubscribe( this,objectType ); } return null; } /** * Sends a SIF_Provide message to the ZIS. This method can be called by * agents that have chosen to use Agent-managed provisioning. If ZIS-managed * or ADK-managed provisioning is enabled for this zone, the method has no * effect. */ public SIF_Ack sifProvide( String[] objectType ) throws ADKException { _checkConnect(); if( getProperties().getProvisioningMode() == AgentProvisioningMode.AGENT ){ return fPrimitives.sifProvide( this,objectType ); } return null; } /** * Sends a SIF_Unprovide message to the ZIS. */ public SIF_Ack sifUnprovide( String[] objectType ) throws ADKException { _checkConnect(); if( getProperties().getProvisioningMode() != AgentProvisioningMode.ZIS ){ fPrimitives.sifUnprovide( this,objectType ); } return null; } /** * Sends a SIF_Ping message to the ZIS that manages this zone. */ public SIF_Ack sifPing() throws ADKException { _checkConnect(); return fPrimitives.sifPing(this); } /** * Sends a SIF_Sleep System Control message to the zone * @return the SIF_Ack returned from the ZIS * @throws ADKException */ public SIF_Ack sifSleep() throws ADKException { _checkConnect(); return fPrimitives.sifSleep(this); } /** * Sends a SIF_Wakeup System Control message to the Zone * @return he SIF_Ack returned from the ZIS * @throws ADKException */ public SIF_Ack sifWakeup() throws ADKException { _checkConnect(); return fPrimitives.sifWakeup(this); } /* (non-Javadoc) * @see openadk.library.Zone#purgeQueue(boolean, boolean) */ public void purgeQueue( boolean incoming, boolean outgoing ) throws ADKException { } /** * Assigns an application-supplied object to this Zone * @param data Any object the application wishes to attach to this Zone instance */ public void setUserData( Object data ) { fUserData = data; } /** * Gets the application-supplied object for this Zone * @return The object passed to the <code>setUserData</code> method */ public Object getUserData() { return fUserData; } /** * Returns the string representation of this zone as "zoneId@zoneUrl" */ @Override public String toString() { StringBuffer b = new StringBuffer(fZoneId); b.append('@'); b.append(fZoneUrl); return b.toString(); } protected void _checkConnect() throws ADKException { if( !isConnected() ) throw new ADKZoneNotConnectedException( "Zone is not connected", this ); } /** * Gets the root Log4j Category for this agent. */ public Logger getLog() { return Agent.getLog( this ); } /** * Gets the ServerLog for the zone.<p> * @return The ServerLog instance for the zone */ public ServerLog getServerLog() { return ServerLog.getInstance( "ADK.Agent$"+getZoneId(),this ); } /** * Returns the ProtocolHandler being used for the zone * @return the ProtocolHandler being used for the zone */ public IProtocolHandler getProtocolHandler() { return fProtocolHandler; } public void setFDispatcher(MessageDispatcher fDispatcher) { this.fDispatcher = fDispatcher; } public MessageDispatcher getFDispatcher() { return fDispatcher; } /** * Undocumented version of the <code>query</code> method that allows the caller to * specify the SIF_MsgId that will be used in the SIF_Message/SIF_Header.<p> * * This method is not part of the public Zone interface. It is intended to be used * by agents that must decide upon the SIF_MsgId of a query before the ADK is called, * or that want to choose the SIF_MsgId that will be used for a query for debugging * purposes or to satisfy some other special requirement. (SIFWorks Student Locator * agent is an example of an agent that requires this method.) * * @param query A Query object describing the parameters of the query, * including optional conditions and field restrictions * @param listener A MessagingListener that will be notified when the * SIF_Request message is sent to the zone. Any other MessagingListeners * registered with the zone will also be called. * @param destinationId The SourceId of the agent to which the SIF Request * will be routed by the zone integration server * @param queryOptions Reserved for future use * @param sifMsgId The value to assign to the SIF_Message/SIF_Header/SIF_MsgId * element, or <code>null</code> to let the framework assign its own value * * @return The SIF_MsgId of the SIF_Request that was sent to the zone. * @throws ADKException If the query cannot be sent * * @since ADK 1.5 */ public String invokeService( Zone zone, ServiceRequestInfo requestInfo, SIFElement payload ) throws ADKException { // Synchronized to prevent MessageDispatcher from processing a SIF_Response (on the // zone thread) while this thread is issuing a SIF_Request. This is required to ensure // that the query() method completes before any SIF_Response processing is performed. // It can lead to situations where the RequestCache is not updated in time, and/or // the QueryResults.onQueryResults method is called before the onQueryPending method // is called to notify the agent that a request was issued. // synchronized( fReqLock ) { _checkConnect(); SIF_ServiceInput req = null; try { SIF_Ack ack = fPrimitives.sifServiceRequest(zone, requestInfo, payload); if( ack.hasError() ) ADKUtils._throw( new SIFException(ack,this),log ); req = (SIF_ServiceInput)ack.message; // Record the request in the agent's RequestCache // RequestInfo ri = getFDispatcher().fRequestCache.storeServiceRequestInfo( req, query, this ); // Call the onQueryResults.onQueryPending method... /* QueryResults target = getFDispatcher().getQueryResultsTarget(null,req,null,query,this); if( target != null ) { SIFMessageInfo inf = new SIFMessageInfo(ack.message,this); inf.setSIFRequestMsgId( inf.getMsgId() ); inf.setSIFRequestVersion( query.getSIFVersions() ); inf.setSIFRequestObjectType( query.getObjectType() ); inf.setSIFRequestInfo( ri ); try { if( BuildOptions.PROFILED ) ProfilerUtils.profileStart( String.valueOf( openadk.sifprofiler.api.OIDs.ADK_SIFREQUEST_REQUESTOR_MESSAGING ), query.getObjectType(), inf.getMsgId() ); target.onQueryPending( inf,this ); } finally { if( BuildOptions.PROFILED ) ProfilerUtils.profileStop(); } } */ } catch( ADKException adke ) { throw adke; } // New to 1.5: Return the SIF_Request/SIF_MsgId to the caller return req.getHeader().getSIF_MsgId(); } } /* * JEN Services */ private HashMap<String, SIFZoneService> serviceProviders = new HashMap<String, SIFZoneService> (); // onRequest private HashMap<String, SIFZoneServiceProxy> serviceSubscribers = new HashMap<String, SIFZoneServiceProxy> (); // onQueryResults private HashMap<String, ArrayList<SIFZoneServiceProxy>> serviceNotifiers = new HashMap<String, ArrayList<SIFZoneServiceProxy>>(); /* * */ protected SIFZoneService getServicePublisher(String serviceName ) { return serviceProviders.get(serviceName); } public void setServiceProviders(String serviceName, SIFZoneService sifZoneService) { serviceProviders.put(serviceName, sifZoneService); } protected SIFZoneServiceProxy getServiceSubscriber(String serviceName ) { return serviceSubscribers.get(serviceName); } public void setServiceSubscribers(String serviceName, SIFZoneServiceProxy sifZoneServiceProxy) { serviceSubscribers.put(serviceName, sifZoneServiceProxy); } protected ArrayList<SIFZoneServiceProxy> getServiceNotifiers(String serviceName) { return serviceNotifiers.get(serviceName); } public void setServiceNotifier(String serviceName, SIFZoneServiceProxy sifZoneServiceProxy) { ArrayList<SIFZoneServiceProxy> list = getServiceNotifiers(serviceName); if (list == null) list = new ArrayList<SIFZoneServiceProxy>(); list.add(sifZoneServiceProxy); serviceNotifiers.put(serviceName, list); } public void reportServiceEvent(ServiceEvent event, String destinationId) throws ADKException { _checkConnect(); SIF_Ack ack = fPrimitives.sifServiceEvent(this, event, destinationId); if( ack.hasError() ) ADKUtils._throw( new SIFException(ack,this),log ); } }