001: /*
002: * Copyright 1999-2004 The Apache Software Foundation
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.apache.jk.common;
018:
019: import java.io.IOException;
020:
021: import javax.management.ObjectName;
022:
023: import org.apache.commons.modeler.Registry;
024: import org.apache.jk.apr.AprImpl;
025: import org.apache.jk.core.JkHandler;
026: import org.apache.jk.core.Msg;
027: import org.apache.jk.core.MsgContext;
028: import org.apache.jk.core.JkChannel;
029: import org.apache.tomcat.util.buf.ByteChunk;
030: import org.apache.tomcat.util.buf.C2BConverter;
031: import org.apache.tomcat.util.buf.MessageBytes;
032:
033: /**
034: * Base class for components using native code ( libjkjni.so ).
035: * It allows to access the jk_env and wrap ( 'box' ? ) a native
036: * jk component, and call it's methods.
037: *
038: * Note that get/setAttribute are expensive ( Strings, etc ),
039: * invoke() is were all optimizations are done. We do recycle
040: * all memory on both C and java sides ( the only exception is
041: * when we attempt pinning but the VM doesn't support it ). The
042: * low level optimizations from ByteBuffer, etc are used to
043: * reduce the overhead of passing strings.
044: *
045: * @author Costin Manolache
046: */
047: public class JniHandler extends JkHandler {
048: protected AprImpl apr;
049:
050: // The native side handler
051: protected long nativeJkHandlerP;
052:
053: protected String jkHome;
054:
055: // Dispatch table codes. Hardcoded for now, will change when we have more handlers.
056: public static final int JK_HANDLE_JNI_DISPATCH = 0x15;
057: public static final int JK_HANDLE_SHM_DISPATCH = 0x16;
058:
059: public static final int MSG_NOTE = 0;
060: public static final int C2B_NOTE = 1;
061: public static final int MB_NOTE = 2;
062: private boolean paused = false;
063:
064: public JniHandler() {
065: }
066:
067: /**
068: */
069: public void setJkHome(String s) {
070: jkHome = s;
071: }
072:
073: public String getJkHome() {
074: return jkHome;
075: }
076:
077: /** You must call initNative() inside the component init()
078: */
079: public void init() throws IOException {
080: // static field init, temp
081: }
082:
083: protected void initNative(String nativeComponentName) {
084: apr = (AprImpl) wEnv.getHandler("apr");
085: if (apr == null) {
086: // In most cases we can just load it automatically.
087: // that requires all libs to be installed in standard places
088: // ( LD_LIBRARY_PATH, /usr/lib
089: try {
090: apr = new AprImpl();
091: wEnv.addHandler("apr", apr);
092: apr.init();
093: if (oname != null) {
094: ObjectName aprname = new ObjectName(oname
095: .getDomain()
096: + ":type=JkHandler, name=apr");
097: Registry.getRegistry().registerComponent(apr,
098: aprname, null);
099: }
100: } catch (Throwable t) {
101: log.debug("Can't load apr", t);
102: apr = null;
103: }
104: }
105: if (apr == null || !apr.isLoaded()) {
106: if (log.isDebugEnabled())
107: log.debug("No apr, disabling jni proxy ");
108: apr = null;
109: return;
110: }
111:
112: try {
113: long xEnv = apr.getJkEnv();
114: nativeJkHandlerP = apr.getJkHandler(xEnv,
115: nativeComponentName);
116:
117: if (nativeJkHandlerP == 0) {
118: log.debug("Component not found, creating it "
119: + nativeComponentName);
120: nativeJkHandlerP = apr.createJkHandler(xEnv,
121: nativeComponentName);
122: }
123: log.debug("Native proxy " + nativeJkHandlerP);
124: apr.releaseJkEnv(xEnv);
125: } catch (Throwable t) {
126: apr = null;
127: log.info("Error calling apr ", t);
128: }
129: }
130:
131: public void appendString(Msg msg, String s,
132: C2BConverter charsetDecoder) throws IOException {
133: ByteChunk bc = charsetDecoder.getByteChunk();
134: charsetDecoder.recycle();
135: charsetDecoder.convert(s);
136: charsetDecoder.flushBuffer();
137: msg.appendByteChunk(bc);
138: }
139:
140: public void pause() throws Exception {
141: synchronized (this ) {
142: paused = true;
143: }
144: }
145:
146: public void resume() throws Exception {
147: synchronized (this ) {
148: paused = false;
149: notifyAll();
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.setNote(C2B_NOTE, c2b);
182:
183: MessageBytes tmpMB = new MessageBytes();
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)
193: throws IOException {
194: if (apr == null)
195: return;
196:
197: if (nativeJkHandlerP == 0) {
198: log.error("Unitialized component " + name + " " + val);
199: return;
200: }
201:
202: long xEnv = apr.getJkEnv();
203:
204: apr.jkSetAttribute(xEnv, nativeJkHandlerP, name, val);
205:
206: apr.releaseJkEnv(xEnv);
207: }
208:
209: public void initJkComponent() throws IOException {
210: if (apr == null)
211: return;
212:
213: if (nativeJkHandlerP == 0) {
214: log.error("Unitialized component ");
215: return;
216: }
217:
218: long xEnv = apr.getJkEnv();
219:
220: apr.jkInit(xEnv, nativeJkHandlerP);
221:
222: apr.releaseJkEnv(xEnv);
223: }
224:
225: public void destroyJkComponent() throws IOException {
226: if (apr == null)
227: return;
228:
229: if (nativeJkHandlerP == 0) {
230: log.error("Unitialized component ");
231: return;
232: }
233:
234: long xEnv = apr.getJkEnv();
235:
236: apr.jkDestroy(xEnv, nativeJkHandlerP);
237:
238: apr.releaseJkEnv(xEnv);
239: }
240:
241: protected void setNativeEndpoint(MsgContext msgCtx) {
242: long xEnv = apr.getJkEnv();
243: msgCtx.setJniEnv(xEnv);
244:
245: long epP = apr.createJkHandler(xEnv, "endpoint");
246: log.debug("create ep " + epP);
247: if (epP == 0)
248: return;
249: apr.jkInit(xEnv, epP);
250: msgCtx.setJniContext(epP);
251:
252: }
253:
254: protected void recycleNative(MsgContext ep) {
255: apr.jkRecycle(ep.getJniEnv(), ep.getJniContext());
256: }
257:
258: /** send and get the response in the same buffer. This calls the
259: * method on the wrapped jk_bean object.
260: */
261: protected int nativeDispatch(Msg msg, MsgContext ep, int code,
262: int raw) throws IOException {
263: if (log.isDebugEnabled())
264: log.debug("Sending packet " + code + " " + raw);
265:
266: if (raw == 0) {
267: msg.end();
268:
269: if (log.isTraceEnabled())
270: msg.dump("OUT:");
271: }
272:
273: // Create ( or reuse ) the jk_endpoint ( the native pair of
274: // MsgContext )
275: long xEnv = ep.getJniEnv();
276: long nativeContext = ep.getJniContext();
277: if (nativeContext == 0 || xEnv == 0) {
278: setNativeEndpoint(ep);
279: xEnv = ep.getJniEnv();
280: nativeContext = ep.getJniContext();
281: }
282:
283: if (xEnv == 0 || nativeContext == 0 || nativeJkHandlerP == 0) {
284: log.error("invokeNative: Null pointer ");
285: return -1;
286: }
287:
288: // Will process the message in the current thread.
289: // No wait needed to receive the response, if any
290: int status = apr.jkInvoke(xEnv, nativeJkHandlerP,
291: nativeContext, code, msg.getBuffer(), 0, msg.getLen(),
292: raw);
293:
294: if (status != 0 && status != 2) {
295: log.error("nativeDispatch: error " + status,
296: new Throwable());
297: }
298:
299: if (log.isDebugEnabled())
300: log.debug("Sending packet - done " + status);
301: return status;
302: }
303:
304: /** Base implementation for invoke. Dispatch the action to the native
305: * code, where invoke() is called on the wrapped jk_bean.
306: */
307: public int invoke(Msg msg, MsgContext ep) throws IOException {
308: long xEnv = ep.getJniEnv();
309: int type = ep.getType();
310:
311: int status = nativeDispatch(msg, ep, type, 0);
312:
313: apr.jkRecycle(xEnv, ep.getJniContext());
314: apr.releaseJkEnv(xEnv);
315: return status;
316: }
317:
318: private static org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory
319: .getLog(JniHandler.class);
320: }
|