Source Code Cross Referenced for Application.java in  » Web-Framework » helma » helma » framework » core » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » Web Framework » helma » helma.framework.core 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /*
0002:         * Helma License Notice
0003:         *
0004:         * The contents of this file are subject to the Helma License
0005:         * Version 2.0 (the "License"). You may not use this file except in
0006:         * compliance with the License. A copy of the License is available at
0007:         * http://adele.helma.org/download/helma/license.txt
0008:         *
0009:         * Copyright 1998-2003 Helma Software. All Rights Reserved.
0010:         *
0011:         * $RCSfile$
0012:         * $Author: hannes $
0013:         * $Revision: 8659 $
0014:         * $Date: 2007-11-26 15:46:36 +0100 (Mon, 26 Nov 2007) $
0015:         */
0016:
0017:        package helma.framework.core;
0018:
0019:        import helma.extensions.ConfigurationException;
0020:        import helma.extensions.HelmaExtension;
0021:        import helma.framework.*;
0022:        import helma.framework.repository.*;
0023:        import helma.main.Server;
0024:        import helma.objectmodel.*;
0025:        import helma.objectmodel.db.*;
0026:        import helma.util.*;
0027:        import helma.doc.DocApplication;
0028:
0029:        import java.io.*;
0030:        import java.lang.reflect.*;
0031:        import java.rmi.*;
0032:        import java.util.*;
0033:        import org.apache.commons.logging.Log;
0034:        import org.apache.commons.logging.LogFactory;
0035:        import java.util.ArrayList;
0036:
0037:        /**
0038:         * The central class of a Helma application. This class keeps a pool of 
0039:         * request evaluators (threads with JavaScript interpreters), waits for
0040:         * requests from the Web server or XML-RPC port and dispatches them to
0041:         * the evaluators.
0042:         */
0043:        public final class Application implements  Runnable {
0044:            // the name of this application
0045:            private String name;
0046:
0047:            // application sources
0048:            ArrayList repositories;
0049:
0050:            // properties and db-properties
0051:            ResourceProperties props;
0052:
0053:            // properties and db-properties
0054:            ResourceProperties dbProps;
0055:
0056:            // This application's main directory
0057:            File appDir;
0058:
0059:            // Helma server hopHome directory
0060:            File hopHome;
0061:
0062:            // embedded db directory
0063:            File dbDir;
0064:
0065:            // this application's node manager
0066:            protected NodeManager nmgr;
0067:
0068:            // the root of the website, if a custom root object is defined.
0069:            // otherwise this is managed by the NodeManager and not cached here.
0070:            Object rootObject = null;
0071:            String rootObjectClass;
0072:
0073:            // The session manager
0074:            SessionManager sessionMgr;
0075:
0076:            /**
0077:             *  The type manager checks if anything in the application's prototype definitions
0078:             * has been updated prior to each evaluation.
0079:             */
0080:            public TypeManager typemgr;
0081:
0082:            /**
0083:             * The skin manager for this application
0084:             */
0085:            protected SkinManager skinmgr;
0086:
0087:            /**
0088:             * Collections for evaluator thread pooling
0089:             */
0090:            protected Stack freeThreads;
0091:            protected Vector allThreads;
0092:            boolean running = false;
0093:            boolean debug;
0094:            long starttime;
0095:            Hashtable dbSources;
0096:
0097:            // map of app modules reflected at app.modules
0098:            Map modules;
0099:
0100:            // internal worker thread for scheduler, session cleanup etc.
0101:            Thread worker;
0102:            // request timeout defaults to 60 seconds
0103:            long requestTimeout = 60000;
0104:            ThreadGroup threadgroup;
0105:
0106:            // threadlocal variable for the current RequestEvaluator
0107:            ThreadLocal currentEvaluator = new ThreadLocal();
0108:
0109:            // Map of requesttrans -> active requestevaluators
0110:            Hashtable activeRequests;
0111:
0112:            String logDir;
0113:
0114:            // Two logs for each application
0115:            Log eventLog;
0116:            Log accessLog;
0117:
0118:            // Symbolic names for each log
0119:            String eventLogName;
0120:            String accessLogName;
0121:
0122:            // A transient node that is shared among all evaluators
0123:            protected INode cachenode;
0124:
0125:            // some fields for statistics
0126:            protected volatile long requestCount = 0;
0127:            protected volatile long xmlrpcCount = 0;
0128:            protected volatile long errorCount = 0;
0129:
0130:            // the URL-prefix to use for links into this application
0131:            private String baseURI;
0132:            // the name of the root prototype as far as href() is concerned
0133:            private String hrefRootPrototype;
0134:
0135:            // the id of the object to use as root object
0136:            String rootId = "0";
0137:
0138:            // Db mappings for some standard prototypes
0139:            private DbMapping rootMapping;
0140:            private DbMapping userRootMapping;
0141:            private DbMapping userMapping;
0142:
0143:            // name of response encoding
0144:            String charset;
0145:
0146:            // password file to use for authenticate() function
0147:            private CryptResource pwfile;
0148:
0149:            // Map of java class names to object prototypes
0150:            ResourceProperties classMapping;
0151:
0152:            // Map of extensions allowed for public skins
0153:            Properties skinExtensions;
0154:
0155:            // time we last read the properties file
0156:            private long lastPropertyRead = -1L;
0157:
0158:            // the set of prototype/function pairs which are allowed to be called via XML-RPC
0159:            private HashSet xmlrpcAccess;
0160:
0161:            // the name under which this app serves XML-RPC requests. Defaults to the app name
0162:            private String xmlrpcHandlerName;
0163:
0164:            // the list of currently active cron jobs
0165:            Hashtable activeCronJobs = null;
0166:            // the list of custom cron jobs
0167:            Hashtable customCronJobs = null;
0168:
0169:            private ResourceComparator resourceComparator;
0170:            private Resource currentCodeResource;
0171:
0172:            // Field to cache unmapped java classes
0173:            private final static String CLASS_NOT_MAPPED = "(unmapped)";
0174:
0175:            /**
0176:             * Namespace search path for global macros
0177:             */
0178:            String[] globalMacroPath = null;
0179:
0180:            /**
0181:             *  Simple constructor for dead application instances.
0182:             */
0183:            public Application(String name) {
0184:                this .name = name;
0185:            }
0186:
0187:            /**
0188:             * Build an application with the given name with the given sources. No
0189:             * Server-wide properties are created or used.
0190:             */
0191:            public Application(String name, Repository[] repositories,
0192:                    File dbDir) throws RemoteException,
0193:                    IllegalArgumentException {
0194:                this (name, null, repositories, null, dbDir);
0195:            }
0196:
0197:            /**
0198:             * Build an application with the given name and server instance. The
0199:             * app directories will be created if they don't exist already.
0200:             */
0201:            public Application(String name, Server server)
0202:                    throws RemoteException, IllegalArgumentException {
0203:                this (name, server, new Repository[0], null, null);
0204:            }
0205:
0206:            /**
0207:             * Build an application with the given name, server instance, sources and
0208:             * db directory.
0209:             */
0210:            public Application(String name, Server server,
0211:                    Repository[] repositories, File customAppDir,
0212:                    File customDbDir) throws RemoteException,
0213:                    IllegalArgumentException {
0214:                if ((name == null) || (name.trim().length() == 0)) {
0215:                    throw new IllegalArgumentException(
0216:                            "Invalid application name: " + name);
0217:                }
0218:
0219:                if (repositories.length == 0) {
0220:                    throw new java.lang.IllegalArgumentException(
0221:                            "No sources defined for application: " + name);
0222:                }
0223:
0224:                this .name = name;
0225:
0226:                this .repositories = new ArrayList();
0227:                this .repositories.addAll(Arrays.asList(repositories));
0228:                resourceComparator = new ResourceComparator(this );
0229:
0230:                appDir = customAppDir;
0231:                dbDir = customDbDir;
0232:
0233:                // system-wide properties, default to null
0234:                ResourceProperties sysProps;
0235:
0236:                // system-wide properties, default to null
0237:                ResourceProperties sysDbProps;
0238:
0239:                sysProps = sysDbProps = null;
0240:                hopHome = null;
0241:
0242:                if (server != null) {
0243:                    hopHome = server.getHopHome();
0244:
0245:                    if (dbDir == null) {
0246:                        dbDir = new File(server.getDbHome(), name);
0247:                    }
0248:
0249:                    // get system-wide properties
0250:                    sysProps = server.getProperties();
0251:                    sysDbProps = server.getDbProperties();
0252:                }
0253:
0254:                if (!dbDir.exists()) {
0255:                    dbDir.mkdirs();
0256:                }
0257:
0258:                if (appDir == null) {
0259:                    for (int i = repositories.length - 1; i >= 0; i--) {
0260:                        if (repositories[i] instanceof  FileRepository) {
0261:                            appDir = new File(repositories[i].getName());
0262:                            break;
0263:                        }
0264:                    }
0265:                }
0266:
0267:                // give the Helma Thread group a name so the threads can be recognized
0268:                threadgroup = new ThreadGroup("TX-" + name);
0269:
0270:                // create app-level properties
0271:                props = new ResourceProperties(this , "app.properties", sysProps);
0272:
0273:                // get log names
0274:                accessLogName = props.getProperty("accessLog",
0275:                        new StringBuffer("helma.").append(name).append(
0276:                                ".access").toString());
0277:                eventLogName = props.getProperty("eventLog", new StringBuffer(
0278:                        "helma.").append(name).append(".event").toString());
0279:
0280:                // create app-level db sources
0281:                dbProps = new ResourceProperties(this , "db.properties",
0282:                        sysDbProps, false);
0283:
0284:                // the passwd file, to be used with the authenticate() function
0285:                CryptResource parentpwfile = null;
0286:
0287:                if (hopHome != null) {
0288:                    parentpwfile = new CryptResource(new FileResource(new File(
0289:                            hopHome, "passwd")), null);
0290:                }
0291:
0292:                pwfile = new CryptResource(repositories[0]
0293:                        .getResource("passwd"), parentpwfile);
0294:
0295:                // the properties that map java class names to prototype names
0296:                classMapping = new ResourceProperties(this , "class.properties");
0297:                classMapping.setIgnoreCase(false);
0298:
0299:                // get class name of root object if defined. Otherwise native Helma objectmodel will be used.
0300:                rootObjectClass = classMapping.getProperty("root");
0301:
0302:                updateProperties();
0303:
0304:                dbSources = new Hashtable();
0305:                modules = new SystemMap();
0306:
0307:                cachenode = new TransientNode("app");
0308:            }
0309:
0310:            /**
0311:             * Get the application ready to run, initializing the evaluators and type manager.
0312:             */
0313:            public synchronized void init() throws DatabaseException,
0314:                    IllegalAccessException, InstantiationException,
0315:                    ClassNotFoundException {
0316:                init(null);
0317:            }
0318:
0319:            /**
0320:             * Get the application ready to run, initializing the evaluators and type manager.
0321:             *
0322:             * @param ignoreDirs comma separated list of directory names to ignore
0323:             */
0324:            public synchronized void init(String ignoreDirs)
0325:                    throws DatabaseException, IllegalAccessException,
0326:                    InstantiationException, ClassNotFoundException {
0327:
0328:                running = true;
0329:
0330:                // create and init type mananger
0331:                typemgr = new TypeManager(this , ignoreDirs);
0332:                // set the context classloader. Note that this must be done before
0333:                // using the logging framework so that a new LogFactory gets created
0334:                // for this app.
0335:                Thread.currentThread().setContextClassLoader(
0336:                        typemgr.getClassLoader());
0337:                try {
0338:                    typemgr.createPrototypes();
0339:                } catch (Exception x) {
0340:                    logError("Error creating prototypes", x);
0341:                }
0342:
0343:                if (Server.getServer() != null) {
0344:                    Vector extensions = Server.getServer().getExtensions();
0345:
0346:                    for (int i = 0; i < extensions.size(); i++) {
0347:                        HelmaExtension ext = (HelmaExtension) extensions.get(i);
0348:
0349:                        try {
0350:                            ext.applicationStarted(this );
0351:                        } catch (ConfigurationException e) {
0352:                            logEvent("couldn't init extension " + ext.getName()
0353:                                    + ": " + e.toString());
0354:                        }
0355:                    }
0356:                }
0357:
0358:                // create and init evaluator/thread lists
0359:                freeThreads = new Stack();
0360:                allThreads = new Vector();
0361:
0362:                // preallocate minThreads request evaluators
0363:                int minThreads = 0;
0364:
0365:                try {
0366:                    minThreads = Integer.parseInt(props
0367:                            .getProperty("minThreads"));
0368:                } catch (Exception ignore) {
0369:                    // not parsable as number, keep 0
0370:                }
0371:
0372:                if (minThreads > 0) {
0373:                    logEvent("Starting " + minThreads + " evaluator(s) for "
0374:                            + name);
0375:                }
0376:
0377:                for (int i = 0; i < minThreads; i++) {
0378:                    RequestEvaluator ev = new RequestEvaluator(this );
0379:
0380:                    if (i == 0) {
0381:                        ev.initScriptingEngine();
0382:                    }
0383:                    freeThreads.push(ev);
0384:                    allThreads.addElement(ev);
0385:                }
0386:
0387:                activeRequests = new Hashtable();
0388:                activeCronJobs = new Hashtable();
0389:                customCronJobs = new Hashtable();
0390:
0391:                // create the skin manager
0392:                skinmgr = new SkinManager(this );
0393:
0394:                // read in root id, root prototype, user prototype
0395:                rootId = props.getProperty("rootid", "0");
0396:                String rootPrototype = props.getProperty("rootprototype",
0397:                        "root");
0398:                String userPrototype = props.getProperty("userprototype",
0399:                        "user");
0400:
0401:                rootMapping = getDbMapping(rootPrototype);
0402:                if (rootMapping == null)
0403:                    throw new RuntimeException("rootPrototype does not exist: "
0404:                            + rootPrototype);
0405:                userMapping = getDbMapping(userPrototype);
0406:                if (userMapping == null)
0407:                    throw new RuntimeException("userPrototype does not exist: "
0408:                            + userPrototype);
0409:
0410:                // The whole user/userroot handling is basically old
0411:                // ugly obsolete crap. Don't bother.
0412:                ResourceProperties p = new ResourceProperties();
0413:                String usernameField = (userMapping != null) ? userMapping
0414:                        .getNameField() : null;
0415:
0416:                if (usernameField == null) {
0417:                    usernameField = "name";
0418:                }
0419:
0420:                p.put("_children", "collection(" + userPrototype + ")");
0421:                p.put("_children.accessname", usernameField);
0422:                userRootMapping = new DbMapping(this , "__userroot__", p);
0423:                userRootMapping.update();
0424:
0425:                // create the node manager
0426:                nmgr = new NodeManager(this );
0427:                nmgr.init(dbDir.getAbsoluteFile(), props);
0428:
0429:                // create and init session manager
0430:                String sessionMgrImpl = props.getProperty("sessionManagerImpl",
0431:                        "helma.framework.core.SessionManager");
0432:                sessionMgr = (SessionManager) Class.forName(sessionMgrImpl)
0433:                        .newInstance();
0434:                logEvent("Using session manager class " + sessionMgrImpl);
0435:                sessionMgr.init(this );
0436:
0437:                // read the sessions if wanted
0438:                if ("true".equalsIgnoreCase(getProperty("persistentSessions"))) {
0439:                    RequestEvaluator ev = getEvaluator();
0440:                    try {
0441:                        ev.initScriptingEngine();
0442:                        sessionMgr.loadSessionData(null, ev.scriptingEngine);
0443:                    } finally {
0444:                        releaseEvaluator(ev);
0445:                    }
0446:                }
0447:
0448:                // reset the classloader to the parent/system/server classloader.
0449:                Thread.currentThread().setContextClassLoader(
0450:                        typemgr.getClassLoader().getParent());
0451:            }
0452:
0453:            /**
0454:             *  Create and start scheduler and cleanup thread
0455:             */
0456:            public synchronized void start() {
0457:                starttime = System.currentTimeMillis();
0458:
0459:                // as first thing, invoke global onStart() function
0460:                RequestEvaluator eval = null;
0461:                try {
0462:                    eval = getEvaluator();
0463:                    eval.invokeInternal(null, "onStart",
0464:                            RequestEvaluator.EMPTY_ARGS);
0465:                } catch (Exception xcept) {
0466:                    logError("Error in " + name + ".onStart()", xcept);
0467:                } finally {
0468:                    releaseEvaluator(eval);
0469:                }
0470:
0471:                worker = new Thread(this , "Worker-" + name);
0472:                worker.setPriority(Thread.NORM_PRIORITY + 1);
0473:                worker.start();
0474:            }
0475:
0476:            /**
0477:             * This is called to shut down a running application.
0478:             */
0479:            public synchronized void stop() {
0480:                // invoke global onStop() function
0481:                RequestEvaluator eval = null;
0482:                try {
0483:                    eval = getEvaluator();
0484:                    eval.invokeInternal(null, "onStop",
0485:                            RequestEvaluator.EMPTY_ARGS);
0486:                } catch (Exception x) {
0487:                    logError("Error in " + name + ".onStop()", x);
0488:                }
0489:
0490:                // mark app as stopped
0491:                running = false;
0492:
0493:                // stop all threads, this app is going down
0494:                if (worker != null) {
0495:                    worker.interrupt();
0496:                }
0497:
0498:                worker = null;
0499:
0500:                // stop evaluators
0501:                if (allThreads != null) {
0502:                    for (Enumeration e = allThreads.elements(); e
0503:                            .hasMoreElements();) {
0504:                        RequestEvaluator ev = (RequestEvaluator) e
0505:                                .nextElement();
0506:
0507:                        ev.stopTransactor();
0508:                    }
0509:                }
0510:
0511:                // remove evaluators
0512:                allThreads.removeAllElements();
0513:                freeThreads.clear();
0514:
0515:                // shut down node manager and embedded db
0516:                try {
0517:                    nmgr.shutdown();
0518:                } catch (DatabaseException dbx) {
0519:                    System.err.println("Error shutting down embedded db: "
0520:                            + dbx);
0521:                }
0522:
0523:                // tell the extensions that we're stopped.
0524:                if (Server.getServer() != null) {
0525:                    Vector extensions = Server.getServer().getExtensions();
0526:
0527:                    for (int i = 0; i < extensions.size(); i++) {
0528:                        HelmaExtension ext = (HelmaExtension) extensions.get(i);
0529:
0530:                        ext.applicationStopped(this );
0531:                    }
0532:                }
0533:
0534:                // store the sessions if wanted
0535:                if ("true".equalsIgnoreCase(getProperty("persistentSessions"))) {
0536:                    // sessionMgr.storeSessionData(null);
0537:                    sessionMgr.storeSessionData(null, eval.scriptingEngine);
0538:                }
0539:                sessionMgr.shutdown();
0540:            }
0541:
0542:            /**
0543:             * Returns true if this app is currently running
0544:             *
0545:             * @return true if the app is running
0546:             */
0547:            public boolean isRunning() {
0548:                return running;
0549:            }
0550:
0551:            /**
0552:             * Get the application directory.
0553:             *
0554:             * @return the application directory, or first file based repository
0555:             */
0556:            public File getAppDir() {
0557:                return appDir;
0558:            }
0559:
0560:            /**
0561:             * Get a comparator for comparing Resources according to the order of
0562:             * repositories they're contained in.
0563:             *
0564:             * @return a comparator that sorts resources according to their repositories
0565:             */
0566:            public ResourceComparator getResourceComparator() {
0567:                return resourceComparator;
0568:            }
0569:
0570:            /**
0571:             * Returns a free evaluator to handle a request.
0572:             */
0573:            public RequestEvaluator getEvaluator() {
0574:                if (!running) {
0575:                    throw new ApplicationStoppedException();
0576:                }
0577:
0578:                // first try
0579:                try {
0580:                    return (RequestEvaluator) freeThreads.pop();
0581:                } catch (EmptyStackException nothreads) {
0582:                    int maxThreads = 50;
0583:
0584:                    String maxThreadsProp = props.getProperty("maxThreads");
0585:                    if (maxThreadsProp != null) {
0586:                        try {
0587:                            maxThreads = Integer.parseInt(maxThreadsProp);
0588:                        } catch (Exception ignore) {
0589:                            logEvent("Couldn't parse maxThreads property: "
0590:                                    + maxThreadsProp);
0591:                        }
0592:                    }
0593:
0594:                    synchronized (this ) {
0595:                        // allocate a new evaluator
0596:                        if (allThreads.size() < maxThreads) {
0597:                            logEvent("Starting engine "
0598:                                    + (allThreads.size() + 1) + " for " + name);
0599:
0600:                            RequestEvaluator ev = new RequestEvaluator(this );
0601:
0602:                            allThreads.addElement(ev);
0603:
0604:                            return (ev);
0605:                        }
0606:                    }
0607:                }
0608:
0609:                // we can't create a new evaluator, so we wait if one becomes available.
0610:                // give it 3 more tries, waiting 3 seconds each time.
0611:                for (int i = 0; i < 4; i++) {
0612:                    try {
0613:                        Thread.sleep(3000);
0614:
0615:                        return (RequestEvaluator) freeThreads.pop();
0616:                    } catch (EmptyStackException nothreads) {
0617:                        // do nothing
0618:                    } catch (InterruptedException inter) {
0619:                        throw new RuntimeException("Thread interrupted.");
0620:                    }
0621:                }
0622:
0623:                // no luck, give up.
0624:                throw new RuntimeException("Maximum Thread count reached.");
0625:            }
0626:
0627:            /**
0628:             * Returns an evaluator back to the pool when the work is done.
0629:             */
0630:            public void releaseEvaluator(RequestEvaluator ev) {
0631:                if (ev != null) {
0632:                    ev.recycle();
0633:                    freeThreads.push(ev);
0634:                }
0635:            }
0636:
0637:            /**
0638:             * This can be used to set the maximum number of evaluators which will be allocated.
0639:             * If evaluators are required beyound this number, an error will be thrown.
0640:             */
0641:            public boolean setNumberOfEvaluators(int n) {
0642:                if ((n < 2) || (n > 511)) {
0643:                    return false;
0644:                }
0645:
0646:                int current = allThreads.size();
0647:
0648:                synchronized (allThreads) {
0649:                    if (n > current) {
0650:                        int toBeCreated = n - current;
0651:
0652:                        for (int i = 0; i < toBeCreated; i++) {
0653:                            RequestEvaluator ev = new RequestEvaluator(this );
0654:
0655:                            freeThreads.push(ev);
0656:                            allThreads.addElement(ev);
0657:                        }
0658:                    } else if (n < current) {
0659:                        int toBeDestroyed = current - n;
0660:
0661:                        for (int i = 0; i < toBeDestroyed; i++) {
0662:                            try {
0663:                                RequestEvaluator re = (RequestEvaluator) freeThreads
0664:                                        .pop();
0665:                                allThreads.removeElement(re);
0666:                                re.stopTransactor();
0667:                            } catch (EmptyStackException empty) {
0668:                                return false;
0669:                            }
0670:                        }
0671:                    }
0672:                }
0673:
0674:                return true;
0675:            }
0676:
0677:            /**
0678:             *  Return the number of currently active threads
0679:             */
0680:            public int getActiveThreads() {
0681:                return 0;
0682:            }
0683:
0684:            /**
0685:             *  Execute a request coming in from a web client.
0686:             */
0687:            public ResponseTrans execute(RequestTrans req) {
0688:                requestCount += 1;
0689:
0690:                // get user for this request's session
0691:                Session session = createSession(req.getSession());
0692:                session.touch();
0693:
0694:                ResponseTrans res = null;
0695:                RequestEvaluator ev = null;
0696:
0697:                // are we responsible for releasing the evaluator and closing the result?
0698:                boolean primaryRequest = false;
0699:
0700:                try {
0701:                    // first look if a request with same user/path/data is already being executed.
0702:                    // if so, attach the request to its output instead of starting a new evaluation
0703:                    // this helps to cleanly solve "doubleclick" kind of users
0704:                    ev = (RequestEvaluator) activeRequests.get(req);
0705:
0706:                    if (ev != null) {
0707:                        res = ev.attachHttpRequest(req);
0708:                    }
0709:
0710:                    if (res == null) {
0711:                        primaryRequest = true;
0712:
0713:                        // if attachHttpRequest returns null this means we came too late
0714:                        // and the other request was finished in the meantime
0715:                        // check if the properties file has been updated
0716:                        updateProperties();
0717:
0718:                        // get evaluator and invoke
0719:                        ev = getEvaluator();
0720:                        res = ev.invokeHttp(req, session);
0721:                    }
0722:                } catch (ApplicationStoppedException stopped) {
0723:                    // let the servlet know that this application has gone to heaven
0724:                    throw stopped;
0725:                } catch (Exception x) {
0726:                    errorCount += 1;
0727:                    res = new ResponseTrans(this , req);
0728:                    res.reportError(name, x.getMessage());
0729:                } finally {
0730:                    if (primaryRequest) {
0731:                        activeRequests.remove(req);
0732:                        releaseEvaluator(ev);
0733:
0734:                        // response needs to be closed/encoded before sending it back
0735:                        try {
0736:                            res.close(charset);
0737:                        } catch (UnsupportedEncodingException uee) {
0738:                            logError("Unsupported response encoding", uee);
0739:                        }
0740:                    } else {
0741:                        res.waitForClose();
0742:                    }
0743:                }
0744:
0745:                return res;
0746:            }
0747:
0748:            /**
0749:             *  Called to execute a method via XML-RPC, usally by helma.main.ApplicationManager
0750:             *  which acts as default handler/request dispatcher.
0751:             */
0752:            public Object executeXmlRpc(String method, Vector args)
0753:                    throws Exception {
0754:                xmlrpcCount += 1;
0755:
0756:                Object retval = null;
0757:                RequestEvaluator ev = null;
0758:
0759:                try {
0760:                    // check if the properties file has been updated
0761:                    updateProperties();
0762:
0763:                    // get evaluator and invoke
0764:                    ev = getEvaluator();
0765:                    retval = ev.invokeXmlRpc(method, args.toArray());
0766:                } finally {
0767:                    releaseEvaluator(ev);
0768:                }
0769:
0770:                return retval;
0771:            }
0772:
0773:            public Object executeExternal(String method, Vector args)
0774:                    throws Exception {
0775:                Object retval = null;
0776:                RequestEvaluator ev = null;
0777:                try {
0778:                    // check if the properties file has been updated
0779:                    updateProperties();
0780:                    // get evaluator and invoke
0781:                    ev = getEvaluator();
0782:                    retval = ev.invokeExternal(method, args.toArray());
0783:                } finally {
0784:                    releaseEvaluator(ev);
0785:                }
0786:                return retval;
0787:            }
0788:
0789:            /**
0790:             * Reset the application's object cache, causing all objects to be refetched from
0791:             * the database.
0792:             */
0793:            public void clearCache() {
0794:                nmgr.clearCache();
0795:            }
0796:
0797:            /**
0798:             * Returns the number of elements in the NodeManager's cache
0799:             */
0800:            public int getCacheUsage() {
0801:                return nmgr.countCacheEntries();
0802:            }
0803:
0804:            /**
0805:             *  Set the application's root element to an arbitrary object. After this is called
0806:             *  with a non-null object, the helma node manager will be bypassed. This function
0807:             * can be used to script and publish any Java object structure with Helma.
0808:             */
0809:            public void setDataRoot(Object root) {
0810:                this .rootObject = root;
0811:            }
0812:
0813:            /**
0814:             * This method returns the root object of this application's object tree.
0815:             */
0816:            public Object getDataRoot() {
0817:                // check if we have a custom root object class
0818:                if (rootObjectClass != null) {
0819:                    // create custom root element.
0820:                    if (rootObject == null) {
0821:                        try {
0822:                            if (classMapping.containsKey("root.factory.class")
0823:                                    && classMapping
0824:                                            .containsKey("root.factory.method")) {
0825:                                String rootFactory = classMapping
0826:                                        .getProperty("root.factory.class");
0827:                                Class c = typemgr.getClassLoader().loadClass(
0828:                                        rootFactory);
0829:                                Method m = c.getMethod(classMapping
0830:                                        .getProperty("root.factory.method"),
0831:                                        (Class[]) null);
0832:
0833:                                rootObject = m.invoke(c, (Object[]) null);
0834:                            } else {
0835:                                String rootClass = classMapping
0836:                                        .getProperty("root");
0837:                                Class c = typemgr.getClassLoader().loadClass(
0838:                                        rootClass);
0839:
0840:                                rootObject = c.newInstance();
0841:                            }
0842:                        } catch (Exception e) {
0843:                            throw new RuntimeException(
0844:                                    "Error creating root object: "
0845:                                            + e.toString());
0846:                        }
0847:                    }
0848:
0849:                    return rootObject;
0850:                }
0851:                // no custom root object is defined - use standard helma objectmodel
0852:                else {
0853:                    return nmgr.safe.getRootNode();
0854:                }
0855:            }
0856:
0857:            /**
0858:             *  Return the prototype of the object to be used as this application's root object
0859:             */
0860:            public DbMapping getRootMapping() {
0861:                return rootMapping;
0862:            }
0863:
0864:            /**
0865:             *  Return the id of the object to be used as this application's root object
0866:             */
0867:            public String getRootId() {
0868:                return rootId;
0869:            }
0870:
0871:            /**
0872:             * Returns the Object which contains registered users of this application.
0873:             */
0874:            public INode getUserRoot() {
0875:                INode users = nmgr.safe.getNode("1", userRootMapping);
0876:
0877:                users.setDbMapping(userRootMapping);
0878:
0879:                return users;
0880:            }
0881:
0882:            /**
0883:             * Returns the node manager for this application. The node manager is
0884:             * the gateway to the helma.objectmodel packages, which perform the mapping
0885:             * of objects to relational database tables or the embedded database.
0886:             */
0887:            public NodeManager getNodeManager() {
0888:                return nmgr;
0889:            }
0890:
0891:            /**
0892:             * Returns a wrapper containing the node manager for this application. The node manager is
0893:             * the gateway to the helma.objectmodel packages, which perform the mapping of objects to
0894:             * relational database tables or the embedded database.
0895:             */
0896:            public WrappedNodeManager getWrappedNodeManager() {
0897:                return nmgr.safe;
0898:            }
0899:
0900:            /**
0901:             * Return the application's session manager
0902:             * @return the SessionManager instance used by this app
0903:             */
0904:            public SessionManager getSessionManager() {
0905:                return sessionMgr;
0906:            }
0907:
0908:            /**
0909:             *  Return a transient node that is shared by all evaluators of this application ("app node")
0910:             */
0911:            public INode getCacheNode() {
0912:                return cachenode;
0913:            }
0914:
0915:            /**
0916:             * Returns a Node representing a registered user of this application by his or her user name.
0917:             */
0918:            public INode getUserNode(String uid) {
0919:                try {
0920:                    INode users = getUserRoot();
0921:
0922:                    return (INode) users.getChildElement(uid);
0923:                } catch (Exception x) {
0924:                    return null;
0925:                }
0926:            }
0927:
0928:            /**
0929:             * Return a prototype for a given node. If the node doesn't specify a prototype,
0930:             * return the generic hopobject prototype.
0931:             */
0932:            public Prototype getPrototype(Object obj) {
0933:                String protoname = getPrototypeName(obj);
0934:
0935:                if (protoname == null) {
0936:                    return typemgr.getPrototype("hopobject");
0937:                }
0938:
0939:                Prototype p = typemgr.getPrototype(protoname);
0940:
0941:                if (p == null) {
0942:                    p = typemgr.getPrototype("hopobject");
0943:                }
0944:
0945:                return p;
0946:            }
0947:
0948:            /**
0949:             * Return the prototype with the given name, if it exists
0950:             */
0951:            public Prototype getPrototypeByName(String name) {
0952:                return typemgr.getPrototype(name);
0953:            }
0954:
0955:            /**
0956:             * Return a collection containing all prototypes defined for this application
0957:             */
0958:            public Collection getPrototypes() {
0959:                return typemgr.getPrototypes();
0960:            }
0961:
0962:            /**
0963:             *  Return a skin for a given object. The skin is found by determining the prototype
0964:             *  to use for the object, then looking up the skin for the prototype.
0965:             */
0966:            public Skin getSkin(String protoname, String skinname,
0967:                    Object[] skinpath) throws IOException {
0968:                Prototype proto = getPrototypeByName(protoname);
0969:
0970:                if (proto == null) {
0971:                    return null;
0972:                }
0973:
0974:                return skinmgr.getSkin(proto, skinname, skinpath);
0975:            }
0976:
0977:            /**
0978:             * Return the session currently associated with a given Hop session ID.
0979:             * Create a new session if necessary.
0980:             */
0981:            public Session createSession(String sessionId) {
0982:                return sessionMgr.createSession(sessionId);
0983:            }
0984:
0985:            /**
0986:             * Return a list of Helma nodes (HopObjects -  the database object representing the user,
0987:             *  not the session object) representing currently logged in users.
0988:             */
0989:            public List getActiveUsers() {
0990:                return sessionMgr.getActiveUsers();
0991:            }
0992:
0993:            /**
0994:             * Return a list of Helma nodes (HopObjects -  the database object representing the user,
0995:             *  not the session object) representing registered users of this application.
0996:             */
0997:            public List getRegisteredUsers() {
0998:                ArrayList list = new ArrayList();
0999:                INode users = getUserRoot();
1000:
1001:                // add all child nodes to the list
1002:                for (Enumeration e = users.getSubnodes(); e.hasMoreElements();) {
1003:                    list.add(e.nextElement());
1004:                }
1005:
1006:                // if none, try to get them from properties (internal db)
1007:                if (list.size() == 0) {
1008:                    for (Enumeration e = users.properties(); e
1009:                            .hasMoreElements();) {
1010:                        list.add(users.getNode((String) e.nextElement()));
1011:                    }
1012:                }
1013:
1014:                return list;
1015:            }
1016:
1017:            /**
1018:             * Return an array of <code>SessionBean</code> objects currently associated
1019:             * with a given Helma user.
1020:             */
1021:            public List getSessionsForUsername(String username) {
1022:                return sessionMgr.getSessionsForUsername(username);
1023:            }
1024:
1025:            /**
1026:             * Return the session currently associated with a given Hop session ID.
1027:             */
1028:            public Session getSession(String sessionId) {
1029:                return sessionMgr.getSession(sessionId);
1030:            }
1031:
1032:            /**
1033:             *  Return the whole session map.
1034:             */
1035:            public Map getSessions() {
1036:                return sessionMgr.getSessions();
1037:            }
1038:
1039:            /**
1040:             * Returns the number of currenty active sessions.
1041:             */
1042:            public int countSessions() {
1043:                return sessionMgr.countSessions();
1044:            }
1045:
1046:            /**
1047:             * Register a user with the given user name and password.
1048:             */
1049:            public INode registerUser(String uname, String password) {
1050:                if (uname == null) {
1051:                    return null;
1052:                }
1053:
1054:                uname = uname.toLowerCase().trim();
1055:
1056:                if ("".equals(uname)) {
1057:                    return null;
1058:                }
1059:
1060:                INode unode;
1061:
1062:                try {
1063:                    INode users = getUserRoot();
1064:
1065:                    // check if a user with this name is already registered
1066:                    unode = (INode) users.getChildElement(uname);
1067:                    if (unode != null) {
1068:                        return null;
1069:                    }
1070:
1071:                    unode = new Node(uname, "user", nmgr.safe);
1072:
1073:                    String usernameField = (userMapping != null) ? userMapping
1074:                            .getNameField() : null;
1075:                    String usernameProp = null;
1076:
1077:                    if (usernameField != null) {
1078:                        usernameProp = userMapping
1079:                                .columnNameToProperty(usernameField);
1080:                    }
1081:
1082:                    if (usernameProp == null) {
1083:                        usernameProp = "name";
1084:                    }
1085:
1086:                    unode.setName(uname);
1087:                    unode.setString(usernameProp, uname);
1088:                    unode.setString("password", password);
1089:
1090:                    return users.addNode(unode);
1091:
1092:                } catch (Exception x) {
1093:                    logEvent("Error registering User: " + x);
1094:
1095:                    return null;
1096:                }
1097:            }
1098:
1099:            /**
1100:             * Log in a user given his or her user name and password.
1101:             */
1102:            public boolean loginSession(String uname, String password,
1103:                    Session session) {
1104:                // Check the name/password of a user and log it in to the current session
1105:                if (uname == null) {
1106:                    return false;
1107:                }
1108:
1109:                uname = uname.toLowerCase().trim();
1110:
1111:                if ("".equals(uname)) {
1112:                    return false;
1113:                }
1114:
1115:                try {
1116:                    INode users = getUserRoot();
1117:                    Node unode = (Node) users.getChildElement(uname);
1118:                    if (unode == null)
1119:                        return false;
1120:
1121:                    String pw = unode.getString("password");
1122:
1123:                    if ((pw != null) && pw.equals(password)) {
1124:                        // let the old user-object forget about this session
1125:                        session.logout();
1126:                        session.login(unode);
1127:
1128:                        return true;
1129:                    }
1130:                } catch (Exception x) {
1131:                    return false;
1132:                }
1133:
1134:                return false;
1135:            }
1136:
1137:            /**
1138:             * Log out a session from this application.
1139:             */
1140:            public void logoutSession(Session session) {
1141:                session.logout();
1142:            }
1143:
1144:            /**
1145:             * In contrast to login, this works outside the Hop user object framework. Instead, the user is
1146:             * authenticated against a passwd file in the application directory. This is to have some sort of
1147:             * authentication available *before* the application is up and running, i.e. for application setup tasks.
1148:             */
1149:            public boolean authenticate(String uname, String password) {
1150:                if ((uname == null) || (password == null)) {
1151:                    return false;
1152:                }
1153:
1154:                return pwfile.authenticate(uname, password);
1155:            }
1156:
1157:            /**
1158:             *  Return the href to the root of this application.
1159:             */
1160:            public String getRootHref() throws UnsupportedEncodingException {
1161:                return getNodeHref(getDataRoot(), null);
1162:            }
1163:
1164:            /**
1165:             * Return a path to be used in a URL pointing to the given element  and action
1166:             */
1167:            public String getNodeHref(Object elem, String actionName)
1168:                    throws UnsupportedEncodingException {
1169:                StringBuffer b = new StringBuffer(baseURI);
1170:
1171:                composeHref(elem, b, 0);
1172:
1173:                if (actionName != null) {
1174:                    b.append(UrlEncoded.encode(actionName, charset));
1175:                }
1176:
1177:                return b.toString();
1178:            }
1179:
1180:            private void composeHref(Object elem, StringBuffer b, int pathCount)
1181:                    throws UnsupportedEncodingException {
1182:                if ((elem == null) || (pathCount > 50)) {
1183:                    return;
1184:                }
1185:
1186:                if ((hrefRootPrototype != null)
1187:                        && hrefRootPrototype.equals(getPrototypeName(elem))) {
1188:                    return;
1189:                }
1190:
1191:                Object parent = getParentElement(elem);
1192:
1193:                if (parent == null) {
1194:                    return;
1195:                }
1196:
1197:                // recurse to parent element
1198:                composeHref(getParentElement(elem), b, ++pathCount);
1199:
1200:                // append ourselves
1201:                String ename = getElementName(elem);
1202:                if (ename != null) {
1203:                    b.append(UrlEncoded.encode(ename, charset));
1204:                    b.append("/");
1205:                }
1206:            }
1207:
1208:            /**
1209:             *  Returns the baseURI for Hrefs in this application.
1210:             */
1211:            public String getBaseURI() {
1212:                return baseURI;
1213:            }
1214:
1215:            /**
1216:             *  This method sets the base URL of this application which will be prepended to
1217:             *  the actual object path.
1218:             */
1219:            public void setBaseURI(String uri) {
1220:                if (uri == null) {
1221:                    this .baseURI = "/";
1222:                } else if (!uri.endsWith("/")) {
1223:                    this .baseURI = uri + "/";
1224:                } else {
1225:                    this .baseURI = uri;
1226:                }
1227:            }
1228:
1229:            /**
1230:             *  Return true if the baseURI property is defined in the application
1231:             *  properties, false otherwise.
1232:             */
1233:            public boolean hasExplicitBaseURI() {
1234:                return props.containsKey("baseuri");
1235:            }
1236:
1237:            /**
1238:             * Returns the prototype name that Hrefs in this application should
1239:             * start with.
1240:             */
1241:            public String getHrefRootPrototype() {
1242:                return hrefRootPrototype;
1243:            }
1244:
1245:            /**
1246:             * Tell other classes whether they should output logging information for
1247:             * this application.
1248:             */
1249:            public boolean debug() {
1250:                return debug;
1251:            }
1252:
1253:            /**
1254:             * Get the current RequestEvaluator, or null if the calling thread
1255:             * is not evaluating a request.
1256:             *
1257:             * @return the RequestEvaluator belonging to the current thread
1258:             */
1259:            public RequestEvaluator getCurrentRequestEvaluator() {
1260:                return (RequestEvaluator) currentEvaluator.get();
1261:            }
1262:
1263:            /**
1264:             * Set the current RequestEvaluator for the calling thread.
1265:             * @param eval the RequestEvaluator belonging to the current thread
1266:             */
1267:            protected void setCurrentRequestEvaluator(RequestEvaluator eval) {
1268:                currentEvaluator.set(eval);
1269:            }
1270:
1271:            /**
1272:             *  Utility function invoker for the methods below. This *must* be called
1273:             *  by an active RequestEvaluator thread.
1274:             */
1275:            private Object invokeFunction(Object obj, String func, Object[] args) {
1276:                RequestEvaluator reval = getCurrentRequestEvaluator();
1277:                if (reval != null) {
1278:                    if (args == null) {
1279:                        args = RequestEvaluator.EMPTY_ARGS;
1280:                    }
1281:                    try {
1282:                        return reval.invokeDirectFunction(obj, func, args);
1283:                    } catch (Exception x) {
1284:                        if (debug) {
1285:                            System.err
1286:                                    .println("Error in Application.invokeFunction ("
1287:                                            + func + "): " + x);
1288:                        }
1289:                    }
1290:                }
1291:                return null;
1292:            }
1293:
1294:            /**
1295:             *  Return the application's classloader
1296:             */
1297:            public ClassLoader getClassLoader() {
1298:                return typemgr.getClassLoader();
1299:            }
1300:
1301:            //////////////////////////////////////////////////////////////////////////////////////////////////////////
1302:            ///   The following methods mimic the IPathElement interface. This allows us
1303:            ///   to script any Java object: If the object implements IPathElement (as does
1304:            ///   the Node class in Helma's internal objectmodel) then the corresponding
1305:            ///   method is called in the object itself. Otherwise, a corresponding script function
1306:            ///   is called on the object.
1307:            //////////////////////////////////////////////////////////////////////////////////////////////////////////
1308:
1309:            /**
1310:             *  Return the name to be used to get this element from its parent
1311:             */
1312:            public String getElementName(Object obj) {
1313:                if (obj instanceof  IPathElement) {
1314:                    return ((IPathElement) obj).getElementName();
1315:                }
1316:
1317:                Object retval = invokeFunction(obj, "getElementName",
1318:                        RequestEvaluator.EMPTY_ARGS);
1319:
1320:                if (retval != null) {
1321:                    return retval.toString();
1322:                }
1323:
1324:                return null;
1325:            }
1326:
1327:            /**
1328:             * Retrieve a child element of this object by name.
1329:             */
1330:            public Object getChildElement(Object obj, String name) {
1331:                if (obj instanceof  IPathElement) {
1332:                    return ((IPathElement) obj).getChildElement(name);
1333:                }
1334:
1335:                Object[] arg = new Object[1];
1336:
1337:                arg[0] = name;
1338:
1339:                return invokeFunction(obj, "getChildElement", arg);
1340:            }
1341:
1342:            /**
1343:             * Return the parent element of this object.
1344:             */
1345:            public Object getParentElement(Object obj) {
1346:                if (obj instanceof  IPathElement) {
1347:                    return ((IPathElement) obj).getParentElement();
1348:                }
1349:
1350:                return invokeFunction(obj, "getParentElement",
1351:                        RequestEvaluator.EMPTY_ARGS);
1352:            }
1353:
1354:            /**
1355:             * Get the name of the prototype to be used for this object. This will
1356:             * determine which scripts, actions and skins can be called on it
1357:             * within the Helma scripting and rendering framework.
1358:             */
1359:            public String getPrototypeName(Object obj) {
1360:                if (obj == null) {
1361:                    return "global";
1362:                } else if (obj instanceof  IPathElement) {
1363:                    // check if e implements the IPathElement interface
1364:                    return ((IPathElement) obj).getPrototype();
1365:                }
1366:
1367:                Class clazz = obj.getClass();
1368:                String className = clazz.getName();
1369:                String protoName = classMapping.getProperty(className);
1370:                if (protoName != null) {
1371:                    return protoName == CLASS_NOT_MAPPED ? null : protoName;
1372:                }
1373:
1374:                // walk down superclass path
1375:                while ((clazz = clazz.getSuperclass()) != null) {
1376:                    protoName = classMapping.getProperty(clazz.getName());
1377:                    if (protoName != null) {
1378:                        // cache the class name for the object so we run faster next time
1379:                        classMapping.setProperty(className, protoName);
1380:                        return protoName;
1381:                    }
1382:                }
1383:                // check interfaces, too
1384:                Class[] classes = obj.getClass().getInterfaces();
1385:                for (int i = 0; i < classes.length; i++) {
1386:                    protoName = classMapping.getProperty(classes[i].getName());
1387:                    if (protoName != null) {
1388:                        // cache the class name for the object so we run faster next time
1389:                        classMapping.setProperty(className, protoName);
1390:                        return protoName;
1391:                    }
1392:                }
1393:                // not mapped - cache negative result
1394:                classMapping.setProperty(className, CLASS_NOT_MAPPED);
1395:                return null;
1396:            }
1397:
1398:            public DocApplication getDoc() {
1399:                RequestEvaluator eval = null;
1400:                try {
1401:                    eval = getEvaluator();
1402:                    return eval.scriptingEngine.getDoc();
1403:                } catch (Exception xcept) {
1404:                    logError("Error in getDoc() for " + name, xcept);
1405:                    return null;
1406:                } finally {
1407:                    releaseEvaluator(eval);
1408:                }
1409:            }
1410:
1411:            ////////////////////////////////////////////////////////////////////////
1412:
1413:            /**
1414:             * Log an application error
1415:             */
1416:            public void logError(String msg, Throwable error) {
1417:                if (eventLog == null) {
1418:                    eventLog = getLogger(eventLogName);
1419:                }
1420:                eventLog.error(msg, error);
1421:            }
1422:
1423:            /**
1424:             * Log an application error
1425:             */
1426:            public void logError(String msg) {
1427:                if (eventLog == null) {
1428:                    eventLog = getLogger(eventLogName);
1429:                }
1430:                eventLog.error(msg);
1431:            }
1432:
1433:            /**
1434:             * Log a generic application event
1435:             */
1436:            public void logEvent(String msg) {
1437:                getEventLog().info(msg);
1438:            }
1439:
1440:            /**
1441:             * Log an application access
1442:             */
1443:            public void logAccess(String msg) {
1444:                getAccessLog().info(msg);
1445:            }
1446:
1447:            /**
1448:             * get the app's event log.
1449:             */
1450:            Log getEventLog() {
1451:                if (eventLog == null) {
1452:                    eventLog = getLogger(eventLogName);
1453:                    // set log level for event log in case it is a helma.util.Logger
1454:                    if (eventLog instanceof  Logger) {
1455:                        if (debug && !eventLog.isDebugEnabled())
1456:                            ((Logger) eventLog).setLogLevel(Logger.DEBUG);
1457:                        else if (!eventLog.isInfoEnabled())
1458:                            ((Logger) eventLog).setLogLevel(Logger.INFO);
1459:                    }
1460:                }
1461:                return eventLog;
1462:            }
1463:
1464:            /**
1465:             * get the app's access log.
1466:             */
1467:            Log getAccessLog() {
1468:                if (accessLog == null) {
1469:                    accessLog = getLogger(accessLogName);
1470:                }
1471:                return accessLog;
1472:            }
1473:
1474:            /**
1475:             *  Get a logger object to log events for this application.
1476:             */
1477:            public Log getLogger(String logname) {
1478:                if ("console".equals(logDir) || "console".equals(logname)) {
1479:                    return Logging.getConsoleLog();
1480:                } else {
1481:                    return LogFactory.getLog(logname);
1482:                }
1483:            }
1484:
1485:            /**
1486:             * The run method performs periodic tasks like executing the scheduler method and
1487:             * kicking out expired user sessions.
1488:             */
1489:            public void run() {
1490:
1491:                // interval between session cleanups
1492:                long lastSessionCleanup = System.currentTimeMillis();
1493:
1494:                while (Thread.currentThread() == worker) {
1495:
1496:                    try {
1497:                        // interval between scheduler runs
1498:                        long sleepInterval = 60000;
1499:
1500:                        try {
1501:                            String sleepProp = props
1502:                                    .getProperty("schedulerInterval");
1503:                            if (sleepProp != null) {
1504:                                sleepInterval = Math.max(1000, Integer
1505:                                        .parseInt(sleepProp) * 1000);
1506:                            } else {
1507:                                sleepInterval = CronJob
1508:                                        .millisToNextFullMinute();
1509:                            }
1510:                        } catch (Exception ignore) {
1511:                            // we'll use the default interval
1512:                        }
1513:
1514:                        // sleep until the next full minute
1515:                        try {
1516:                            Thread.sleep(sleepInterval);
1517:                        } catch (InterruptedException x) {
1518:                            worker = null;
1519:                            break;
1520:                        }
1521:
1522:                        // purge sessions
1523:                        try {
1524:                            lastSessionCleanup = sessionMgr
1525:                                    .cleanupSessions(lastSessionCleanup);
1526:                        } catch (Exception x) {
1527:                            logError("Error in session cleanup: " + x, x);
1528:                        } catch (LinkageError x) {
1529:                            logError("Error in session cleanup: " + x, x);
1530:                        }
1531:
1532:                        // execute cron jobs
1533:                        try {
1534:                            executeCronJobs();
1535:                        } catch (Exception x) {
1536:                            logError("Error in cron job execution: " + x, x);
1537:                        } catch (LinkageError x) {
1538:                            logError("Error in cron job execution: " + x, x);
1539:                        }
1540:
1541:                    } catch (VirtualMachineError error) {
1542:                        logError("Error in scheduler loop: " + error, error);
1543:                    }
1544:                }
1545:
1546:                // when interrupted, shutdown running cron jobs
1547:                synchronized (activeCronJobs) {
1548:                    for (Iterator i = activeCronJobs.values().iterator(); i
1549:                            .hasNext();) {
1550:                        ((CronRunner) i.next()).interrupt();
1551:                        i.remove();
1552:                    }
1553:                }
1554:
1555:                logEvent("Scheduler for " + name + " exiting");
1556:            }
1557:
1558:            /**
1559:             * Executes cron jobs for the application, which are either
1560:             * defined in app.properties or via app.addCronJob().
1561:             * This method is called by run().
1562:             */
1563:            private void executeCronJobs() {
1564:                // loop-local cron job data
1565:                List jobs = CronJob.parse(props.getSubProperties("cron."));
1566:                Date date = new Date();
1567:
1568:                jobs.addAll(customCronJobs.values());
1569:                CronJob.sort(jobs);
1570:
1571:                if (debug) {
1572:                    logEvent("Running cron jobs: " + jobs);
1573:                }
1574:                if (!activeCronJobs.isEmpty()) {
1575:                    logEvent("Cron jobs still running from last minute: "
1576:                            + activeCronJobs);
1577:                }
1578:
1579:                for (Iterator i = jobs.iterator(); i.hasNext();) {
1580:                    CronJob job = (CronJob) i.next();
1581:
1582:                    if (job.appliesToDate(date)) {
1583:                        // check if the job is already active ...
1584:                        if (activeCronJobs.containsKey(job.getName())) {
1585:                            logEvent(job
1586:                                    + " is still active, skipped in this minute");
1587:
1588:                            continue;
1589:                        }
1590:
1591:                        RequestEvaluator evaluator;
1592:
1593:                        try {
1594:                            evaluator = getEvaluator();
1595:                        } catch (RuntimeException rt) {
1596:                            if (running) {
1597:                                logEvent("couldn't execute " + job
1598:                                        + ", maximum thread count reached");
1599:
1600:                                continue;
1601:                            } else {
1602:                                break;
1603:                            }
1604:                        }
1605:
1606:                        // if the job has a long timeout or we're already late during this minute
1607:                        // the job is run from an extra thread
1608:                        if ((job.getTimeout() > 20000)
1609:                                || (CronJob.millisToNextFullMinute() < 30000)) {
1610:                            CronRunner runner = new CronRunner(evaluator, job);
1611:
1612:                            activeCronJobs.put(job.getName(), runner);
1613:                            runner.start();
1614:                        } else {
1615:                            try {
1616:                                evaluator.invokeInternal(null, job
1617:                                        .getFunction(),
1618:                                        RequestEvaluator.EMPTY_ARGS, job
1619:                                                .getTimeout());
1620:                            } catch (Exception ex) {
1621:                                logEvent("error running " + job + ": " + ex);
1622:                            } finally {
1623:                                releaseEvaluator(evaluator);
1624:                            }
1625:                        }
1626:                    }
1627:                }
1628:            }
1629:
1630:            /**
1631:             * Check whether a prototype is for scripting a java class, i.e. if there's an entry
1632:             * for it in the class.properties file.
1633:             */
1634:            public boolean isJavaPrototype(String typename) {
1635:                return classMapping.contains(typename);
1636:            }
1637:
1638:            /**
1639:             * Return the java class that a given prototype wraps, or null.
1640:             */
1641:            public String getJavaClassForPrototype(String typename) {
1642:
1643:                for (Iterator it = classMapping.entrySet().iterator(); it
1644:                        .hasNext();) {
1645:                    Map.Entry entry = (Map.Entry) it.next();
1646:
1647:                    if (typename.equals(entry.getValue())) {
1648:                        return (String) entry.getKey();
1649:                    }
1650:                }
1651:
1652:                return null;
1653:            }
1654:
1655:            /**
1656:             * Return a DbSource object for a given name. A DbSource is a relational database defined
1657:             * in a db.properties file.
1658:             */
1659:            public DbSource getDbSource(String name) {
1660:                String dbSrcName = name.toLowerCase();
1661:                DbSource dbs = (DbSource) dbSources.get(dbSrcName);
1662:
1663:                if (dbs != null) {
1664:                    return dbs;
1665:                }
1666:
1667:                try {
1668:                    dbs = new DbSource(name, dbProps);
1669:                    dbSources.put(dbSrcName, dbs);
1670:                } catch (Exception problem) {
1671:                    logEvent("Error creating DbSource " + name + ": ");
1672:                    logEvent(problem.toString());
1673:                }
1674:
1675:                return dbs;
1676:            }
1677:
1678:            /**
1679:             * Return the name of this application
1680:             */
1681:            public String getName() {
1682:                return name;
1683:            }
1684:
1685:            /**
1686:             * Add a repository to this app's repository list. This is used for
1687:             * ZipRepositories contained in top-level file repositories, for instance.
1688:             *
1689:             * @param rep the repository to add
1690:             * @return if the repository was not yet contained
1691:             */
1692:            public boolean addRepository(Repository rep) {
1693:                if (rep != null && !repositories.contains(rep)) {
1694:                    // Add the new repository before its parent repository.
1695:                    // This establishes the order of compilation between FileRepositories
1696:                    // and embedded ZipRepositories.
1697:                    Repository parent = rep.getParentRepository();
1698:                    if (parent != null) {
1699:                        int idx = repositories.indexOf(parent);
1700:                        if (idx > -1) {
1701:                            repositories.add(idx, rep);
1702:                            return true;
1703:                        }
1704:                    }
1705:                    // no parent or parent not in app's repositories, add at end of list.
1706:                    repositories.add(rep);
1707:                    return true;
1708:                }
1709:                return false;
1710:            }
1711:
1712:            /**
1713:             * Searches for the index of the given repository for this app.
1714:             * The arguement must be a root argument, or -1 will be returned.
1715:             *
1716:             * @param   rep one of this app's root repositories.
1717:             * @return  the index of the first occurrence of the argument in this
1718:             *          list; returns <tt>-1</tt> if the object is not found.
1719:             */
1720:            public int getRepositoryIndex(Repository rep) {
1721:                return repositories.indexOf(rep);
1722:            }
1723:
1724:            /**
1725:             * Returns the repositories of this application
1726:             * @return iterator through application repositories
1727:             */
1728:            public List getRepositories() {
1729:                return Collections.unmodifiableList(repositories);
1730:            }
1731:
1732:            /**
1733:             * Set the code resource currently being evaluated/compiled. This is used
1734:             * to set the proper parent repository when a new repository is added
1735:             * via app.addRepository().
1736:             *
1737:             * @param resource the resource being currently evaluated/compiled
1738:             */
1739:            public void setCurrentCodeResource(Resource resource) {
1740:                currentCodeResource = resource;
1741:            }
1742:
1743:            /**
1744:             * Set the code resource currently being evaluated/compiled. This is used
1745:             * to set the proper parent repository when a new repository is added
1746:             * via app.addRepository().
1747:
1748:             * @return the resource being currently evaluated/compiled
1749:             */
1750:            public Resource getCurrentCodeResource() {
1751:                return currentCodeResource;
1752:            }
1753:
1754:            /**
1755:             * Return the directory of the Helma server
1756:             */
1757:            public File getServerDir() {
1758:                return hopHome;
1759:            }
1760:
1761:            /**
1762:             * Get the DbMapping associated with a prototype name in this application
1763:             */
1764:            public DbMapping getDbMapping(String typename) {
1765:                Prototype proto = typemgr.getPrototype(typename);
1766:
1767:                if (proto == null) {
1768:                    return null;
1769:                }
1770:
1771:                return proto.getDbMapping();
1772:            }
1773:
1774:            /**
1775:             * Return the current upload status.
1776:             * @param req the upload RequestTrans
1777:             * @return the current upload status.
1778:             */
1779:            public UploadStatus getUploadStatus(RequestTrans req) {
1780:                String uploadId = (String) req.get("upload_id");
1781:                if (uploadId == null)
1782:                    return null;
1783:
1784:                String sessionId = req.getSession();
1785:                Session session = getSession(sessionId);
1786:                if (session == null)
1787:                    return null;
1788:                return session.createUpload(uploadId);
1789:            }
1790:
1791:            private synchronized void updateProperties() {
1792:                // if so property file has been updated, re-read props.
1793:                if (props.lastModified() > lastPropertyRead) {
1794:                    // force property update
1795:                    props.update();
1796:
1797:                    // character encoding to be used for responses
1798:                    charset = props.getProperty("charset", "ISO-8859-1");
1799:
1800:                    // debug flag
1801:                    debug = "true".equalsIgnoreCase(props.getProperty("debug"));
1802:
1803:                    // if rhino debugger is enabled use higher (10 min) default request timeout
1804:                    String defaultReqTimeout = "true".equalsIgnoreCase(props
1805:                            .getProperty("rhino.debug")) ? "600" : "60";
1806:                    String reqTimeout = props.getProperty("requesttimeout",
1807:                            defaultReqTimeout);
1808:                    try {
1809:                        requestTimeout = Long.parseLong(reqTimeout) * 1000L;
1810:                    } catch (Exception ignore) {
1811:                        // go with default value
1812:                        requestTimeout = 60000L;
1813:                    }
1814:
1815:                    // set base URI
1816:                    String base = props.getProperty("baseuri");
1817:
1818:                    if (base != null) {
1819:                        setBaseURI(base);
1820:                    } else if (baseURI == null) {
1821:                        baseURI = "/";
1822:                    }
1823:
1824:                    hrefRootPrototype = props.getProperty("hrefrootprototype");
1825:
1826:                    // update the XML-RPC access list, containting prototype.method
1827:                    // entries of functions that may be called via XML-RPC
1828:                    String xmlrpcAccessProp = props.getProperty("xmlrpcaccess");
1829:                    HashSet xra = new HashSet();
1830:
1831:                    if (xmlrpcAccessProp != null) {
1832:                        StringTokenizer st = new StringTokenizer(
1833:                                xmlrpcAccessProp, ",; ");
1834:
1835:                        while (st.hasMoreTokens()) {
1836:                            String token = st.nextToken().trim();
1837:
1838:                            xra.add(token.toLowerCase());
1839:                        }
1840:                    }
1841:
1842:                    xmlrpcAccess = xra;
1843:
1844:                    // if node manager exists, update it
1845:                    if (nmgr != null) {
1846:                        nmgr.updateProperties(props);
1847:                    }
1848:
1849:                    // update extensions
1850:                    if (Server.getServer() != null) {
1851:                        Vector extensions = Server.getServer().getExtensions();
1852:
1853:                        for (int i = 0; i < extensions.size(); i++) {
1854:                            HelmaExtension ext = (HelmaExtension) extensions
1855:                                    .get(i);
1856:
1857:                            try {
1858:                                ext.applicationUpdated(this );
1859:                            } catch (ConfigurationException e) {
1860:                                logEvent("Error updating extension " + ext
1861:                                        + ": " + e);
1862:                            }
1863:                        }
1864:                    }
1865:
1866:                    logDir = props.getProperty("logdir", "log");
1867:                    if (System.getProperty("helma.logdir") == null) {
1868:                        // set up helma.logdir system property in case we're using it
1869:                        // FIXME: this sets a global System property, should be per-app
1870:                        File dir = new File(logDir);
1871:                        System.setProperty("helma.logdir", dir
1872:                                .getAbsolutePath());
1873:                    }
1874:
1875:                    // set log level for event log in case it is a helma.util.Logger
1876:                    if (eventLog instanceof  Logger) {
1877:                        if (debug && !eventLog.isDebugEnabled())
1878:                            ((Logger) eventLog).setLogLevel(Logger.DEBUG);
1879:                        else if (!eventLog.isInfoEnabled())
1880:                            ((Logger) eventLog).setLogLevel(Logger.INFO);
1881:                    }
1882:
1883:                    // set prop read timestamp
1884:                    lastPropertyRead = props.lastModified();
1885:                }
1886:            }
1887:
1888:            /**
1889:             *  Get a checksum that mirrors the state of this application in the sense
1890:             *  that if anything in the applciation changes, the checksum hopefully will
1891:             *  change, too.
1892:             */
1893:            public long getChecksum() {
1894:                return starttime + typemgr.getLastCodeUpdate()
1895:                        + props.getChecksum();
1896:            }
1897:
1898:            /**
1899:             * Proxy method to get a property from the applications properties.
1900:             */
1901:            public String getProperty(String propname) {
1902:                return props.getProperty(propname);
1903:            }
1904:
1905:            /**
1906:             * Proxy method to get a property from the applications properties.
1907:             */
1908:            public String getProperty(String propname, String defvalue) {
1909:                return props.getProperty(propname, defvalue);
1910:            }
1911:
1912:            /**
1913:             * Get the application's app properties
1914:             *
1915:             * @return the properties reflecting the app.properties
1916:             */
1917:            public ResourceProperties getProperties() {
1918:                return props;
1919:            }
1920:
1921:            /**
1922:             * Get the application's db properties
1923:             *
1924:             * @return the properties reflecting the db.properties
1925:             */
1926:            public ResourceProperties getDbProperties() {
1927:                return dbProps;
1928:            }
1929:
1930:            /**
1931:             * Return the XML-RPC handler name for this app. The contract is to
1932:             * always return the same string, even if it has been changed in the properties file
1933:             * during runtime, so the app gets unregistered correctly.
1934:             */
1935:            public String getXmlRpcHandlerName() {
1936:                if (xmlrpcHandlerName == null) {
1937:                    xmlrpcHandlerName = props.getProperty("xmlrpcHandlerName",
1938:                            this .name);
1939:                }
1940:
1941:                return xmlrpcHandlerName;
1942:            }
1943:
1944:            /**
1945:             * Return a string representation for this app.
1946:             */
1947:            public String toString() {
1948:                return "[Application " + name + "]";
1949:            }
1950:
1951:            /**
1952:             *
1953:             */
1954:            public int countThreads() {
1955:                return threadgroup.activeCount();
1956:            }
1957:
1958:            /**
1959:             *
1960:             */
1961:            public int countEvaluators() {
1962:                return allThreads.size();
1963:            }
1964:
1965:            /**
1966:             *
1967:             */
1968:            public int countFreeEvaluators() {
1969:                return freeThreads.size();
1970:            }
1971:
1972:            /**
1973:             *
1974:             */
1975:            public int countActiveEvaluators() {
1976:                return allThreads.size() - freeThreads.size();
1977:            }
1978:
1979:            /**
1980:             *
1981:             */
1982:            public int countMaxActiveEvaluators() {
1983:                // return typemgr.countRegisteredRequestEvaluators () -1;
1984:                // not available due to framework refactoring
1985:                return -1;
1986:            }
1987:
1988:            /**
1989:             *
1990:             */
1991:            public long getRequestCount() {
1992:                return requestCount;
1993:            }
1994:
1995:            /**
1996:             *
1997:             */
1998:            public long getXmlrpcCount() {
1999:                return xmlrpcCount;
2000:            }
2001:
2002:            /**
2003:             *
2004:             */
2005:            public long getErrorCount() {
2006:                return errorCount;
2007:            }
2008:
2009:            /**
2010:             *
2011:             *
2012:             * @return ...
2013:             */
2014:            public long getStarttime() {
2015:                return starttime;
2016:            }
2017:
2018:            /**
2019:             * Return the name of the character encoding used by this application
2020:             *
2021:             * @return the character encoding
2022:             */
2023:            public String getCharset() {
2024:                return charset;
2025:            }
2026:
2027:            /**
2028:             * Periodically called to log thread stats for this application
2029:             */
2030:            public void printThreadStats() {
2031:                logEvent("Thread Stats for " + name + ": "
2032:                        + threadgroup.activeCount() + " active");
2033:
2034:                Runtime rt = Runtime.getRuntime();
2035:                long free = rt.freeMemory();
2036:                long total = rt.totalMemory();
2037:
2038:                logEvent("Free memory: " + (free / 1024) + " kB");
2039:                logEvent("Total memory: " + (total / 1024) + " kB");
2040:            }
2041:
2042:            /**
2043:             * Check if a method may be invoked via XML-RPC on a prototype.
2044:             */
2045:            protected void checkXmlRpcAccess(String proto, String method)
2046:                    throws Exception {
2047:                String key = proto + "." + method;
2048:
2049:                // XML-RPC access items are case insensitive and stored in lower case
2050:                if (!xmlrpcAccess.contains(key.toLowerCase())) {
2051:                    throw new Exception("Method " + key
2052:                            + " is not callable via XML-RPC");
2053:                }
2054:            }
2055:
2056:            class CronRunner extends Thread {
2057:                RequestEvaluator this Evaluator;
2058:                CronJob job;
2059:
2060:                public CronRunner(RequestEvaluator this Evaluator, CronJob job) {
2061:                    this .this Evaluator = this Evaluator;
2062:                    this .job = job;
2063:                }
2064:
2065:                public void run() {
2066:                    try {
2067:                        this Evaluator.invokeInternal(null, job.getFunction(),
2068:                                RequestEvaluator.EMPTY_ARGS, job.getTimeout());
2069:                    } catch (Exception ex) {
2070:                        logEvent("error running " + job + ": " + ex);
2071:                    } finally {
2072:                        releaseEvaluator(this Evaluator);
2073:                        this Evaluator = null;
2074:                        activeCronJobs.remove(job.getName());
2075:                    }
2076:                }
2077:
2078:                public String toString() {
2079:                    return "CronRunner[" + job + "]";
2080:                }
2081:            }
2082:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.