001: /*
002: * Helma License Notice
003: *
004: * The contents of this file are subject to the Helma License
005: * Version 2.0 (the "License"). You may not use this file except in
006: * compliance with the License. A copy of the License is available at
007: * http://adele.helma.org/download/helma/license.txt
008: *
009: * Copyright 1998-2003 Helma Software. All Rights Reserved.
010: *
011: * $RCSfile$
012: * $Author: root $
013: * $Revision: 8604 $
014: * $Date: 2007-09-28 15:16:38 +0200 (Fre, 28 Sep 2007) $
015: */
016:
017: package helma.framework.core;
018:
019: import helma.objectmodel.INode;
020: import helma.objectmodel.db.DbSource;
021: import helma.util.CronJob;
022: import helma.util.SystemMap;
023: import helma.util.WrappedMap;
024: import helma.framework.repository.*;
025: import helma.framework.FutureResult;
026: import helma.main.Server;
027:
028: import java.io.File;
029: import java.io.Serializable;
030: import java.io.IOException;
031: import java.util.*;
032:
033: import org.apache.commons.logging.Log;
034: import org.apache.commons.logging.LogFactory;
035:
036: /**
037: * Application bean that provides a handle to the scripting environment to
038: * application specific functionality.
039: */
040: public class ApplicationBean implements Serializable {
041: Application app;
042: WrappedMap properties = null;
043:
044: /**
045: * Creates a new ApplicationBean object.
046: *
047: * @param app ...
048: */
049: public ApplicationBean(Application app) {
050: this .app = app;
051: }
052:
053: /**
054: * Clear the application cache.
055: */
056: public void clearCache() {
057: app.clearCache();
058: }
059:
060: /**
061: * Get the app's event logger. This is a Log with the
062: * category helma.[appname].event.
063: *
064: * @return the app logger.
065: */
066: public Log getLogger() {
067: return app.getEventLog();
068: }
069:
070: /**
071: * Get the app logger. This is a commons-logging Log with the
072: * category <code>logname</code>.
073: *
074: * @return a logger for the given log name.
075: */
076: public Log getLogger(String logname) {
077: return LogFactory.getLog(logname);
078: }
079:
080: /**
081: * Log a INFO message to the app log.
082: *
083: * @param msg the log message
084: */
085: public void log(Object msg) {
086: getLogger().info(msg);
087: }
088:
089: /**
090: * Log a INFO message to the log defined by logname.
091: *
092: * @param logname the name (category) of the log
093: * @param msg the log message
094: */
095: public void log(String logname, Object msg) {
096: getLogger(logname).info(msg);
097: }
098:
099: /**
100: * Log a DEBUG message to the app log if debug is set to true in
101: * app.properties.
102: *
103: * @param msg the log message
104: */
105: public void debug(Object msg) {
106: if (app.debug()) {
107: getLogger().debug(msg);
108: }
109: }
110:
111: /**
112: * Log a DEBUG message to the log defined by logname
113: * if debug is set to true in app.properties.
114: *
115: * @param logname the name (category) of the log
116: * @param msg the log message
117: */
118: public void debug(String logname, Object msg) {
119: if (app.debug()) {
120: getLogger(logname).debug(msg);
121: }
122: }
123:
124: /**
125: * Returns the app's repository list.
126: *
127: * @return the an array containing this app's repositories
128: */
129: public Object[] getRepositories() {
130: return app.getRepositories().toArray();
131: }
132:
133: /**
134: * Add a repository to the app's repository list. The .zip extension
135: * is automatically added, if the original library path does not
136: * point to an existing file or directory.
137: *
138: * @param obj the repository, relative or absolute path to the library.
139: */
140: public void addRepository(Object obj) {
141: Resource current = app.getCurrentCodeResource();
142: Repository parent = current == null ? null : current
143: .getRepository().getRootRepository();
144: Repository rep;
145: if (obj instanceof String) {
146: String path = (String) obj;
147: File file = new File(path).getAbsoluteFile();
148: if (!file.exists()) {
149: file = new File(path + ".zip").getAbsoluteFile();
150: }
151: if (!file.exists()) {
152: file = new File(path + ".js").getAbsoluteFile();
153: }
154: if (!file.exists()) {
155: throw new RuntimeException(
156: "Repository path does not exist: " + obj);
157: }
158: if (file.isDirectory()) {
159: rep = new FileRepository(file, parent);
160: } else if (file.isFile()) {
161: if (file.getName().endsWith(".zip")) {
162: rep = new ZipRepository(file, parent);
163: } else {
164: rep = new SingleFileRepository(file, parent);
165: }
166: } else {
167: throw new RuntimeException(
168: "Unrecognized file type in addRepository: "
169: + obj);
170: }
171: } else if (obj instanceof Repository) {
172: rep = (Repository) obj;
173: } else {
174: throw new RuntimeException(
175: "Invalid argument to addRepository: " + obj);
176: }
177: app.addRepository(rep);
178: try {
179: app.typemgr.checkRepository(rep, true);
180: } catch (IOException iox) {
181: getLogger().error("Error checking repository " + rep, iox);
182: }
183: }
184:
185: /**
186: * Get the app's classloader
187: * @return the app's classloader
188: */
189: public ClassLoader getClassLoader() {
190: return app.getClassLoader();
191: }
192:
193: /**
194: * Return the number of currently active sessions
195: * @return the current number of active sessions
196: */
197: public int countSessions() {
198: return app.countSessions();
199: }
200:
201: /**
202: * Get a session object for the specified session id
203: * @param sessionID the session id
204: * @return the session belonging to the session id, or null
205: */
206: public SessionBean getSession(String sessionID) {
207: if (sessionID == null) {
208: return null;
209: }
210:
211: Session session = app.getSession(sessionID.trim());
212:
213: if (session == null) {
214: return null;
215: }
216:
217: return new SessionBean(session);
218: }
219:
220: /**
221: * Create a new session with the given session id
222: * @param sessionID the session id
223: * @return the newly created session
224: */
225: public SessionBean createSession(String sessionID) {
226: if (sessionID == null) {
227: return null;
228: }
229:
230: Session session = app.createSession(sessionID.trim());
231:
232: if (session == null) {
233: return null;
234: }
235:
236: return new SessionBean(session);
237: }
238:
239: /**
240: * Get an array of all active sessions
241: * @return an array of session beans
242: */
243: public SessionBean[] getSessions() {
244: Map sessions = app.getSessions();
245: SessionBean[] array = new SessionBean[sessions.size()];
246: int i = 0;
247:
248: Iterator it = sessions.values().iterator();
249: while (it.hasNext()) {
250: array[i++] = new SessionBean((Session) it.next());
251: }
252:
253: return array;
254: }
255:
256: /**
257: * Register a user with the given name and password using the
258: * database mapping of the User prototype
259: * @param username the user name
260: * @param password the user password
261: * @return the newly registered user, or null if we failed
262: */
263: public INode registerUser(String username, String password) {
264: if ((username == null) || (password == null)
265: || "".equals(username.trim())
266: || "".equals(password.trim())) {
267: return null;
268: } else {
269: return app.registerUser(username, password);
270: }
271: }
272:
273: /**
274: * Get a user object with the given name
275: * @param username the user name
276: * @return the user object, or null
277: */
278: public INode getUser(String username) {
279: if ((username == null) || "".equals(username.trim())) {
280: return null;
281: }
282:
283: return app.getUserNode(username);
284: }
285:
286: /**
287: * Get an array of currently active registered users
288: * @return an array of user nodes
289: */
290: public INode[] getActiveUsers() {
291: List activeUsers = app.getActiveUsers();
292:
293: return (INode[]) activeUsers.toArray(new INode[0]);
294: }
295:
296: /**
297: * Get an array of all registered users
298: * @return an array containing all registered users
299: */
300: public INode[] getRegisteredUsers() {
301: List registeredUsers = app.getRegisteredUsers();
302:
303: return (INode[]) registeredUsers.toArray(new INode[0]);
304: }
305:
306: /**
307: * Get an array of all currently active sessions for a given user node
308: * @param usernode the user node
309: * @return an array of sessions for the given user
310: */
311: public SessionBean[] getSessionsForUser(INode usernode) {
312: if (usernode == null) {
313: return new SessionBean[0];
314: } else {
315: return getSessionsForUser(usernode.getName());
316: }
317: }
318:
319: /**
320: * Get an array of all currently active sessions for a given user name
321: * @param username the user node
322: * @return an array of sessions for the given user
323: */
324: public SessionBean[] getSessionsForUser(String username) {
325: if ((username == null) || "".equals(username.trim())) {
326: return new SessionBean[0];
327: }
328:
329: List userSessions = app.getSessionsForUsername(username);
330:
331: return (SessionBean[]) userSessions.toArray(new SessionBean[0]);
332: }
333:
334: /**
335: * Add a cron job that will run once a minute
336: * @param functionName the function name
337: */
338: public void addCronJob(String functionName) {
339: CronJob job = new CronJob(functionName);
340:
341: job.setFunction(functionName);
342: app.customCronJobs.put(functionName, job);
343: }
344:
345: /**
346: * Add a cron job that will run at the specified time intervals
347: *
348: * @param functionName the function name
349: * @param year comma separated list of years, or *
350: * @param month comma separated list of months, or *
351: * @param day comma separated list of days, or *
352: * @param weekday comma separated list of weekdays, or *
353: * @param hour comma separated list of hours, or *
354: * @param minute comma separated list of minutes, or *
355: */
356: public void addCronJob(String functionName, String year,
357: String month, String day, String weekday, String hour,
358: String minute) {
359: CronJob job = CronJob.newJob(functionName, year, month, day,
360: weekday, hour, minute);
361:
362: app.customCronJobs.put(functionName, job);
363: }
364:
365: /**
366: * Unregister a previously registered cron job
367: * @param functionName the function name
368: */
369: public void removeCronJob(String functionName) {
370: app.customCronJobs.remove(functionName);
371: }
372:
373: /**
374: * Returns an read-only map of the custom cron jobs registered with the app
375: *
376: * @return a map of cron jobs
377: */
378: public Map getCronJobs() {
379: return new WrappedMap(app.customCronJobs, true);
380: }
381:
382: /**
383: * Returns the number of elements in the NodeManager's cache
384: */
385: public int getCacheusage() {
386: return app.getCacheUsage();
387: }
388:
389: /**
390: * Returns the app's data node used to share data between the app's evaluators
391: *
392: * @return the app.data node
393: */
394: public INode getData() {
395: return app.getCacheNode();
396: }
397:
398: /**
399: * Returns the app's modules map used to register application modules
400: *
401: * @return the module map
402: */
403: public Map getModules() {
404: return app.modules;
405: }
406:
407: /**
408: * Returns the absolute path of the app dir. When using repositories this
409: * equals the first file based repository.
410: *
411: * @return the app dir
412: */
413: public String getDir() {
414: return app.getAppDir().getAbsolutePath();
415: }
416:
417: /**
418: * @return the app name
419: */
420: public String getName() {
421: return app.getName();
422: }
423:
424: /**
425: * @return the application start time
426: */
427: public Date getUpSince() {
428: return new Date(app.starttime);
429: }
430:
431: /**
432: * @return the number of requests processed by this app
433: */
434: public long getRequestCount() {
435: return app.getRequestCount();
436: }
437:
438: /**
439: * @return the number of XML-RPC requests processed
440: */
441: public long getXmlrpcCount() {
442: return app.getXmlrpcCount();
443: }
444:
445: /**
446: * @return the number of errors encountered
447: */
448: public long getErrorCount() {
449: return app.getErrorCount();
450: }
451:
452: /**
453: * @return the wrapped helma.framework.core.Application object
454: */
455: public Application get__app__() {
456: return app;
457: }
458:
459: /**
460: * Get a wrapper around the app's properties
461: *
462: * @return a readonly wrapper around the application's app properties
463: */
464: public Map getProperties() {
465: if (properties == null) {
466: properties = new WrappedMap(app.getProperties(), true);
467: }
468: return properties;
469: }
470:
471: /**
472: * Get a wrapper around the app's db properties
473: *
474: * @return a readonly wrapper around the application's db properties
475: */
476: public Map getDbProperties() {
477: return new WrappedMap(app.getDbProperties(), true);
478: }
479:
480: /**
481: * Return a DbSource object for a given name.
482: */
483: public DbSource getDbSource(String name) {
484: return app.getDbSource(name);
485: }
486:
487: /**
488: * Get a wrapper around the app's apps.properties
489: *
490: * @return a readonly wrapper around the application's apps.properties
491: */
492: public Map getAppsProperties() {
493: Server server = Server.getServer();
494: if (server == null)
495: return new SystemMap();
496: return new WrappedMap(server.getAppsProperties(app.getName()),
497: true);
498: }
499:
500: /**
501: * Get an array of this app's prototypes
502: *
503: * @return an array containing the app's prototypes
504: */
505: public Prototype[] getPrototypes() {
506: return (Prototype[]) app.getPrototypes().toArray(
507: new Prototype[0]);
508: }
509:
510: /**
511: * Get a prototype by name.
512: *
513: * @param name the prototype name
514: * @return the prototype
515: */
516: public Prototype getPrototype(String name) {
517: return app.getPrototypeByName(name);
518: }
519:
520: /**
521: * Get the number of currently available threads/request evaluators
522: * @return the currently available threads
523: */
524: public int getFreeThreads() {
525: return app.countFreeEvaluators();
526: }
527:
528: /**
529: * Get the number of currently active request threads
530: * @return the number of currently active threads
531: */
532: public int getActiveThreads() {
533: return app.countActiveEvaluators();
534: }
535:
536: /**
537: * Get the maximal thread number for this application
538: * @return the maximal number of threads/request evaluators
539: */
540: public int getMaxThreads() {
541: return app.countEvaluators();
542: }
543:
544: /**
545: * Set the maximal thread number for this application
546: * @param n the maximal number of threads/request evaluators
547: */
548: public void setMaxThreads(int n) {
549: // add one to the number to compensate for the internal scheduler.
550: app.setNumberOfEvaluators(n + 1);
551: }
552:
553: /**
554: * Return a skin for a given object. The skin is found by determining the prototype
555: * to use for the object, then looking up the skin for the prototype.
556: */
557: public Skin getSkin(String protoname, String skinname,
558: Object[] skinpath) {
559: try {
560: return app.getSkin(protoname, skinname, skinpath);
561: } catch (Exception x) {
562: return null;
563: }
564: }
565:
566: /**
567: * Return a map of skin resources
568: *
569: * @return a map containing the skin resources
570: */
571: public Map getSkinfiles() {
572: Map skinz = new SystemMap();
573:
574: for (Iterator it = app.getPrototypes().iterator(); it.hasNext();) {
575: Prototype p = (Prototype) it.next();
576:
577: Object skinmap = p.getScriptableSkinMap();
578: skinz.put(p.getName(), skinmap);
579: skinz.put(p.getLowerCaseName(), skinmap);
580: }
581:
582: return skinz;
583: }
584:
585: /**
586: * Return a map of skin resources including the app-specific skinpath
587: *
588: * @param skinpath an array of directory paths or HopObjects to search for skins
589: * @return a map containing the skin resources
590: */
591: public Map getSkinfilesInPath(Object[] skinpath) {
592: Map skinz = new SystemMap();
593:
594: for (Iterator it = app.getPrototypes().iterator(); it.hasNext();) {
595: Prototype p = (Prototype) it.next();
596:
597: Object skinmap = p.getScriptableSkinMap(skinpath);
598: skinz.put(p.getName(), skinmap);
599: skinz.put(p.getLowerCaseName(), skinmap);
600: }
601:
602: return skinz;
603: }
604:
605: /**
606: * Return the absolute application directory (appdir property
607: * in apps.properties file)
608: * @return the app directory as absolute path
609: */
610: public String getAppDir() {
611: return app.getAppDir().getAbsolutePath();
612: }
613:
614: /**
615: * Return the absolute server directory
616: * @return the server directory as absolute path
617: */
618: public String getServerDir() {
619: File f = app.getServerDir();
620:
621: if (f == null) {
622: return app.getAppDir().getAbsolutePath();
623: }
624:
625: return f.getAbsolutePath();
626: }
627:
628: /**
629: * Return the app's default charset/encoding.
630: * @return the app's charset
631: */
632: public String getCharset() {
633: return app.getCharset();
634: }
635:
636: /**
637: * Set the path for global macro resolution
638: * @param path an array of global namespaces, or null
639: */
640: public void setGlobalMacroPath(String[] path) {
641: app.globalMacroPath = path;
642: }
643:
644: /**
645: * Get the path for global macro resolution
646: * @return an array of global namespaces, or null
647: */
648: public String[] getGlobalMacroPath() {
649: return app.globalMacroPath;
650: }
651:
652: /**
653: * Trigger a synchronous Helma invocation with a default timeout of 30 seconds.
654: *
655: * @param thisObject the object to invoke the function on,
656: * or null for global invokation
657: * @param function the function or function name to invoke
658: * @param args an array of arguments
659: * @return the value returned by the function
660: * @throws Exception exception thrown by the function
661: */
662: public Object invoke(Object this Object, Object function,
663: Object[] args) throws Exception {
664: // default timeout of 30 seconds
665: return invoke(this Object, function, args, 30000L);
666: }
667:
668: /**
669: * Trigger a synchronous Helma invocation.
670: *
671: * @param thisObject the object to invoke the function on,
672: * or null for global invokation
673: * @param function the function or function name to invoke
674: * @param args an array of arguments
675: * @param timeout the timeout in milliseconds. After waiting
676: * this long, we will try to interrupt the invocation
677: * @return the value returned by the function
678: * @throws Exception exception thrown by the function
679: */
680: public Object invoke(Object this Object, Object function,
681: Object[] args, long timeout) throws Exception {
682: RequestEvaluator reval = app.getEvaluator();
683: try {
684: return reval.invokeInternal(this Object, function, args,
685: timeout);
686: } finally {
687: app.releaseEvaluator(reval);
688: }
689: }
690:
691: /**
692: * Trigger an asynchronous Helma invocation. This method returns
693: * immedately with an object that allows to track the result of the
694: * function invocation with the following properties:
695: *
696: * <ul>
697: * <li>running - true while the function is running, false afterwards</li>
698: * <li>result - the value returned by the function, if any</li>
699: * <li>exception - the exception thrown by the function, if any</li>
700: * <li>waitForResult() - wait indefinitely until invocation terminates
701: * and return the result</li>
702: * <li>waitForResult(t) - wait for the specified number of milliseconds
703: * for invocation to terminate and return the result</li>
704: * </ul>
705: *
706: * @param thisObject the object to invoke the function on,
707: * or null for global invokation
708: * @param function the function or function name to invoke
709: * @param args an array of arguments
710: * this long, we will try to interrupt the invocation
711: * @return an object with the properties described above
712: */
713: public FutureResult invokeAsync(Object this Object,
714: final Object function, final Object[] args) {
715: // default timeout of 15 minutes
716: return new AsyncInvoker(this Object, function, args, 60000L * 15);
717: }
718:
719: /**
720: * Trigger an asynchronous Helma invocation. This method returns
721: * immedately with an object that allows to track the result of the
722: * function invocation with the following methods and properties:
723: *
724: * <ul>
725: * <li>running - true while the function is running, false afterwards</li>
726: * <li>result - the value returned by the function, if any</li>
727: * <li>exception - the exception thrown by the function, if any</li>
728: * <li>waitForResult() - wait indefinitely until invocation terminates
729: * and return the result</li>
730: * <li>waitForResult(t) - wait for the specified number of milliseconds
731: * for invocation to terminate and return the result</li>
732: * </ul>
733: *
734: * @param thisObject the object to invoke the function on,
735: * or null for global invokation
736: * @param function the function or function name to invoke
737: * @param args an array of arguments
738: * @param timeout the timeout in milliseconds. After waiting
739: * this long, we will try to interrupt the invocation
740: * @return an object with the properties described above
741: */
742: public FutureResult invokeAsync(Object this Object, Object function,
743: Object[] args, long timeout) {
744: return new AsyncInvoker(this Object, function, args, timeout);
745: }
746:
747: /**
748: * Return a string presentation of this AppBean
749: * @return string description of this app bean object
750: */
751: public String toString() {
752: return "[Application " + app.getName() + "]";
753: }
754:
755: class AsyncInvoker extends Thread implements FutureResult {
756:
757: private Object this Object;
758: private Object function;
759: private Object[] args;
760: private long timeout;
761:
762: private Object result;
763: private Exception exception;
764: private boolean running = true;
765:
766: private AsyncInvoker(Object this Obj, Object func,
767: Object[] args, long timeout) {
768: this Object = this Obj;
769: function = func;
770: this .args = args;
771: this .timeout = timeout;
772: start();
773: }
774:
775: public void run() {
776: RequestEvaluator reval = null;
777: try {
778: reval = app.getEvaluator();
779: setResult(reval.invokeInternal(this Object, function,
780: args, timeout));
781: } catch (Exception x) {
782: setException(x);
783: } finally {
784: running = false;
785: app.releaseEvaluator(reval);
786: }
787: }
788:
789: public synchronized boolean getRunning() {
790: return running;
791: }
792:
793: private synchronized void setResult(Object obj) {
794: result = obj;
795: running = false;
796: notifyAll();
797: }
798:
799: public synchronized Object getResult() {
800: return result;
801: }
802:
803: public synchronized Object waitForResult()
804: throws InterruptedException {
805: if (!running)
806: return result;
807: wait();
808: return result;
809: }
810:
811: public synchronized Object waitForResult(long timeout)
812: throws InterruptedException {
813: if (!running)
814: return result;
815: wait(timeout);
816: return result;
817: }
818:
819: private synchronized void setException(Exception x) {
820: exception = x;
821: running = false;
822: notifyAll();
823: }
824:
825: public synchronized Exception getException() {
826: return exception;
827: }
828:
829: public String toString() {
830: return new StringBuffer("AsyncInvokeThread{running: ")
831: .append(running).append(", result: ")
832: .append(result).append(", exception: ").append(
833: exception).append("}").toString();
834: }
835:
836: }
837: }
|