/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.ode.bpel.memdao; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentLinkedQueue; import javax.xml.namespace.QName; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.ode.bpel.common.CorrelationKey; import org.apache.ode.bpel.dao.CorrelationSetDAO; import org.apache.ode.bpel.dao.CorrelatorDAO; import org.apache.ode.bpel.dao.PartnerLinkDAO; import org.apache.ode.bpel.dao.ProcessDAO; import org.apache.ode.bpel.dao.ProcessInstanceDAO; /** * A very simple, in-memory implementation of the {@link ProcessDAO} interface. */ class ProcessDaoImpl extends DaoBaseImpl implements ProcessDAO { private static final Log __log = LogFactory.getLog(ProcessDaoImpl.class); private QName _processId; private QName _type; private long _version; final Map<String, CorrelatorDaoImpl> _correlators = new ConcurrentHashMap<String, CorrelatorDaoImpl>(); protected final Map<Long, ProcessInstanceDAO> _instances = new ConcurrentHashMap<Long, ProcessInstanceDAO>(); protected final Map<Long, Long> _instancesAge = new ConcurrentHashMap<Long, Long>(); protected final Map<Integer, PartnerLinkDAO> _plinks = new ConcurrentHashMap<Integer, PartnerLinkDAO>(); private Map<QName, ProcessDaoImpl> _store; private BpelDAOConnectionImpl _conn; private int _executionCount = 0; private Collection<Long> _instancesToRemove = new ConcurrentLinkedQueue<Long>(); private static volatile long _lastRemoval = 0; private String _guid; public ProcessDaoImpl(BpelDAOConnectionImpl conn, Map<QName, ProcessDaoImpl> store, QName processId, QName type, String guid, long version) { if (__log.isDebugEnabled()) { __log.debug("Creating ProcessDao object for process \"" + processId + "\"."); } _guid = guid; _conn = conn; _store = store; _processId = processId; _type = type; _version = version; } public QName getProcessId() { return _processId; } public CorrelatorDAO getCorrelator(String cid) { CorrelatorDAO ret = _correlators.get(cid); if (ret == null) { throw new IllegalArgumentException("no such correlator: " + cid); } return ret; } public Collection<CorrelatorDAO> getCorrelators() { // Note: _correlators.values() is a Collection<CorrealatorDaoImpl>. We can't just return this object // since Collection<CorrelatorDAO> is /not/ assignment compatible with Collection<CorrelatorDaoImpl>. // However, a immutable Collection<CorrelationDAO> is assignment compatible with Collection<CorrelatorDaoImpl>, // but.... we need to introduce some ambiguity into the type hierarchy so that Java will infer the correct type. // Make an ambiguous collection. Collection<? extends CorrelatorDAO> foo = _correlators.values(); // In order to get a collection of the super-type from a sub-type we must make the collection read-only. return Collections.unmodifiableCollection(foo); } public void removeRoutes(String routeId, ProcessInstanceDAO target) { for (CorrelatorDAO correlatorDAO : _correlators.values()) { correlatorDAO.removeRoutes(routeId, target); } } public ProcessInstanceDAO createInstance(CorrelatorDAO correlator) { final ProcessInstanceDaoImpl newInstance = new ProcessInstanceDaoImpl(_conn, this, correlator); _conn.defer(new Runnable() { public void run() { _instances.put(newInstance.getInstanceId(), newInstance); _instancesAge.put(newInstance.getInstanceId(), System.currentTimeMillis()); } }); discardOldInstances(); // Removing right away on rollback final Long iid = newInstance.getInstanceId(); _conn.onRollback(new Runnable() { public void run() { _instances.remove(iid); _instancesAge.remove(iid); } }); _executionCount++; return newInstance; } public ProcessInstanceDAO getInstance(Long instanceId) { return _instances.get(instanceId); } public Collection<ProcessInstanceDAO> findInstance(CorrelationKey key) { ArrayList<ProcessInstanceDAO> result = new ArrayList<ProcessInstanceDAO>(); for (ProcessInstanceDAO instance : _instances.values()) { for (CorrelationSetDAO corrSet : instance.getCorrelationSets()) { if (corrSet.getValue().equals(key)) result.add(instance); } } return result; } public void instanceCompleted(ProcessInstanceDAO instance) { // Cleaning up if (__log.isDebugEnabled()) __log.debug("Removing completed process instance " + instance.getInstanceId() + " from in-memory store."); _instancesAge.remove(instance.getInstanceId()); ProcessInstanceDAO removed = _instances.remove(instance.getInstanceId()); if (removed == null) { // Checking for leftover instances that should be removed ArrayList<Long> removals = new ArrayList<Long>(_instancesToRemove); for (Long iid : removals) { _instances.remove(iid); } _instancesToRemove.removeAll(removals); // The instance can't be found probably because the transaction isn't committed yet and // it doesn't exist. Saving its id for later cleanup. _instancesToRemove.add(instance.getInstanceId()); } } public void delete() { _store.remove(_processId); } public long getVersion() { return _version; } public String getDeployer() { return "nobody"; } public QName getType() { return _type; } public CorrelatorDAO addCorrelator(String correlator) { CorrelatorDaoImpl corr = new CorrelatorDaoImpl(correlator, _conn); _correlators.put(corr.getCorrelatorId(), corr); return corr; } /** * Nothing to do. */ public void update() { //TODO Check requirement for persisting. } public int getNumInstances() { // Instances are removed after execution, using a counter instead return _executionCount; } public ProcessInstanceDAO getInstanceWithLock(Long iid) { return getInstance(iid); } public int getActivityFailureCount() { return 0; } public Date getActivityFailureDateTime() { return null; } public String getGuid() { return _guid; } public void setGuid(String guid) { _guid = guid; } /** * Discard in-memory instances that exceeded their time-to-live to prevent memory leaks */ void discardOldInstances() { long now = System.currentTimeMillis(); if (now > _lastRemoval + (_conn._mexTtl/10)) { _lastRemoval = now; Object[] oldInstances = _instancesAge.keySet().toArray(); for (int i=oldInstances.length-1; i>=0; i--) { Long id = (Long) oldInstances[i]; Long age = _instancesAge.get(id); if (age != null && now-age > _conn._mexTtl) { __log.warn("Discarding in-memory instance "+id+" because it exceeded its time-to-live: "+_instances.get(id)); _instances.remove(id); _instancesAge.remove(id); } } } } }