org.apache.kyuubi.util.reflect.DynConstructors Maven / Gradle / Ivy
/*
* 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.kyuubi.util.reflect;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
/** Copied from parquet-common */
public class DynConstructors {
private DynConstructors() {}
public static class Ctor extends DynMethods.UnboundMethod {
private final Constructor ctor;
private final Class extends C> constructed;
private Ctor(Constructor constructor, Class extends C> constructed) {
super(null, "newInstance");
this.ctor = constructor;
this.constructed = constructed;
}
public Class extends C> getConstructedClass() {
return constructed;
}
public C newInstanceChecked(Object... args) throws Exception {
try {
if (args.length > ctor.getParameterCount()) {
return ctor.newInstance(Arrays.copyOfRange(args, 0, ctor.getParameterCount()));
} else {
return ctor.newInstance(args);
}
} catch (InstantiationException | IllegalAccessException e) {
throw e;
} catch (InvocationTargetException e) {
if (e.getCause() instanceof Exception) {
throw (Exception) e.getCause();
}
if (e.getCause() instanceof RuntimeException) {
throw (RuntimeException) e.getCause();
}
throw new RuntimeException(e.getCause());
}
}
public C newInstance(Object... args) {
try {
return newInstanceChecked(args);
} catch (Exception e) {
if (e instanceof RuntimeException) {
throw (RuntimeException) e;
}
throw new RuntimeException(e);
}
}
@Override
@SuppressWarnings("unchecked")
public R invoke(Object target, Object... args) {
if (target != null) {
throw new IllegalArgumentException("Invalid call to constructor: target must be null");
}
return (R) newInstance(args);
}
@Override
@SuppressWarnings("unchecked")
public R invokeChecked(Object target, Object... args) throws Exception {
if (target != null) {
throw new IllegalArgumentException("Invalid call to constructor: target must be null");
}
return (R) newInstanceChecked(args);
}
@Override
public DynMethods.BoundMethod bind(Object receiver) {
throw new IllegalStateException("Cannot bind constructors");
}
@Override
public boolean isStatic() {
return true;
}
@Override
public String toString() {
return getClass().getSimpleName() + "(constructor=" + ctor + ", class=" + constructed + ")";
}
}
public static Builder builder() {
return new Builder();
}
public static Builder builder(Class> baseClass) {
return new Builder(baseClass);
}
@SuppressWarnings("rawtypes")
public static class Builder {
private final Class> baseClass;
private ClassLoader loader = Thread.currentThread().getContextClassLoader();
private Ctor ctor = null;
private Map problems = new HashMap<>();
public Builder(Class> baseClass) {
this.baseClass = baseClass;
}
public Builder() {
this.baseClass = null;
}
/**
* Set the {@link ClassLoader} used to lookup classes by name.
*
* If not set, the current thread's ClassLoader is used.
*
* @param newLoader a ClassLoader
* @return this Builder for method chaining
*/
public Builder loader(ClassLoader newLoader) {
this.loader = newLoader;
return this;
}
public Builder impl(String className, Class>... types) {
// don't do any work if an implementation has been found
if (ctor != null) {
return this;
}
try {
Class> targetClass = Class.forName(className, true, loader);
impl(targetClass, types);
} catch (NoClassDefFoundError | ClassNotFoundException e) {
// cannot load this implementation
problems.put(className, e);
}
return this;
}
public Builder impl(Class targetClass, Class>... types) {
// don't do any work if an implementation has been found
if (ctor != null) {
return this;
}
try {
ctor = new Ctor(targetClass.getConstructor(types), targetClass);
} catch (NoSuchMethodException e) {
// not the right implementation
problems.put(methodName(targetClass, types), e);
}
return this;
}
public Builder hiddenImpl(Class>... types) {
hiddenImpl(baseClass, types);
return this;
}
@SuppressWarnings({"unchecked", "rawtypes"})
public Builder hiddenImpl(String className, Class>... types) {
// don't do any work if an implementation has been found
if (ctor != null) {
return this;
}
try {
Class targetClass = Class.forName(className, true, loader);
hiddenImpl(targetClass, types);
} catch (NoClassDefFoundError | ClassNotFoundException e) {
// cannot load this implementation
problems.put(className, e);
}
return this;
}
public Builder hiddenImpl(Class targetClass, Class>... types) {
// don't do any work if an implementation has been found
if (ctor != null) {
return this;
}
try {
Constructor hidden = targetClass.getDeclaredConstructor(types);
AccessController.doPrivileged(new MakeAccessible(hidden));
ctor = new Ctor(hidden, targetClass);
} catch (SecurityException e) {
// unusable
problems.put(methodName(targetClass, types), e);
} catch (NoSuchMethodException e) {
// not the right implementation
problems.put(methodName(targetClass, types), e);
}
return this;
}
@SuppressWarnings("unchecked")
public Ctor buildChecked() throws NoSuchMethodException {
if (ctor != null) {
return ctor;
}
throw buildCheckedException(baseClass, problems);
}
@SuppressWarnings("unchecked")
public Ctor build() {
if (ctor != null) {
return ctor;
}
throw buildRuntimeException(baseClass, problems);
}
}
private static class MakeAccessible implements PrivilegedAction {
private Constructor> hidden;
MakeAccessible(Constructor> hidden) {
this.hidden = hidden;
}
@Override
public Void run() {
hidden.setAccessible(true);
return null;
}
}
private static NoSuchMethodException buildCheckedException(
Class> baseClass, Map problems) {
NoSuchMethodException exc =
new NoSuchMethodException(
"Cannot find constructor for " + baseClass + "\n" + formatProblems(problems));
problems.values().forEach(exc::addSuppressed);
return exc;
}
private static RuntimeException buildRuntimeException(
Class> baseClass, Map problems) {
RuntimeException exc =
new RuntimeException(
"Cannot find constructor for " + baseClass + "\n" + formatProblems(problems));
problems.values().forEach(exc::addSuppressed);
return exc;
}
private static String formatProblems(Map problems) {
StringBuilder sb = new StringBuilder();
boolean first = true;
for (Map.Entry problem : problems.entrySet()) {
if (first) {
first = false;
} else {
sb.append("\n");
}
sb.append("\tMissing ")
.append(problem.getKey())
.append(" [")
.append(problem.getValue().getClass().getName())
.append(": ")
.append(problem.getValue().getMessage())
.append("]");
}
return sb.toString();
}
private static String methodName(Class> targetClass, Class>... types) {
StringBuilder sb = new StringBuilder();
sb.append(targetClass.getName()).append("(");
boolean first = true;
for (Class> type : types) {
if (first) {
first = false;
} else {
sb.append(",");
}
sb.append(type.getName());
}
sb.append(")");
return sb.toString();
}
}