blob: fcd87b87e85d36143acca6d6044385c0cdc78528 [file] [log] [blame]
/*
* 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.cocoon.core.container.spring.avalon;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
/**
* Proxy for all poolable components.
*
* @version $Id$
* @since 2.2
*/
public class PoolableProxyHandler implements InvocationHandler, Runnable {
/** Logger (we use the same logging mechanism as Spring!) */
private final Log log = LogFactory.getLog(getClass());
private final ThreadLocal componentHolder = new ThreadLocal();
private final PoolableFactoryBean handler;
private final String attributeName;
public PoolableProxyHandler(PoolableFactoryBean handler) {
this.handler = handler;
this.attributeName = PoolableProxyHandler.class.getName() + '/' + this.hashCode(); // see https://issues.apache.org/jira/browse/COCOON-2259
}
/**
* @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object, java.lang.reflect.Method, java.lang.Object[])
*/
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
if ( method.getName().equals("putBackIntoAvalonPool") ) {
this.run();
// attributes might already been destroyed, because this handler might be run
// for a component that is released by another component insided recycle()
// which got called by PoolableFactoryBean.enteringPool() <- putIntoPool() <- run() (below),
// which is registered as destruction callback on the request attributes -
// such a destruction callback is called *after* the request attributes
// are set back to null
final RequestAttributes attributes = RequestContextHolder.getRequestAttributes();
if (attributes != null) {
// removing the attribute removes the call back handler!
attributes.removeAttribute(this.attributeName, RequestAttributes.SCOPE_REQUEST);
}
return null;
}
if ( method.getName().equals("hashCode") && args == null ) {
return new Integer(this.hashCode());
}
if ( this.componentHolder.get() == null ) {
this.componentHolder.set(this.handler.getFromPool());
RequestContextHolder.currentRequestAttributes().registerDestructionCallback(this.attributeName, this, RequestAttributes.SCOPE_REQUEST);
if (log.isDebugEnabled()) {
log.debug("getFromPool attributeName=" + attributeName + " class=" + componentHolder.get().getClass());
}
}
try {
return method.invoke(this.componentHolder.get(), args);
} catch (InvocationTargetException ite) {
throw ite.getTargetException();
}
}
/**
* @see java.lang.Runnable#run()
*/
public void run() {
final Object o = this.componentHolder.get();
if ( o != null ) {
this.handler.putIntoPool(o);
if (log.isDebugEnabled()) {
log.debug("putIntoPool attributeName=" + attributeName + " class=" + componentHolder.get().getClass());
}
} else {
if (log.isDebugEnabled()) {
log.debug("no object to put into pool attributeName=" + attributeName);
}
}
this.componentHolder.remove();
}
}