Return-Path: X-Original-To: archive-asf-public-internal@cust-asf2.ponee.io Delivered-To: archive-asf-public-internal@cust-asf2.ponee.io Received: from cust-asf.ponee.io (cust-asf.ponee.io [163.172.22.183]) by cust-asf2.ponee.io (Postfix) with ESMTP id 5EF09200BDA for ; Tue, 13 Dec 2016 16:48:57 +0100 (CET) Received: by cust-asf.ponee.io (Postfix) id 5BE89160B23; Tue, 13 Dec 2016 15:48:57 +0000 (UTC) Delivered-To: archive-asf-public@cust-asf.ponee.io Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by cust-asf.ponee.io (Postfix) with SMTP id 7D975160B07 for ; Tue, 13 Dec 2016 16:48:56 +0100 (CET) Received: (qmail 91561 invoked by uid 500); 13 Dec 2016 15:48:55 -0000 Mailing-List: contact dev-help@brooklyn.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@brooklyn.apache.org Delivered-To: mailing list dev@brooklyn.apache.org Received: (qmail 91546 invoked by uid 99); 13 Dec 2016 15:48:55 -0000 Received: from git1-us-west.apache.org (HELO git1-us-west.apache.org) (140.211.11.23) by apache.org (qpsmtpd/0.29) with ESMTP; Tue, 13 Dec 2016 15:48:55 +0000 Received: by git1-us-west.apache.org (ASF Mail Server at git1-us-west.apache.org, from userid 33) id 5E013E3934; Tue, 13 Dec 2016 15:48:55 +0000 (UTC) From: geomacy To: dev@brooklyn.apache.org Reply-To: dev@brooklyn.apache.org References: In-Reply-To: Subject: [GitHub] brooklyn-server pull request #482: DSL deferred execution Content-Type: text/plain Message-Id: <20161213154855.5E013E3934@git1-us-west.apache.org> Date: Tue, 13 Dec 2016 15:48:55 +0000 (UTC) archived-at: Tue, 13 Dec 2016 15:48:57 -0000 Github user geomacy commented on a diff in the pull request: https://github.com/apache/brooklyn-server/pull/482#discussion_r92196849 --- Diff: camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/DslDeferredFunctionCall.java --- @@ -0,0 +1,215 @@ +/* + * Copyright 2016 The Apache Software Foundation. + * + * Licensed 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.camp.brooklyn.spi.dsl; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Collections; +import java.util.List; +import java.util.Set; +import java.util.concurrent.Callable; +import java.util.concurrent.ConcurrentHashMap; + +import org.apache.brooklyn.api.mgmt.Task; +import org.apache.brooklyn.camp.brooklyn.spi.dsl.methods.BrooklynDslCommon; +import org.apache.brooklyn.core.entity.EntityInternal; +import org.apache.brooklyn.core.mgmt.BrooklynTaskTags; +import org.apache.brooklyn.util.core.task.Tasks; +import org.apache.brooklyn.util.exceptions.Exceptions; +import org.apache.brooklyn.util.guava.Maybe; +import org.apache.brooklyn.util.javalang.Reflections; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.base.Joiner; +import com.google.common.base.Objects; +import com.google.common.collect.ImmutableList; + +public class DslDeferredFunctionCall extends BrooklynDslDeferredSupplier { + private static final Logger log = LoggerFactory.getLogger(DslDeferredFunctionCall.class); + private static final Set DEPRECATED_ACCESS_WARNINGS = Collections.newSetFromMap(new ConcurrentHashMap()); + + private static final long serialVersionUID = 3243262633795112155L; + + private Object object; + private String fnName; + private List args; + + public DslDeferredFunctionCall(Object o, String fn, List args) { + this.object = o; + this.fnName = fn; + this.args = args; + } + + @Override + public Maybe getImmediately() { + return invokeOnDeferred(object, true); + } + + @Override + public Task newTask() { + return Tasks.builder() + .displayName("Deferred function call " + object + "." + fnName + "(" + toString(args) + ")") + .tag(BrooklynTaskTags.TRANSIENT_TASK_TAG) + .dynamic(false) + .body(new Callable() { + @Override + public Object call() throws Exception { + return invokeOnDeferred(object, false).get(); + } + + }).build(); + } + + protected Maybe invokeOnDeferred(Object obj, boolean immediate) { + Maybe resolvedMaybe = resolve(obj, immediate); + if (resolvedMaybe.isPresent()) { + Object instance = resolvedMaybe.get(); + + if (instance == null) { + throw new IllegalArgumentException("Deferred function call, " + object + + " evaluates to null (when calling " + fnName + "(" + toString(args) + "))"); + } + + return invokeOn(instance); + } else { + if (immediate) { + return Maybe.absent("Could not evaluate immediately " + obj); + } else { + return Maybe.absent(Maybe.getException(resolvedMaybe)); + } + } + } + + protected Maybe invokeOn(Object obj) { + return invokeOn(obj, fnName, args); + } + + protected static Maybe invokeOn(Object obj, String fnName, List args) { + Object instance = obj; + List instanceArgs = args; + Maybe method = Reflections.getMethodFromArgs(instance, fnName, instanceArgs); + + if (method.isAbsent()) { + instance = BrooklynDslCommon.class; + instanceArgs = ImmutableList.builder().add(obj).addAll(args).build(); + method = Reflections.getMethodFromArgs(instance, fnName, instanceArgs); + } + + if (method.isAbsent()) { + Maybe facade; + try { + facade = Reflections.invokeMethodFromArgs(BrooklynDslCommon.DslFacades.class, "wrap", ImmutableList.of(obj)); + } catch (IllegalArgumentException | IllegalAccessException | InvocationTargetException e) { + facade = Maybe.absent(); + } + + if (facade.isPresent()) { + instance = facade.get(); + instanceArgs = args; + method = Reflections.getMethodFromArgs(instance, fnName, instanceArgs); + } + } + + if (method.isPresent()) { + Method m = method.get(); + + checkCallAllowed(m); + + try { + // Value is most likely another BrooklynDslDeferredSupplier - let the caller handle it, + return Maybe.of(Reflections.invokeMethodFromArgs(instance, m, instanceArgs)); + } catch (IllegalArgumentException | IllegalAccessException | InvocationTargetException e) { + // If the method is there but not executable for whatever reason fail with a fatal error, don't return an absent. + throw Exceptions.propagate(new InvocationTargetException(e, "Error invoking '"+fnName+"("+toString(instanceArgs)+")' on '"+instance+"'")); + } + } else { + return Maybe.absent(new IllegalArgumentException("No such function '"+fnName+"("+toString(args)+")' on "+obj)); + } + } + + protected Maybe resolve(Object object, boolean immediate) { + if (object instanceof DslFunctionSource || object == null) { + return Maybe.of(object); + } + + Maybe resultMaybe = Tasks.resolving(object, Object.class) + .context(((EntityInternal)entity()).getExecutionContext()) + .deep(true) + .immediately(immediate) + .recursive(false) + .getMaybe(); + + if (resultMaybe.isPresent()) { + // No nice way to figure out whether the object is deferred. Try to resolve it + // until it matches the input value as a poor man's replacement. + Object result = resultMaybe.get(); + if (result == object) { + return resultMaybe; + } else { + return resolve(result, immediate); + } + } else { + return resultMaybe; + } + } + + private static void checkCallAllowed(Method m) { + DslAccessible dslAccessible = m.getAnnotation(DslAccessible.class); + boolean isAnnotationAllowed = dslAccessible != null; + if (isAnnotationAllowed) return; + + // TODO white-list using brooklyn.properties (at runtime) + + Class clazz = m.getDeclaringClass(); + Package whiteListPackage = BrooklynDslCommon.class.getPackage(); + boolean isPackageAllowed = (clazz.getPackage() != null && // Proxy objects don't have a package + clazz.getPackage().getName().startsWith(whiteListPackage.getName())); + if (isPackageAllowed) { + if (DEPRECATED_ACCESS_WARNINGS.add(m)) { + log.warn("Deprecated since 0.11.0. The method '" + m.toString() + "' called by DSL should be white listed using the " + DslAccessible.class.getSimpleName() + " annotation. Support for DSL callable methods under the " + whiteListPackage + " will be fremoved in a future release."); --- End diff -- typo "fremoved". Would be nice to break this line up over several (currently 297 characters wide!) --- If your project is set up for it, you can reply to this email and have your reply appear on GitHub as well. If your project does not have this feature enabled and wishes so, or if the feature is enabled but not working, please contact infrastructure at infrastructure@apache.org or file a JIRA ticket with INFRA. ---