20、协作工作流应用中的服务集成与事件通知

协作工作流应用中的服务集成与事件通知

在协作工作流应用中,任务状态管理、事件通知以及服务集成是非常重要的部分。下面将详细介绍相关的服务和实现机制。

1. 任务状态管理与 StarEntry

在工作流程中, StarEntry 用于反映任务的当前状态,包含了 STAR 信息。 TaskEntry 则对任务上发生的状态变化进行建模。当调用 write 方法时,如果当前的 TaskEntry 存在,会将其移除,并根据 StarEntry 的状态创建一个新的条目。 StarEntry 的状态实际上就是对创建的 TaskEntry 的状态更改命令。若出现非法的状态更改,会在 TaskEntry 的子类中抛出异常。以下是相关代码示例:

// remove the current StarEntry
StarEntry removalTemplate = new StarEntry();
removalTemplate.location = location;
removalTemplate.taskId = taskId;
takeIfExists(removalTemplate);
// write the new StarEntry
Lease lease = space.write(template, null, Lease.FOREVER);

这些状态对象会触发事件通知给已注册状态更改事件的监听器,可利用此机制通知 EventMailbox 监听器活动状态的变化。

2. 事件邮箱服务(Event Mailbox Service)

事件邮箱服务(Mercury)允许客户端存储事件通知。当一个实体向该服务注册后,服务会收集针对该实体的事件,直到实体发起事件的传递。

2.1 事件邮箱接口(EventMailbox)

事件邮箱服务由 EventMailbox 接口定义:

public interface EventMailbox
{
    MailboxRegistration register (long leaseDuration) throws RemoteException;
}

客户端调用 register 方法,会得到一个 MailboxRegistration 对象。

2.2 邮箱注册接口(MailboxRegistration)

MailboxRegistration 接口允许实体管理远程事件传递的时间和位置:

package net.jini.event;

public interface MailboxRegistration
{
    Lease getLease();
    RemoteEventListener getListener();
    void enableDelivery(RemoteEventListener target) throws RemoteException;
    void disableDelivery() throws RemoteException;
}

通过该接口,实体可以获取 RemoteEventListener ,并在需要的方法中使用。实体可以断开网络连接而不用担心丢失事件,事件会由邮箱监听器存储。之后,实体可以激活并调用 enableDelivery 方法指定一个 RemoteEventListener 作为保存事件的目标。

2.3 JavaSpace 的 notify 方法

JavaSpace 接口的 notify 方法用于注册对匹配指定模板的未来传入条目的兴趣:

EventRegistration notify(Entry tmpl, Transaction txn,
              RemoteEventListener listener, long lease,
              MarshalledObject handback)
        throws RemoteException, TransactionException;

当匹配的条目被写入时,指定的 RemoteEventListener 会被通知。调用 notify 时,需要提供租赁时间的上限,即希望 JavaSpaces 服务记住该注册的时长。每次调用 notify 会返回一个 net.jini.core.event.EventRegistration 对象。

2.4 事件邮箱服务与 JavaSpaces 的连接

以下代码展示了如何将事件邮箱服务与 JavaSpaces 连接起来:

EventMailbox mailbox;
JavaSpace space;
// register with the EventMailbox Service
MailboxRegistration reg = mailbox.register (Lease.FOREVER);

// get the remote event listener for the registration
RemoteEventListener listener = reg.getListener();

// create a template to match on
SomeEntry template = new SomeEntry();

// create a MarshalledObject to handback
MarshalledObject handback = new MarshalledObject(myData);

// supply the template, listener, and handback to the JavaSpace notify method
EventRegistration eventReg = space.notify(template,
                                          null,
                                          listener,
                                          Lease.FOREVER,
                                          handback)

当匹配模板的条目被写入 JavaSpace 时,监听器(邮箱)会收到一个 RemoteEvent 对象的通知。可以通过 MailboxRegistration enableDelivery 方法控制通知的传递。

3. 邮箱服务(MailboxService)

MailboxService 提供了对事件邮箱注册和事件传递的管理,以及用户邮箱映射功能。

3.1 邮箱管理器接口(MailboxManager)

MailboxManager 接口是 EventMailbox MailboxRegistration 接口的包装器,允许将用户映射到注册信息:

package org.jworkplace.mailbox;

import java.rmi.RemoteException;
import net.jini.core.lease.LeaseDeniedException;
import net.jini.core.lease.UnknownLeaseException;
import net.jini.core.event.RemoteEventListener;
import net.jini.admin.Administrable;

public interface MailboxManager extends Administrable {

    public void register(String user, long duration) throws RemoteException,
                                    LeaseDeniedException;
    public RemoteEventListener getListener(String user) throws RemoteException;
    public void start(String user, RemoteEventListener listener) throws RemoteException;
    public void pause(String user) throws RemoteException;
    public void stop(String user) throws RemoteException,
                                         UnknownLeaseException;
}
3.2 邮箱服务实现(MailboxService)

MailboxService 实现了 RemoteMailboxManager 接口,以下是其主要代码:

package org.jworkplace.mailbox;

import java.rmi.Remote;
import java.rmi.RemoteException;
import java.rmi.RMISecurityManager;
import java.rmi.server.RemoteObject;
import java.io.File;
import java.io.IOException;

import java.util.Set;
import java.util.Map;
import java.util.HashSet;
import java.util.HashMap;
import java.util.Collection;
import java.util.Collections;

import net.jini.discovery.LookupDiscovery;
import net.jini.discovery.DiscoveryListener;
import net.jini.discovery.DiscoveryEvent;
import net.jini.core.lookup.ServiceRegistrar;
import net.jini.core.entry.Entry;
import net.jini.lookup.entry.Name;
import net.jini.lookup.entry.ServiceInfo;

import net.jini.event.EventMailbox;
import net.jini.event.MailboxRegistration;
import net.jini.lease.LeaseRenewalService;
import net.jini.lease.LeaseRenewalManager;
import net.jini.core.lease.Lease;
import net.jini.core.lease.LeaseDeniedException;
import net.jini.core.lease.UnknownLeaseException;
import net.jini.core.event.RemoteEventListener;

import org.jworkplace.util.ServiceFinder;
import org.jworkplace.service.*;

public class MailboxService extends ServiceImpl implements ServiceAdmin,
                                    RemoteMailboxManager, DiscoveryListener
{
    // The interface to the EventMailbox
    private EventMailbox mailbox;
    // The lease renewal manager to renew the registration leases
    // this could also use the LeaseRenewalService
    private LeaseRenewalManager leaseRenewalMgr;

    // performs Lookup discovery of the EventMailbox service
    private LookupDiscovery mgt;

    // A mapping of user to registration
    private Map userMap;

    public static void main(String args[]) throws Exception {
        if (System.getSecurityManager() == null) {
            System.setSecurityManager(new RMISecurityManager());
        }

        try {
           new MailboxService(args);
        }  catch(IOException e) {
        e.printStackTrace();
        System.exit(1);
        }

        try {
         Thread.sleep(Long.MAX_VALUE);
      }  catch(InterruptedException e) {  }

        System.exit(0);
    }

    //
    public MailboxService(String[] args) throws IOException {
        super(args);

        // Enable a thread safe collection
        userMap = Collections.synchronizedMap(new HashMap());

        // Instantiate the lease renewal manager
        leaseRenewalMgr = new LeaseRenewalManager();

        // now start finding registrars (lookup services)
        mgt = new LookupDiscovery(LookupDiscovery.NO_GROUPS);
        mgt.addDiscoveryListener(this);
        ((LookupDiscovery)mgt).setGroups(LookupDiscovery.ALL_GROUPS);
    }

    // register the supplied user with the EventMailbox service
    // and store the mapping
    public void register(String user, long duration) throws RemoteException, LeaseDeniedException
    {
        MailboxRegistration reg = mailbox.register(duration);
        leaseRenewalMgr.renewFor(reg.getLease(), duration, null);
        userMap.put(user, reg);
    }

    // get the listener for this user
    public RemoteEventListener getListener(String user) throws RemoteException {
        MailboxRegistration reg = (MailboxRegistration)userMap.get(user);
        if(reg != null) {
           return reg.getListener();
        }
        return null;
    }

    // enable delivery to the listener specified
    public void start(String user, RemoteEventListener listener) throws RemoteException {
        MailboxRegistration reg = (MailboxRegistration)userMap.get(user);
        if(reg != null)
           reg.enableDelivery(listener);
    }

    // disable delivery and queue notifications
    public void pause(String user) throws RemoteException {
        MailboxRegistration reg = (MailboxRegistration)userMap.get(user);
        if(reg != null)
           reg.disableDelivery();
    }

    // disable and stop notification
    public void stop(String user) throws RemoteException, UnknownLeaseException {
        // cancel lease
        MailboxRegistration reg = (MailboxRegistration)userMap.get(user);
        if(reg != null) {
           Lease lease = reg.getLease();
           lease.cancel();
        }
    }

    /*
    ** DiscoveryListener Interface
    */
    public synchronized void discarded(DiscoveryEvent e) { }

    // Find the EventMailbox Service
    public synchronized void discovered(DiscoveryEvent de) {
       // get the array of lookup services discovered
       ServiceRegistrar[] registrars = de.getRegistrars();

       // If we already have found the EventMailbox return
       if(mailbox == null) {
         try {
            // loop through the LUS's to find the EventMailbox
            for(int i=0; i < registrars.length; i++) {

               // Call the ServiceFinder utility
               mailbox = (EventMailbox)ServiceFinder.findEMS(registrars[i]);
               if(mailbox != null) {
                   // Terminate the discovery threads
                   mgt.terminate();
                   break;
               }
            }
         }  catch (Exception e) {  e.printStackTrace(); }
       }
    }
}

当用户成为工人(例如被分配到任务)时, WorkflowService 会将用户注册到事件邮箱服务,并创建 StarEntry 模板,注册匹配条目空间通知。当分配给工人的任何 StarEntry 被写入空间时,工人会收到通知。

4. 工作流服务(Workflow Service)

工作流服务在协作工作流应用中起着核心作用,负责管理流程定义和激活流程实例。

4.1 工作流管理器接口(WorkflowManager)

WorkflowManager 接口提供了管理工作流流程定义的方法:

package org.jworkplace.workflow;

import java.rmi.RemoteException;
import net.jini.admin.Administrable;
import com.sun.jini.proxy.UUID;

import org.jworkplace.mailbox.MailboxManager;

/*
** This interface defines the methods required to manage
** workflow process definitions
*/
public interface WorkflowManager extends Administrable {

   // create a workflow process, the UUID is a universal unique
   // identifier generated by the workflow system
   public UUID create(ProcessDef processDef) throws RemoteException;

   // retrieve a process definition
   public ProcessDef getProcessDef(String owner, String processName) throws RemoteException;

   // get all process names
   public String[] getProcessNames(String owner) throws RemoteException;

   // update an existing process
   public void updateProcess(UUID processId, ProcessDef processDef) throws RemoteException;

   // remove an existing process definition
   public void removeProcess(UUID processId) throws RemoteException;

   // get the mailbox manager proxy interface
   public MailboxManager getMailboxManager() throws RemoteException;

   // create a process listener for a type of task
   public void createProcessListener(UUID processId, TaskEntry taskEntry) throws RemoteException;

}

该接口定义了创建、检索、更新和删除流程定义的方法,还可以获取邮箱管理器代理接口,并为特定类型的任务创建流程监听器。

4.2 工作流流程定义(Workflow Process Definition)

工作流流程定义由 ProcessDef 类表示,包含一个唯一的 ID 和一个按时间排序的工作项列表:

public class ProcessDef extends AbstractEntry
{
    // unique id assigned by Workflow service
    private UUID id;

    // The human readable name of the process template
    private String processName;

    // The owner of the process definition
    private String processOwner;

    // A time sorted work activity list
    private Map workItemList;
    public ProcessDef() {
        this(null, null);
    }

    public ProcessDef(String processName, String processOwner) {
          this.processName = processName;
          this.processOwner = processOwner;
          workItemList = Collections.synchronizedSortedMap(new TreeMap());
    }

    public void setId(UUID id) {  this.id = id; }
    public UUID getId() {  return id; }
    public String getProcessName() {  return processName; }
    public String getProcessOwner() {  return processOwner; }
    public void setProcessOwner(String owner) {  this.processOwner = owner; }

    public void addWorkItem(WorkItem item) throws RemoteException
    {
         workItemList.put(item, item);
    }

    public void updateWorkItem(WorkItem item) throws RemoteException
    {
         removeWorkItem(item);
         addWorkItem(item);
    }

    public WorkItem getWorkItem(String id) throws RemoteException {
        System.out.println("getWorkItem: " + id);
        Iterator itr = (workItemList.values()).iterator();
        while(itr.hasNext()) {
          WorkItem item = (WorkItem)itr.next();
          if( item.getId().equals(id)) {
              return item;
          }
        }
        return null;
    }

    public WorkItem getWorkItem(WorkItem template) throws RemoteException
    {
          return getWorkItem(template.getId());
    }

    public WorkItem getFirstItem() throws RemoteException {
        WorkItem[] items = getWorkItems();
        return items[0];
    }

    public WorkItem getNextItem(String id) throws RemoteException
    {
           WorkItem successor = null;
           WorkItem item = getWorkItem(id);
           if(item != null) {
             SortedMap tailView = ((SortedMap)workItemList).tailMap(item);
             Object[] array = (tailView.keySet()).toArray();
             if(array.length > 1)  {
               successor = (WorkItem)array[1];
             }
           }
           return successor;
    }

    public void removeWorkItem(WorkItem template) throws RemoteException
    {
        Iterator itr = (workItemList.values()).iterator();
        while(itr.hasNext()) {
          WorkItem item = (WorkItem)itr.next();
           if( item.getId().equals(template.getId())) {
              workItemList.remove(item);
              break;
           }
        }
    }

    public WorkItem[] getWorkItems() throws RemoteException
    {
          Collection collection = workItemList.values();
          return (WorkItem[])collection.toArray(new WorkItem[0]);
    }
}

工作项( WorkItem )由管理员定义并分配给特定的工人。在更复杂的应用中,分配可以先到特定角色,再到特定个人,这样可以将流程定义与流程执行分离。

4.3 工作流服务实现(WorkflowService)

WorkflowService 实现了 WorkflowManager WorkProcess 接口,负责管理流程定义和激活流程实例:

package org.jworkplace.workflow;

import org.jworkplace.workplace.WorkPlaceFactory;
import org.jworkplace.login.LoginHandler;
import org.jworkplace.mailbox.MailboxManager;
import org.jworkplace.account.Session;
import org.jworkplace.util.ServiceFinder;
import org.jworkplace.service.*;

public class WorkflowService extends ServiceImpl implements ServiceAdmin,
                               WorkflowBackend, DiscoveryListener
{
   /** UUID generator */
  private UUIDFactory uuidFactory = new UUIDFactory();

  // processId to processDef
  private Map processMap;

  // worker to processId
  private Map userMap;

  // owner to list of owned processDefs
  private Map ownerMap;

  // thread to invoke process instance
  private WorkflowEngine engine;
  // JavaSpace proxy interface
  private StarInterface space;
  private static String starInterfaceClass = StarInterface.class.getName();

  // Event Mailbox proxy interface
  private MailboxManager mailbox;
  private static String mailboxManagerClass = MailboxManager.class.getName();

  // process definition queue
  private UUID processToActivate;
  private boolean ready = false;

  // Lookup discovery manager
  private LookupDiscovery mgt;


  public static void main(String args[]) throws Exception {
        if (System.getSecurityManager() == null) {
            System.setSecurityManager(new RMISecurityManager());
        }

        try {
           new WorkflowService(args);
        }  catch(IOException e) {
        e.printStackTrace();
        System.exit(1);
        }

        try {
         Thread.sleep(Long.MAX_VALUE);
      }  catch(InterruptedException e) {  }

        System.exit(0);
    }

    public WorkflowService(String[] args) throws IOException {
        super(args);

        // processId to processDef
        processMap = Collections.synchronizedMap(new HashMap());

        // worker to processId
        userMap = Collections.synchronizedMap(new HashMap());
        // owner to list of owned processDefs
        ownerMap = Collections.synchronizedMap(new HashMap());

        // initialize factory
        UUID uuid = uuidFactory.newUUID();

        // now start finding registrars (lookup services)
        mgt = new LookupDiscovery(LookupDiscovery.NO_GROUPS);
        mgt.addDiscoveryListener(this);
        ((LookupDiscovery)mgt).setGroups(LookupDiscovery.ALL_GROUPS);

        // start threads
        engine = new WorkflowEngine();

    }

    //
    // Workflow Manager Interface
    //

   // create the process definition and queue the process for the engine thread
    public UUID create(ProcessDef processDef)throws RemoteException
    {
          // generate a unique id for this process
          UUID uuid = uuidFactory.newUUID();
          processDef.setId(uuid);
          processMap.put(uuid, processDef);

          // get the owner and determine if other processes
          // belonging to this owner are already defined
          String owner = processDef.getProcessOwner();
          if(!ownerMap.containsKey(owner)) {
             List processList = new ArrayList();
             ownerMap.put(owner, processList);
          }

          // add this process definition to the owners list
          List processList = (List)ownerMap.get(owner);
          int index = -1;
          index = ( (processList.lastIndexOf(owner) == -1 )
                                        ? processList.size() : index+1);
          processList.add(index, processDef);

          // notify the engine a process instance should be created
          queueProcess(uuid);
          // return the unique reference
          return uuid;
    }

    // returns a Process Defintion given a owner and process name
    public ProcessDef getProcessDef(String owner, String processName) throws RemoteException
    {
       List processList = (List)ownerMap.get(owner);
       if(processList == null)
          throw new RemoteException();

       ProcessDef item = null;
       Iterator itr = processList.iterator();
       while(itr.hasNext()) {
         item = (ProcessDef)itr.next();
         if(processName.equals( item.getProcessName()))
            break;
       }
       return item;
    }

    // returns the list of processes defined by a given owner
    public String[] getProcessNames(String owner) throws RemoteException
    {
        List processList = (List)ownerMap.get(owner);
        if(processList == null)
          throw new RemoteException();

        Iterator itr = processList.iterator();
        ArrayList nameList = new ArrayList();
        while(itr.hasNext()) {
          ProcessDef item = (ProcessDef)itr.next();
          nameList.add(item.getProcessName());
        }
        return (String[])nameList.toArray(new String[0]);
    }

    // updates a process defintion
    public void updateProcess(UUID processId, ProcessDef processDef) throws RemoteException {
         processMap.put(processId, processDef);
    }

    // removes a process definition
    public void removeProcess(UUID processId) throws RemoteException
    {
         processMap.remove(processId);
    }

    // return the MailboxManager used by this workflow service
    public MailboxManager getMailboxManager() throws RemoteException
    {
         return mailbox;
    }

    // Called by the engine thread with a CompleteTask object
    // during process instantiation
    public void createProcessListener(UUID processId, TaskEntry taskEntry) throws RemoteException
    {
        // get the process definition associated with this id
        ProcessDef processDef = (ProcessDef)processMap.get(processId);

        try {

            // create a TaskListener
            TaskListener listener = new TaskListener(
                            (WorkflowBackend)WorkflowService.this, space);

            // pass the TaskListener the Process Definition
            MarshalledObject handback = new MarshalledObject(processDef);

            // register for notification of state objects
            space.setRemoteEventListener(taskEntry, handback, listener);
         }  catch(Exception e) {  e.printStackTrace(); }
    }

    // waits for next process definition
    public synchronized UUID getProcessId()
    {
       while (ready == false) {
          try {
              wait();
          }  catch (InterruptedException e) {  }
       }
       ready = false;
       notifyAll();
       return processToActivate;
     }

     // queues a process to activate the process engine
     public synchronized void queueProcess(UUID value)
     {
         while (ready == true) {
             try {
                wait();
             }  catch (InterruptedException e) {  }
         }
         processToActivate = value;
         ready = true;
         notifyAll();
     }
}

WorkflowService 主要完成以下三个映射:
- processMap :提供流程 ID 到流程定义的映射。
- userMap :提供工人到流程的映射。
- ownerMap :提供流程管理员到流程的映射。

4.4 工作流程接口(WorkProcess)

WorkProcess 接口为客户端应用提供了与 WorkflowService 交互的 API:

package org.jworkplace.workflow;

import java.rmi.RemoteException;
import java.io.IOException;
import net.jini.admin.Administrable;

import org.jworkplace.account.Session;

public interface WorkProcess
{
    public Session connect() throws RemoteException, IOException;
    public void disconnect() throws RemoteException, IOException;
    public void updateTask(Task item) throws RemoteException, IOException;
    public Task[] getTasksAssigned(Resource resource) throws RemoteException;
}

客户端可以通过该接口连接和断开与工作流引擎的连接,接收任务分配并更新已分配的任务。

4.5 工作流程实现

WorkflowService 也实现了 RemoteWorkProcess 接口:

    // connect to the workflow engine
    public Session connect(UUID processId) throws RemoteException
    {
         return new Session();
    }

    // disconnect from the workflow engine
    public void disconnect(Session session) throws RemoteException
    {
         session.isActive(false);
    }

    // Determine if the task is still valid
    public void updateTask(Task task) throws RemoteException
    {
          ProcessDef processDef = null;
          // Determine if the task is still valid
          if(!processMap.containsKey(task.processId)) {
             return;
          }
          //  update the task list for the worker
          updateTaskList(task);

          // get the associated work item
          processDef = (ProcessDef)processMap.get(task.processId);
          WorkItem item = processDef.getWorkItem(task.taskId);

          // update the StarEntry
          write(item, task);
    }

    private void updateTaskList(Task task) {
       List taskList = null;
       Task item = null;
       try {
          // find the task in the task list
          if(userMap.containsKey(task.user)) {
             taskList = (List)userMap.get(task.user);
             Iterator itr = taskList.iterator();
             while(itr.hasNext()) {
                item = (Task)itr.next();
                if(item.taskId.equals( task.taskId)) {
                   int pos = taskList.indexOf(item);
                   // update the task to reflect any changes
                   taskList.set(pos,task);
                }
             }
          }
       }  catch(Exception e) {  e.printStackTrace(); }
    }

    // called by clients to get task assingments
    public Task[] getTasksAssigned(Resource resource) throws RemoteException {
       List taskList = null;
       try {
          // assume resource contains worker
          String worker = resource.getResource();
          if(!userMap.containsKey(worker)) return null;

          // get the tasklist associated with this worker
          taskList = (List)userMap.get(worker);
       }  catch(Exception e) {  e.printStackTrace();
                              return null; }
       return (Task[])taskList.toArray(new Task[0]);
    }

    //
    // Use the StarInterface proxy to interface with JavaSpaces
    //
    private synchronized void write(WorkItem item, Task task) {
      try {
           if(space == null) {
               System.out.println("WorkflowService::write Lost in space...unable to continue");

               shutdown();
            }

            // Use the item and task information
            // to create a StarEntry description
            Time time = item.activityTime();
            String resource = item.getAssignedTo();
            StarEntry entry = new StarEntry(task.processId,
                                            task.taskId,
                                            time.getStartTime(),
                                            time.getStopTime(),
                                            item.description,
                                            task.status,
                                            resource);

            // Write the StarEntry to JavaSpaces
            space.write(entry);

       }  catch (Exception e) {  e.printStackTrace(); }
    }
5. 依赖服务的发现

WorkflowService 需要发现 StarService MailboxManager 服务。通过实现 DiscoveryListener 接口来完成服务发现:

    //
    //  DiscoveryListener Interface
    //
    public synchronized void discarded(DiscoveryEvent e) { }

    public synchronized void discovered(DiscoveryEvent de) {
       // get the array of lookup services discovered
       ServiceRegistrar[] registrars = de.getRegistrars();

       // find JavaSpace proxy
       findStarInterface(registrars);

       // find our mailbox manager
       findMailboxManager(registrars);

       // found everything we need terminate discovery
       if(space != null && mailbox != null)
               mgt.terminate();
    }

    // loop through discovered service registrars
    private void findStarInterface(ServiceRegistrar[] registrars) {
       if(space == null) {
         try {
            Class[] cls = new Class[] {  Class.forName(starInterfaceClass) } ;
            ServiceTemplate template = new ServiceTemplate(null, cls, null);
            for(int i=0; i < registrars.length; i++) {
               space = (StarInterface)findService(registrars[i], template);
               if(space != null) {
                   break;
               }
            }
         }  catch (Exception e) {  e.printStackTrace(); }
       }
    }

    // loop through discovered service registrars
    private void findMailboxManager(ServiceRegistrar[] registrars) {
       if(mailbox == null) {
         try {
            Class[] cls = new Class[] {  Class.forName(mailboxManagerClass) } ;
            ServiceTemplate template = new ServiceTemplate(null, cls, null);
            for(int i=0; i < registrars.length; i++) {
               mailbox = (MailboxManager)findService(registrars[i], template);
               if(mailbox != null) {
                   break;
               }
            }
         }  catch (Exception e) {  e.printStackTrace(); }
       }
    }

    // if unable to find service register for notification
    private synchronized Object findService(ServiceRegistrar registrar,
                                            ServiceTemplate template) throws RemoteException {
        Object service = null;
        try {
            service = registrar.lookup( template );
        }  catch ( RemoteException ex ) {
            ex.printStackTrace();
        }
        // TRANSITION_NOMATCH_MATCH to indicate when template match occurs
        // generate remote event
        if ( service == null ) {
          registrar.notify(template, ServiceRegistrar.TRANSITION_NOMATCH_MATCH,
                                  (WorkflowBackend)this, null, Long.MAX_VALUE);
        }
        return service;
    }

    //
    //  ServiceEvent Listener
    //
    public synchronized void notify(RemoteEvent event) throws UnknownEventException, RemoteException
    {
        // If service event received determine which service
        // has joined the community
        if(event instanceof ServiceEvent) {
           ServiceEvent serviceEvent = (ServiceEvent)event;
           ServiceItem item = serviceEvent.getServiceItem();
           if(item.service instanceof StarInterface) {
              space = (StarInterface)item.service;
           }  else if(item.service instanceof MailboxManager) {
              mailbox = (MailboxManager)item.service;
           }
        }
    }

当发现服务时,会调用相应的方法进行处理。如果未能找到服务,会使用 ServiceRegistrar notify 方法注册通知,当服务加入社区时会收到通知。

6. 工作流引擎实现(WorkflowEngine)

WorkflowEngine 负责创建 CompleteTask 状态对象的空间监听器,为流程定义中的每个工作项创建任务并更新每个工人的任务列表,以及在工人未注册时将其注册到 MailboxManager

// Internal class of WorkflowService
private class WorkflowEngine extends Thread {

    public WorkflowEngine() {
        super();
        start();
    }

    public void run() {
        try {
            while (!isInterrupted()) {
                  // wait for process to be started
                  UUID uuid = getProcessId();

                  // create a task completion listener for this process
                  createProcessListener(uuid, new CompleteTask(uuid));
                  // create all tasks to activate
                  activateProcess(uuid);
              }
          }  catch (Exception e) {  e.printStackTrace();
            }  finally { }
    }

    private void activateProcess(UUID processId) {
        ProcessDef processDef = (ProcessDef)processMap.get(processId);
        try {
            WorkItem[] items = processDef.getWorkItems();
            for(int i=0; i<items.length; i++) {
                System.out.println("Engine activateProcess: " + items[i].getId());

                // create a Task for each WorkItem
                WorkItem item = (WorkItem)items[i];
                String processName = processDef.getProcessName();
                Task task = createTask(processId, processName, item);

                // update the task list for the worker
                if(task != null)
                   updateTaskList(task);

                // write a StarEntry for each activity
                write(items[i], task);
            }
        }  catch (Exception e) {  e.printStackTrace(); }
    }

    private Task createTask(UUID processId, String processName, WorkItem item) {
        Task task = null;
        try {
            String taskId = item.getId();
            String worker = item.getAssignedTo();
            task = new Task(processId, processName, taskId, worker, ActivityState.TASK_INIT);
            // does the worker already have a mailbox
            RemoteEventListener listener = mailbox.getListener(worker);
            if(listener == null) {
                // register the worker
                mailbox.register(worker, Long.MAX_VALUE);

                // create the registration template for the worker
                StarEntry entry = new StarEntry();
                entry.resource = item.getAssignedTo();

                // get the mailbox for worker
                listener = mailbox.getListener(worker);
                MarshalledObject handback = new MarshalledObject(worker);

                // set the worker mailbox to get space notifications
                space.setRemoteEventListener(entry, handback, listener);
            }
        }  catch (Exception e) {  e.printStackTrace(); }

        return task;
    }

    private synchronized void updateTaskList(Task task) {
        try {
            String worker = task.user;
            if(!userMap.containsKey(worker)) {
               List taskList = new ArrayList();
               userMap.put(worker, taskList);
            }
            List taskList = (List)userMap.get(worker);
            taskList.add(task);
        }  catch (Exception e) {  e.printStackTrace(); }
    }
}
7. 任务监听器(TaskListener)

TaskListener 是服务端监听器,为工作流系统中定义的每个流程注册。当 CompleteTask 对象被写入空间时,会收到通知:

package org.jworkplace.workflow;

import java.rmi.RemoteException;
import java.rmi.MarshalledObject;
import java.rmi.server.UnicastRemoteObject;
import java.io.IOException;
import net.jini.core.event.RemoteEventListener;
import net.jini.core.event.RemoteEvent;

public class TaskListener implements RemoteEventListener {

    private RemoteWorkProcess engine;
    private StarInterface space;

    public TaskListener(RemoteWorkProcess workProcess, StarInterface starInterface) throws RemoteException {
          this.engine = workProcess;
          this.space = starInterface;

          UnicastRemoteObject.exportObject(this);
    }

    // this should create a new thread
    public void notify(RemoteEvent event) {
        try {
           CompleteTask template = new CompleteTask();
           while(template != null) {
             // for each complete task take from space
             template = (CompleteTask)space.takeStarIfExists(template);
             if(template != null) {

                // the handback will contain the Process Definition
                MarshalledObject handback = event.getRegistrationObject();
                if(handback != null) {
                   ProcessDef processDef = (ProcessDef)handback.get();

                   // Get the successor task
                   WorkItem item = processDef.getNextItem(template.taskId);
                   if (item != null) {
                      // Create a task template
                      String processName = processDef.getProcessName();
                      String taskId = item.getId();
                      String worker = item.getAssignedTo();
                      Task task = new Task(template.processId,
                                           processName,
                                           taskId,
                                            worker,
                                            ActivityState.TASK_READY);

                      // Put the next task in a ready state
                      engine.updateTask(task);
                   }
                }
             }
           }
        }  catch (Exception e) {  e.printStackTrace(); }
    }
}

通过以上的服务和机制,可以实现一个完整的协作工作流应用,包括任务状态管理、事件通知和服务集成。这些功能的组合使得工作流系统更加高效和灵活,能够满足不同场景下的协作需求。

协作工作流应用中的服务集成与事件通知(续)

8. 邮件事件处理器(MailEventHandler)

邮件事件处理器在整个协作工作流系统中扮演着重要的角色,它负责处理与邮件相关的事件。虽然文档中没有给出 MailEventHandler 的具体代码实现,但我们可以推测它会与之前提到的 EventMailbox MailboxManager 等服务进行交互,以实现事件的接收、处理和通知等功能。

例如, MailEventHandler 可能会监听 EventMailbox 中的事件,当有新的事件到来时,根据事件的类型和内容进行相应的处理。可能的处理方式包括向用户发送邮件通知、更新任务状态等。

9. 工作流系统的整体流程与交互

为了更好地理解整个工作流系统的运作,下面通过一个流程图来展示各个组件之间的交互过程:

graph LR
    classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px;
    classDef data fill:#FFEBEB,stroke:#E68994,stroke-width:2px;

    A(客户端):::process -->|连接与操作| B(WorkflowService):::process
    B -->|创建流程| C(ProcessDef):::data
    B -->|映射管理| D(processMap):::data
    B -->|映射管理| E(userMap):::data
    B -->|映射管理| F(ownerMap):::data
    B -->|发现服务| G(StarService):::process
    B -->|发现服务| H(MailboxManager):::process
    C -->|工作项分配| I(WorkItem):::data
    I -->|创建任务| J(Task):::data
    J -->|更新任务列表| E
    B -->|注册通知| K(EventMailbox):::process
    K -->|事件存储| L(事件队列):::data
    M(CompleteTask对象写入):::data -->|触发通知| N(TaskListener):::process
    N -->|处理任务| B
    B -->|更新任务状态| J
    B -->|写入JavaSpace| G
    G -->|通知匹配| K
    K -->|激活通知| A

从这个流程图中可以看出,客户端与 WorkflowService 进行交互, WorkflowService 负责管理流程定义、映射关系以及发现依赖服务。工作项被分配后创建任务,任务状态的更新会影响任务列表。 CompleteTask 对象的写入会触发 TaskListener 的处理,进而更新任务状态并通知客户端。

10. 关键技术点总结

以下是对整个协作工作流系统中关键技术点的总结:

技术点 描述
StarEntry TaskEntry 用于管理任务的状态和状态变化,通过 write 方法实现状态的更新和创建。
EventMailbox 服务 允许客户端存储事件通知,通过 MailboxRegistration 接口管理事件的传递。
JavaSpace notify 方法 用于注册对未来传入条目的兴趣,实现事件的通知机制。
MailboxService 提供用户邮箱映射功能,管理事件邮箱注册和事件传递。
WorkflowService 核心服务,负责管理流程定义、激活流程实例,完成流程 ID、工人和流程管理员到流程的映射。
WorkflowEngine 负责创建空间监听器、创建任务和更新任务列表,以及注册工人到 MailboxManager
TaskListener 服务端监听器,当 CompleteTask 对象被写入空间时,触发后续任务的处理。
11. 实际应用与拓展

在实际应用中,这个协作工作流系统可以应用于各种场景,如项目管理、任务分配、审批流程等。通过对系统的进一步拓展,可以实现更多的功能:

  • 权限管理 :增加权限管理模块,对不同用户或角色的操作进行限制,确保系统的安全性和数据的保密性。
  • 任务调度优化 :结合更复杂的算法和策略,对任务的调度进行优化,提高工作效率。
  • 可视化界面 :开发可视化界面,让用户可以更直观地查看和管理工作流,提高用户体验。
12. 总结

通过对协作工作流系统中各个组件和服务的介绍,我们了解了任务状态管理、事件通知和服务集成的实现方式。各个组件之间相互协作,形成了一个完整的工作流系统。通过合理的设计和实现,可以提高工作流系统的效率和灵活性,满足不同场景下的协作需求。

在实际开发中,需要根据具体的业务需求对系统进行定制和优化,同时要注意服务的发现和管理,确保系统的稳定性和可靠性。希望本文对理解和实现协作工作流系统有所帮助。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值