/**
*
* 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.ComponentDefinitionException;
import org.osgi.service.blueprint.container.ReifiedType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public abstract class AbstractRecipe implements Recipe {
private static final Logger LOGGER = LoggerFactory
.getLogger(AbstractRecipe.class);
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 boolean canConvert(Object obj, ReifiedType type) {
return ExecutionContext.Holder.getContext().canConvert(obj, type);
}
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;
}
return doLoadType(typeName, fromClassLoader, true, false);
}
private ReifiedType doLoadType(String typeName,
ClassLoader fromClassLoader, boolean checkNestedIfFailed,
boolean retry) {
try {
return GenericType.parse(typeName,
fromClassLoader != null ? fromClassLoader
: ExecutionContext.Holder.getContext());
} catch (ClassNotFoundException e) {
String errorMessage = "Unable to load class " + typeName
+ " from recipe " + this;
if (checkNestedIfFailed) {
int lastDot = typeName.lastIndexOf('.');
if (lastDot > 0 && lastDot < typeName.length()) {
String nestedTypeName = typeName.substring(0, lastDot)
+ "$" + typeName.substring(lastDot + 1);
LOGGER.debug(errorMessage
+ ", trying to load a nested class "
+ nestedTypeName);
try {
return doLoadType(nestedTypeName, fromClassLoader,
false, true);
} catch (ComponentDefinitionException e2) {
// ignore, the recursive call will throw this exception,
// but ultimately the exception referencing the original
// typeName has to be thrown
}
}
}
if (!retry) {
LOGGER.error(errorMessage);
}
throw new ComponentDefinitionException(errorMessage, e);
}
}
public void destroy(Object instance) {
}
public List<Recipe> getConstructorDependencies() {
return Collections.emptyList();
}
public String toString() {
return getClass().getSimpleName() + "[" + "name='" + name + '\'' + ']';
}
}