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 }