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: *
017: */
018: package org.apache.ivy.core;
019:
020: import java.lang.ref.WeakReference;
021: import java.util.HashMap;
022: import java.util.LinkedList;
023: import java.util.List;
024: import java.util.Map;
025: import java.util.Stack;
026:
027: import org.apache.ivy.Ivy;
028: import org.apache.ivy.core.event.EventManager;
029: import org.apache.ivy.core.module.descriptor.DependencyDescriptor;
030: import org.apache.ivy.core.resolve.ResolveData;
031: import org.apache.ivy.core.settings.IvySettings;
032: import org.apache.ivy.plugins.circular.CircularDependencyStrategy;
033: import org.apache.ivy.util.Message;
034: import org.apache.ivy.util.MessageLogger;
035:
036: /**
037: * This class represents an execution context of an Ivy action. It contains several getters to
038: * retrieve information, like the used Ivy instance, the cache location...
039: *
040: * @see IvyThread
041: */
042: public class IvyContext {
043:
044: private static ThreadLocal/*<Stack<IvyContext>>*/current = new ThreadLocal();
045:
046: private Ivy defaultIvy;
047:
048: private WeakReference/*<Ivy>*/ivy = new WeakReference(null);
049:
050: private Map contextMap = new HashMap();
051:
052: private Thread operatingThread;
053:
054: private ResolveData resolveData;
055:
056: private DependencyDescriptor dd;
057:
058: public IvyContext() {
059: }
060:
061: public IvyContext(IvyContext ctx) {
062: defaultIvy = ctx.defaultIvy;
063: ivy = ctx.ivy;
064: contextMap = new HashMap(ctx.contextMap);
065: operatingThread = ctx.operatingThread;
066: resolveData = ctx.resolveData;
067: dd = ctx.dd;
068: }
069:
070: public static IvyContext getContext() {
071: Stack cur = getCurrentStack();
072: if (cur.isEmpty()) {
073: cur.push(new IvyContext());
074: }
075: return (IvyContext) cur.peek();
076: }
077:
078: private static Stack/*<IvyContext>*/getCurrentStack() {
079: Stack cur = (Stack) current.get();
080: if (cur == null) {
081: cur = new Stack();
082: current.set(cur);
083: }
084: return cur;
085: }
086:
087: /**
088: * Creates a new IvyContext and pushes it as the current context in the current thread.
089: * <p>
090: * {@link #popContext()} should usually be called when the job for which this context has been
091: * pushed is finished.
092: * </p>
093: * @return the newly pushed context
094: */
095: public static IvyContext pushNewContext() {
096: return pushContext(new IvyContext());
097: }
098:
099: /**
100: * Creates a new IvyContext as a copy of the current one and pushes it as the current context in
101: * the current thread.
102: * <p>
103: * {@link #popContext()} should usually be called when the job for which this context has been
104: * pushed is finished.
105: * </p>
106: * @return the newly pushed context
107: */
108: public static IvyContext pushNewCopyContext() {
109: return pushContext(new IvyContext(getContext()));
110: }
111:
112: /**
113: * Changes the context associated with this thread. This is especially useful when launching a
114: * new thread, to associate it with the same context as the initial one. Do not forget to call
115: * {@link #popContext()} when done.
116: *
117: * @param context
118: * the new context to use in this thread.
119: * @return the pushed context
120: */
121: public static IvyContext pushContext(IvyContext context) {
122: getCurrentStack().push(context);
123: return context;
124: }
125:
126: /**
127: * Pops one context used with this thread. This is usually called after having finished a task
128: * for which a call to {@link #pushNewContext()} or {@link #pushContext(IvyContext)} was done
129: * prior to beginning the task.
130: *
131: * @return the popped context
132: */
133: public static IvyContext popContext() {
134: return (IvyContext) getCurrentStack().pop();
135: }
136:
137: /**
138: * Reads the first object from the list saved under given key in the first context from the
139: * context stack in which this key is defined. If value under key in any of the contexts form
140: * the stack represents non List object then a RuntimeException is thrown.
141: * <p>
142: * This methods does a similar job to {@link #peek(String)}, except that it considers the whole
143: * context stack and not only one instance.
144: * </p>
145: *
146: * @param key
147: * context key for the string
148: * @return top object from the list (index 0) of the first context in the stack containing this
149: * key or null if no key or list empty in all contexts from the context stack
150: * @see #peek(String)
151: */
152: public static Object peekInContextStack(String key) {
153: Object value = null;
154: Stack contextStack = getCurrentStack();
155: for (int i = contextStack.size() - 1; i >= 0 && value == null; i--) {
156: IvyContext ctx = (IvyContext) contextStack.get(i);
157: value = ctx.peek(key);
158: }
159: return value;
160: }
161:
162: /**
163: * Returns the current ivy instance.
164: * <p>
165: * When calling any public ivy method on an ivy instance, a reference to this instance is put in
166: * this context, and thus accessible using this method, until no code reference this instance
167: * and the garbage collector collects it.
168: * </p>
169: * <p>
170: * Then, or if no ivy method has been called, a default ivy instance is returned by this method,
171: * so that it never returns <code>null</code>.
172: * </p>
173: *
174: * @return the current ivy instance
175: */
176: public Ivy getIvy() {
177: Ivy ivy = peekIvy();
178: return ivy == null ? getDefaultIvy() : ivy;
179: }
180:
181: /**
182: * Returns the Ivy instance associated with this context, or <code>null</code> if no such
183: * instance is currently associated with this context.
184: * <p>
185: * If you want get a default Ivy instance in case no instance if currently associated, use
186: * {@link #getIvy()}.
187: * </p>
188: *
189: * @return the current ivy instance, or <code>null</code> if there is no current ivy instance.
190: */
191: public Ivy peekIvy() {
192: Ivy ivy = (Ivy) this .ivy.get();
193: return ivy;
194: }
195:
196: private Ivy getDefaultIvy() {
197: if (defaultIvy == null) {
198: defaultIvy = Ivy.newInstance();
199: try {
200: defaultIvy.configureDefault();
201: } catch (Exception e) {
202: //???
203: }
204: }
205: return defaultIvy;
206: }
207:
208: public void setIvy(Ivy ivy) {
209: this .ivy = new WeakReference(ivy);
210: operatingThread = Thread.currentThread();
211: }
212:
213: public IvySettings getSettings() {
214: return getIvy().getSettings();
215: }
216:
217: public CircularDependencyStrategy getCircularDependencyStrategy() {
218: return getSettings().getCircularDependencyStrategy();
219: }
220:
221: public Object get(String key) {
222: WeakReference ref = (WeakReference) contextMap.get(key);
223: return ref == null ? null : ref.get();
224: }
225:
226: public void set(String key, Object value) {
227: contextMap.put(key, new WeakReference(value));
228: }
229:
230: /**
231: * Reads the first object from the list saved under given key in the context. If value under key
232: * represents non List object then a RuntimeException is thrown.
233: *
234: * @param key
235: * context key for the string
236: * @return top object from the list (index 0) or null if no key or list empty
237: */
238: public Object peek(String key) {
239: synchronized (contextMap) {
240: Object o = contextMap.get(key);
241: if (o == null) {
242: return null;
243: }
244: if (o instanceof List) {
245: if (((List) o).size() == 0) {
246: return null;
247: }
248: Object ret = ((List) o).get(0);
249: return ret;
250: } else {
251: throw new RuntimeException(
252: "Cannot top from non List object " + o);
253: }
254: }
255: }
256:
257: /**
258: * Removes and returns first object from the list saved under given key in the context. If value
259: * under key represents non List object then a RuntimeException is thrown.
260: *
261: * @param key
262: * context key for the string
263: * @return top object from the list (index 0) or null if no key or list empty
264: */
265: public Object pop(String key) {
266: synchronized (contextMap) {
267: Object o = contextMap.get(key);
268: if (o == null) {
269: return null;
270: }
271: if (o instanceof List) {
272: if (((List) o).size() == 0) {
273: return null;
274: }
275: Object ret = ((List) o).remove(0);
276: return ret;
277: } else {
278: throw new RuntimeException(
279: "Cannot pop from non List object " + o);
280: }
281: }
282: }
283:
284: /**
285: * Removes and returns first object from the list saved under given key in the context but only
286: * if it equals the given expectedValue - if not a false value is returned. If value under key
287: * represents non List object then a RuntimeException is thrown.
288: *
289: * @param key
290: * context key for the string
291: * @return true if the r
292: */
293: public boolean pop(String key, Object expectedValue) {
294: synchronized (contextMap) {
295: Object o = contextMap.get(key);
296: if (o == null) {
297: return false;
298: }
299: if (o instanceof List) {
300: if (((List) o).size() == 0) {
301: return false;
302: }
303: Object top = ((List) o).get(0);
304: if (!top.equals(expectedValue)) {
305: return false;
306: }
307: ((List) o).remove(0);
308: return true;
309: } else {
310: throw new RuntimeException(
311: "Cannot pop from non List object " + o);
312: }
313: }
314: }
315:
316: /**
317: * Puts a new object at the start of the list saved under given key in the context. If value
318: * under key represents non List object then a RuntimeException is thrown. If no list exists
319: * under given key a new LinkedList is created. This is kept without WeakReference in opposite
320: * to the put() results.
321: *
322: * @param key
323: * key context key for the string
324: * @param value
325: * value to be saved under the key
326: */
327: public void push(String key, Object value) {
328: synchronized (contextMap) {
329: if (!contextMap.containsKey(key)) {
330: contextMap.put(key, new LinkedList());
331: }
332: Object o = contextMap.get(key);
333: if (o instanceof List) {
334: ((List) o).add(0, value);
335: } else {
336: throw new RuntimeException(
337: "Cannot push to non List object " + o);
338: }
339: }
340: }
341:
342: public Thread getOperatingThread() {
343: return operatingThread;
344: }
345:
346: public MessageLogger getMessageLogger() {
347: // calling getIvy() instead of peekIvy() is not possible here: it will initialize a default
348: // Ivy instance, with default settings, but settings themselves may log messages and lead to
349: // a call to this method. So we use the current Ivy instance if any, or the default Ivy
350: // instance, or the default MessageLogger.
351: Ivy ivy = peekIvy();
352: if (ivy == null) {
353: if (defaultIvy == null) {
354: return Message.getDefaultLogger();
355: } else {
356: return defaultIvy.getLoggerEngine();
357: }
358: } else {
359: return ivy.getLoggerEngine();
360: }
361: }
362:
363: public EventManager getEventManager() {
364: return getIvy().getEventManager();
365: }
366:
367: public void checkInterrupted() {
368: getIvy().checkInterrupted();
369: }
370:
371: public void setResolveData(ResolveData data) {
372: this .resolveData = data;
373: }
374:
375: public ResolveData getResolveData() {
376: return resolveData;
377: }
378:
379: public void setDependencyDescriptor(DependencyDescriptor dd) {
380: this .dd = dd;
381: }
382:
383: public DependencyDescriptor getDependencyDescriptor() {
384: return dd;
385: }
386:
387: }
|