Java tutorial
/* * Copyright (c) 2004 - 2006, Jonathan Ross <jonross@alum.mit.edu> * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ package org.cauldron.execution; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.BeanFactory; import org.cauldron.Context; import org.cauldron.DataException; import org.cauldron.Task; import org.cauldron.TaskException; import org.cauldron.TaskFactory; import org.cauldron.data.Conversions; import org.cauldron.data.TextTemplate; /** * <p> * The default implementation of {@link Context}. Create instances through * {@link TaskFactory}, not by using the constructors. * </p> * <p> * Context execution variables are supported with a two-level Map-of-Maps; * syntax for accessing subcontext Map is <code>"level1key:level2key"</code>. * Lack of <code>"level1key:"</code> prefix denotes a default Map. * </p> */ public class ContextImpl implements Context { /** List of tasks on which to run {@link Task#finish} after execution. */ private ArrayList cleanups; /** Is this context running any tasks. */ private boolean running = false; /** Is this context running any async tasks. */ private boolean async = false; /** Map of Maps to store context variables. */ private HashMap subcontexts; /** Task configuration source */ private TaskConfig config; final static Log log = LogFactory.getLog(ContextImpl.class); public ContextImpl() { subcontexts = new HashMap(); subcontexts.put("", new HashMap()); } /** * Set task configuration source; used by {@link TaskFactory#newContext}. */ public void setSource(Object source) { if (source == null) config = null; else if (source instanceof BeanFactory) config = new SpringTaskConfig((BeanFactory) source); else if (source instanceof String) config = new JNDITaskConfig((String) source); else throw new IllegalArgumentException("Unsupported task configuration source"); } /** * @inheritdoc */ public Object run(Task task, Object input) throws TaskException { if (!running) cleanups = new ArrayList(); if (!async) cleanups.add(task); else synchronized (this) { cleanups.add(task); } Object output; // Save state for recursive call. // running will be set back to false after last task completes. // async will be set back to false after last async task completes. boolean wasRunning = running; boolean wasAsync = async; running = true; async = async || task instanceof Task.Asynchronous; try { if (task instanceof Task.Stateful) task = (Task) ((Task.Stateful) task).clone(); output = task.run(this, input); } finally { running = wasRunning; async = wasAsync; } if (!running) for (Iterator ci = cleanups.iterator(); ci.hasNext();) ((Task) ci.next()).finish(); return output; } /** @inheritDoc */ public Task find(String name) { if (config != null) return config.find(name); else return null; } /** @inheritDoc */ public Object get(String key) { SubContext sc = getSub(key); if (async) synchronized (sc.map) { return sc.map.get(sc.suffix); } else return sc.map.get(sc.suffix); } /** @inheritDoc */ public Object put(String key, Object value) { SubContext sc = getSub(key); if (async) synchronized (sc.map) { return sc.map.put(sc.suffix, value); } else return sc.map.put(sc.suffix, value); } /** @inheritDoc */ public Object remove(String key) { SubContext sc = getSub(key); if (async) synchronized (sc.map) { return sc.map.remove(sc.suffix); } else return sc.map.remove(sc.suffix); } /** @inheritDoc */ public Iterator getKeys(String name) { SubContext sc = getSub(name); List keys = new ArrayList(); if (async) synchronized (sc.map) { keys.addAll(sc.map.keySet()); } else keys.addAll(sc.map.keySet()); return keys.iterator(); } /** @inheritDoc */ public void addSubContext(String name, Map map) throws DataException { if (running) throw new DataException("Cannot add subcontexts during execution"); subcontexts.put(name, map); } /** @inheritDoc */ public Map getSubContext(String name) throws DataException { Map map = getSub(name).map; if (async) return Collections.synchronizedMap(map); else return map; } /** @inheritDoc */ public String substitute(String s) { return TextTemplate.substitute(s, new ContextMap(this)); } /** @inheritDoc */ public Object convert(Object obj, Class targetType) { return Conversions.convert(obj, targetType); } /** * Return subcontext information for the given key. * * @throws DataException if the named subcontext does not exist. */ private SubContext getSub(String key) { SubContext sc = new SubContext(); int index = key.indexOf(':'); if (index == -1) { sc.prefix = ""; sc.suffix = key; } else { sc.prefix = key.substring(0, index); sc.suffix = key.substring(index + 1); } sc.map = (Map) subcontexts.get(sc.prefix); if (sc.map == null) throw new DataException("No subcontext Map named " + sc.prefix); return sc; } /* * Compound return value used above. */ private class SubContext { String prefix, suffix; Map map; } }