blob: e6664d2d569cfad605efa921b71cd0a932aa90a8 [file] [log] [blame]
Marcel Offermans5be5f142011-04-26 10:47:12 +00001/*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
Marcel Offermans706fb272010-11-15 12:52:58 +000019package org.apache.felix.dm;
Marcel Offermans61a81142010-04-02 15:16:50 +000020
21import java.lang.reflect.InvocationTargetException;
22import java.lang.reflect.Method;
23import java.lang.reflect.Modifier;
Marcel Offermans1fbc41e2010-06-15 09:11:45 +000024import java.lang.reflect.Proxy;
Marcel Offermans227dd712011-04-19 07:14:22 +000025import java.util.Arrays;
Marcel Offermans5be5f142011-04-26 10:47:12 +000026import java.util.LinkedHashMap;
27import java.util.Map;
Marcel Offermans61a81142010-04-02 15:16:50 +000028
Marcel Offermans5be5f142011-04-26 10:47:12 +000029/**
30 * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
31 */
Marcel Offermans61a81142010-04-02 15:16:50 +000032public class InvocationUtil {
Marcel Offermanse0906072011-05-17 11:09:05 +000033 private static final Map /* <Key, Method> */ m_methodCache;
34 static {
35 int size = 2048;
36 try {
37 String value = System.getProperty(DependencyManager.METHOD_CACHE_SIZE);
38 if (value != null) {
39 size = Integer.parseInt(value);
40 }
41 }
42 catch (Exception e) {}
43 m_methodCache = new LRUMap(Math.max(size, 64));
44 }
Marcel Offermans227dd712011-04-19 07:14:22 +000045
Marcel Offermans26081d32010-07-12 12:43:42 +000046 public static Object invokeCallbackMethod(Object instance, String methodName, Class[][] signatures, Object[][] parameters) throws NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException {
Marcel Offermans61a81142010-04-02 15:16:50 +000047 Class currentClazz = instance.getClass();
48 while (currentClazz != null) {
49 try {
Marcel Offermans26081d32010-07-12 12:43:42 +000050 return invokeMethod(instance, currentClazz, methodName, signatures, parameters, false);
Marcel Offermans61a81142010-04-02 15:16:50 +000051 }
52 catch (NoSuchMethodException nsme) {
53 // ignore
54 }
55 currentClazz = currentClazz.getSuperclass();
56 }
57 throw new NoSuchMethodException(methodName);
58 }
59
Marcel Offermansa6ffb992010-05-19 11:56:44 +000060 public static Object invokeMethod(Object object, Class clazz, String name, Class[][] signatures, Object[][] parameters, boolean isSuper) throws NoSuchMethodException, InvocationTargetException, IllegalArgumentException, IllegalAccessException {
Marcel Offermansc4ee1162010-05-11 08:44:24 +000061 if (object == null) {
62 throw new IllegalArgumentException("Instance cannot be null");
63 }
64 if (clazz == null) {
65 throw new IllegalArgumentException("Class cannot be null");
66 }
Marcel Offermans1fbc41e2010-06-15 09:11:45 +000067
68 // if we're talking to a proxy here, dig one level deeper to expose the
69 // underlying invocation handler (we do the same for injecting instances)
70 if (Proxy.isProxyClass(clazz)) {
71 object = Proxy.getInvocationHandler(object);
72 clazz = object.getClass();
73 }
74
Marcel Offermans61a81142010-04-02 15:16:50 +000075 Method m = null;
76 for (int i = 0; i < signatures.length; i++) {
77 Class[] signature = signatures[i];
Marcel Offermans227dd712011-04-19 07:14:22 +000078 m = getDeclaredMethod(clazz, name, signature, isSuper);
79 if (m != null) {
80 return m.invoke(object, parameters[i]);
Marcel Offermans61a81142010-04-02 15:16:50 +000081 }
82 }
Marcel Offermans46e26fb2010-04-05 12:05:09 +000083 throw new NoSuchMethodException(name);
Marcel Offermans61a81142010-04-02 15:16:50 +000084 }
Marcel Offermans227dd712011-04-19 07:14:22 +000085
86 private static Method getDeclaredMethod(Class clazz, String name, Class[] signature, boolean isSuper) {
87 // first check our cache
88 Key key = new Key(clazz, name, signature);
89 Method m = null;
90 synchronized (m_methodCache) {
Marcel Offermans5be5f142011-04-26 10:47:12 +000091 m = (Method) m_methodCache.get(key);
92 if (m != null) {
Marcel Offermans227dd712011-04-19 07:14:22 +000093 return m;
94 }
Marcel Offermans84ac9372011-05-30 09:59:24 +000095 else if (m_methodCache.containsKey(key)) {
96 // the key is in our cache, it just happens to have a null value
97 return null;
98 }
Marcel Offermans227dd712011-04-19 07:14:22 +000099 }
100 // then do a lookup
101 try {
102 m = clazz.getDeclaredMethod(name, signature);
103 if (!(isSuper && Modifier.isPrivate(m.getModifiers()))) {
104 m.setAccessible(true);
105 }
106 }
107 catch (NoSuchMethodException e) {
108 // ignore
109 }
110 synchronized (m_methodCache) {
111 m_methodCache.put(key, m);
112 }
113 return m;
114 }
115
116 public static class Key {
117 private final Class m_clazz;
118 private final String m_name;
119 private final Class[] m_signature;
120
121 public Key(Class clazz, String name, Class[] signature) {
122 m_clazz = clazz;
123 m_name = name;
124 m_signature = signature;
125 }
126
127 public int hashCode() {
128 final int prime = 31;
129 int result = 1;
130 result = prime * result + ((m_clazz == null) ? 0 : m_clazz.hashCode());
131 result = prime * result + ((m_name == null) ? 0 : m_name.hashCode());
132 result = prime * result + Arrays.hashCode(m_signature);
133 return result;
134 }
135
136 public boolean equals(Object obj) {
137 if (this == obj)
138 return true;
139 if (obj == null)
140 return false;
141 if (getClass() != obj.getClass())
142 return false;
143 Key other = (Key) obj;
144 if (m_clazz == null) {
145 if (other.m_clazz != null)
146 return false;
147 }
148 else if (!m_clazz.equals(other.m_clazz))
149 return false;
150 if (m_name == null) {
151 if (other.m_name != null)
152 return false;
153 }
154 else if (!m_name.equals(other.m_name))
155 return false;
156 if (!Arrays.equals(m_signature, other.m_signature))
157 return false;
158 return true;
159 }
160 }
Marcel Offermans5be5f142011-04-26 10:47:12 +0000161
162 public static class LRUMap extends LinkedHashMap {
163 private final int m_size;
164
165 public LRUMap(int size) {
166 m_size = size;
167 }
168
169 protected boolean removeEldestEntry(java.util.Map.Entry eldest) {
170 return size() > m_size;
171 }
172 }
Marcel Offermans61a81142010-04-02 15:16:50 +0000173}