/**
* Copyright (c) 2000-present Liferay, Inc. All rights reserved.
*
* This library is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; either version 2.1 of the License, or (at your option)
* any later version.
*
* This library is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*/
package com.liferay.portal.configuration.metatype.bnd.util;
import aQute.bnd.annotation.metatype.Configurable;
import com.liferay.portal.kernel.util.CharPool;
import com.liferay.portal.kernel.util.ReflectionUtil;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Dictionary;
import java.util.Map;
import java.util.concurrent.atomic.AtomicLong;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
/**
* @author Shuyang Zhou
*/
public class ConfigurableUtil {
public static <T> T createConfigurable(
Class<T> clazz, Dictionary<?, ?> properties) {
return _createConfigurableSnapshot(
clazz, Configurable.createConfigurable(clazz, properties));
}
public static <T> T createConfigurable(
Class<T> clazz, Map<?, ?> properties) {
return _createConfigurableSnapshot(
clazz, Configurable.createConfigurable(clazz, properties));
}
private static <T> T _createConfigurableSnapshot(
Class<T> interfaceClass, T configurable) {
String snapshotClassName = interfaceClass.getName().concat("Snapshot");
snapshotClassName = snapshotClassName.concat(
String.valueOf(_counter.getAndIncrement()));
try {
byte[] snapshotClassData = _generateSnapshotClassData(
interfaceClass, snapshotClassName, configurable);
Class<T> snapshotClass = (Class<T>)_defineClassMethod.invoke(
interfaceClass.getClassLoader(), snapshotClassName,
snapshotClassData, 0, snapshotClassData.length);
Constructor<T> snapshotClassConstructor =
snapshotClass.getConstructor(configurable.getClass());
return snapshotClassConstructor.newInstance(configurable);
}
catch (Throwable t) {
throw new RuntimeException(
"Unable to create snapshot class for " + interfaceClass, t);
}
}
private static <T> byte[] _generateSnapshotClassData(
Class<T> interfaceClass, String snapshotClassName, T configurable)
throws Exception {
String snapshotClassBinaryName = _getClassBinaryName(snapshotClassName);
String objectClassBinaryName = _getClassBinaryName(
Object.class.getName());
ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_MAXS);
classWriter.visit(
Opcodes.V1_6, Opcodes.ACC_PUBLIC | Opcodes.ACC_SUPER,
snapshotClassBinaryName, null, objectClassBinaryName,
new String[] {_getClassBinaryName(interfaceClass.getName())});
Method[] declaredMethods = interfaceClass.getDeclaredMethods();
// Fields
for (Method method : declaredMethods) {
Class<?> returnType = method.getReturnType();
if (returnType.isPrimitive() || returnType.isEnum() ||
(returnType == String.class)) {
continue;
}
FieldVisitor fieldVisitor = classWriter.visitField(
Opcodes.ACC_PRIVATE + Opcodes.ACC_FINAL, method.getName(),
Type.getDescriptor(returnType), null, null);
fieldVisitor.visitEnd();
}
// Constructor
Class<?> configurableClass = configurable.getClass();
MethodVisitor constructorMethodVisitor = classWriter.visitMethod(
Opcodes.ACC_PUBLIC, "<init>",
Type.getMethodDescriptor(
Type.VOID_TYPE, Type.getType(configurableClass)),
null, null);
constructorMethodVisitor.visitCode();
constructorMethodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
constructorMethodVisitor.visitMethodInsn(
Opcodes.INVOKESPECIAL, objectClassBinaryName, "<init>", "()V",
false);
for (Method method : declaredMethods) {
Class<?> returnType = method.getReturnType();
if (returnType.isPrimitive() || returnType.isEnum() ||
(returnType == String.class)) {
continue;
}
constructorMethodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
constructorMethodVisitor.visitVarInsn(Opcodes.ALOAD, 1);
String methodName = method.getName();
constructorMethodVisitor.visitMethodInsn(
Opcodes.INVOKEVIRTUAL,
_getClassBinaryName(configurableClass.getName()), methodName,
Type.getMethodDescriptor(method), false);
constructorMethodVisitor.visitFieldInsn(
Opcodes.PUTFIELD, snapshotClassBinaryName, methodName,
Type.getDescriptor(returnType));
}
constructorMethodVisitor.visitInsn(Opcodes.RETURN);
constructorMethodVisitor.visitMaxs(0, 0);
constructorMethodVisitor.visitEnd();
// Methods
for (Method method : declaredMethods) {
String methodName = method.getName();
Class<?> returnType = method.getReturnType();
MethodVisitor methodVisitor = classWriter.visitMethod(
Opcodes.ACC_PUBLIC, methodName,
Type.getMethodDescriptor(method), null, null);
methodVisitor.visitCode();
method.setAccessible(true);
if (returnType.isPrimitive() || (returnType == String.class)) {
Object result = method.invoke(configurable);
if (result == null) {
methodVisitor.visitInsn(Opcodes.ACONST_NULL);
}
else {
methodVisitor.visitLdcInsn(result);
}
Type returnValueType = Type.getType(returnType);
methodVisitor.visitInsn(
returnValueType.getOpcode(Opcodes.IRETURN));
}
else if (returnType.isEnum()) {
Enum<?> result = (Enum<?>)method.invoke(configurable);
String fieldName = result.name();
Field enumField = ReflectionUtil.getDeclaredField(
returnType, fieldName);
methodVisitor.visitFieldInsn(
Opcodes.GETSTATIC,
_getClassBinaryName(returnType.getName()), fieldName,
Type.getDescriptor(enumField.getType()));
methodVisitor.visitInsn(Opcodes.ARETURN);
}
else {
methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
methodVisitor.visitFieldInsn(
Opcodes.GETFIELD, snapshotClassBinaryName, methodName,
Type.getDescriptor(returnType));
methodVisitor.visitInsn(Opcodes.ARETURN);
}
methodVisitor.visitMaxs(0, 0);
methodVisitor.visitEnd();
}
classWriter.visitEnd();
return classWriter.toByteArray();
}
private static String _getClassBinaryName(String className) {
return className.replace(CharPool.PERIOD, CharPool.FORWARD_SLASH);
}
private static final AtomicLong _counter = new AtomicLong();
private static final Method _defineClassMethod;
static {
try {
_defineClassMethod = ReflectionUtil.getDeclaredMethod(
ClassLoader.class, "defineClass", String.class, byte[].class,
int.class, int.class);
}
catch (Throwable t) {
throw new ExceptionInInitializerError(t);
}
}
}