/*
* This file is part of the WfMOpen project.
* Copyright (C) 2001-2006 Danet GmbH (www.danet.de), BU BTS.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* $Id: WfXmlAuditHandler.java 2292 2007-03-01 12:33:11Z schnelle $
*
* $Log$
* Revision 1.9 2007/02/17 21:19:48 mlipp
* Workflow service caching redone.
*
* Revision 1.8 2007/02/06 08:35:34 schnelle
* Started automatic generation of wsdl description.
*
* Revision 1.7 2007/01/31 22:55:36 mlipp
* Some more refactoring and fixes of problems introduced by refactoring.
*
* Revision 1.6 2007/01/31 14:53:06 schnelle
* Small corrections wvaluating the resource reference.
*
* Revision 1.5 2007/01/31 12:24:07 drmlipp
* Design revisited.
*
* Revision 1.4 2007/01/30 21:04:17 mlipp
* Fixed package/process id retrieval.
*
* Revision 1.3 2007/01/30 13:11:37 schnelle
* Corrected check to decide the sending of a completed message.
*
* Revision 1.2 2007/01/30 11:56:14 drmlipp
* Merged Wf-XML branch.
*
* Revision 1.1.2.10 2007/01/29 15:04:20 schnelle
* Renaming of Observer to ObserverRegistry and URIDecoder to ResourceReference.
*
* Revision 1.1.2.9 2007/01/29 13:40:32 schnelle
* Storing of the sender base in the servlet context.
*
* Revision 1.1.2.8 2007/01/26 15:50:29 schnelle
* Added encoding for process id and package id.
*
* Revision 1.1.2.7 2007/01/24 14:22:39 schnelle
* Observer handler starts on servlet startup.
*
* Revision 1.1.2.6 2007/01/24 10:56:50 schnelle
* Prepared return of a result for aobservers.
*
* Revision 1.1.2.5 2007/01/16 11:05:42 schnelle
* Refactoring: Moved subscription handling methods to own class.
*
* Revision 1.1.2.4 2007/01/11 11:37:10 schnelle
* Added subscription if an oberver key is given in the creation of a process.
*
* Revision 1.1.2.3 2007/01/11 10:41:53 schnelle
* Sending notification messages to all observers.
*
* Revision 1.1.2.2 2007/01/11 10:23:52 schnelle
* Creation of StateChanged notifications.
*
* Revision 1.1.2.1 2007/01/10 13:41:28 schnelle
* Implemented subscribe.
*
*/
package de.danet.an.workflow.clients.wfxml;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.rmi.RemoteException;
import java.sql.SQLException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Collection;
import java.util.Date;
import java.util.Iterator;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.NameCallback;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.callback.TextOutputCallback;
import javax.security.auth.callback.UnsupportedCallbackException;
import javax.security.auth.login.LoginContext;
import javax.security.auth.login.LoginException;
import javax.xml.soap.SOAPConnection;
import javax.xml.soap.SOAPConnectionFactory;
import javax.xml.soap.SOAPElement;
import javax.xml.soap.SOAPException;
import javax.xml.soap.SOAPMessage;
import javax.xml.transform.TransformerException;
import org.xml.sax.SAXException;
import de.danet.an.workflow.api.EventSubscriber;
import de.danet.an.workflow.api.FactoryConfigurationError;
import de.danet.an.workflow.api.ProcessClosedAuditEvent;
import de.danet.an.workflow.api.SAXEventBuffer;
import de.danet.an.workflow.api.WorkflowService;
import de.danet.an.workflow.api.WorkflowServiceFactory;
import de.danet.an.workflow.clients.wfxml.ObserverRegistry.ObserverInfo;
import de.danet.an.workflow.omgcore.InvalidPerformerException;
import de.danet.an.workflow.omgcore.ProcessData;
import de.danet.an.workflow.omgcore.ResultNotAvailableException;
import de.danet.an.workflow.omgcore.WfAuditEvent;
import de.danet.an.workflow.omgcore.WfAuditHandler;
import de.danet.an.workflow.omgcore.WfStateAuditEvent;
/**
* This class is an event handler foe all audit events of the workflow engine.
* All events are collected and distributed to the subscribed observers.
*
* @author Dirk Schnelle
*
* TODO: Check if this is better implemented as an mbean.
* TODO: Move the functionality of message creation to the response generators.
*/
class WfXmlAuditHandler implements WfAuditHandler {
/** Logger instance. */
private static final org.apache.commons.logging.Log logger
= org.apache.commons.logging.LogFactory.getLog(WfAuditHandler.class);
/** Formatter to produce xsd time stamps. */
protected static DateFormat xsdDateTimeFormat
= new SimpleDateFormat ("yyyy-MM-dd'T'HH:mm:ss");
/** The workflow service. */
private WorkflowService workflowService = null;
/** the observer registry. */
private ObserverRegistry observerRegistry;
/**
* Constructs a new object.
*
* @param wfs the workflow service
* @param obs the observer registry.
*/
public WfXmlAuditHandler (WorkflowService wfs, ObserverRegistry obs) {
workflowService = wfs;
this.observerRegistry = obs;
}
/* (non-Javadoc)
* @see de.danet.an.workflow.omgcore.WfAuditHandler#receiveEvent(de.danet.an.workflow.omgcore.WfAuditEvent)
*/
public void receiveEvent(WfAuditEvent event)
throws InvalidPerformerException,
RemoteException {
String type = event.eventType();
if (!type.equals(WfAuditEvent.PROCESS_STATE_CHANGED)) {
return;
}
WfStateAuditEvent stateEvent = (WfStateAuditEvent) event;
String mgrName = stateEvent.processMgrName();
String[] ids = mgrName.split("/");
String packageId = ids[0];
String processId = ids[1];
String processKey = stateEvent.processKey();
Collection observers;
try {
observers = observerRegistry.getObservers(packageId, processId, processKey);
} catch (SQLException sqle) {
logger.warn("error retrieving observer list", sqle);
return;
}
for (Iterator iterator=observers.iterator(); iterator.hasNext();) {
ObserverInfo observerInfo = (ObserverInfo)iterator.next();
ResourceReference resRef = new ResourceReference
(observerInfo.getSenderBase(),
packageId, processId, event.processKey());
InstanceResponseGenerator irg = new InstanceResponseGenerator
(observerRegistry, workflowService, resRef);
try {
sendStateChanged
(observerInfo.getObserverKey(), irg, stateEvent);
if (stateEvent instanceof ProcessClosedAuditEvent) {
ProcessData result
= ((ProcessClosedAuditEvent)stateEvent).result();
sendCompleted (observerInfo.getObserverKey(), irg, result);
}
} catch (IllegalStateException e) {
logger.warn ("Problem sending event to "
+ observerInfo.getObserverKey() + ": "
+ e.getMessage(), e);
try {
observerRegistry.unsubscribe(observerInfo.getObserverKey());
} catch (SQLException ee) {
logger.warn("error removing observer", ee);
return;
}
}
}
// Do some cleanup if the process is no more available.
if (stateEvent.newState().startsWith("closed")) {
try {
observerRegistry.unsubscribe(packageId, processId, processKey);
} catch (SQLException e) {
logger.warn("error unsubscribing observer", e);
return;
}
}
}
/**
* Sends a {@link Consts.STATE_CHANGE_REQUEST} message to all observers.
* @param observers observers.
* @param newState new state.
* @param oldState old state.
*/
private void sendStateChanged
(String observer, InstanceResponseGenerator irg,
WfStateAuditEvent event) {
String newState = StateMapper.omg2asapState(event.newState());
String oldState = StateMapper.omg2asapState(event.oldState());
try {
SOAPMessage message
= irg.createStateChangedMessage (observer, newState, oldState);
call(message, observer);
} catch (MalformedURLException e) {
throw (IllegalStateException)
(new IllegalStateException (e.getMessage()).initCause(e));
} catch (SOAPException e) {
throw (IllegalStateException)
(new IllegalStateException (e.getMessage()).initCause(e));
}
}
/**
* Sends a {@link Consts.STATE_CHANGE_REQUEST} message to all observers.
* @param obs observer persistence.
* @param observers observers.
* @param result result of the process.
* @throws SAXException
* @throws TransformerException
* @throws ResultNotAvailableException
* @throws RemoteException
*/
private void sendCompleted(String observer, InstanceResponseGenerator irg,
ProcessData result) throws RemoteException {
try {
SOAPMessage message = irg.createCompletedMessage(observer, result);
call(message, observer);
} catch (MalformedURLException e) {
throw (IllegalStateException)
(new IllegalStateException (e.getMessage()).initCause(e));
} catch (SOAPException e) {
throw (IllegalStateException)
(new IllegalStateException (e.getMessage()).initCause(e));
}
}
/**
* Calls the default endpoint with the given message.
* @param request the SOAP request.
* @return SOAP response.
* @throws SOAPException
* @throws MalformedURLException
*/
private void call(SOAPMessage message, String observer)
throws SOAPException, MalformedURLException {
SOAPConnectionFactory factory = SOAPConnectionFactory.newInstance();
SOAPConnection connection = factory.createConnection();
URL endpoint = new URL(observer);
connection.call(message, endpoint);
}
}