001 /**
002 *
003 * Licensed to the Apache Software Foundation (ASF) under one or more
004 * contributor license agreements. See the NOTICE file distributed with
005 * this work for additional information regarding copyright ownership.
006 * The ASF licenses this file to You under the Apache License, Version 2.0
007 * (the "License"); you may not use this file except in compliance with
008 * the License. You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018 package org.apache.xbean.recipe;
019
020 import java.util.Collections;
021 import java.util.List;
022 import java.util.Map;
023 import java.util.concurrent.atomic.AtomicLong;
024 import java.lang.reflect.Type;
025
026 public abstract class AbstractRecipe implements Recipe {
027 private static final AtomicLong ID = new AtomicLong(1);
028 private long id;
029 private String name;
030
031 protected AbstractRecipe() {
032 id = ID.getAndIncrement();
033 }
034
035 public String getName() {
036 return name;
037 }
038
039 public void setName(String name) {
040 if (name == null) throw new NullPointerException("name is null");
041 this.name = name;
042 }
043
044 public float getPriority() {
045 return 0;
046 }
047
048 public Object create() throws ConstructionException {
049 return create(null);
050 }
051
052 public final Object create(ClassLoader classLoader) throws ConstructionException {
053 // if classloader was passed in, set it on the thread
054 ClassLoader oldClassLoader = null;
055 if (classLoader != null) {
056 oldClassLoader = Thread.currentThread().getContextClassLoader();
057 Thread.currentThread().setContextClassLoader(classLoader);
058 }
059
060 try {
061 return create(Object.class, false);
062 } finally {
063 // if we set a thread context class loader, reset it
064 if (classLoader != null) {
065 Thread.currentThread().setContextClassLoader(oldClassLoader);
066 }
067 }
068 }
069
070 public final Object create(Type expectedType, boolean lazyRefAllowed) throws ConstructionException {
071 if (expectedType == null) throw new NullPointerException("expectedType is null");
072
073 // assure there is a valid thread context class loader
074 ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader();
075 if (oldClassLoader == null) {
076 Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
077 }
078
079 // if there is no execution context, create one
080 boolean createNewContext = !ExecutionContext.isContextSet();
081 if (createNewContext) {
082 ExecutionContext.setContext(new DefaultExecutionContext());
083 }
084
085 try {
086 ExecutionContext context = ExecutionContext.getContext();
087
088 // if this recipe has already been executed in this context, return the currently registered value
089 if (getName() != null && context.containsObject(getName()) && !(context.getObject(getName()) instanceof Recipe)) {
090 return context.getObject(getName());
091 }
092
093 // execute the recipe
094 context.push(this);
095 try {
096 return internalCreate(expectedType, lazyRefAllowed);
097 } finally {
098 Recipe popped = context.pop();
099 if (popped != this) {
100 //noinspection ThrowFromFinallyBlock
101 throw new IllegalStateException("Internal Error: recipe stack is corrupt:" +
102 " Expected " + this + " to be popped of the stack but " + popped + " was");
103 }
104 }
105 } finally {
106 // if we set a new execution context, remove it from the thread
107 if (createNewContext) {
108 ExecutionContext context = ExecutionContext.getContext();
109 ExecutionContext.setContext(null);
110
111 Map<String,List<Reference>> unresolvedRefs = context.getUnresolvedRefs();
112 if (!unresolvedRefs.isEmpty()) {
113 throw new UnresolvedReferencesException(unresolvedRefs);
114 }
115 }
116
117 // if we set a thread context class loader, clear it
118 if (oldClassLoader == null) {
119 Thread.currentThread().setContextClassLoader(null);
120 }
121 }
122 }
123
124 protected abstract Object internalCreate(Type expectedType, boolean lazyRefAllowed) throws ConstructionException;
125
126 public List<Recipe> getNestedRecipes() {
127 return Collections.emptyList();
128 }
129
130 public List<Recipe> getConstructorRecipes() {
131 return Collections.emptyList();
132 }
133
134 public String toString() {
135 if (name != null) {
136 return name;
137 }
138
139 String string = getClass().getSimpleName();
140 if (string.endsWith("Recipe")) {
141 string = string.substring(0, string.length() - "Recipe".length());
142 }
143 return string + "@" + id;
144 }
145 }