001: /**
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */package org.apache.openejb.core.timer;
017:
018: import org.apache.openejb.util.LogCategory;
019: import org.apache.openejb.util.Logger;
020:
021: import javax.transaction.RollbackException;
022: import javax.transaction.Status;
023: import javax.transaction.Synchronization;
024: import javax.transaction.SystemException;
025: import javax.transaction.Transaction;
026: import javax.transaction.TransactionManager;
027: import java.util.ArrayList;
028: import java.util.Collection;
029: import java.util.Collections;
030: import java.util.HashMap;
031: import java.util.Map;
032: import java.util.Set;
033: import java.util.TreeMap;
034: import java.util.TreeSet;
035: import java.util.Date;
036: import java.util.concurrent.atomic.AtomicLong;
037: import java.util.concurrent.ConcurrentHashMap;
038:
039: public class MemoryTimerStore implements TimerStore {
040: private static final Logger log = Logger.getInstance(
041: LogCategory.TIMER, "org.apache.openejb.util.resources");
042: private final Map<Long, TimerData> taskStore = new ConcurrentHashMap<Long, TimerData>();
043: private final Map<Transaction, TimerDataView> tasksByTransaction = new HashMap<Transaction, TimerDataView>();
044: private final AtomicLong counter = new AtomicLong(0);
045:
046: private final TransactionManager transactionManager;
047:
048: public MemoryTimerStore(TransactionManager transactionManager) {
049: this .transactionManager = transactionManager;
050: }
051:
052: public TimerData getTimer(String deploymentId, long timerId) {
053: try {
054: TimerDataView tasks = getTasks();
055: TimerData timerData = tasks.getTasks().get(
056: new Long(timerId));
057: return timerData;
058: } catch (TimerStoreException e) {
059: return null;
060: }
061: }
062:
063: public Collection<TimerData> getTimers(String deploymentId) {
064: try {
065: TimerDataView tasks = getTasks();
066: Collection<TimerData> timerDatas = new ArrayList<TimerData>(
067: tasks.getTasks().values());
068: return timerDatas;
069: } catch (TimerStoreException e) {
070: return Collections.emptySet();
071: }
072: }
073:
074: public Collection<TimerData> loadTimers(
075: EjbTimerServiceImpl timerService, String deploymentId)
076: throws TimerStoreException {
077: TimerDataView tasks = getTasks();
078: Collection<TimerData> timerDatas = new ArrayList<TimerData>(
079: tasks.getTasks().values());
080: return timerDatas;
081: }
082:
083: // used to re-register a TimerData, if a cancel() is rolledback...
084: public void addTimerData(TimerData timerData)
085: throws TimerStoreException {
086: getTasks().addTimerData(timerData);
087: }
088:
089: public TimerData createTimer(EjbTimerServiceImpl timerService,
090: String deploymentId, Object primaryKey, Object info,
091: Date expiration, long intervalDuration)
092: throws TimerStoreException {
093: long id = counter.incrementAndGet();
094: TimerData timerData = new TimerData(id, timerService,
095: deploymentId, primaryKey, info, expiration,
096: intervalDuration);
097: getTasks().addTimerData(timerData);
098: return timerData;
099: }
100:
101: public void removeTimer(long id) {
102: try {
103: getTasks().removeTimerData(new Long(id));
104: } catch (TimerStoreException e) {
105: log.warning(
106: "Unable to remove timer data from memory store", e);
107: }
108: }
109:
110: public void updateIntervalTimer(TimerData timerData) {
111: }
112:
113: private TimerDataView getTasks() throws TimerStoreException {
114: Transaction transaction = null;
115: int status = Status.STATUS_NO_TRANSACTION;
116: try {
117: transaction = transactionManager.getTransaction();
118: if (transaction != null) {
119: status = transaction.getStatus();
120: }
121: } catch (SystemException e) {
122: }
123:
124: if (status != Status.STATUS_ACTIVE
125: && status != Status.STATUS_MARKED_ROLLBACK) {
126: return new LiveTimerDataView();
127: }
128:
129: TxTimerDataView tasks = (TxTimerDataView) tasksByTransaction
130: .get(transaction);
131: if (tasks == null) {
132: tasks = new TxTimerDataView(transaction);
133: tasksByTransaction.put(transaction, tasks);
134: }
135: return tasks;
136: }
137:
138: private interface TimerDataView {
139: Map<Long, TimerData> getTasks();
140:
141: void addTimerData(TimerData timerData);
142:
143: void removeTimerData(Long timerId);
144: }
145:
146: private class LiveTimerDataView implements TimerDataView {
147: public Map<Long, TimerData> getTasks() {
148: return new TreeMap<Long, TimerData>(taskStore);
149: }
150:
151: public void addTimerData(TimerData timerData) {
152: taskStore.put(new Long(timerData.getId()), timerData);
153: }
154:
155: public void removeTimerData(Long timerId) {
156: taskStore.remove(timerId);
157: }
158: }
159:
160: private class TxTimerDataView implements Synchronization,
161: TimerDataView {
162: private final Map<Long, TimerData> tasks;
163: private final Map<Long, TimerData> add = new TreeMap<Long, TimerData>();
164: private final Set<Long> remove = new TreeSet<Long>();
165:
166: public TxTimerDataView(Transaction transaction)
167: throws TimerStoreException {
168: try {
169: transaction.registerSynchronization(this );
170: } catch (RollbackException e) {
171: throw new TimerStoreException(
172: "Transaction has been rolled back");
173: } catch (SystemException e) {
174: throw new TimerStoreException(
175: "Error registering transaction synchronization callback");
176: }
177: this .tasks = new TreeMap<Long, TimerData>(taskStore);
178: }
179:
180: public Map<Long, TimerData> getTasks() {
181: return Collections.unmodifiableMap(tasks);
182: }
183:
184: public void addTimerData(TimerData timerData) {
185: Long timerId = new Long(timerData.getId());
186:
187: // if it was previously removed...
188: if (remove.contains(timerId)) {
189: // remove it from the remove set
190: remove.remove(timerId);
191: // put the work back into the current tasks set
192: tasks.put(timerId, timerData);
193:
194: } else {
195: // if it is not in the current tasks
196: if (!tasks.containsKey(timerId)) {
197: // put it in the add set
198: add.put(timerId, timerData);
199:
200: // put the work into the current tasks set
201: tasks.put(timerId, timerData);
202: }
203: }
204: }
205:
206: public void removeTimerData(Long timerId) {
207: // if it was previously added...
208: if (add.containsKey(timerId)) {
209: // remove it from the add set
210: add.remove(timerId);
211: // re-remove the work from the current tasks set
212: tasks.remove(timerId);
213:
214: } else {
215: // if it is in the current tasks
216: if (tasks.containsKey(timerId)) {
217: // add it in the remove set
218: remove.add(timerId);
219:
220: // remove the work from the current tasks set
221: tasks.remove(timerId);
222: }
223: }
224: }
225:
226: public void beforeCompletion() {
227: }
228:
229: public void afterCompletion(int status) {
230: // if the tx was not committed, there is nothign to update
231: if (status != Status.STATUS_COMMITTED)
232: return;
233:
234: // add the new work
235: taskStore.putAll(add);
236:
237: // remove work
238: taskStore.keySet().removeAll(remove);
239:
240: }
241: }
242: }
|