1 /**
2 * AUTOHIT 2003
3 * Copyright Erich P Gatejen (c) 1989,1997,2003,2004
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or (at
8 * your option) any later version.
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 *
18 * Additional license information can be found in the documentation.
19 * @author Erich P Gatejen
20 */
21 package autohit.server.command;
22
23 import java.util.Vector;
24 import java.io.Serializable;
25
26 import autohit.common.channels.Atom;
27 import autohit.common.channels.Injector;
28 import autohit.common.channels.Receipt;
29 import autohit.server.ServerException;
30 import autohit.server.SystemContext;
31 import autohit.universe.Universe;
32 import autohit.common.AutohitErrorCodes;
33 import autohit.common.channels.ChannelException;
34
35 /**
36 * A command base class. All command inplementations should use extend it.
37 * <p>
38 * THIS IS NOT THREAD SAFE!!!! EVERY CommandServer should create their own
39 * instances!!!! There are object fields that store the command parameters.
40 * It's the cleanest way to do it. Cope and deal.
41 * <p>
42 * All exceptions should/will be caught and reported on the response channel
43 * except if the command is poorly formed (it still will try to report) or is a
44 * very serious problem.
45 * <p>
46 * <code>
47 * Command sequence is as follows:
48 * 1- Command class instantiation
49 * 2- Get command parameters. Kept in a vector. Contstructed with
50 * the helper static method createCommand()
51 * 0-UNI - (has default) Universe
52 * 1-RESPONSE - (has default) Injector for control response channel
53 * 2-TARGET - (has default) Injector for target channel
54 * 3-CLASS - Implementation class
55 * 4-COMMAND - Command string
56 * 5-OBJECT - Data object
57 * 3- Call the command with call()
58 * - Setup base
59 * 4- Call verify in subclass()
60 * - Subclass should call assert method in base for required/optional
61 * for the parameters.
62 * - The assert method will set the object fields.
63 * - The base assert will throw an exception for any error. It
64 * MUST be passed along. The base will handle any reporting.
65 * 5- Base will accept the command.
66 * 6- Passed into chained execute()
67 * - Problems should be thrown as exceptions
68 * 7- Base will ack or nak the command.
69 * 8- Return a receipt
70 * </code>
71 *
72 * @author Erich P. Gatejen
73 * @version 1.0 <i>Version History</i><code>EPG - Initial - 24Jul03
74 * EPG - Rewrite - 31Jul03
75 * </code>
76 */
77 public abstract class Command implements Serializable {
78
79 /**
80 * Constants */
81 public final static char RESPONSE_ELEMENT_SEPERATOR = '|';
82 public final static int UNI_LIST_INDEX = 0;
83 public final static int RESPONSE_LIST_INDEX = 1;
84 public final static int TARGET_LIST_INDEX = 2;
85 public final static int CLASS_LIST_INDEX = 3;
86 public final static int COMMAND_LIST_INDEX = 4;
87 public final static int OBJECT_LIST_INDEX = 5;
88
89 /**
90 * Command objects. Don't change these! */
91 public Universe uni;
92 public Injector response;
93 public Injector target;
94 public String classobject;
95 public String command;
96 public Object data;
97 public int uniqueID;
98
99 private Vector commandlist;
100 protected SystemContext sc;
101
102 /**
103 * Execute the command.
104 *
105 * @throws ServerException
106 * @return return the manreadable message for success.
107 */
108 abstract public String execute() throws ServerException;
109
110 /**
111 * Verify the command. Basically, it should just call the assert method
112 * with the six parameters indicating if the fields are required or not
113 * (optional). The first three (uni, target, and reponse) have defaults, if
114 * nothing is passed. However, the the assert will throw an exception if it
115 * is marked as required but isn't present.
116 * <p>
117 * The exception from accept should not be intercepted! The following is an
118 * example implementation.
119 * <p>
120 * <code>
121 *
122 * public String verify() throws ServerException {
123 * this.assert(true,true,false,false,false,true);
124 * return "parameters are good.";
125 * }
126 * </code>
127 *
128 * @throws ServerException
129 * @return return the manreadable message for accepting the command.
130 */
131 abstract public String verify() throws ServerException;
132
133 /**
134 * Get the textual name for the command.
135 *
136 * @return return the manreadable name of this command.
137 */
138 abstract public String getName();
139
140 /**
141 * Execute the command. String Method.
142 * <p>
143 * it will throw a ServerException if the command is poorly formed and
144 * cannot be accepted or there is a serious system problem. Outwise it
145 * should just log issues to the response (or default) and return no
146 * receipt.
147 *
148 * @param c
149 * the System Context
150 * @param cl
151 * the command list in a Vector
152 * @throws ServerException
153 * @return a receipt for the transaction (given by the responseChannel).
154 * This can be a log injector.
155 */
156 public Receipt call(SystemContext c, Vector cl) throws ServerException {
157
158 // Setup
159 String victorydance = ".";
160 commandlist = cl;
161 sc = c;
162 Receipt rr = null;
163 uniqueID = sc.uniqueInteger();
164
165 // Catch any wild-assed exceptions
166 try {
167
168 // Verify the parameters
169 try {
170 this.verify();
171 } catch (ServerException se) {
172 // Abort the command. If the response channel is valid, send it
173 // there,
174 // otherwise use the default
175 if (response == null)
176 response = sc.getRootLogger().sinjector;
177 this.respond("malformed and aborted!", AutohitErrorCodes.EVENT_COMMAND_FAILED, null);
178 return null;
179 }
180
181 // Accept the command
182 this.respond("accepted.", AutohitErrorCodes.EVENT_COMMAND_ACCEPTED, null);
183
184 // Execute it
185 try {
186 String vic = this.execute();
187 if (vic != null)
188 victorydance = vic;
189
190 } catch (ServerException sse) {
191 // Failed command. nak and break out
192 this.respond("failed. code[" + sse.numeric + "] " + sse.getMessage(),
193 AutohitErrorCodes.EVENT_COMMAND_FAILED, null);
194 return null;
195 }
196
197 // Issue the receipt
198 rr = new Receipt();
199 rr.setAsInteger(uniqueID);
200
201 // Ack the command
202 this.respond("succeeded. Receipt issued. " + victorydance,
203 AutohitErrorCodes.EVENT_COMMAND_COMPLELTED, rr);
204
205 } catch (ChannelException ece) {
206 // This is pretty bad. If we can't use the channel, then the system
207 // state is dubious. What good is a command, if you can't give it
208 // a response? So PANIC!
209 throw new ServerException(
210 this.getMsgHeader()
211 + "PANIC! Could not use the responseChannel. This makes it useless. exception="
212 + ece.getMessage(),
213 ServerException.CODE_SERVER_PANIC,
214 ece);
215 } catch (Exception e) {
216 // This should not have happened. Bad programmer should have
217 // trapped
218 // all exceptions before this!
219 throw new ServerException(
220 this.getMsgHeader()
221 + "Software Detected Fault in Command.class. Someone let an exception out. File a bug. exception="
222 + e.getMessage(),
223 ServerException.CODE_SW_DETECTED_FAULT,
224 e);
225 }
226 return rr;
227 }
228
229 /**
230 * Assert the parameters.
231 *
232 * @param univ
233 * is 0-UNI required?
234 * @param resp
235 * is 1-RESPONSE required?
236 * @param targ
237 * is 2-TARGET required?
238 * @param cobj
239 * is 3-CLASS required?
240 * @param cmd
241 * is 4-COMMAND required?
242 * @param dobj
243 * is 5-OBJECT required?
244 * @throws ServerException.
245 * Do not intercept it!
246 */
247 protected void assertparam(boolean univ, boolean resp, boolean targ, boolean cobj, boolean cmd, boolean dobj)
248 throws ServerException {
249
250 uni = null;
251 response = null;
252 target = null;
253 classobject = null;
254 command = null;
255 data = null;
256
257 try {
258 Object thang;
259
260 // 0-UNI
261 thang = commandlist.elementAt(UNI_LIST_INDEX);
262 if (thang == null) {
263 if (univ) {
264 throw new ServerException("0-UNI Universe is required", ServerException.CODE_COMMAND_ERROR);
265 } else {
266 uni = sc.getUniverse(); //default
267 }
268 } else if (!(thang instanceof Universe)) {
269 throw new ServerException(
270 "0-UNI Universe paramter is not a Universe",
271 ServerException.CODE_COMMAND_ERROR);
272 } else
273 uni = (Universe) thang; // accept passed
274
275 // 1-RESPONSE
276 thang = commandlist.elementAt(RESPONSE_LIST_INDEX);
277 if (thang == null) {
278 if (resp) {
279 throw new ServerException("1-RESPONSE Response is required", ServerException.CODE_COMMAND_ERROR);
280 } else {
281 response = sc.getRootLogger().sinjector; //default
282 }
283 } else if (!(thang instanceof Injector)) {
284 throw new ServerException(
285 "1-RESPONSE Response paramter is not an Injector",
286 ServerException.CODE_COMMAND_ERROR);
287 } else
288 response = (Injector) thang; // accept passed
289
290 // 2-TARGET
291 thang = commandlist.elementAt(TARGET_LIST_INDEX);
292 if (thang == null) {
293 if (targ) {
294 throw new ServerException("2-TARGET Response is required", ServerException.CODE_COMMAND_ERROR);
295 } else {
296 target = sc.getRootLogger().sinjector; //default
297 }
298 } else if (!(thang instanceof Injector)) {
299 throw new ServerException(
300 "2-TARGET Response paramter is not an Injector",
301 ServerException.CODE_COMMAND_ERROR);
302 } else
303 target = (Injector) thang; // accept passed
304
305 // 3-CLASS
306 if (cobj) {
307 thang = commandlist.elementAt(CLASS_LIST_INDEX);
308 if (thang == null) {
309 throw new ServerException("3-CLASS Class is required", ServerException.CODE_COMMAND_ERROR);
310 }
311 if (!(thang instanceof String)) {
312 throw new ServerException(
313 "3-CLASS Class paramter is not a String",
314 ServerException.CODE_COMMAND_ERROR);
315 }
316 classobject = (String) thang;
317 }
318
319 // 4-COMMAND
320 if (cmd) {
321 thang = commandlist.elementAt(COMMAND_LIST_INDEX);
322 if (thang == null) {
323 throw new ServerException("4-COMMAND Command is required", ServerException.CODE_COMMAND_ERROR);
324 }
325 if (!(thang instanceof String)) {
326 throw new ServerException(
327 "4-COMMAND Command paramter is not a String",
328 ServerException.CODE_COMMAND_ERROR);
329 }
330 command = (String) thang;
331 }
332
333 // 5-OBJECT
334 if (dobj) {
335 thang = commandlist.elementAt(OBJECT_LIST_INDEX);
336 if (thang == null) {
337 throw new ServerException("5-OBJECT Data Object is required", ServerException.CODE_COMMAND_ERROR);
338 }
339 if (!(thang instanceof Object)) {
340 throw new ServerException(
341 "5-OBJECT Data Object paramter is not an Object",
342 ServerException.CODE_COMMAND_ERROR);
343 }
344 data = (Object) thang;
345 }
346
347 } catch (ServerException se) {
348 throw se;
349 } catch (Exception e) {
350 throw new ServerException(
351 "Command list corrupt. message=" + e.getMessage(),
352 ServerException.CODE_COMMAND_FAULT,
353 e);
354 }
355 }
356
357 // HELPER
358 public String getMsgHeader() {
359 return "CMD:" + this.getName() + " id[" + uniqueID + "] ";
360 }
361
362 /**
363 * Send response. The response MUST be set.
364 *
365 * @param info
366 * text of the response
367 * @param code
368 * code of the response. Usually an EVENT
369 * @param rr
370 * Receipt for the command. May be null, if none was issued.
371 */
372 public void respond(String info, int code, Receipt rr) throws Exception {
373 response.post(new CommandResponseAtom(code, this.getMsgHeader() + info, Atom.ROUTINE, uniqueID, rr));
374 }
375
376 /**
377 * Send target. The target MUST be set.
378 *
379 * @param info
380 * text of the response
381 * @param code
382 * code of the response. Usually an EVENT
383 * @param rr
384 * Receipt for the command. May be null, if none was issued.
385 */
386 public void sendTarget(String info, int code, Receipt rr) throws Exception {
387 target.post(new CommandResponseAtom(code, this.getMsgHeader() + info, Atom.ROUTINE, uniqueID, rr));
388 }
389
390 /**
391 * Create a command list. It is a very good object to make all passed items
392 * Serializable. You cannot predict how the command will be issued. you can
393 * pass null for any parameter you don't want to pass.
394 *
395 * @param univ
396 * Specify a universe object?
397 * @param resp
398 * Specify a response Injector?
399 * @param targ
400 * Specify a target Injector?
401 * @param cobj
402 * Specify a command Class implementation (string name)?
403 * @param cmd
404 * Specify a command string?
405 * @param dobj
406 * Specify a data object?
407 * @return a Vector representing the command.
408 */
409 static public Vector createCommand(
410 Universe univ,
411 Injector resp,
412 Injector targ,
413 String cobj,
414 String cmd,
415 Object dobj) {
416
417 Vector cmdList = new Vector(OBJECT_LIST_INDEX + 1);
418 cmdList.setSize(OBJECT_LIST_INDEX + 1);
419 cmdList.set(UNI_LIST_INDEX, univ);
420 cmdList.set(RESPONSE_LIST_INDEX, resp);
421 cmdList.set(TARGET_LIST_INDEX, targ);
422 cmdList.set(CLASS_LIST_INDEX, cobj);
423 cmdList.set(COMMAND_LIST_INDEX, cmd);
424 cmdList.set(OBJECT_LIST_INDEX, dobj);
425
426 return cmdList;
427 }
428
429 }
|