From ffcebbf14919aba170ec7577d880ef2e0c270052 Mon Sep 17 00:00:00 2001 From: Josh Long Date: Wed, 3 Oct 2018 13:32:08 -0700 Subject: [PATCH] try to avoid reflective access that make Java 9+ unhappy --- .../JCommanderParameterResolver.java | 133 ++++++++++-------- 1 file changed, 72 insertions(+), 61 deletions(-) diff --git a/spring-shell-jcommander-adapter/src/main/java/org/springframework/shell/jcommander/JCommanderParameterResolver.java b/spring-shell-jcommander-adapter/src/main/java/org/springframework/shell/jcommander/JCommanderParameterResolver.java index a03d3ff4..2c30efdf 100644 --- a/spring-shell-jcommander-adapter/src/main/java/org/springframework/shell/jcommander/JCommanderParameterResolver.java +++ b/spring-shell-jcommander-adapter/src/main/java/org/springframework/shell/jcommander/JCommanderParameterResolver.java @@ -1,23 +1,21 @@ /* - * Copyright 2015 the original author or authors. - * - * 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. - */ + * Copyright 2015 the original author or authors. + * + * 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.springframework.shell.jcommander; -import static org.springframework.shell.Utils.unCamelify; - import java.lang.annotation.Annotation; import java.util.Arrays; import java.util.Collection; @@ -27,6 +25,17 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.stream.Collectors; import java.util.stream.Stream; +import javax.validation.Validation; +import javax.validation.Validator; +import javax.validation.ValidatorFactory; +import javax.validation.metadata.BeanDescriptor; + +import com.beust.jcommander.DynamicParameter; +import com.beust.jcommander.JCommander; +import com.beust.jcommander.Parameter; +import com.beust.jcommander.ParameterException; +import com.beust.jcommander.ParametersDelegate; + import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.MethodParameter; @@ -37,28 +46,18 @@ import org.springframework.shell.ParameterResolver; import org.springframework.shell.ValueResult; import org.springframework.util.ReflectionUtils; -import com.beust.jcommander.DynamicParameter; -import com.beust.jcommander.JCommander; -import com.beust.jcommander.Parameter; -import com.beust.jcommander.ParameterException; -import com.beust.jcommander.ParametersDelegate; - -import javax.validation.Validation; -import javax.validation.Validator; -import javax.validation.ValidatorFactory; -import javax.validation.metadata.BeanDescriptor; -import javax.validation.metadata.MethodDescriptor; -import javax.validation.metadata.ParameterDescriptor; +import static org.springframework.shell.Utils.unCamelify; /** * Provides integration with JCommander. * * @author Eric Bottard + * @author Josh Long */ public class JCommanderParameterResolver implements ParameterResolver { - private static final Collection> JCOMMANDER_ANNOTATIONS = - Arrays.asList(Parameter.class, DynamicParameter.class, ParametersDelegate.class); + private static final Collection> JCOMMANDER_ANNOTATIONS = Arrays.asList(Parameter.class, + DynamicParameter.class, ParametersDelegate.class); private Validator validator = Validation.buildDefaultValidatorFactory().getValidator(); @@ -70,24 +69,29 @@ public class JCommanderParameterResolver implements ParameterResolver { @Override public boolean supports(MethodParameter parameter) { AtomicBoolean isSupported = new AtomicBoolean(false); - Class parameterType = parameter.getParameterType(); - ReflectionUtils.doWithFields(parameterType, field -> { - ReflectionUtils.makeAccessible(field); - boolean hasAnnotation = Arrays.stream(field.getAnnotations()) - .map(Annotation::annotationType) - .anyMatch(JCOMMANDER_ANNOTATIONS::contains); - isSupported.compareAndSet(false, hasAnnotation); - }); + if (isLegalReflectiveAccess(parameterType)) { + ReflectionUtils.doWithFields(parameterType, field -> { + if (isLegalReflectiveAccess(field.getType())) { + ReflectionUtils.makeAccessible(field); + boolean hasAnnotation = Arrays.stream(field.getAnnotations()) + .map(Annotation::annotationType) + .anyMatch(JCOMMANDER_ANNOTATIONS::contains); + isSupported.compareAndSet(false, hasAnnotation); + } + }); - ReflectionUtils.doWithMethods(parameterType, method -> { - ReflectionUtils.makeAccessible(method); - boolean hasAnnotation = Arrays.stream(method.getAnnotations()) - .map(Annotation::annotationType) - .anyMatch(Parameter.class::equals); - isSupported.compareAndSet(false, hasAnnotation); - }); + ReflectionUtils.doWithMethods(parameterType, method -> { + if (isLegalReflectiveAccess(method.getDeclaringClass())) { + ReflectionUtils.makeAccessible(method); + boolean hasAnnotation = Arrays.stream(method.getAnnotations()) + .map(Annotation::annotationType) + .anyMatch(Parameter.class::equals); + isSupported.compareAndSet(false, hasAnnotation); + } + }); + } return isSupported.get(); } @@ -108,29 +112,36 @@ public class JCommanderParameterResolver implements ParameterResolver { @Override public Stream describe(MethodParameter parameter) { JCommander jCommander = createJCommander(parameter); - Stream jCommanderDescriptions = streamAllJCommanderDescriptions(jCommander); + Stream jCommanderDescriptions = streamAllJCommanderDescriptions( + jCommander); BeanDescriptor constraintsForClass = validator.getConstraintsForClass(parameter.getParameterType()); return jCommanderDescriptions - .map(j -> new ParameterDescription(parameter, unCamelify(j.getParameterized().getType().getSimpleName())) - .keys(Arrays.asList(j.getParameter().names())) - .help(j.getDescription()) - .mandatoryKey(!j.equals(jCommander.getMainParameter())) - // Not ideal as this does not take reverse-conversion into account, but just toString() - .defaultValue(j.getDefault() == null ? "" : String.valueOf(j.getDefault())) - .elementDescriptor(constraintsForClass.getConstraintsForProperty(j.getParameterized().getName())) - ); + .map(j -> new ParameterDescription(parameter, + unCamelify(j.getParameterized().getType().getSimpleName())) + .keys(Arrays.asList(j.getParameter().names())) + .help(j.getDescription()) + .mandatoryKey(!j.equals(jCommander.getMainParameter())) + // Not ideal as this does not take reverse-conversion into account, but just toString() + .defaultValue(j.getDefault() == null ? "" : String.valueOf(j.getDefault())) + .elementDescriptor( + constraintsForClass.getConstraintsForProperty(j.getParameterized().getName()))); } /** - * Return all JCommander parameter descriptions, including the "main" parameter if present. + * Return all JCommander parameter descriptions, including the "main" parameter + * if present. */ private Stream streamAllJCommanderDescriptions(JCommander jCommander) { return Stream.concat( jCommander.getParameters().stream(), - jCommander.getMainParameter() != null ? Stream.of(jCommander.getMainParameter()) : Stream.empty() - ); + jCommander.getMainParameter() != null ? Stream.of(jCommander.getMainParameter()) : Stream.empty()); + } + + // Java 9+ warn if you try to reflect on JDK types + private static boolean isLegalReflectiveAccess(Class clzz) { + return (!clzz.getName().startsWith("java")); } @Override @@ -146,9 +157,9 @@ public class JCommanderParameterResolver implements ParameterResolver { return Collections.emptyList(); } return streamAllJCommanderDescriptions(jCommander) - .filter(p -> !p.isAssigned()) - .flatMap(p -> Arrays.stream(p.getParameter().names())) - .map(CompletionProposal::new) - .collect(Collectors.toList()); + .filter(p -> !p.isAssigned()) + .flatMap(p -> Arrays.stream(p.getParameter().names())) + .map(CompletionProposal::new) + .collect(Collectors.toList()); } }