summaryrefslogtreecommitdiffstats
path: root/sandbox/old/contrib/implementation-ruby/container/src/main/java/org/apache/tuscany/container/ruby/RubyReferenceProxy.java
blob: 4d9119448433f857625e07fe155e6317e045c12b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
/*
 * 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.tuscany.container.ruby;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

import org.jruby.IRuby;
import org.jruby.RubyException;

import net.sf.cglib.asm.ClassWriter;
import net.sf.cglib.asm.CodeVisitor;
import net.sf.cglib.asm.Constants;
import net.sf.cglib.asm.Type;

/**
 * This is a proxy that will mediate reference calls from the JavaScript. The mediation code here will be reviewed when the DataMediation
 * infrastructure is ready. This proxy assmes that there is no verloading of service methods on the reference interface i.e. there are no two service
 * methods that have the same method name or operation name.
 */
public class RubyReferenceProxy {

    private Class interfaze;

    private Object wireProxy;
    
    private IRuby rubyEngine;

    public RubyReferenceProxy(Class interfaze, Object wireProxy, IRuby rubyEng) {
        this.interfaze = interfaze;
        this.wireProxy = wireProxy;
        this.rubyEngine = rubyEng;
    }

    public Object createProxy() {
        try {
            GenericProxyClassLoader classloader = new GenericProxyClassLoader();
            final byte[] byteCode = generateGenericInterface(interfaze);

            Class genericInterface = classloader.defineClass(byteCode);
            InvocationHandler proxyHandler = new RubyRefInvocInterceptor(wireProxy, interfaze, rubyEngine);
            // return genericInterface.cast(Proxy.newProxyInstance(classloader, new Class[]{genericInterface}, proxyHandler));
            return Proxy.newProxyInstance(classloader,
                                          new Class[]{genericInterface},
                                          proxyHandler);
        } catch (Exception e) {
            return null;
        }
    }

    private static byte[] generateGenericInterface(Class serviceInterface) {
        String interfazeName = serviceInterface.getCanonicalName();
        ClassWriter cw = new ClassWriter(false);

        cw.visit(Constants.V1_5,
                 Constants.ACC_PUBLIC + Constants.ACC_ABSTRACT + Constants.ACC_INTERFACE,
                 interfazeName.replace('.',
                                       '/'),
                 "java/lang/Object",
                 null,
                 serviceInterface.getSimpleName() + ".java");

        StringBuffer argsAndReturn = new StringBuffer("(");
        Method[] methods = serviceInterface.getMethods();
        for (int count = 0; count < methods.length; ++count) {
            argsAndReturn = new StringBuffer("(");
            Class[] paramTypes = methods[count].getParameterTypes();
            Class returnType = methods[count].getReturnType();

            for (int paramCount = 0; paramCount < paramTypes.length; ++paramCount) {
                argsAndReturn.append(Type.getType(Object.class));
            }
            argsAndReturn.append(")");
            argsAndReturn.append(Type.getType(Object.class));

            Class[] exceptionTypes = methods[count].getExceptionTypes();
            String[] exceptions = new String[exceptionTypes.length];
            for (int excCount = 0; excCount < exceptionTypes.length; ++excCount) {
                exceptions[excCount] = exceptionTypes[excCount].getName();
                exceptions[excCount] = exceptions[excCount].replace('.',
                                                                    '/');
            }

            CodeVisitor cv = cw.visitMethod(Constants.ACC_PUBLIC + Constants.ACC_ABSTRACT,
                                            methods[count].getName(),
                                            argsAndReturn.toString(),
                                            exceptions,
                                            null);
            cw.visitEnd();
        }

        cw.visitEnd();

        return cw.toByteArray();
    }

    private class GenericProxyClassLoader extends ClassLoader {
        public Class defineClass(byte[] byteArray) {
            try {
                return defineClass(null,
                                   byteArray,
                                   0,
                                   byteArray.length);
            } catch (Throwable e) {
                return null;
            }
        }

    }
}