[
https://issues.apache.org/jira/browse/BROOKLYN-513?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=16034342#comment-16034342
]
ASF GitHub Bot commented on BROOKLYN-513:
-----------------------------------------
Github user aledsage commented on a diff in the pull request:
https://github.com/apache/brooklyn-server/pull/714#discussion_r119809971
--- Diff:
utils/common/src/main/java/org/apache/brooklyn/util/javalang/MethodAccessibleReflections.java
---
@@ -0,0 +1,142 @@
+/*
+ * 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.util.javalang;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.Set;
+
+import org.apache.brooklyn.util.guava.Maybe;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.collect.Sets;
+
+/**
+ * Use {@link Reflections} methods to access this. The methods are
declared here (to this
+ * package-private class) so we can avoid having an ever-growing single
Reflections class!
+ */
+class MethodAccessibleReflections {
+
+ private static final Logger LOG =
LoggerFactory.getLogger(MethodAccessibleReflections.class);
+
+ /**
+ * Contains method.toString() representations for methods we have
logged about failing to
+ * set accessible (or to find an alternative accessible version). Use
this to ensure we
+ * log.warn just once per method, rather than risk flooding our log.
+ */
+ private static final Set<String> SET_ACCESSIBLE_FAILED_LOGGED_METHODS
= Sets.newConcurrentHashSet();
+
+ /**
+ * Contains method.toString() representations for methods we have
logged about having set
+ * accessible. Having to setAccessible is discouraged, so want a
single log.warn once per
+ * method.
+ */
+ private static final Set<String>
SET_ACCESSIBLE_SUCCEEDED_LOGGED_METHODS = Sets.newConcurrentHashSet();
+
+ static boolean trySetAccessible(Method method) {
+ try {
+ method.setAccessible(true);
+ if
(SET_ACCESSIBLE_SUCCEEDED_LOGGED_METHODS.add(method.toString())) {
+ LOG.warn("Discouraged use of setAccessible, called for
method " + method);
+ } else {
+ if (LOG.isTraceEnabled()) LOG.trace("Discouraged use of
setAccessible, called for method " + method);
+ }
+ return true;
+
+ } catch (SecurityException e) {
+ boolean added =
SET_ACCESSIBLE_FAILED_LOGGED_METHODS.add(method.toString());
+ if (added) {
+ LOG.warn("Problem setting accessible for method " +
method, e);
+ } else {
+ if (LOG.isTraceEnabled()) LOG.trace("Problem setting
accessible for method " + method, e);
+ }
+ return false;
+ }
+ }
+
+ /**
+ * @see {@link Reflections#findAccessibleMethod(Method)}
+ */
+ static Method findAccessibleMethod(Method method) {
+ if (!Modifier.isPublic(method.getModifiers())) {
+ trySetAccessible(method);
+ return method;
+ }
+ boolean declaringClassPublic =
Modifier.isPublic(method.getDeclaringClass().getModifiers());
+ if (!declaringClassPublic) {
+ // reflectively calling a public method on a private class can
fail, unless we first set it
+ // call setAccessible. But first see if there is a public
method on a public super-type
+ // that we can call instead!
+ Maybe<Method> publicMethod = tryFindPublicEquivalent(method);
+ if (publicMethod.isPresent()) {
+ LOG.debug("Switched method for publicly accessible
equivalent: method={}; origMethod={}", publicMethod.get(), method);
+ return publicMethod.get();
+ } else {
+ trySetAccessible(method);
+ return method;
+ }
+ }
+
+ return method;
+ }
+
+ private static Maybe<Method> tryFindPublicEquivalent(Method method) {
+ if (Modifier.isStatic(method.getModifiers())) {
+ return Maybe.absent();
+ }
+
+ Class<?> clazz = method.getDeclaringClass();
+
+ for (Class<?> interf : clazz.getInterfaces()) {
+ Maybe<Method> altMethod = tryFindPublicMethod(interf,
method.getName(), method.getParameterTypes());
+ if (altMethod.isPresent()) {
+ return altMethod;
+ }
+ }
+
+ Class<?> superClazz = clazz.getSuperclass();
+ while (superClazz != null) {
+ Maybe<Method> altMethod = tryFindPublicMethod(superClazz,
method.getName(), method.getParameterTypes());
+ if (altMethod.isPresent()) {
+ return altMethod;
+ }
+ superClazz = superClazz.getSuperclass();
+ }
+
+ return Maybe.absent();
+ }
+
+ private static Maybe<Method> tryFindPublicMethod(Class<?> clazz,
String methodName, Class<?>... parameterTypes) {
+ if (!Modifier.isPublic(clazz.getModifiers())) {
+ return Maybe.absent();
+ }
+
+ try {
+ Method altMethod = clazz.getMethod(methodName, parameterTypes);
+ if (Modifier.isPublic(altMethod.getModifiers()) &&
!Modifier.isStatic(altMethod.getModifiers())) {
+ return Maybe.of(altMethod);
--- End diff --
Not sure I follow - at the top of this method it checks for
`Modifier.isPublic(clazz.getModifiers())`. Does that cover what you're
suggesting?
> yaml location for azurecompute-arm templateOptions.ipOptions fails
> ------------------------------------------------------------------
>
> Key: BROOKLYN-513
> URL: https://issues.apache.org/jira/browse/BROOKLYN-513
> Project: Brooklyn
> Issue Type: Bug
> Affects Versions: 0.11.0
> Reporter: Aled Sage
>
> When attempting to use the very latest jclouds 2.1.0-SNAPSHOT
> azurecompute-arm with the location yaml below, it fails to convert this to an
> {{IpOptions}} instance.
> {noformat}
> templateOptions:
> ipOptions:
> - subnet: manual-subnet-ambari
> {noformat}
> This is because of how jclouds declares its builder. The
> {{IpOptions.builder()}} method returns an instance of
> {{AutoValue_IpOptions.Builder}}, but that class is package-private [1].
> Therefore reflectively trying to call
> {{builder.subnet("manual-subnet-ambari")}} is failing because the method is
> not accessible.
> [1]
> https://github.com/jclouds/jclouds-labs/blob/master/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/options/IpOptions.java#L60
--
This message was sent by Atlassian JIRA
(v6.3.15#6346)