/** * * 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 + '\'' + ']'; } }