Skip to content

Commit

Permalink
Caching introduced in GenericInvocationHandler.invoke() (improves per…
Browse files Browse the repository at this point in the history
…formance, see: #84)
  • Loading branch information
typekpb committed Mar 23, 2014
1 parent ad68bb0 commit 63e6c17
Show file tree
Hide file tree
Showing 8 changed files with 380 additions and 10 deletions.
42 changes: 32 additions & 10 deletions src/main/java/com/p6spy/engine/proxy/GenericInvocationHandler.java
Expand Up @@ -19,15 +19,19 @@
*/
package com.p6spy.engine.proxy;

import com.p6spy.engine.common.P6WrapperIsWrapperDelegate;
import com.p6spy.engine.common.P6WrapperUnwrapDelegate;
import net.sf.cglib.proxy.InvocationHandler;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

import net.sf.cglib.proxy.InvocationHandler;

import com.p6spy.engine.common.P6WrapperIsWrapperDelegate;
import com.p6spy.engine.common.P6WrapperUnwrapDelegate;
import com.p6spy.engine.proxy.cache.Cache;
import com.p6spy.engine.proxy.cache.CacheFactory;
import com.p6spy.engine.proxy.cache.MethodMatcherCacheKey;

/**
* Base class for invocation handlers. This class is designed to be a generic implementation
* of the {@link InvocationHandler} interface which delegates the invocation of {@link Delegate} objects
Expand All @@ -37,8 +41,11 @@
*/
public class GenericInvocationHandler<T> implements InvocationHandler {
private final Map<MethodMatcher, Delegate> delegateMap;

private final T underlying;

final static Cache<MethodMatcherCacheKey, MethodMatcher> cache = CacheFactory
.<MethodMatcherCacheKey, MethodMatcher> newCache();

/**
* Creates a new invocation handler for the given object.
Expand Down Expand Up @@ -78,13 +85,24 @@ Delegate getDelegate(MethodMatcher methodMatcher) {
}

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
for (MethodMatcher methodMatcher : delegateMap.keySet()) {
if (methodMatcher.matches(method)) {
return delegateMap.get(methodMatcher).invoke(proxy, underlying, method, args);
MethodMatcher methodMatcher = cache.get(new MethodMatcherCacheKey(getClass(), method));

if (null == methodMatcher) {
for (MethodMatcher matcher : delegateMap.keySet()) {
if (matcher.matches(method)) {
methodMatcher = matcher;
cache.put(new MethodMatcherCacheKey(this.getClass(), method), methodMatcher);
break;
}
}

}

try {
if (null != methodMatcher) {
final Delegate delegate = delegateMap.get(methodMatcher);
return delegate.invoke(proxy, underlying, method, args);
}

return method.invoke(underlying, args);
} catch (InvocationTargetException e) {
throw e.getCause();
Expand All @@ -95,4 +113,8 @@ protected T getUnderlying() {
return underlying;
}

public static void clearCache() {
cache.clear();
}

}
35 changes: 35 additions & 0 deletions src/main/java/com/p6spy/engine/proxy/cache/Cache.java
@@ -0,0 +1,35 @@
/*
* #%L
* P6Spy
* %%
* Copyright (C) 2002 - 2014 P6Spy
* %%
* Licensed 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.
* #L%
*/
package com.p6spy.engine.proxy.cache;


/**
* @author Peter Butkovic
*/
public interface Cache<K, V> {

V get(K key);

boolean contains(K key);

void put(K key, V value);

void clear();
}
35 changes: 35 additions & 0 deletions src/main/java/com/p6spy/engine/proxy/cache/CacheFactory.java
@@ -0,0 +1,35 @@
/*
* #%L
* P6Spy
* %%
* Copyright (C) 2002 - 2014 P6Spy
* %%
* Licensed 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.
* #L%
*/
package com.p6spy.engine.proxy.cache;


/**
* @author Peter Butkovic
*/
public class CacheFactory {

public static final <K,V> Cache<K,V> newCache() {
// TODO we'd be able to use any other caching solution here
// any 3.rd party chache (ehcache,...) or some simple LRUMap from apache commons
// based on class availability on classpath OR some config switch,...
return new HashMapBasedCache<K,V>();
}

}
56 changes: 56 additions & 0 deletions src/main/java/com/p6spy/engine/proxy/cache/HashMapBasedCache.java
@@ -0,0 +1,56 @@
/*
* #%L
* P6Spy
* %%
* Copyright (C) 2002 - 2014 P6Spy
* %%
* Licensed 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.
* #L%
*/
package com.p6spy.engine.proxy.cache;

import java.util.HashMap;
import java.util.Map;

/**
* @author Peter Butkovic
*/
public class HashMapBasedCache<K, V> implements Cache<K, V> {

private Map<K, V> map;

public HashMapBasedCache() {
super();
this.map = new HashMap<K,V>();
}

@Override
public V get(K key) {
return map.get(key);
}

@Override
public boolean contains(K key) {
return map.containsKey(key);
}

@Override
public void put(K key, V value) {
map.put(key, value);
}

@Override
public void clear() {
map.clear();
}
}
@@ -0,0 +1,73 @@
/*
* #%L
* P6Spy
* %%
* Copyright (C) 2002 - 2014 P6Spy
* %%
* Licensed 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.
* #L%
*/
package com.p6spy.engine.proxy.cache;

import java.lang.reflect.Method;

import com.p6spy.engine.proxy.GenericInvocationHandler;

/**
* @author Peter Butkovic
*/
public class MethodMatcherCacheKey {

private final Method method;

@SuppressWarnings("rawtypes")
private final Class<? extends GenericInvocationHandler> invHandlerClass;

public MethodMatcherCacheKey(@SuppressWarnings("rawtypes")
Class<? extends GenericInvocationHandler> invHandlerClass, Method method) {
super();
this.method = method;
this.invHandlerClass = invHandlerClass;
}

@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((invHandlerClass == null) ? 0 : invHandlerClass.hashCode());
result = prime * result + ((method == null) ? 0 : method.hashCode());
return result;
}

@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
MethodMatcherCacheKey other = (MethodMatcherCacheKey) obj;
if (invHandlerClass == null) {
if (other.invHandlerClass != null)
return false;
} else if (!invHandlerClass.equals(other.invHandlerClass))
return false;
if (method == null) {
if (other.method != null)
return false;
} else if (!method.equals(other.method))
return false;
return true;
}
}
5 changes: 5 additions & 0 deletions src/main/java/com/p6spy/engine/spy/P6ModuleManager.java
Expand Up @@ -36,6 +36,7 @@

import com.p6spy.engine.common.P6LogQuery;
import com.p6spy.engine.common.P6Util;
import com.p6spy.engine.proxy.GenericInvocationHandler;
import com.p6spy.engine.spy.option.EnvironmentVariables;
import com.p6spy.engine.spy.option.P6OptionChangedListener;
import com.p6spy.engine.spy.option.P6OptionsRepository;
Expand Down Expand Up @@ -72,6 +73,10 @@ private synchronized static void initMe() {

instance = new P6ModuleManager();
P6LogQuery.initialize();

// get rid of old cached stuff
GenericInvocationHandler.clearCache();

} catch (IOException e) {
handleInitEx(e);
} catch (MBeanRegistrationException e) {
Expand Down
64 changes: 64 additions & 0 deletions src/test/java/com/p6spy/engine/proxy/cache/CacheTest.java
@@ -0,0 +1,64 @@
/*
* #%L
* P6Spy
* %%
* Copyright (C) 2002 - 2014 P6Spy
* %%
* Licensed 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.
* #L%
*/
package com.p6spy.engine.proxy.cache;

import org.junit.Assert;
import org.junit.Test;

/**
* @author Peter Butkovic
*/
public class CacheTest {

@Test
public void testNewCacheInstance() {
Assert.assertNotNull(CacheFactory.newCache());
Assert.assertNotEquals(CacheFactory.newCache(), CacheFactory.newCache());
}

@Test
public void testPutAndGet() {
final Cache<String, String> cache = CacheFactory.<String, String>newCache();
Assert.assertNull(cache.get("key1"));
cache.put("key1", "value1");
Assert.assertNotNull(cache.get("key1"));
Assert.assertEquals("value1", cache.get("key1"));
Assert.assertNull(cache.get("key2"));
}

@Test
public void testPutAndContains() {
final Cache<String, String> cache = CacheFactory.<String, String>newCache();
Assert.assertFalse(cache.contains("key1"));
cache.put("key1", "value1");
Assert.assertTrue(cache.contains("key1"));
Assert.assertFalse(cache.contains("key2"));
}

@Test
public void testClear() {
final Cache<String, String> cache = CacheFactory.<String, String>newCache();
cache.put("key1", "value1");
Assert.assertTrue(cache.contains("key1"));
cache.clear();
Assert.assertFalse(cache.contains("key1"));
}

}

0 comments on commit 63e6c17

Please sign in to comment.