/* * 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.brooklyn.core.objs.proxy; import static com.google.common.base.Preconditions.checkNotNull; import java.lang.reflect.InvocationTargetException; import java.util.Map; import org.apache.brooklyn.core.mgmt.internal.ManagementContextInternal; import org.apache.brooklyn.util.collections.MutableMap; import org.apache.brooklyn.util.exceptions.Exceptions; import org.apache.brooklyn.util.javalang.Reflections; import com.google.common.base.Optional; /** */ public class InternalFactory { protected final ManagementContextInternal managementContext; /** * For tracking if constructor has been called by framework, or in legacy way (i.e. directly). * * To be deleted once we delete support for constructing directly (and expecting configure() to be * called inside the constructor, etc). * * @author aled */ public static class FactoryConstructionTracker { private static ThreadLocal<Boolean> constructing = new ThreadLocal<Boolean>(); public static boolean isConstructing() { return (constructing.get() == Boolean.TRUE); } static void reset() { constructing.set(Boolean.FALSE); } static void setConstructing() { constructing.set(Boolean.TRUE); } } /** * Returns true if this is a "new-style" policy (i.e. where not expected callers to use the constructor directly to instantiate it). * * @param clazz */ public static boolean isNewStyle(Class<?> clazz) { try { clazz.getConstructor(new Class[0]); return true; } catch (NoSuchMethodException e) { return false; } } public InternalFactory(ManagementContextInternal managementContext) { this.managementContext = checkNotNull(managementContext, "managementContext"); } /** * Constructs an instance (e.g. of entity, location, enricher or policy. * If new-style, calls no-arg constructor; if old-style, uses spec to pass in config. */ protected <T> T construct(Class<? extends T> clazz, Map<String, ?> constructorFlags) { try { if (isNewStyle(clazz)) { return constructNewStyle(clazz); } else { return constructOldStyle(clazz, MutableMap.copyOf(constructorFlags)); } } catch (Exception e) { throw Exceptions.propagate(e); } } /** * Constructs a new instance (fails if no no-arg constructor). */ protected <T> T constructNewStyle(Class<T> clazz) { if (!isNewStyle(clazz)) { throw new IllegalStateException("Class "+clazz+" must have a no-arg constructor"); } try { FactoryConstructionTracker.setConstructing(); try { return clazz.newInstance(); } finally { FactoryConstructionTracker.reset(); } } catch (Exception e) { throw Exceptions.propagate(e); } } protected <T> T constructOldStyle(Class<T> clazz, Map<String,?> flags) throws InstantiationException, IllegalAccessException, InvocationTargetException { FactoryConstructionTracker.setConstructing(); Optional<T> v; try { v = Reflections.invokeConstructorWithArgs(clazz, new Object[] {MutableMap.copyOf(flags)}, true); } finally { FactoryConstructionTracker.reset(); } if (v.isPresent()) { return v.get(); } else { throw new IllegalStateException("No valid constructor defined for "+clazz+" (expected no-arg or single java.util.Map argument)"); } } }