1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18 package org.apache.jk.common;
19
20 import java.io.IOException;
21
22 import javax.management.ObjectName;
23
24 import org.apache.jk.apr.AprImpl;
25 import org.apache.jk.core.JkHandler;
26 import org.apache.jk.core.Msg;
27 import org.apache.jk.core.MsgContext;
28 import org.apache.jk.core.JkChannel;
29 import org.apache.tomcat.util.buf.ByteChunk;
30 import org.apache.tomcat.util.buf.C2BConverter;
31 import org.apache.tomcat.util.buf.MessageBytes;
32 import org.apache.tomcat.util.modeler.Registry;
33
34
35 /**
36 * Base class for components using native code ( libjkjni.so ).
37 * It allows to access the jk_env and wrap ( 'box' ? ) a native
38 * jk component, and call it's methods.
39 *
40 * Note that get/setAttribute are expensive ( Strings, etc ),
41 * invoke() is were all optimizations are done. We do recycle
42 * all memory on both C and java sides ( the only exception is
43 * when we attempt pinning but the VM doesn't support it ). The
44 * low level optimizations from ByteBuffer, etc are used to
45 * reduce the overhead of passing strings.
46 *
47 * @author Costin Manolache
48 */
49 public class JniHandler extends JkHandler {
50 protected AprImpl apr;
51
52 // The native side handler
53 protected long nativeJkHandlerP;
54
55 protected String jkHome;
56
57 // Dispatch table codes. Hardcoded for now, will change when we have more handlers.
58 public static final int JK_HANDLE_JNI_DISPATCH=0x15;
59 public static final int JK_HANDLE_SHM_DISPATCH=0x16;
60
61
62 public static final int MSG_NOTE=0;
63 public static final int MB_NOTE=2;
64 private boolean paused = false;
65
66
67 public JniHandler() {
68 }
69
70 /**
71 */
72 public void setJkHome( String s ) {
73 jkHome=s;
74 }
75
76 public String getJkHome() {
77 return jkHome;
78 }
79
80 /** You must call initNative() inside the component init()
81 */
82 public void init() throws IOException {
83 // static field init, temp
84 }
85
86 protected void initNative(String nativeComponentName) {
87 apr=(AprImpl)wEnv.getHandler("apr");
88 if( apr==null ) {
89 // In most cases we can just load it automatically.
90 // that requires all libs to be installed in standard places
91 // ( LD_LIBRARY_PATH, /usr/lib
92 try {
93 apr=new AprImpl();
94 wEnv.addHandler("apr", apr);
95 apr.init();
96 if( oname != null ) {
97 ObjectName aprname=new ObjectName(oname.getDomain() +
98 ":type=JkHandler, name=apr");
99 Registry.getRegistry(null, null).registerComponent(apr, aprname, null);
100 }
101 } catch( Throwable t ) {
102 log.debug("Can't load apr", t);
103 apr=null;
104 }
105 }
106 if( apr==null || ! apr.isLoaded() ) {
107 if( log.isDebugEnabled() )
108 log.debug("No apr, disabling jni proxy ");
109 apr=null;
110 return;
111 }
112
113 try {
114 long xEnv=apr.getJkEnv();
115 nativeJkHandlerP=apr.getJkHandler(xEnv, nativeComponentName );
116
117 if( nativeJkHandlerP==0 ) {
118 log.debug("Component not found, creating it " + nativeComponentName );
119 nativeJkHandlerP=apr.createJkHandler(xEnv, nativeComponentName);
120 }
121 log.debug("Native proxy " + nativeJkHandlerP );
122 apr.releaseJkEnv(xEnv);
123 } catch( Throwable t ) {
124 apr=null;
125 log.info("Error calling apr ", t);
126 }
127 }
128
129 public void appendString( Msg msg, String s, C2BConverter charsetDecoder)
130 throws IOException
131 {
132 ByteChunk bc=charsetDecoder.getByteChunk();
133 charsetDecoder.recycle();
134 charsetDecoder.convert( s );
135 charsetDecoder.flushBuffer();
136 msg.appendByteChunk( bc );
137 }
138
139 public void pause() throws Exception {
140 synchronized(this) {
141 paused = true;
142 }
143 }
144
145 public void resume() throws Exception {
146 synchronized(this) {
147 paused = false;
148 notifyAll();
149 }
150 }
151
152
153 /** Create a msg context to be used with the shm channel
154 */
155 public MsgContext createMsgContext() {
156 if( nativeJkHandlerP==0 || apr==null )
157 return null;
158
159 synchronized(this) {
160 try{
161 while(paused) {
162 wait();
163 }
164 }catch(InterruptedException ie) {
165 // Ignore, since it can't happen
166 }
167 }
168
169 try {
170 MsgContext msgCtx=new MsgContext();
171 MsgAjp msg=new MsgAjp();
172
173 msgCtx.setSource( (JkChannel)this );
174 msgCtx.setWorkerEnv( wEnv );
175
176 msgCtx.setNext( this );
177
178 msgCtx.setMsg( MSG_NOTE, msg); // XXX Use noteId
179
180 C2BConverter c2b=new C2BConverter( "iso-8859-1" );
181 msgCtx.setConverter( c2b );
182
183 MessageBytes tmpMB= MessageBytes.newInstance();
184 msgCtx.setNote( MB_NOTE, tmpMB );
185 return msgCtx;
186 } catch( Exception ex ) {
187 log.error("Can't create endpoint", ex);
188 return null;
189 }
190 }
191
192 public void setNativeAttribute(String name, String val) throws IOException {
193 if( apr==null ) return;
194
195 if( nativeJkHandlerP == 0 ) {
196 log.error( "Unitialized component " + name+ " " + val );
197 return;
198 }
199
200 long xEnv=apr.getJkEnv();
201
202 apr.jkSetAttribute( xEnv, nativeJkHandlerP, name, val );
203
204 apr.releaseJkEnv( xEnv );
205 }
206
207 public void initJkComponent() throws IOException {
208 if( apr==null ) return;
209
210 if( nativeJkHandlerP == 0 ) {
211 log.error( "Unitialized component " );
212 return;
213 }
214
215 long xEnv=apr.getJkEnv();
216
217 apr.jkInit( xEnv, nativeJkHandlerP );
218
219 apr.releaseJkEnv( xEnv );
220 }
221
222 public void destroyJkComponent() throws IOException {
223 if( apr==null ) return;
224
225 if( nativeJkHandlerP == 0 ) {
226 log.error( "Unitialized component " );
227 return;
228 }
229
230 long xEnv=apr.getJkEnv();
231
232 apr.jkDestroy( xEnv, nativeJkHandlerP );
233
234 apr.releaseJkEnv( xEnv );
235 }
236
237
238
239 protected void setNativeEndpoint(MsgContext msgCtx) {
240 long xEnv=apr.getJkEnv();
241 msgCtx.setJniEnv( xEnv );
242
243 long epP=apr.createJkHandler(xEnv, "endpoint");
244 log.debug("create ep " + epP );
245 if( epP == 0 ) return;
246 apr.jkInit( xEnv, epP );
247 msgCtx.setJniContext( epP );
248
249 }
250
251 protected void recycleNative(MsgContext ep) {
252 apr.jkRecycle(ep.getJniEnv(), ep.getJniContext());
253 }
254
255 /** send and get the response in the same buffer. This calls the
256 * method on the wrapped jk_bean object.
257 */
258 protected int nativeDispatch( Msg msg, MsgContext ep, int code, int raw )
259 throws IOException
260 {
261 if( log.isDebugEnabled() )
262 log.debug( "Sending packet " + code + " " + raw);
263
264 if( raw == 0 ) {
265 msg.end();
266
267 if( log.isTraceEnabled() ) msg.dump("OUT:" );
268 }
269
270 // Create ( or reuse ) the jk_endpoint ( the native pair of
271 // MsgContext )
272 long xEnv=ep.getJniEnv();
273 long nativeContext=ep.getJniContext();
274 if( nativeContext==0 || xEnv==0 ) {
275 setNativeEndpoint( ep );
276 xEnv=ep.getJniEnv();
277 nativeContext=ep.getJniContext();
278 }
279
280 if( xEnv==0 || nativeContext==0 || nativeJkHandlerP==0 ) {
281 log.error("invokeNative: Null pointer ");
282 return -1;
283 }
284
285 // Will process the message in the current thread.
286 // No wait needed to receive the response, if any
287 int status=AprImpl.jkInvoke( xEnv,
288 nativeJkHandlerP,
289 nativeContext,
290 code, msg.getBuffer(), 0, msg.getLen(), raw );
291
292 if( status != 0 && status != 2 ) {
293 log.error( "nativeDispatch: error " + status, new Throwable() );
294 }
295
296 if( log.isDebugEnabled() ) log.debug( "Sending packet - done " + status);
297 return status;
298 }
299
300 /** Base implementation for invoke. Dispatch the action to the native
301 * code, where invoke() is called on the wrapped jk_bean.
302 */
303 public int invoke(Msg msg, MsgContext ep )
304 throws IOException
305 {
306 long xEnv=ep.getJniEnv();
307 int type=ep.getType();
308
309 int status=nativeDispatch(msg, ep, type, 0 );
310
311 apr.jkRecycle(xEnv, ep.getJniContext());
312 apr.releaseJkEnv( xEnv );
313 return status;
314 }
315
316 private static org.apache.juli.logging.Log log=
317 org.apache.juli.logging.LogFactory.getLog( JniHandler.class );
318 }