/**
*
* 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
*
* http://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.aries.blueprint.di;
import java.lang.reflect.Type;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
import org.apache.aries.blueprint.container.GenericType;
import org.osgi.service.blueprint.container.ReifiedType;
import org.osgi.service.blueprint.container.ComponentDefinitionException;
public abstract class AbstractRecipe implements Recipe {
protected final String name;
protected boolean prototype = true;
protected AbstractRecipe(String name) {
if (name == null) throw new NullPointerException("name is null");
this.name = name;
}
public String getName() {
return name;
}
public boolean isPrototype() {
return prototype;
}
public void setPrototype(boolean prototype) {
this.prototype = prototype;
}
public final Object create() throws ComponentDefinitionException {
// Ensure a container has been set
ExecutionContext context = ExecutionContext.Holder.getContext();
// if this recipe has already been executed in this context, return the currently registered value
Object result = context.getPartialObject(name);
if (result != null) {
return result;
}
// execute the recipe
context.push(this);
boolean didCreate = false;
try {
if (!prototype) {
FutureTask<Object> objectCreation = new FutureTask<Object>(new Callable<Object>() {
public Object call() throws ComponentDefinitionException {
return internalCreate();
}
});
Future<Object> resultFuture = context.addFullObject(name, objectCreation);
// are we the first to try to create it
if (resultFuture == null) {
didCreate = true;
objectCreation.run();
resultFuture = objectCreation;
}
try {
result = resultFuture.get();
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
} catch (ExecutionException ee) {
if (ee.getCause() instanceof ComponentDefinitionException)
throw (ComponentDefinitionException) ee.getCause();
else if (ee.getCause() instanceof RuntimeException)
throw (RuntimeException) ee.getCause();
else
throw (Error) ee.getCause();
}
} else {
result = internalCreate();
}
} finally {
if (didCreate) context.removePartialObject(name);
Recipe popped = context.pop();
if (popped != this) {
//noinspection ThrowFromFinallyBlock
throw new IllegalStateException("Internal Error: recipe stack is corrupt:" +
" Expected " + this + " to be popped of the stack but was " + popped);
}
}
return result;
}
protected abstract Object internalCreate() throws ComponentDefinitionException;
protected void addPartialObject(Object obj) {
if (!prototype) {
ExecutionContext.Holder.getContext().addPartialObject(name, obj);
}
}
protected Object convert(Object obj, ReifiedType type) throws Exception {
return ExecutionContext.Holder.getContext().convert(obj, type);
}
protected Object convert(Object obj, Type type) throws Exception {
return ExecutionContext.Holder.getContext().convert(obj, new GenericType(type));
}
protected Class loadClass(String className) {
ReifiedType t = loadType(className, null);
return t != null ? t.getRawClass() : null;
}
protected ReifiedType loadType(String typeName) {
return loadType(typeName, null);
}
protected ReifiedType loadType(String typeName, ClassLoader fromClassLoader) {
if (typeName == null) {
return null;
}
try {
return GenericType.parse(typeName, fromClassLoader != null ? fromClassLoader : ExecutionContext.Holder.getContext());
} catch (ClassNotFoundException e) {
throw new ComponentDefinitionException("Unable to load class " + typeName + " from recipe " + this, e);
}
}
public void destroy(Object instance) {
}
public List<Recipe> getConstructorDependencies() {
return Collections.emptyList();
}
public String toString() {
return getClass().getSimpleName() + "[" +
"name='" + name + '\'' +
']';
}
}