io.cloudslang.lang.entities.bindings.values.PyObjectValueProxyFactory Maven / Gradle / Ivy
/*******************************************************************************
* (c) Copyright 2016 Hewlett-Packard Development Company, L.P.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Apache License v2.0 which accompany this distribution.
*
* The Apache License is available at
* http://www.apache.org/licenses/LICENSE-2.0
*
*******************************************************************************/
package io.cloudslang.lang.entities.bindings.values;
import java.io.Serializable;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import javassist.util.proxy.MethodFilter;
import javassist.util.proxy.MethodHandler;
import javassist.util.proxy.Proxy;
import javassist.util.proxy.ProxyFactory;
import javassist.util.proxy.ProxyObject;
import org.apache.commons.lang.ClassUtils;
import org.python.core.Py;
import org.python.core.PyObject;
import org.python.core.PyType;
/**
* PyObjectValue proxy factory
*
* Created by Ifat Gavish on 04/05/2016
*/
public class PyObjectValueProxyFactory {
private static final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
private static final Lock writeLock = readWriteLock.writeLock();
public static final String PROXY_CLASS_SUFFIX = "Value";
private static ConcurrentMap proxyClasses = new ConcurrentHashMap<>();
public static PyObjectValue create(Serializable content, boolean sensitive) {
PyObject pyObject = Py.java2py(content);
try {
PyObjectValueProxyClass proxyClass = getProxyClass(pyObject);
PyObjectValue pyObjectValue = (PyObjectValue) proxyClass.getConstructor()
.newInstance(proxyClass.getParams());
((Proxy) pyObjectValue).setHandler(new PyObjectValueMethodHandler(content, sensitive, pyObject));
return pyObjectValue;
} catch (Exception e) {
throw new RuntimeException("Failed to create a proxy to new instance for PyObjectValue and " +
pyObject.getClass().getSimpleName(), e);
}
}
private static PyObjectValueProxyClass getProxyClass(PyObject pyObject) throws Exception {
String proxyClassName = pyObject.getClass() + PROXY_CLASS_SUFFIX;
PyObjectValueProxyClass proxyClass = proxyClasses.get(proxyClassName);
if (proxyClass == null) {
writeLock.lock();
try {
proxyClass = proxyClasses.get(proxyClassName);
if (proxyClass == null) {
ProxyFactory factory = new ProxyFactory();
factory.setSuperclass(pyObject.getClass());
factory.setInterfaces(new Class[]{PyObjectValue.class});
factory.setFilter(new PyObjectValueMethodFilter());
factory.setUseWriteReplace(false);
proxyClasses.putIfAbsent(proxyClassName, createProxyClass(factory.createClass(), pyObject));
proxyClass = proxyClasses.get(proxyClassName);
}
} finally {
writeLock.unlock();
}
}
return proxyClass;
}
private static PyObjectValueProxyClass createProxyClass(Class proxyClass, PyObject pyObject) throws Exception {
Constructor> constructor = proxyClass.getConstructors()[0];
for (Constructor> con : proxyClass.getConstructors()) {
if (con.getParameterTypes().length < constructor.getParameterTypes().length) {
constructor = con;
}
}
Object[] params = new Object[constructor.getParameterTypes().length];
for (int index = 0; index < constructor.getParameterTypes().length; index++) {
Class> parameterType = constructor.getParameterTypes()[index];
params[index] = getParamDefaultValue(pyObject, parameterType);
}
return new PyObjectValueProxyClass(proxyClass, constructor, params);
}
@SuppressWarnings("unchecked")
private static Object getParamDefaultValue(PyObject pyObject, Class> parameterType) throws Exception {
if (parameterType.equals(PyType.class)) {
return pyObject.getType();
} else if (parameterType.isPrimitive()) {
return ClassUtils.primitiveToWrapper(parameterType).getConstructor(String.class).newInstance("0");
} else if (Number.class.isAssignableFrom(parameterType) || String.class.isAssignableFrom(parameterType)) {
return parameterType.getConstructor(String.class).newInstance("0");
} else {
return null;
}
}
private static class PyObjectValueMethodFilter implements MethodFilter {
@Override
public boolean isHandled(Method method) {
return Modifier.isPublic(method.getModifiers());
}
}
private static class PyObjectValueMethodHandler implements MethodHandler, Serializable {
private static final String ACCESSED_GETTER_METHOD = "isAccessed";
protected Value value;
protected PyObject pyObject;
protected boolean accessed;
public PyObjectValueMethodHandler(Serializable content, boolean sensitive, PyObject pyObject) {
this.value = ValueFactory.create(content, sensitive);
this.pyObject = pyObject;
this.accessed = false;
}
@Override
public Object invoke(Object self, Method thisMethod, Method proceed, Object[] args) throws Throwable {
if (thisMethod.getName().equals(ACCESSED_GETTER_METHOD)) {
return accessed;
} else if (Value.class.isAssignableFrom(thisMethod.getDeclaringClass())) {
Method valueMethod = value.getClass().getMethod(thisMethod.getName(), thisMethod.getParameterTypes());
return valueMethod.invoke(value, args);
} else if (PyObject.class.isAssignableFrom(thisMethod.getDeclaringClass())) {
Method pyObjectMethod = pyObject.getClass()
.getMethod(thisMethod.getName(), thisMethod.getParameterTypes());
if (!thisMethod.getName().equals("toString")) {
accessed = true;
}
return pyObjectMethod.invoke(pyObject, getPyObjectArgs(args));
} else {
throw new RuntimeException("Failed to invoke PyObjectValue method. Implementing class not found");
}
}
private Object[] getPyObjectArgs(Object[] args) {
Object[] pyObjectArgs = new Object[args.length];
for (int index = 0; index < args.length; index++) {
if (args[index] instanceof PyObjectValue) {
PyObjectValueMethodHandler handler = (PyObjectValueMethodHandler) ((ProxyObject) args[index])
.getHandler();
handler.accessed = true;
pyObjectArgs[index] = handler.pyObject;
} else {
pyObjectArgs[index] = args[index];
}
}
return pyObjectArgs;
}
}
}