| /* |
| * Copyright 1999-2004 The Apache Software Foundation |
| * |
| * 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. |
| */ |
| |
| package org.apache.jk.common; |
| |
| import java.io.IOException; |
| |
| import javax.management.ObjectName; |
| |
| import org.apache.commons.modeler.Registry; |
| import org.apache.jk.apr.AprImpl; |
| import org.apache.jk.core.JkHandler; |
| import org.apache.jk.core.Msg; |
| import org.apache.jk.core.MsgContext; |
| import org.apache.jk.core.JkChannel; |
| import org.apache.tomcat.util.buf.ByteChunk; |
| import org.apache.tomcat.util.buf.C2BConverter; |
| import org.apache.tomcat.util.buf.MessageBytes; |
| |
| |
| /** |
| * Base class for components using native code ( libjkjni.so ). |
| * It allows to access the jk_env and wrap ( 'box' ? ) a native |
| * jk component, and call it's methods. |
| * |
| * Note that get/setAttribute are expensive ( Strings, etc ), |
| * invoke() is were all optimizations are done. We do recycle |
| * all memory on both C and java sides ( the only exception is |
| * when we attempt pinning but the VM doesn't support it ). The |
| * low level optimizations from ByteBuffer, etc are used to |
| * reduce the overhead of passing strings. |
| * |
| * @author Costin Manolache |
| */ |
| public class JniHandler extends JkHandler { |
| protected AprImpl apr; |
| |
| // The native side handler |
| protected long nativeJkHandlerP; |
| |
| protected String jkHome; |
| |
| // Dispatch table codes. Hardcoded for now, will change when we have more handlers. |
| public static final int JK_HANDLE_JNI_DISPATCH=0x15; |
| public static final int JK_HANDLE_SHM_DISPATCH=0x16; |
| |
| |
| public static final int MSG_NOTE=0; |
| public static final int MB_NOTE=2; |
| private boolean paused = false; |
| |
| |
| public JniHandler() { |
| } |
| |
| /** |
| */ |
| public void setJkHome( String s ) { |
| jkHome=s; |
| } |
| |
| public String getJkHome() { |
| return jkHome; |
| } |
| |
| /** You must call initNative() inside the component init() |
| */ |
| public void init() throws IOException { |
| // static field init, temp |
| } |
| |
| protected void initNative(String nativeComponentName) { |
| apr=(AprImpl)wEnv.getHandler("apr"); |
| if( apr==null ) { |
| // In most cases we can just load it automatically. |
| // that requires all libs to be installed in standard places |
| // ( LD_LIBRARY_PATH, /usr/lib |
| try { |
| apr=new AprImpl(); |
| wEnv.addHandler("apr", apr); |
| apr.init(); |
| if( oname != null ) { |
| ObjectName aprname=new ObjectName(oname.getDomain() + |
| ":type=JkHandler, name=apr"); |
| Registry.getRegistry(null, null).registerComponent(apr, aprname, null); |
| } |
| } catch( Throwable t ) { |
| log.debug("Can't load apr", t); |
| apr=null; |
| } |
| } |
| if( apr==null || ! apr.isLoaded() ) { |
| if( log.isDebugEnabled() ) |
| log.debug("No apr, disabling jni proxy "); |
| apr=null; |
| return; |
| } |
| |
| try { |
| long xEnv=apr.getJkEnv(); |
| nativeJkHandlerP=apr.getJkHandler(xEnv, nativeComponentName ); |
| |
| if( nativeJkHandlerP==0 ) { |
| log.debug("Component not found, creating it " + nativeComponentName ); |
| nativeJkHandlerP=apr.createJkHandler(xEnv, nativeComponentName); |
| } |
| log.debug("Native proxy " + nativeJkHandlerP ); |
| apr.releaseJkEnv(xEnv); |
| } catch( Throwable t ) { |
| apr=null; |
| log.info("Error calling apr ", t); |
| } |
| } |
| |
| public void appendString( Msg msg, String s, C2BConverter charsetDecoder) |
| throws IOException |
| { |
| ByteChunk bc=charsetDecoder.getByteChunk(); |
| charsetDecoder.recycle(); |
| charsetDecoder.convert( s ); |
| charsetDecoder.flushBuffer(); |
| msg.appendByteChunk( bc ); |
| } |
| |
| public void pause() throws Exception { |
| synchronized(this) { |
| paused = true; |
| } |
| } |
| |
| public void resume() throws Exception { |
| synchronized(this) { |
| paused = false; |
| notifyAll(); |
| } |
| } |
| |
| |
| /** Create a msg context to be used with the shm channel |
| */ |
| public MsgContext createMsgContext() { |
| if( nativeJkHandlerP==0 || apr==null ) |
| return null; |
| |
| synchronized(this) { |
| try{ |
| while(paused) { |
| wait(); |
| } |
| }catch(InterruptedException ie) { |
| // Ignore, since it can't happen |
| } |
| } |
| |
| try { |
| MsgContext msgCtx=new MsgContext(); |
| MsgAjp msg=new MsgAjp(); |
| |
| msgCtx.setSource( (JkChannel)this ); |
| msgCtx.setWorkerEnv( wEnv ); |
| |
| msgCtx.setNext( this ); |
| |
| msgCtx.setMsg( MSG_NOTE, msg); // XXX Use noteId |
| |
| C2BConverter c2b=new C2BConverter( "iso-8859-1" ); |
| msgCtx.setConverter( c2b ); |
| |
| MessageBytes tmpMB= MessageBytes.newInstance(); |
| msgCtx.setNote( MB_NOTE, tmpMB ); |
| return msgCtx; |
| } catch( Exception ex ) { |
| log.error("Can't create endpoint", ex); |
| return null; |
| } |
| } |
| |
| public void setNativeAttribute(String name, String val) throws IOException { |
| if( apr==null ) return; |
| |
| if( nativeJkHandlerP == 0 ) { |
| log.error( "Unitialized component " + name+ " " + val ); |
| return; |
| } |
| |
| long xEnv=apr.getJkEnv(); |
| |
| apr.jkSetAttribute( xEnv, nativeJkHandlerP, name, val ); |
| |
| apr.releaseJkEnv( xEnv ); |
| } |
| |
| public void initJkComponent() throws IOException { |
| if( apr==null ) return; |
| |
| if( nativeJkHandlerP == 0 ) { |
| log.error( "Unitialized component " ); |
| return; |
| } |
| |
| long xEnv=apr.getJkEnv(); |
| |
| apr.jkInit( xEnv, nativeJkHandlerP ); |
| |
| apr.releaseJkEnv( xEnv ); |
| } |
| |
| public void destroyJkComponent() throws IOException { |
| if( apr==null ) return; |
| |
| if( nativeJkHandlerP == 0 ) { |
| log.error( "Unitialized component " ); |
| return; |
| } |
| |
| long xEnv=apr.getJkEnv(); |
| |
| apr.jkDestroy( xEnv, nativeJkHandlerP ); |
| |
| apr.releaseJkEnv( xEnv ); |
| } |
| |
| |
| |
| protected void setNativeEndpoint(MsgContext msgCtx) { |
| long xEnv=apr.getJkEnv(); |
| msgCtx.setJniEnv( xEnv ); |
| |
| long epP=apr.createJkHandler(xEnv, "endpoint"); |
| log.debug("create ep " + epP ); |
| if( epP == 0 ) return; |
| apr.jkInit( xEnv, epP ); |
| msgCtx.setJniContext( epP ); |
| |
| } |
| |
| protected void recycleNative(MsgContext ep) { |
| apr.jkRecycle(ep.getJniEnv(), ep.getJniContext()); |
| } |
| |
| /** send and get the response in the same buffer. This calls the |
| * method on the wrapped jk_bean object. |
| */ |
| protected int nativeDispatch( Msg msg, MsgContext ep, int code, int raw ) |
| throws IOException |
| { |
| if( log.isDebugEnabled() ) |
| log.debug( "Sending packet " + code + " " + raw); |
| |
| if( raw == 0 ) { |
| msg.end(); |
| |
| if( log.isTraceEnabled() ) msg.dump("OUT:" ); |
| } |
| |
| // Create ( or reuse ) the jk_endpoint ( the native pair of |
| // MsgContext ) |
| long xEnv=ep.getJniEnv(); |
| long nativeContext=ep.getJniContext(); |
| if( nativeContext==0 || xEnv==0 ) { |
| setNativeEndpoint( ep ); |
| xEnv=ep.getJniEnv(); |
| nativeContext=ep.getJniContext(); |
| } |
| |
| if( xEnv==0 || nativeContext==0 || nativeJkHandlerP==0 ) { |
| log.error("invokeNative: Null pointer "); |
| return -1; |
| } |
| |
| // Will process the message in the current thread. |
| // No wait needed to receive the response, if any |
| int status=AprImpl.jkInvoke( xEnv, |
| nativeJkHandlerP, |
| nativeContext, |
| code, msg.getBuffer(), 0, msg.getLen(), raw ); |
| |
| if( status != 0 && status != 2 ) { |
| log.error( "nativeDispatch: error " + status, new Throwable() ); |
| } |
| |
| if( log.isDebugEnabled() ) log.debug( "Sending packet - done " + status); |
| return status; |
| } |
| |
| /** Base implementation for invoke. Dispatch the action to the native |
| * code, where invoke() is called on the wrapped jk_bean. |
| */ |
| public int invoke(Msg msg, MsgContext ep ) |
| throws IOException |
| { |
| long xEnv=ep.getJniEnv(); |
| int type=ep.getType(); |
| |
| int status=nativeDispatch(msg, ep, type, 0 ); |
| |
| apr.jkRecycle(xEnv, ep.getJniContext()); |
| apr.releaseJkEnv( xEnv ); |
| return status; |
| } |
| |
| private static org.apache.commons.logging.Log log= |
| org.apache.commons.logging.LogFactory.getLog( JniHandler.class ); |
| } |