blob: d12b63f008b7f9bdcb50b1e314f41ced52e605cd [file] [log] [blame]
/*
* 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
*
* https://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.ivy.core;
import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Stack;
import org.apache.ivy.Ivy;
import org.apache.ivy.core.event.EventManager;
import org.apache.ivy.core.module.descriptor.DependencyDescriptor;
import org.apache.ivy.core.resolve.ResolveData;
import org.apache.ivy.core.settings.IvySettings;
import org.apache.ivy.plugins.circular.CircularDependencyStrategy;
import org.apache.ivy.util.Message;
import org.apache.ivy.util.MessageLogger;
/**
* This class represents an execution context of an Ivy action. It contains several getters to
* retrieve information, like the used Ivy instance, the cache location...
*
* @see IvyThread
*/
public class IvyContext {
private static ThreadLocal<Stack<IvyContext>> current = new ThreadLocal<>();
private Ivy defaultIvy;
private WeakReference<Ivy> ivy = new WeakReference<>(null);
private Map<String, Object> contextMap = new HashMap<>();
private Thread operatingThread;
private ResolveData resolveData;
private DependencyDescriptor dd;
public IvyContext() {
}
public IvyContext(IvyContext ctx) {
defaultIvy = ctx.defaultIvy;
ivy = ctx.ivy;
contextMap = new HashMap<>(ctx.contextMap);
operatingThread = ctx.operatingThread;
resolveData = ctx.resolveData;
dd = ctx.dd;
}
public static IvyContext getContext() {
Stack<IvyContext> cur = getCurrentStack();
if (cur.isEmpty()) {
cur.push(new IvyContext());
}
return cur.peek();
}
private static Stack<IvyContext> getCurrentStack() {
Stack<IvyContext> cur = current.get();
if (cur == null) {
cur = new Stack<>();
current.set(cur);
}
return cur;
}
/**
* Creates a new IvyContext and pushes it as the current context in the current thread.
* <p>
* {@link #popContext()} should usually be called when the job for which this context has been
* pushed is finished.
* </p>
*
* @return the newly pushed context
*/
public static IvyContext pushNewContext() {
return pushContext(new IvyContext());
}
/**
* Creates a new IvyContext as a copy of the current one and pushes it as the current context in
* the current thread.
* <p>
* {@link #popContext()} should usually be called when the job for which this context has been
* pushed is finished.
* </p>
*
* @return the newly pushed context
*/
public static IvyContext pushNewCopyContext() {
return pushContext(new IvyContext(getContext()));
}
/**
* Changes the context associated with this thread. This is especially useful when launching a
* new thread, to associate it with the same context as the initial one. Do not forget to call
* {@link #popContext()} when done.
*
* @param context
* the new context to use in this thread.
* @return the pushed context
*/
public static IvyContext pushContext(IvyContext context) {
getCurrentStack().push(context);
return context;
}
/**
* Pops one context used with this thread. This is usually called after having finished a task
* for which a call to {@link #pushNewContext()} or {@link #pushContext(IvyContext)} was done
* prior to beginning the task.
*
* @return the popped context
*/
public static IvyContext popContext() {
return getCurrentStack().pop();
}
/**
* Reads the first object from the list saved under given key in the first context from the
* context stack in which this key is defined. If value under key in any of the contexts form
* the stack represents non List object then a RuntimeException is thrown.
* <p>
* This methods does a similar job to {@link #peek(String)}, except that it considers the whole
* context stack and not only one instance.
* </p>
*
* @param key
* context key for the string
* @return top object from the list (index 0) of the first context in the stack containing this
* key or null if no key or list empty in all contexts from the context stack
* @see #peek(String)
*/
public static Object peekInContextStack(String key) {
Object value = null;
Stack<IvyContext> contextStack = getCurrentStack();
for (int i = contextStack.size() - 1; i >= 0 && value == null; i--) {
IvyContext ctx = contextStack.get(i);
value = ctx.peek(key);
}
return value;
}
/**
* Returns the current ivy instance.
* <p>
* When calling any public ivy method on an ivy instance, a reference to this instance is put in
* this context, and thus accessible using this method, until no code reference this instance
* and the garbage collector collects it.
* </p>
* <p>
* Then, or if no ivy method has been called, a default ivy instance is returned by this method,
* so that it never returns <code>null</code>.
* </p>
*
* @return the current ivy instance
*/
public Ivy getIvy() {
Ivy ivy = peekIvy();
return ivy == null ? getDefaultIvy() : ivy;
}
/**
* Returns the Ivy instance associated with this context, or <code>null</code> if no such
* instance is currently associated with this context.
* <p>
* If you want get a default Ivy instance in case no instance if currently associated, use
* {@link #getIvy()}.
* </p>
*
* @return the current ivy instance, or <code>null</code> if there is no current ivy instance.
*/
public Ivy peekIvy() {
return this.ivy.get();
}
private Ivy getDefaultIvy() {
if (defaultIvy == null) {
defaultIvy = Ivy.newInstance();
try {
defaultIvy.configureDefault();
} catch (Exception e) {
Message.debug(e);
// ???
}
}
return defaultIvy;
}
public void setIvy(Ivy ivy) {
this.ivy = new WeakReference<>(ivy);
operatingThread = Thread.currentThread();
}
public IvySettings getSettings() {
return getIvy().getSettings();
}
public CircularDependencyStrategy getCircularDependencyStrategy() {
return getSettings().getCircularDependencyStrategy();
}
@SuppressWarnings("unchecked")
public <T> T get(String key) {
WeakReference<T> ref = (WeakReference<T>) contextMap.get(key);
return (ref == null) ? null : ref.get();
}
public <T> void set(String key, T value) {
contextMap.put(key, new WeakReference<>(value));
}
/**
* Reads the first object from the list saved under given key in the context. If value under key
* represents non List object then a RuntimeException is thrown.
*
* @param key
* context key for the string
* @return top object from the list (index 0) or null if no key or list empty
*/
@SuppressWarnings("unchecked")
public Object peek(String key) {
synchronized (contextMap) {
Object o = contextMap.get(key);
if (o == null) {
return null;
}
if (o instanceof List) {
if (((List<Object>) o).size() == 0) {
return null;
}
return ((List<Object>) o).get(0);
} else {
throw new RuntimeException("Cannot top from non List object " + o);
}
}
}
/**
* Removes and returns first object from the list saved under given key in the context. If value
* under key represents non List object then a RuntimeException is thrown.
*
* @param key
* context key for the string
* @return top object from the list (index 0) or null if no key or list empty
*/
@SuppressWarnings("unchecked")
public Object pop(String key) {
synchronized (contextMap) {
Object o = contextMap.get(key);
if (o == null) {
return null;
}
if (o instanceof List) {
if (((List<Object>) o).size() == 0) {
return null;
}
return ((List<Object>) o).remove(0);
} else {
throw new RuntimeException("Cannot pop from non List object " + o);
}
}
}
/**
* Removes and returns first object from the list saved under given key in the context but only
* if it equals the given expectedValue - if not a false value is returned. If value under key
* represents non List object then a RuntimeException is thrown.
*
* @param key
* context key for the string
* @param expectedValue
* expected value of the key
* @return true if the r
*/
@SuppressWarnings("unchecked")
public boolean pop(String key, Object expectedValue) {
synchronized (contextMap) {
Object o = contextMap.get(key);
if (o == null) {
return false;
}
if (o instanceof List) {
if (((List<Object>) o).size() == 0) {
return false;
}
Object top = ((List<Object>) o).get(0);
if (!top.equals(expectedValue)) {
return false;
}
((List<Object>) o).remove(0);
return true;
} else {
throw new RuntimeException("Cannot pop from non List object " + o);
}
}
}
/**
* Puts a new object at the start of the list saved under given key in the context. If value
* under key represents non List object then a RuntimeException is thrown. If no list exists
* under given key a new LinkedList is created. This is kept without WeakReference in opposite
* to the put() results.
*
* @param key
* key context key for the string
* @param value
* value to be saved under the key
*/
@SuppressWarnings("unchecked")
public void push(String key, Object value) {
synchronized (contextMap) {
if (!contextMap.containsKey(key)) {
contextMap.put(key, new LinkedList<>());
}
Object o = contextMap.get(key);
if (o instanceof List) {
((List<Object>) o).add(0, value);
} else {
throw new RuntimeException("Cannot push to non List object " + o);
}
}
}
public Thread getOperatingThread() {
return operatingThread;
}
public MessageLogger getMessageLogger() {
// calling getIvy() instead of peekIvy() is not possible here: it will initialize a default
// Ivy instance, with default settings, but settings themselves may log messages and lead to
// a call to this method. So we use the current Ivy instance if any, or the default Ivy
// instance, or the default MessageLogger.
Ivy ivy = peekIvy();
if (ivy == null) {
if (defaultIvy == null) {
return Message.getDefaultLogger();
} else {
return defaultIvy.getLoggerEngine();
}
} else {
return ivy.getLoggerEngine();
}
}
public EventManager getEventManager() {
return getIvy().getEventManager();
}
public void checkInterrupted() {
getIvy().checkInterrupted();
}
public void setResolveData(ResolveData data) {
this.resolveData = data;
}
public ResolveData getResolveData() {
return resolveData;
}
public void setDependencyDescriptor(DependencyDescriptor dd) {
this.dd = dd;
}
public DependencyDescriptor getDependencyDescriptor() {
return dd;
}
}