diff --git a/Application/functions.js b/Application/functions.js
new file mode 100644
index 00000000..04cda7fa
--- /dev/null
+++ b/Application/functions.js
@@ -0,0 +1,35 @@
+/**
+ * construct an application object so that we can use
+ * skins for non-active applications too
+ * @arg name
+ */
+function constructor(name) {
+ this.name = name;
+}
+
+
+/**
+ * overrides the internal href-function, as
+ * helma.framework.core.Application.getNodeHref(Object,String)
+ * isn't able to compute correct urls for non-node objects.
+ * @arg action of application
+ */
+function href(action) {
+ var url = getProperty("baseURI");
+ url = (url==null || url=="null") ? "" : url;
+ url += this.name + "/" + ( (action!=null && action!="") ? action : "main" );
+ return url;
+}
+
+
+/**
+ * return true/false to determine if application is running
+ */
+function isActive() {
+ if ( root.getApplication(this.name)==null )
+ return false;
+ else
+ return true;
+}
+
+
diff --git a/Application/head.skin b/Application/head.skin
new file mode 100644
index 00000000..a52e6407
--- /dev/null
+++ b/Application/head.skin
@@ -0,0 +1,10 @@
+
AppManager <% this.title %>
+<% this.description prefix="
" %>
+
+ ->
+ /main">AppDoc |
+ public |
+ ?app=<% this.title %>&action=flush">flush |
+ ?app=<% this.title %>&action=restart">restart |
+ ?app=<% this.title %>&action=stop">stop
+
diff --git a/Application/macros.js b/Application/macros.js
new file mode 100644
index 00000000..9eec3fe5
--- /dev/null
+++ b/Application/macros.js
@@ -0,0 +1,172 @@
+/**
+ * macro rendering a skin
+ * @param name name of skin
+ */
+function skin_macro(par) {
+ if ( par && par.name ) {
+ this.renderSkin(par.name);
+ }
+}
+
+
+/**
+ * macro-wrapper for href-function
+ * @param action name of action to call on this prototype, default main
+ */
+function href_macro(par) {
+ return this.href( (par&&par.action)?par.action:"main" );
+}
+
+
+/**
+ * Macro returning the URL of an application.
+ * using absoluteURI if set in app.properties, otherwise we go for the href calculated by
+ * the application (which is using baseURI if set)
+ */
+function url_macro() {
+ var str = this.getProperty("absoluteuri");
+ if ( str!=null && str!="" ) {
+ return str;
+ } else {
+ return this.getRootHref();
+ }
+}
+
+
+/**
+ * Macro returning the title of an application
+ */
+function title_macro() {
+ var title = this.name;
+ return(title);
+}
+
+
+/**
+ * Macro rendering a description of an application from a
+ * file called description.txt or doc.html located in the
+ * app's root. Limits description to 200 chars.
+ * @param Object Macro parameter object
+ */
+function description_macro(param) {
+ var str = "";
+ var appHome = this.getAppDir();
+ var f = new File(this.getAppDir().toString(), "description.txt");
+ if (!f.exists())
+ f = new File(this.getAppDir().toString(), "doc.html");
+ if (f.exists()) {
+ str = f.readAll();
+ if (str.length > 200)
+ str = str.substring(0, 200) + "...";
+ }
+ return(str);
+}
+
+
+/**
+ * Macro returning the server's uptime nicely formatted
+ */
+function uptime_macro() {
+ return formatAge( (java.lang.System.currentTimeMillis()-this.starttime) / 1000 );
+}
+
+
+/**
+ * Macro returning the number of active sessions.
+ * parameter used by global.formatCount
+ * @see global.formatCount
+ */
+function countSessions_macro(par) {
+ if ( this.isActive()==true )
+ return this.sessions.size() + formatCount(this.sessions.size(),par);
+ else
+ return 0;
+}
+
+
+/**
+ * Macro returning the number of logged-in users or the list
+ * of logged-in users if http-parameter showusers is set to true.
+ * Makes use of this.countUsers_macro and this.listUsers_macro
+ * @see application.countUsers_macro
+ * @see application.listUsers_macro
+ */
+function users_macro(par) {
+ if ( req.data.showusers=="true" ) {
+ this.listUsers_macro(par);
+ } else {
+ this.countUsers_macro(par);
+ }
+}
+
+
+/**
+ * Macro returning the number of logged-in users if application
+ * is active
+ * parameter used by global.formatCount
+ * @see global.formatCount
+ */
+function countUsers_macro(par) {
+ if ( this.isActive()==true )
+ return this.activeUsers.size() + formatCount(this.activeUsers.size(),par);
+ else
+ return 0;
+}
+
+
+/**
+ * Macro rendering the list of logged-in users if application is active
+ * @param separator html-code written between elements
+ */
+function listUsers_macro(par) {
+ var separator = (par && par.separator) ? par.separator : ", ";
+ if ( this.activeUsers.size()==0 )
+ return "";
+ var e = this.activeUsers.keys();
+ while ( e.hasMoreElements() ) {
+ res.write ( e.nextElement() );
+ if ( e.hasMoreElements() ) {
+ res.write ( separator );
+ }
+ }
+}
+
+
+/**
+ * Macro returning the number of active evaluators (=threads)
+ */
+function countActiveEvaluators_macro() {
+ return this.countActiveEvaluators();
+}
+
+
+/**
+ * Macro returning the number of free evaluators (=threads)
+ */
+function countFreeEvaluators_macro() {
+ return this.countFreeEvaluators();
+}
+
+
+/**
+ * Macro formatting the 5min average of requests.
+ */
+function getRequestAvg_macro(par) {
+ if ( app.requestStat==null || app.requestStat.get(this.name)==null )
+ return 0;
+ if ( this.isActive() ) {
+ var obj = app.requestStat.get(this.name);
+ return obj.last5Min + formatCount(obj.last5Min,par);
+ } else {
+ return 0 + formatCount(0,par);
+ }
+}
+
+
+/**
+ * Macro formatting app.properties
+ */
+function properties_macro(par) {
+ formatProperties( this.getProperties(), par );
+}
+
diff --git a/Application/main.hac b/Application/main.hac
new file mode 100644
index 00000000..40a43c35
--- /dev/null
+++ b/Application/main.hac
@@ -0,0 +1,10 @@
+/**
+ * renders AppManager
+ */
+
+if ( checkAddress()==false ) return;
+if ( checkAuth(this)==false ) return;
+
+res.skin="global";
+res.body = this.renderSkinAsString("main");
+
diff --git a/Application/main.skin b/Application/main.skin
new file mode 100644
index 00000000..0b05f12d
--- /dev/null
+++ b/Application/main.skin
@@ -0,0 +1,42 @@
+<% this.skin name="head" %>
+
+
+
+ application |
+
+
+ active sessions: |
+ |
+ <% this.countSessions %> |
+
+
+ ?showusers=true">logged-in users: |
+ |
+ <% this.users %> |
+
+
+ active evaluators: |
+ |
+ <% this.countActiveEvaluators %> |
+
+
+ free evaluators: |
+ |
+ <% this.countFreeEvaluators %> |
+
+
+ 5min-request-avg: |
+ |
+ <% this.getRequestAvg %> |
+
+
+ uptime: |
+ |
+ <% this.uptime %> |
+
+
+ app.properties |
+
+<% this.properties prefix="" separator=" | | " suffix=" |
" %>
+
+
diff --git a/Application/mrtg.hac b/Application/mrtg.hac
new file mode 100644
index 00000000..fb324794
--- /dev/null
+++ b/Application/mrtg.hac
@@ -0,0 +1,23 @@
+/**
+ * prints session- and thread-stats for mrtg-tool
+ * doesn't check username or password, so that we don't have
+ * to write them cleartext in a mrtg-configfile
+ */
+
+if ( checkAddress()==false ) return;
+
+if ( this.isActive()==false ) {
+ res.write ( "0\n0\n0\n0\n");
+ return;
+}
+
+if ( req.data.action=="sessions" ) {
+ res.write ( this.sessions.size() + "\n0\n0\n0\n" );
+ return;
+}
+
+if ( req.data.action=="threads" ) {
+ res.write ( this.countActiveEvaluators() + "\n" + this.countFreeEvaluators() + "\n0\n0\n");
+ return;
+}
+
diff --git a/Application/navig_active.skin b/Application/navig_active.skin
new file mode 100644
index 00000000..afd05311
--- /dev/null
+++ b/Application/navig_active.skin
@@ -0,0 +1,15 @@
+
+
"><% this.title %>
+
<% this.countSessions singular=" Session" plural=" Sessions" %>, <% this.getRequestAvg singular=" Request" plural=" Requests" %>/5min
+
+
+
+
diff --git a/Application/navig_disabled.skin b/Application/navig_disabled.skin
new file mode 100644
index 00000000..c49223d1
--- /dev/null
+++ b/Application/navig_disabled.skin
@@ -0,0 +1,10 @@
+
diff --git a/Application/redirectpublic.hac b/Application/redirectpublic.hac
new file mode 100644
index 00000000..7ead5169
--- /dev/null
+++ b/Application/redirectpublic.hac
@@ -0,0 +1,10 @@
+/**
+ * performs a redirect to the public site
+ * (workaround, we can't access application object from docapplication for some reason)
+ * @see application.url_macro
+ */
+
+if ( checkAddress()==false ) return;
+if ( checkAuth(this)==false ) return;
+
+res.redirect ( this.url_macro() );
diff --git a/DocApplication/functions.js b/DocApplication/functions.js
new file mode 100644
index 00000000..fbbd7872
--- /dev/null
+++ b/DocApplication/functions.js
@@ -0,0 +1,14 @@
+/**
+ * overrides the internal href-function, as
+ * helma.framework.core.Application.getNodeHref(Object,String)
+ * isn't able to compute correct urls for non-node objects.
+ * @arg action of application
+ */
+function href(action) {
+ var url = getProperty("baseURI");
+ url = (url==null || url=="null") ? "" : url;
+ url += this.name + "/api/" + ( (action!=null && action!="") ? action : "main" );
+ return url;
+}
+
+
diff --git a/DocApplication/head.skin b/DocApplication/head.skin
new file mode 100644
index 00000000..1bd8e6a2
--- /dev/null
+++ b/DocApplication/head.skin
@@ -0,0 +1,8 @@
+AppDoc <% this.name %>
+
+ ->
+ AppManager |
+ ">public
+
+
+
diff --git a/DocApplication/index.skin b/DocApplication/index.skin
new file mode 100644
index 00000000..51fc7746
--- /dev/null
+++ b/DocApplication/index.skin
@@ -0,0 +1,11 @@
+
+AppDoc <% this.name %>
+
+
+
+
+<% this.index skin="indexList" %>
+
+
+
+
diff --git a/DocApplication/macros.js b/DocApplication/macros.js
new file mode 100644
index 00000000..a18e1142
--- /dev/null
+++ b/DocApplication/macros.js
@@ -0,0 +1,74 @@
+/**
+ * macro rendering a skin
+ * @param name name of skin
+ */
+function skin_macro(par) {
+ if ( par && par.name ) {
+ this.renderSkin(par.name);
+ }
+}
+
+
+/**
+ * macro-wrapper for href-function
+ * @param action name of action to call on this prototype, default main
+ */
+function href_macro(par) {
+ return this.href( (par&&par.action)?par.action:"main" );
+}
+
+
+/**
+ * link to the "real" application object (ie not the DocApplication)
+ */
+function parentlink_macro(par) {
+ var url = getProperty("baseURI");
+ url = (url==null || url=="null") ? "" : url;
+ url += this.name + "/";
+ url += (par&&par.action)?par.action:"main";
+ return url;
+}
+
+
+/**
+ * list all prototypes of this application
+ * @param skin name of skin to render on prototype
+ */
+function prototypes_macro(par) {
+ var skin = (par && par.skin&&par.skin!="")?par.skin:"appList";
+ var arr = this.listPrototypes();
+ for ( var i=0; i
+
+
+
+ Prototypes |
+
+ <% this.prototypes skin="appList" %>
+
+
+
+
diff --git a/DocApplication/navig.skin b/DocApplication/navig.skin
new file mode 100644
index 00000000..f639e52a
--- /dev/null
+++ b/DocApplication/navig.skin
@@ -0,0 +1,14 @@
+<% this.name %>
+
+
+
+
+
+ Prototypes:
+ <% this.prototypes skin="navig" %>
+
+
diff --git a/DocFunction/functions.js b/DocFunction/functions.js
new file mode 100644
index 00000000..4fac6021
--- /dev/null
+++ b/DocFunction/functions.js
@@ -0,0 +1,20 @@
+
+/**
+ * overrides the internal href-function, as
+ * helma.framework.core.Application.getNodeHref(Object,String)
+ * isn't able to compute correct urls for non-node objects.
+ * @arg action of prototype
+ */
+function href(action) {
+ var url = getProperty("baseURI");
+ url = (url==null || url=="null") ? "" : url;
+ url += this.getParentElement().getParentElement().getName() + "/api/" + this.getParentElement().getName() + "/" + this.getTypeName().toLowerCase()+"_"+this.name + "/" + ( (action!=null && action!="") ? action : "main" );
+ return url;
+}
+
+
+function getApplication() {
+ return this.getParentElement().getParentElement();
+}
+
+
diff --git a/DocFunction/indexList.skin b/DocFunction/indexList.skin
new file mode 100644
index 00000000..3c0bc71f
--- /dev/null
+++ b/DocFunction/indexList.skin
@@ -0,0 +1,5 @@
+
+ <% this.name %> |
+ <% this.typename %> in <% this.prototypename %>
+ | ("><% this.prototypename %>/<% this.location %>) |
+
diff --git a/DocFunction/indexListSeparator.skin b/DocFunction/indexListSeparator.skin
new file mode 100644
index 00000000..f8d0edd0
--- /dev/null
+++ b/DocFunction/indexListSeparator.skin
@@ -0,0 +1,3 @@
+
+ <% param.letter %> |
+
diff --git a/DocFunction/listElementAction.skin b/DocFunction/listElementAction.skin
new file mode 100644
index 00000000..67c5da69
--- /dev/null
+++ b/DocFunction/listElementAction.skin
@@ -0,0 +1,7 @@
+
+
+ <% param.group %> |
+
+ "><% this.name %> <% this.comment %> <% this.tags %>
|
+
+
diff --git a/DocFunction/listElementFunction.skin b/DocFunction/listElementFunction.skin
new file mode 100644
index 00000000..b692f1ca
--- /dev/null
+++ b/DocFunction/listElementFunction.skin
@@ -0,0 +1,7 @@
+
+
+ <% param.group %> |
+
+ "><% this.name %>(<% this.args %>) in <% docprototype.name %>/<% this.location %><% this.comment %> <% this.tags %>
|
+
+
diff --git a/DocFunction/listElementMacro.skin b/DocFunction/listElementMacro.skin
new file mode 100644
index 00000000..52a10dbd
--- /dev/null
+++ b/DocFunction/listElementMacro.skin
@@ -0,0 +1,9 @@
+
+
+ <% param.group %> |
+
+ "><% docprototype.name %>.<% this.name %>
+ in <% docprototype.name %>/<% this.location %>
+ <% this.comment %> <% this.tags %>
|
+
+
diff --git a/DocFunction/listElementSkin.skin b/DocFunction/listElementSkin.skin
new file mode 100644
index 00000000..71185419
--- /dev/null
+++ b/DocFunction/listElementSkin.skin
@@ -0,0 +1,7 @@
+
+
+ <% param.group %> |
+
+ "><% docprototype.name %>.<% this.name %> <% this.comment %> <% this.tags %>
|
+
+
diff --git a/DocFunction/listElementTemplate.skin b/DocFunction/listElementTemplate.skin
new file mode 100644
index 00000000..81893dd8
--- /dev/null
+++ b/DocFunction/listElementTemplate.skin
@@ -0,0 +1,7 @@
+
+
+ <% param.group %> |
+
+ "><% this.name %>() <% this.comment %> <% this.tags %>
|
+
+
diff --git a/DocFunction/macros.js b/DocFunction/macros.js
new file mode 100644
index 00000000..2f58e1ce
--- /dev/null
+++ b/DocFunction/macros.js
@@ -0,0 +1,136 @@
+/**
+ * macro rendering a skin
+ * @param name name of skin
+ */
+function skin_macro(par) {
+ if ( par && par.name ) {
+ this.renderSkin(par.name);
+ }
+}
+
+
+/**
+ * macro-wrapper for href-function
+ * @param action name of action to call on this prototype, default main
+ */
+function href_macro(par) {
+ return this.href( (par&&par.action)?par.action:"main" );
+}
+
+
+/**
+ * macro returning name of file this method resides in
+ */
+function location_macro(par) {
+ var f = new File ( this.getLocation() );
+ return f.getName();
+}
+
+
+/**
+ * macro returning the type of this method (Action, Template, Skin, Macro, Function)
+ */
+function typename_macro(par) {
+ return this.getTypeName();
+}
+
+
+/**
+ * macro returning a link to the prototype page
+ * @param action name of action to call on this prototype, default main
+ */
+function prototypehref_macro(par) {
+ return this.getDocPrototype().href( (par&&par.action)?par.action:"main" )
+}
+
+
+/**
+ * macro returning the name of the prototype this method belongs to
+ */
+function prototypename_macro(par) {
+ return this.getDocPrototype().getName();
+}
+
+
+/**
+ * macro returning the comment text of this method
+ * (excluding the tags!)
+ * @param size (optional) text is cutoff after a number of chars
+ */
+function comment_macro(par) {
+ var str = this.getComment();
+ if ( par && par.length && str.length > par.size ) {
+ return ( str.substring(0,par.size) );
+ } else {
+ return ( str );
+ }
+}
+
+
+/**
+ * macro rendering the list of tags
+ */
+function tags_macro() {
+ var arr = this.listTags();
+ var argCt = 0;
+ for ( var i in arr ) {
+ if ( arr[i].getKind()==Packages.helma.doc.DocTag.ARG )
+ argCt++;
+ res.write( arr[i].render(argCt,this) );
+ }
+}
+
+
+/**
+ * macro rendering sequence of arg1, arg2 etc
+ * according to number of arguments in doctags.
+ */
+function args_macro() {
+ var ct = this.countTags(Packages.helma.doc.DocTag.ARG);
+ for ( var i=0; i<%';
+ }
+ var arr = str2.split("%>");
+ var str3 = "";
+ for ( var i=0; i';
+ }
+ var arr = str3.split("
");
+ var str4 = "";
+ for ( var i=0; i' + (i+1) + ': '
+ if ( i<100 ) str4+=' ';
+ str4 += arr[i] + "
";
+ }
+ return ( str4 );
+}
+
+
+/**
+ * macro returning the fullname of this method
+ */
+function fullname_macro(par) {
+ return this.getFullName();
+}
+
+
diff --git a/DocFunction/main.hac b/DocFunction/main.hac
new file mode 100644
index 00000000..ef413f29
--- /dev/null
+++ b/DocFunction/main.hac
@@ -0,0 +1,8 @@
+if ( checkAddress()==false ) return;
+if ( checkAuth(this)==false ) return;
+
+res.body = this.renderSkinAsString("main");
+res.skin = "api";
+res.title = "Application " + this.name;
+
+
diff --git a/DocFunction/main.skin b/DocFunction/main.skin
new file mode 100644
index 00000000..29a7bc8f
--- /dev/null
+++ b/DocFunction/main.skin
@@ -0,0 +1,14 @@
+
+
+
+
+
+ in <% docprototype.name%>/<% this.location %>:
+
+
+
+<% this.source %>
+
diff --git a/DocPrototype/appList.skin b/DocPrototype/appList.skin
new file mode 100644
index 00000000..e132ca63
--- /dev/null
+++ b/DocPrototype/appList.skin
@@ -0,0 +1,10 @@
+
+
+ Prototype
+
|
+ "><% this.name %>
+ <% this.comment %>
+
+ |
+
+
diff --git a/DocPrototype/functions.js b/DocPrototype/functions.js
new file mode 100644
index 00000000..053c0541
--- /dev/null
+++ b/DocPrototype/functions.js
@@ -0,0 +1,14 @@
+
+/**
+ * overrides the internal href-function, as
+ * helma.framework.core.Application.getNodeHref(Object,String)
+ * isn't able to compute correct urls for non-node objects.
+ * @arg action of prototype
+ */
+function href(action) {
+ var url = getProperty("baseURI");
+ url = (url==null || url=="null") ? "" : url;
+ url += this.getParentElement().getName() + "/api/" + this.name + "/" + ( (action!=null && action!="") ? action : "main" );
+ return url;
+}
+
diff --git a/DocPrototype/listFooter.skin b/DocPrototype/listFooter.skin
new file mode 100644
index 00000000..d3f6097b
--- /dev/null
+++ b/DocPrototype/listFooter.skin
@@ -0,0 +1,3 @@
+
+
+
diff --git a/DocPrototype/listHeader.skin b/DocPrototype/listHeader.skin
new file mode 100644
index 00000000..bbe57421
--- /dev/null
+++ b/DocPrototype/listHeader.skin
@@ -0,0 +1,7 @@
+
+
+
+
+
+ <% param.desc %> |
+
diff --git a/DocPrototype/macros.js b/DocPrototype/macros.js
new file mode 100644
index 00000000..3db36027
--- /dev/null
+++ b/DocPrototype/macros.js
@@ -0,0 +1,90 @@
+/**
+ * macro rendering a skin
+ * @param name name of skin
+ */
+function skin_macro(par) {
+ if ( par && par.name ) {
+ this.renderSkin(par.name);
+ }
+}
+
+
+/**
+ * macro-wrapper for href-function
+ * @param action name of action to call on this prototype, default main
+ */
+function href_macro(par) {
+ return this.href( (par&&par.action)?par.action:"main" );
+}
+
+
+/**
+ * macro returning the comment for this prototype
+ */
+function comment_macro(par) {
+ return this.getComment();
+}
+
+
+/**
+ * macro formatting list of actions of this prototype
+ */
+function actions_macro(par) {
+ this.printMethods( Packages.helma.doc.DocElement.ACTION, "listElementAction","Actions" );
+}
+
+
+/**
+ * macro formatting list of templates of this prototype
+ */
+function templates_macro(par) {
+ this.printMethods( Packages.helma.doc.DocElement.TEMPLATE, "listElementTemplate","Templates" );
+}
+
+
+/**
+ * macro formatting list of functions of this prototype
+ */
+function functions_macro(par) {
+ this.printMethods( Packages.helma.doc.DocElement.FUNCTION, "listElementFunction","Functions" );
+}
+
+
+/**
+ * macro formatting list of skins of this prototype
+ */
+function skins_macro(par) {
+ this.printMethods( Packages.helma.doc.DocElement.SKIN, "listElementSkin","Skins" );
+}
+
+
+/**
+ * macro formatting list of macros of this prototype
+ */
+function macros_macro(par) {
+ this.printMethods( Packages.helma.doc.DocElement.MACRO, "listElementMacro","Macros" );
+}
+
+
+/**
+ * macro-utility: renders a list of methods of this prototype
+ * usage of docprototype.listHeader/listFooter skin is hardcoded
+ * @arg type integer - which type of methods are listed
+ * @arg skin skin to be called on method
+ * @arg desc string describing the type of method (ie "Skins", "Actions")
+ */
+function printMethods(type,skin,desc) {
+ var arr = this.listFunctions(type);
+ if ( arr.length > 0 ) {
+ var obj = new Object();
+ obj.desc = desc;
+ this.renderSkin("listHeader",obj);
+ for ( var i in arr ) {
+ arr[i].renderSkin(skin,obj);
+ }
+ this.renderSkin("listFooter",obj);
+ }
+}
+
+
+
diff --git a/DocPrototype/main.hac b/DocPrototype/main.hac
new file mode 100644
index 00000000..ef413f29
--- /dev/null
+++ b/DocPrototype/main.hac
@@ -0,0 +1,8 @@
+if ( checkAddress()==false ) return;
+if ( checkAuth(this)==false ) return;
+
+res.body = this.renderSkinAsString("main");
+res.skin = "api";
+res.title = "Application " + this.name;
+
+
diff --git a/DocPrototype/main.skin b/DocPrototype/main.skin
new file mode 100644
index 00000000..71821440
--- /dev/null
+++ b/DocPrototype/main.skin
@@ -0,0 +1,9 @@
+
+Prototype <% this.name %>
+
+<% this.actions %>
+<% this.skins %>
+<% this.macros %>
+<% this.functions %>
+<% this.templates %>
+
diff --git a/DocPrototype/navig.skin b/DocPrototype/navig.skin
new file mode 100644
index 00000000..cc6f041f
--- /dev/null
+++ b/DocPrototype/navig.skin
@@ -0,0 +1,2 @@
+
+"><% this.name %>
diff --git a/DocTag/renderfunctions.js b/DocTag/renderfunctions.js
new file mode 100644
index 00000000..ede11b25
--- /dev/null
+++ b/DocTag/renderfunctions.js
@@ -0,0 +1,46 @@
+/**
+ * function renders list of tags, language is hardcoded here
+ * @arg number of current argument (for formatting arg1, arg2 etc)
+ * @arg method method-object from which we try to find other prototypes/methods
+ * when we're formatting a see tag
+ */
+function render(argCt,docFunc) {
+ var str = "";
+ if ( this.getKind() == this.ARG ) {
+ str = "arg" + argCt + ": " + format(this.text);
+ } else if ( this.getKind() == this.PARAM ) {
+ str = "Parameter " + this.name;
+ if ( this.text!=null && this.text!="" ) {
+ str += ": " + format(this.text);
+ }
+ } else if ( this.getKind() == this.RETURNS ) {
+ str = "Returns: " + format(this.text);
+ } else if ( this.getKind() == this.AUTHOR ) {
+ str = "by " + format(this.text) + "";
+ } else if ( this.getKind() == this.VERSION ) {
+ str = "Version " + format(this.text) + "";
+ } else if ( this.getKind() == this.RELEASE ) {
+ str = "since" + format(this.text) + "";
+ } else if ( this.getKind() == this.SEE ) {
+ if ( this.text.indexOf("http://")==0 ) {
+ str = '' + this.text + '';
+ } else {
+ var tmp = new java.lang.String(this.text);
+ tmp = tmp.trim();
+ var arr = tmp.split(".");
+ var obj = docFunc.getApplication().getDocPrototype(arr[0]);
+ if( arr.length>1 && obj.getFunction(arr[1])!=null ) {
+ str = 'See also: ' + format(tmp) + '';
+ } else if ( obj!=null ) {
+ str = 'See also: ' + format(tmp) + '';
+ }
+ }
+ if ( str=="" ) {
+ str = "See also: " + format(this.text);
+ }
+ }
+ return str + "
";
+}
+
+
+
diff --git a/Global/api.skin b/Global/api.skin
new file mode 100644
index 00000000..d152e04d
--- /dev/null
+++ b/Global/api.skin
@@ -0,0 +1,17 @@
+
+
+<% skin name="head" %>
+
+
+
+
+
+
+
diff --git a/Global/functions.js b/Global/functions.js
new file mode 100644
index 00000000..62858d0b
--- /dev/null
+++ b/Global/functions.js
@@ -0,0 +1,155 @@
+
+
+/**
+ * scheduler function, runs global.appStat every minute
+ */
+function scheduler() {
+ appStat();
+ return 60000;
+}
+
+
+/**
+ * initializes requestStat storage on startup
+ */
+function onStart() {
+ app.requestStat = new HopObject();
+ app.addressFilter = new Packages.helma.util.InetAddressFilter();
+ var str = root.getProperty("allowadmin");
+ if ( str!=null && str!="" ) {
+ var arr = str.split(",");
+ for ( var i in arr ) {
+ var str = new java.lang.String(arr[i]);
+ app.addressFilter.addAddress(str.trim());
+ }
+ }
+}
+
+
+/**
+ * updates the request stats in app.requestStat every 5 minutes
+ */
+function appStat() {
+ if ( app.requestStat==null ) {
+ app.requestStat = new HopObject();
+ }
+ if( (new Date()-300000) < app.requestStat.lastRun ) {
+ return;
+ }
+ var arr = root.getApplications();
+ for ( var i=0; ib.name )
+ return 1;
+ else if ( a.name==b.name )
+ return 0;
+ else
+ return -1;
+}
+
+
+/**
+ * utility function to sort property-arrays by key
+ */
+function sortProps(a,b) {
+ if ( a>b )
+ return 1;
+ else if ( a==b )
+ return 0;
+ else
+ return -1;
+}
+
+/**
+ * check access to an application or the whole server, authenticate against md5-encrypted
+ * properties of base-app or the particular application. if username or password aren't set
+ * go into stealth-mode and return a 404. if username|password are wrong, prepare response-
+ * object for http-auth and return false.
+ * @arg application-object
+ */
+function checkAuth(appObj) {
+ var ok = false;
+
+ // check against root
+ var rootUsername = root.getProperty("adminusername");
+ var rootPassword = root.getProperty("adminpassword");
+
+ if ( rootUsername==null || rootUsername=="" || rootPassword==null || rootPassword=="" )
+ return forceStealth();
+
+ var uname = req.getUsername();
+ var pwd = req.getPassword();
+
+ if ( uname==null || uname=="" || pwd==null || pwd=="" )
+ return forceAuth();
+
+ var md5password = calcMD5(uname);
+ var md5username = calcMD5(pwd);
+
+ if ( md5username==rootUsername && md5password==rootPassword )
+ return true;
+
+ if ( appObj!=null && appObj.isActive() ) {
+ // check against application
+ var appUsername = appObj.getProperty("adminusername");
+ var appPassword = appObj.getProperty("adminpassword");
+ if ( appUsername==null || appUsername=="" || appPassword==null || appPassword=="" )
+ return forceStealth();
+ if ( md5username==appUsername && md5password==appPassword )
+ return true;
+ }
+ return forceAuth();
+}
+
+
+/**
+ * check access to the base-app by ip-addresses
+ */
+function checkAddress() {
+ if ( !app.addressFilter.matches(java.net.InetAddress.getByName(req.data.http_remotehost)) )
+ return forceStealth();
+ else
+ return true;
+}
+
+
+/**
+ * response is reset to 401 / authorization required
+ */
+function forceAuth(appObj) {
+ res.status = 401;
+ res.realm = (appObj==null) ? "helma" : appObj.name;
+ res.reset();
+ res.write ("Authorization Required. The server could not verify that you are authorized to access the requested page.");
+ return false;
+}
+
+/**
+ * response is reset to 404 / notfound
+ */
+function forceStealth() {
+ res.reset();
+ res.status = 404;
+ return false;
+}
+
+
+
diff --git a/Global/global.skin b/Global/global.skin
new file mode 100644
index 00000000..a80b0da1
--- /dev/null
+++ b/Global/global.skin
@@ -0,0 +1,14 @@
+
+
+<% skin name="head" %>
+
+
+
+
+ <% skin name="navig" %> |
+ <% response.body %> |
+
+
+
+
+
diff --git a/Global/head.skin b/Global/head.skin
new file mode 100644
index 00000000..4f5659c0
--- /dev/null
+++ b/Global/head.skin
@@ -0,0 +1,53 @@
+
+ <% response.title %>
+
+
\ No newline at end of file
diff --git a/Global/macros.js b/Global/macros.js
new file mode 100644
index 00000000..5f0b72c5
--- /dev/null
+++ b/Global/macros.js
@@ -0,0 +1,97 @@
+/**
+ * macro rendering a skin
+ * @param name name of skin
+ */
+function skin_macro(par) {
+ if ( par && par.name ) {
+ renderSkin(par.name);
+ }
+}
+
+
+/**
+ * Macro returning the actual date and time.
+ */
+function now_macro() {
+ var date = new Date();
+ return(date.format("dd.MM.yyyy, HH:mm'h' zzz"));
+}
+
+
+/**
+ * macro-utility: formatting property lists
+ */
+function formatProperties(props,par) {
+ var prefix = (par && par.prefix) ? par.prefix : "";
+ var suffix = (par && par.suffix) ? par.suffix : "";
+ var separator = (par && par.separator) ? par.separator : "";
+ var e = props.keys();
+ var arr = new Array();
+ if ( e==null )
+ return "";
+ while ( e.hasMoreElements() ) {
+ arr[arr.length] = e.nextElement();
+ }
+ arr.sort(sortProps);
+ for ( var i in arr ) {
+ // don't print the admin-password
+ if ( arr[i].toLowerCase()=="adminusername" || arr[i].toLowerCase()=="adminpassword" ) continue;
+ res.write ( prefix + arr[i] + separator + props.getProperty(arr[i]) + suffix );
+ }
+}
+
+
+/**
+ * macro-utility: formatting an integer value as human readable bytes
+ */
+function formatBytes(bytes) {
+ if ( bytes > Math.pow(2,30) ) {
+ res.write( Math.round( 100*bytes/Math.pow(2,30) ) / 100 + "gb" );
+ } else if ( bytes > Math.pow(2,20) ) {
+ res.write( Math.round( 100*bytes/Math.pow(2,20) ) / 100 + "mb" );
+ } else {
+ res.write( Math.round( 100*bytes/Math.pow(2,10) ) / 100 + "kb" );
+ }
+}
+
+
+/**
+ * macro-utility: formatting time in millis as human readable age
+ */
+function formatAge(age) {
+ var str = "";
+ var days = Math.floor(age/86400);
+ var age = age - days * 86400;
+ var hours = Math.floor(age / 3600);
+ var age = age - hours * 3600;
+ var minutes = Math.floor(age / 60);
+ var seconds = Math.floor(age - minutes * 60);
+ if (days > 0)
+ str += (days + " days, ");
+ if (hours>0)
+ str += (hours + "h, ");
+ str += (minutes + "min");
+ if (days == 0) str += (", " + seconds + "sec");
+ return(str);
+}
+
+
+/**
+ * macro-utility: formatting a number-suffix by choosing between singular and plural
+ * @arg value number to be formatted
+ * @arg param-object object to get fields
+ * @param singular string used for value==1
+ * @param plural string used for value!=1
+ */
+function formatCount(ct, par) {
+ if ( !par || !par.singular || !par.plural ) {
+ return "";
+ }
+ if ( ct==1 )
+ return par.singular;
+ else
+ return par.plural;
+}
+
+
+
diff --git a/Global/md5.js b/Global/md5.js
new file mode 100644
index 00000000..3d31a709
--- /dev/null
+++ b/Global/md5.js
@@ -0,0 +1,178 @@
+/*
+ * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message
+ * Digest Algorithm, as defined in RFC 1321.
+ * Copyright (C) Paul Johnston 1999 - 2000.
+ * Updated by Greg Holt 2000 - 2001.
+ * See http://pajhome.org.uk/site/legal.html for details.
+ */
+
+
+/**
+ * Take a string and return the hex representation of its MD5.
+ * @arg string
+ */
+function calcMD5(str)
+{
+ x = str2blks_MD5(str);
+ a = 1732584193;
+ b = -271733879;
+ c = -1732584194;
+ d = 271733878;
+
+ for(i = 0; i < x.length; i += 16)
+ {
+ olda = a;
+ oldb = b;
+ oldc = c;
+ oldd = d;
+
+ a = ff(a, b, c, d, x[i+ 0], 7 , -680876936);
+ d = ff(d, a, b, c, x[i+ 1], 12, -389564586);
+ c = ff(c, d, a, b, x[i+ 2], 17, 606105819);
+ b = ff(b, c, d, a, x[i+ 3], 22, -1044525330);
+ a = ff(a, b, c, d, x[i+ 4], 7 , -176418897);
+ d = ff(d, a, b, c, x[i+ 5], 12, 1200080426);
+ c = ff(c, d, a, b, x[i+ 6], 17, -1473231341);
+ b = ff(b, c, d, a, x[i+ 7], 22, -45705983);
+ a = ff(a, b, c, d, x[i+ 8], 7 , 1770035416);
+ d = ff(d, a, b, c, x[i+ 9], 12, -1958414417);
+ c = ff(c, d, a, b, x[i+10], 17, -42063);
+ b = ff(b, c, d, a, x[i+11], 22, -1990404162);
+ a = ff(a, b, c, d, x[i+12], 7 , 1804603682);
+ d = ff(d, a, b, c, x[i+13], 12, -40341101);
+ c = ff(c, d, a, b, x[i+14], 17, -1502002290);
+ b = ff(b, c, d, a, x[i+15], 22, 1236535329);
+
+ a = gg(a, b, c, d, x[i+ 1], 5 , -165796510);
+ d = gg(d, a, b, c, x[i+ 6], 9 , -1069501632);
+ c = gg(c, d, a, b, x[i+11], 14, 643717713);
+ b = gg(b, c, d, a, x[i+ 0], 20, -373897302);
+ a = gg(a, b, c, d, x[i+ 5], 5 , -701558691);
+ d = gg(d, a, b, c, x[i+10], 9 , 38016083);
+ c = gg(c, d, a, b, x[i+15], 14, -660478335);
+ b = gg(b, c, d, a, x[i+ 4], 20, -405537848);
+ a = gg(a, b, c, d, x[i+ 9], 5 , 568446438);
+ d = gg(d, a, b, c, x[i+14], 9 , -1019803690);
+ c = gg(c, d, a, b, x[i+ 3], 14, -187363961);
+ b = gg(b, c, d, a, x[i+ 8], 20, 1163531501);
+ a = gg(a, b, c, d, x[i+13], 5 , -1444681467);
+ d = gg(d, a, b, c, x[i+ 2], 9 , -51403784);
+ c = gg(c, d, a, b, x[i+ 7], 14, 1735328473);
+ b = gg(b, c, d, a, x[i+12], 20, -1926607734);
+
+ a = hh(a, b, c, d, x[i+ 5], 4 , -378558);
+ d = hh(d, a, b, c, x[i+ 8], 11, -2022574463);
+ c = hh(c, d, a, b, x[i+11], 16, 1839030562);
+ b = hh(b, c, d, a, x[i+14], 23, -35309556);
+ a = hh(a, b, c, d, x[i+ 1], 4 , -1530992060);
+ d = hh(d, a, b, c, x[i+ 4], 11, 1272893353);
+ c = hh(c, d, a, b, x[i+ 7], 16, -155497632);
+ b = hh(b, c, d, a, x[i+10], 23, -1094730640);
+ a = hh(a, b, c, d, x[i+13], 4 , 681279174);
+ d = hh(d, a, b, c, x[i+ 0], 11, -358537222);
+ c = hh(c, d, a, b, x[i+ 3], 16, -722521979);
+ b = hh(b, c, d, a, x[i+ 6], 23, 76029189);
+ a = hh(a, b, c, d, x[i+ 9], 4 , -640364487);
+ d = hh(d, a, b, c, x[i+12], 11, -421815835);
+ c = hh(c, d, a, b, x[i+15], 16, 530742520);
+ b = hh(b, c, d, a, x[i+ 2], 23, -995338651);
+
+ a = ii(a, b, c, d, x[i+ 0], 6 , -198630844);
+ d = ii(d, a, b, c, x[i+ 7], 10, 1126891415);
+ c = ii(c, d, a, b, x[i+14], 15, -1416354905);
+ b = ii(b, c, d, a, x[i+ 5], 21, -57434055);
+ a = ii(a, b, c, d, x[i+12], 6 , 1700485571);
+ d = ii(d, a, b, c, x[i+ 3], 10, -1894986606);
+ c = ii(c, d, a, b, x[i+10], 15, -1051523);
+ b = ii(b, c, d, a, x[i+ 1], 21, -2054922799);
+ a = ii(a, b, c, d, x[i+ 8], 6 , 1873313359);
+ d = ii(d, a, b, c, x[i+15], 10, -30611744);
+ c = ii(c, d, a, b, x[i+ 6], 15, -1560198380);
+ b = ii(b, c, d, a, x[i+13], 21, 1309151649);
+ a = ii(a, b, c, d, x[i+ 4], 6 , -145523070);
+ d = ii(d, a, b, c, x[i+11], 10, -1120210379);
+ c = ii(c, d, a, b, x[i+ 2], 15, 718787259);
+ b = ii(b, c, d, a, x[i+ 9], 21, -343485551);
+
+ a = add(a, olda);
+ b = add(b, oldb);
+ c = add(c, oldc);
+ d = add(d, oldd);
+ }
+ return rhex(a) + rhex(b) + rhex(c) + rhex(d);
+}
+
+
+
+/*
+ * Convert a 32-bit number to a hex string with ls-byte first
+ */
+var hex_chr = "0123456789abcdef";
+function rhex(num)
+{
+ str = "";
+ for(j = 0; j <= 3; j++)
+ str += hex_chr.charAt((num >> (j * 8 + 4)) & 0x0F) +
+ hex_chr.charAt((num >> (j * 8)) & 0x0F);
+ return str;
+}
+
+/*
+ * Convert a string to a sequence of 16-word blocks, stored as an array.
+ * Append padding bits and the length, as described in the MD5 standard.
+ */
+function str2blks_MD5(str)
+{
+ nblk = ((str.length + 8) >> 6) + 1;
+ blks = new Array(nblk * 16);
+ for(i = 0; i < nblk * 16; i++) blks[i] = 0;
+ for(i = 0; i < str.length; i++)
+ blks[i >> 2] |= str.charCodeAt(i) << ((i % 4) * 8);
+ blks[i >> 2] |= 0x80 << ((i % 4) * 8);
+ blks[nblk * 16 - 2] = str.length * 8;
+ return blks;
+}
+
+/*
+ * Add integers, wrapping at 2^32. This uses 16-bit operations internally
+ * to work around bugs in some JS interpreters.
+ */
+function add(x, y)
+{
+ var lsw = (x & 0xFFFF) + (y & 0xFFFF);
+ var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
+ return (msw << 16) | (lsw & 0xFFFF);
+}
+
+/*
+ * Bitwise rotate a 32-bit number to the left
+ */
+function rol(num, cnt)
+{
+ return (num << cnt) | (num >>> (32 - cnt));
+}
+
+/*
+ * These functions implement the basic operation for each round of the
+ * algorithm.
+ */
+function cmn(q, a, b, x, s, t)
+{
+ return add(rol(add(add(a, q), add(x, t)), s), b);
+}
+function ff(a, b, c, d, x, s, t)
+{
+ return cmn((b & c) | ((~b) & d), a, b, x, s, t);
+}
+function gg(a, b, c, d, x, s, t)
+{
+ return cmn((b & d) | (c & (~d)), a, b, x, s, t);
+}
+function hh(a, b, c, d, x, s, t)
+{
+ return cmn(b ^ c ^ d, a, b, x, s, t);
+}
+function ii(a, b, c, d, x, s, t)
+{
+ return cmn(c ^ (b | (~d)), a, b, x, s, t);
+}
diff --git a/Global/navig.skin b/Global/navig.skin
new file mode 100644
index 00000000..87cc3ae5
--- /dev/null
+++ b/Global/navig.skin
@@ -0,0 +1,17 @@
+">
" title="helma" border="0" width="174" height="35" align="baseline" style="border-width:3px;border-color:white;">
+
+
+
+ <% root.appCount filter="active" singular=" app" plural=" apps"%> on <% root.hostname %>
+
+
+<% root.appList filter="active" %>
+
+
+
+
+ and <% root.appCount filter="disabled" %> disabled apps:
+
+
+<% root.appList filter="disabled" skin="navig_disabled" %>
+
diff --git a/Root/functions.js b/Root/functions.js
new file mode 100644
index 00000000..55a0baea
--- /dev/null
+++ b/Root/functions.js
@@ -0,0 +1,36 @@
+
+/**
+ * lists all applications in appdir.
+ * for active apps use this.getApplications() = helma.main.Server.getApplications()
+ */
+function getAllApplications() {
+ var appsDir = this.getAppsHome();
+ var dir = appsDir.list();
+ var arr = new Array();
+ for ( var i=0; i str.lastIndexOf('/') ) ? str.lastIndexOf('\\') : str.lastIndexOf('/');
+ res.write ( arr[i].substring(pos+1) + ".jar" );
+ if ( i < arr.length-1 ) res.write ( separator );
+ }
+}
+
+
+/**
+ * Macro that returns the name and version of the server's os
+ */
+function os_macro() {
+ return java.lang.System.getProperty("os.name") + " " + java.lang.System.getProperty("os.arch") + " " + java.lang.System.getProperty("os.version");
+}
+
+
+/**
+ * Macro that returns anything from server.properties
+ */
+function property_macro(par) {
+ if ( par && par.key ) {
+ return this.getProperty(key);
+ } else {
+ return "";
+ }
+}
+
+
+/**
+ * Macro formatting server.properties
+ */
+function properties_macro(par) {
+ formatProperties(this.getProperties(),par);
+}
+
+
+/**
+ * Macro that returns the timezone of this server
+ */
+function timezone_macro(par) {
+ return java.util.TimeZone.getDefault().getDisplayName(false, java.util.TimeZone.LONG) + " (" + java.util.TimeZone.getDefault().getID() + ")";
+}
+
+
diff --git a/Root/main.hac b/Root/main.hac
new file mode 100644
index 00000000..5cb4d0e3
--- /dev/null
+++ b/Root/main.hac
@@ -0,0 +1,46 @@
+
+/**
+ * main action, show server-stats
+ * perform start, stop, restart and flush-action
+ *
+ */
+
+
+if ( checkAddress()==false ) return;
+
+if ( req.data.app!=null && req.data.app!="" && req.data.action!=null && req.data.action!="" ) {
+
+ var appObj = root.getApp(req.data.app);
+ // check access for application. md5-encoded uname/pwd can also be put in
+ // app.properties to limit access to a single app
+ if ( checkAuth(appObj)==false ) return;
+
+ if ( req.data.action=="start" ) {
+ this.startApplication(req.data.app);
+ res.redirect ( appObj.href("main") );
+
+ } else if ( req.data.action=="stop" ) {
+ if ( checkAuth()==false ) return;
+ this.stopApplication(req.data.app);
+ res.redirect ( root.href("main") );
+
+ } else if ( req.data.action=="restart" ) {
+ this.stopApplication(req.data.app);
+ this.startApplication(req.data.app);
+ res.redirect ( appObj.href("main") );
+
+ } else if ( req.data.action=="flush" ) {
+ appObj.clearAppCache();
+ res.redirect ( appObj.href("main") );
+
+ }
+
+}
+
+// output only to root
+if ( checkAuth()==false ) return;
+
+res.skin = "global";
+res.title = "Helma Object Publisher - Serverinfo";
+res.body = this.renderSkinAsString("main");
+
diff --git a/Root/main.skin b/Root/main.skin
new file mode 100644
index 00000000..f3e7910b
--- /dev/null
+++ b/Root/main.skin
@@ -0,0 +1,101 @@
+<% this.hostname %> (<% this.hostaddress %>)
+
+
+
+
+ helma |
+
+
+
+ uptime: |
+ |
+ <% this.uptime %> |
+
+
+
+ version: |
+ |
+ <% this.version %> |
+
+
+
+ homedir: |
+ |
+ <% this.home %> |
+
+
+
+ total active sessions |
+ |
+ <% this.countSessions %> |
+
+
+ total request 5-min-avg |
+ |
+ <% this.countRequests %> |
+
+
+
+ jre |
+
+
+
+ free memory: |
+ |
+ <% this.jvmFreeMemory %> |
+
+
+
+
+ used memory: |
+ |
+ <% this.jvmUsedMemory %> |
+
+
+
+ total memory: |
+ |
+ <% this.jvmTotalMemory %> |
+
+
+
+ java: |
+ |
+ <% this.jvm %> |
+
+
+ javahome: |
+ |
+ <% this.jvmHome %> |
+
+
+ os: |
+ |
+ <% this.os %> |
+
+
+ localtime: |
+ |
+ <% now %> |
+
+
+ timezone: |
+ |
+ <% this.timezone %> |
+
+
+ loaded Jars: |
+ |
+ <% this.jvmJars %> |
+
+
+
+ server.properties |
+
+
+<% this.properties prefix="" separator=" | | " suffix=" |
" %>
+
+
+
+
+
diff --git a/Root/makekey.hac b/Root/makekey.hac
new file mode 100644
index 00000000..cadf28c0
--- /dev/null
+++ b/Root/makekey.hac
@@ -0,0 +1,10 @@
+
+if ( checkAddress()==false ) return;
+if ( checkAuth()==false ) return;
+
+if ( req.data.value!=null ) {
+ res.write ( calcMD5(req.data.value) );
+} else {
+ res.write ( '' );
+}
+
diff --git a/app.properties b/app.properties
new file mode 100644
index 00000000..544f3805
--- /dev/null
+++ b/app.properties
@@ -0,0 +1,5 @@
+# Set this property according to your
+# server configuration:
+
+baseURI = /manage/
+
diff --git a/class.properties b/class.properties
new file mode 100644
index 00000000..181c3887
--- /dev/null
+++ b/class.properties
@@ -0,0 +1,11 @@
+#
+# Map Java classes to the prototype used to script them
+#
+
+helma.main.Server = root
+helma.framework.core.Application = application
+helma.doc.DocApplication = docapplication
+helma.doc.DocPrototype = docprototype
+helma.doc.DocFunction = docfunction
+helma.doc.DocTag = doctag
+