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: import java.util.Vector;
021:
022: import org.apache.jk.apr.AprImpl;
023: import org.apache.jk.core.Msg;
024: import org.apache.jk.core.MsgContext;
025: import org.apache.jk.core.WorkerEnv;
026: import org.apache.tomcat.util.IntrospectionUtils;
027: import org.apache.tomcat.util.buf.C2BConverter;
028:
029: /* The code is a bit confusing at this moment - the class is used as
030: a Bean, or ant Task, or CLI - i.e. you set properties and call execute.
031:
032: That's different from the rest of jk handlers wich are stateless ( but
033: similar with Coyote-http ).
034: */
035:
036: /** Handle the shared memory objects.
037: *
038: * @author Costin Manolache
039: */
040: public class Shm extends JniHandler {
041: String file = "/tmp/shm.file";
042: int size;
043: String host = "localhost";
044: int port = 8009;
045: String unixSocket;
046:
047: boolean help = false;
048: boolean unregister = false;
049: boolean reset = false;
050: String dumpFile = null;
051:
052: Vector groups = new Vector();
053:
054: // Will be dynamic ( getMethodId() ) after things are stable
055: static final int SHM_WRITE_SLOT = 2;
056: static final int SHM_RESET = 5;
057: static final int SHM_DUMP = 6;
058:
059: public Shm() {
060: }
061:
062: /** Scoreboard location
063: */
064: public void setFile(String f) {
065: file = f;
066: }
067:
068: /** Copy the scoreboard in a file for debugging
069: * Will also log a lot of information about what's in the scoreboard.
070: */
071: public void setDump(String dumpFile) {
072: this .dumpFile = dumpFile;
073: }
074:
075: /** Size. Used only if the scoreboard is to be created.
076: */
077: public void setSize(int size) {
078: this .size = size;
079: }
080:
081: /** Set this to get the scoreboard reset.
082: * The shm segment will be destroyed and a new one created,
083: * with the provided size.
084: *
085: * Requires "file" and "size".
086: */
087: public void setReset(boolean b) {
088: reset = true;
089: }
090:
091: /** Ajp13 host
092: */
093: public void setHost(String host) {
094: this .host = host;
095: }
096:
097: /** Mark this instance as belonging to a group
098: */
099: public void setGroup(String grp) {
100: groups.addElement(grp);
101: }
102:
103: /** Ajp13 port
104: */
105: public void setPort(int port) {
106: this .port = port;
107: }
108:
109: /** Unix socket where tomcat is listening.
110: * Use it only if tomcat is on the same host, of course
111: */
112: public void setUnixSocket(String unixSocket) {
113: this .unixSocket = unixSocket;
114: }
115:
116: /** Set this option to mark the tomcat instance as
117: 'down', so apache will no longer forward messages to it.
118: Note that requests with a session will still try this
119: host first.
120:
121: This can be used to implement gracefull shutdown.
122:
123: Host and port are still required, since they are used
124: to identify tomcat.
125: */
126: public void setUnregister(boolean unregister) {
127: this .unregister = true;
128: }
129:
130: public void init() throws IOException {
131: super .initNative("shm");
132: if (apr == null)
133: return;
134: if (file == null) {
135: log.error("No shm file, disabling shared memory");
136: apr = null;
137: return;
138: }
139:
140: // Set properties and call init.
141: setNativeAttribute("file", file);
142: if (size > 0)
143: setNativeAttribute("size", Integer.toString(size));
144:
145: initJkComponent();
146: }
147:
148: public void resetScoreboard() throws IOException {
149: if (apr == null)
150: return;
151: MsgContext mCtx = createMsgContext();
152: Msg msg = (Msg) mCtx.getMsg(0);
153: msg.reset();
154:
155: msg.appendByte(SHM_RESET);
156:
157: this .invoke(msg, mCtx);
158: }
159:
160: public void dumpScoreboard(String fname) throws IOException {
161: if (apr == null)
162: return;
163: MsgContext mCtx = createMsgContext();
164: Msg msg = (Msg) mCtx.getMsg(0);
165: C2BConverter c2b = (C2BConverter) mCtx.getNote(C2B_NOTE);
166: msg.reset();
167:
168: msg.appendByte(SHM_DUMP);
169:
170: appendString(msg, fname, c2b);
171:
172: this .invoke(msg, mCtx);
173: }
174:
175: /** Register a tomcat instance
176: * XXX make it more flexible
177: */
178: public void registerTomcat(String host, int port, String unixDomain)
179: throws IOException {
180: String instanceId = host + ":" + port;
181:
182: String slotName = "TOMCAT:" + instanceId;
183: MsgContext mCtx = createMsgContext();
184: Msg msg = (Msg) mCtx.getMsg(0);
185: msg.reset();
186: C2BConverter c2b = (C2BConverter) mCtx.getNote(C2B_NOTE);
187:
188: msg.appendByte(SHM_WRITE_SLOT);
189: appendString(msg, slotName, c2b);
190:
191: int channelCnt = 1;
192: if (unixDomain != null)
193: channelCnt++;
194:
195: // number of groups. 0 means the default lb.
196: msg.appendInt(groups.size());
197: for (int i = 0; i < groups.size(); i++) {
198: appendString(msg, (String) groups.elementAt(i), c2b);
199: appendString(msg, instanceId, c2b);
200: }
201:
202: // number of channels for this instance
203: msg.appendInt(channelCnt);
204:
205: // The body:
206: appendString(msg, "channel.socket:" + host + ":" + port, c2b);
207: msg.appendInt(1);
208: appendString(msg, "tomcatId", c2b);
209: appendString(msg, instanceId, c2b);
210:
211: if (unixDomain != null) {
212: appendString(msg, "channel.apr:" + unixDomain, c2b);
213: msg.appendInt(1);
214: appendString(msg, "tomcatId", c2b);
215: appendString(msg, instanceId, c2b);
216: }
217:
218: System.out.println("Register " + instanceId);
219: this .invoke(msg, mCtx);
220: }
221:
222: public void unRegisterTomcat(String host, int port)
223: throws IOException {
224: String slotName = "TOMCAT:" + host + ":" + port;
225: MsgContext mCtx = createMsgContext();
226: Msg msg = (Msg) mCtx.getMsg(0);
227: msg.reset();
228: C2BConverter c2b = (C2BConverter) mCtx.getNote(C2B_NOTE);
229:
230: msg.appendByte(SHM_WRITE_SLOT);
231: appendString(msg, slotName, c2b);
232:
233: // number of channels for this instance
234: msg.appendInt(0);
235: msg.appendInt(0);
236:
237: System.out.println("UnRegister " + slotName);
238: this .invoke(msg, mCtx);
239: }
240:
241: public void destroy() throws IOException {
242: destroyJkComponent();
243: }
244:
245: public int invoke(Msg msg, MsgContext ep) throws IOException {
246: if (apr == null)
247: return 0;
248: log.debug("ChannelShm.invoke: " + ep);
249: super .nativeDispatch(msg, ep, JK_HANDLE_SHM_DISPATCH, 0);
250: return 0;
251: }
252:
253: private static org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory
254: .getLog(Shm.class);
255:
256: //-------------------- Main - use the shm functions from ant or CLI ------
257:
258: /** Local initialization - for standalone use
259: */
260: public void initCli() throws IOException {
261: WorkerEnv wEnv = new WorkerEnv();
262: AprImpl apr = new AprImpl();
263: wEnv.addHandler("apr", apr);
264: wEnv.addHandler("shm", this );
265: apr.init();
266: if (!apr.isLoaded()) {
267: log
268: .error("No native support. "
269: + "Make sure libapr.so and libjkjni.so are available in LD_LIBRARY_PATH");
270: return;
271: }
272: }
273:
274: public void execute() {
275: try {
276: if (help)
277: return;
278: initCli();
279: init();
280:
281: if (reset) {
282: resetScoreboard();
283: } else if (dumpFile != null) {
284: dumpScoreboard(dumpFile);
285: } else if (unregister) {
286: unRegisterTomcat(host, port);
287: } else {
288: registerTomcat(host, port, unixSocket);
289: }
290: } catch (Exception ex) {
291: log.error("Error executing Shm", ex);
292: }
293: }
294:
295: public void setHelp(boolean b) {
296: System.out.println("Usage: ");
297: System.out.println(" Shm [OPTIONS]");
298: System.out.println();
299: System.out.println(" -file SHM_FILE");
300: System.out
301: .println(" -group GROUP ( can be specified multiple times )");
302: System.out.println(" -host HOST");
303: System.out.println(" -port PORT");
304: System.out.println(" -unixSocket UNIX_FILE");
305: // System.out.println(" -priority XXX");
306: // System.out.println(" -lbFactor XXX");
307: help = true;
308: return;
309: }
310:
311: public static void main(String args[]) {
312: try {
313: Shm shm = new Shm();
314:
315: if (args.length == 0 || ("-?".equals(args[0]))) {
316: shm.setHelp(true);
317: return;
318: }
319:
320: IntrospectionUtils.processArgs(shm, args);
321: shm.execute();
322: } catch (Exception ex) {
323: ex.printStackTrace();
324: }
325: }
326: }
|