diff --git a/ccm-core/src/com/arsdigita/bebop/Component.java b/ccm-core/src/com/arsdigita/bebop/Component.java index 834e56303..964e9107f 100755 --- a/ccm-core/src/com/arsdigita/bebop/Component.java +++ b/ccm-core/src/com/arsdigita/bebop/Component.java @@ -24,9 +24,10 @@ import com.arsdigita.util.Lockable; import com.arsdigita.xml.Element; /** - * The common interface implemented by all Bebop - * components. During its lifetime, a component receives the following - * calls from the containing page. + * The common interface implemented by all Bebop components. + * + * During its lifetime, a component receives the following calls + * from the containing page. * *
The top-level container for all Bebop components and - * containers.
+ *The top-level container for all Bebop components and containers.
+ * *setStyleSheet("style.css", "text/css").
-// * -// * These values will ultimately wind up in a <link> tag in -// * the head of the HTML page. -// *
-// * Note that the stylesheet set with this call has nothing to do with the -// * XSLT stylesheet (transformer) that is applied to the XML generated -// * from this page. -// * -// * @param styleSheetURI the location of the stylesheet -// * @param styleSheetType the MIME type of the stylesheet, usually -// * text/css -// * @pre ! isLocked() -// * @deprecated Use {@link #addClientStylesheet addClientStylesheet} -// * instead. Will be removed on 2001-05-31. -// */ -// public void setStyleSheet(String styleSheetURI, String styleSheetType) { -// Assert.isUnlocked(this); -// addClientStylesheet(styleSheetURI, styleSheetType); -// } /** * Adds a client-side stylesheet that should be used in HTML * output. Arbitrarily many client-side stylesheets can be added with @@ -532,7 +511,7 @@ public class Page extends BlockStylable implements Container { * *
Note that the stylesheet set with this call has nothing to do with * the XSLT stylesheet (transformer) that is applied to the XML generated - * from this page. + * from this page! * * @param styleSheetURI the location of the stylesheet * @param mimeType the MIME type of the stylesheet, usually diff --git a/ccm-core/src/com/arsdigita/bebop/page/BebopApplicationServlet.java b/ccm-core/src/com/arsdigita/bebop/page/BebopApplicationServlet.java index 6f060157d..e69ba8e62 100755 --- a/ccm-core/src/com/arsdigita/bebop/page/BebopApplicationServlet.java +++ b/ccm-core/src/com/arsdigita/bebop/page/BebopApplicationServlet.java @@ -42,6 +42,20 @@ import javax.servlet.http.HttpServletResponse; import org.apache.log4j.Logger; /** + * A common servlet to provide a generic URL-to-Bebop-Page map based dispatch + * pattern. + * It provides methods to setup a url - page map. The doService method uses + * the request's URL to determine the page to display and forwards to the + * presentation manager for the Page handling. + * + * This class is a servlet based version of BebopMapDispatcher and associated + * classes and is generally used in the same way by legacy free applications. + * + * Subclasses usually overwrite the doService method to create Page objects + * and use this.put method to construct the mapping. + * + * Subclasses may overwrite the doService method to add additional functionality, + * e.g. permission checking. * * @author Justin Ross <jross@redhat.com> * @author chris gilbert - allow BebopApplicationServlet pages to disable @@ -53,12 +67,18 @@ public class BebopApplicationServlet extends BaseApplicationServlet { private static final Logger s_log = Logger.getLogger (BebopApplicationServlet.class); - // String pathInfo => Page page + /** URL (pathinfo) -> Page object mapping. Based on it (and the http + * request url) the doService method to selects a page to display */ private final Map m_pages = new HashMap(); // Set of pathinfo private final Set m_clientCacheDisabledPages = new HashSet(); /** + * Initializer uses parent class's initializer to setup the servlet request / + * response and application context. Usually a user of this class will NOT + * overwrite this method but the user extension point doInit() to perform + * local initialization tasks, in case of this servlet typically to setup + * the page-url mapping using the provided mapping methods of this class. * * @throws ServletException */ @@ -68,15 +88,29 @@ public class BebopApplicationServlet extends BaseApplicationServlet { } /** + * User extension point, overwrite this method to setup a URL - page mapping * - * @param pathInfo - * @param page + * @throws ServletException + */ + @Override + public void doInit() throws ServletException { + // nothing here + } + + /** + * Adds one Url-Page mapping to the internal mapping table. + * + * @param pathInfo url stub for a page to display + * @param page Page object to display */ protected final void put(final String pathInfo, final Page page) { Assert.exists(pathInfo, String.class); Assert.exists(page, Page.class); - Assert.isTrue(pathInfo.startsWith("/"), "path starts with '/'"); + // Current Implementation requires pathInfo to start with a leading '/' + // SUN Servlet API specifies: "PathInfo *may be empty* or will start + // with a '/' character." + Assert.isTrue(pathInfo.startsWith("/"), "path starts not with '/'"); m_pages.put(pathInfo, page); } @@ -88,12 +122,15 @@ public class BebopApplicationServlet extends BaseApplicationServlet { */ protected final void disableClientCaching(String pathInfo) { Assert.exists(pathInfo, String.class); - Assert.isTrue(m_pages.containsKey(pathInfo), + Assert.isTrue(m_pages.containsKey(pathInfo), "Page " + pathInfo + " has not been put in servlet"); - m_clientCacheDisabledPages.add(pathInfo); + m_clientCacheDisabledPages.add(pathInfo); } /** + * Main processing unit searches in the page map for the request's url + * and forwards the page to display to the appropriate presentation + * manager to serve the page. * * @param sreq * @param sresp diff --git a/ccm-core/src/com/arsdigita/bebop/page/BebopMapDispatcher.java b/ccm-core/src/com/arsdigita/bebop/page/BebopMapDispatcher.java index 815beefb5..a281d362e 100755 --- a/ccm-core/src/com/arsdigita/bebop/page/BebopMapDispatcher.java +++ b/ccm-core/src/com/arsdigita/bebop/page/BebopMapDispatcher.java @@ -31,7 +31,13 @@ import org.apache.log4j.Logger; import org.xml.sax.helpers.DefaultHandler; /** - * Common base class for a generic URL-to-Bebop-Page dispatching pattern. + * Common base class for a generic URL-to-Bebop-Page dispatching pattern. This + * class provides methods to setup a url - page map. URL is used as a key to a + * dispatcher type class whose dispatch() method will provide the Page handling. + * + * Dispatching is done by the parent class on a general url - dispatcher class + * mapping. + * * This class may be used directly by applications, or it may be subclassed * to be coded with a specific map or to override the map lookup for certain * sets of URLs. @@ -53,8 +59,8 @@ public class BebopMapDispatcher extends MapDispatcher { public BebopMapDispatcher() { super(); - //mount the confirmation page Map m = new HashMap(); + //mount the confirmation page m.put(ConfirmPage.CONFIRM_URL, new ConfirmPage()); setMap(m); } diff --git a/ccm-core/src/com/arsdigita/dispatcher/MapDispatcher.java b/ccm-core/src/com/arsdigita/dispatcher/MapDispatcher.java index 33510c039..c5c20c309 100755 --- a/ccm-core/src/com/arsdigita/dispatcher/MapDispatcher.java +++ b/ccm-core/src/com/arsdigita/dispatcher/MapDispatcher.java @@ -34,16 +34,14 @@ import org.xml.sax.SAXException; import org.xml.sax.helpers.DefaultHandler; /** - * Base class for - * a generic URL-to-Dispatcher mapping dispatch. This class may - * be used directly by applications, or it may be subclassed to - * be coded with a specific - * map or override the map lookup for certain sets of URLs. + * Base class for a generic URL-to-Dispatcher mapping dispatch. This class may + * be used directly by applications, or it may be subclassed to be coded with + * a specific map or override the map lookup for certain sets of URLs. *
* URLs are mapped to Dispatcher objects. When the dispatch method is called, - * this first looks up the remainingURL (from the request context) in the map and - * dispatches to the target object. Then we call - * the dispatch method on the target. + * this first looks up the remainingURL (from the request context) in the map + * and dispatches to the target object. Then we call the dispatch method on + * the target. * *
A configurable "not found" dispatcher is available to handle the case * where no target is mapped for a URL. By default, this is set to @@ -55,9 +53,9 @@ import org.xml.sax.helpers.DefaultHandler; * exact mapping. If a not-found handler is specified, it is up to this * specified dispatcher to set the status code appropriately. * - *
Note that any URLs that do not have a file extension will - * automatically be required to have a trailing slash. Your relative - * links must compensate for this. + *
Note that any URLs that do not have a file extension will automatically + * be required to have a trailing slash. Your relative links MUST COMPENSATE + * for this. * *
Example: Assume we set up a map dispatcher as follows: *
@@ -100,8 +98,9 @@ public class MapDispatcher implements Dispatcher {
private static final Logger s_log =
Logger.getLogger(MapDispatcher.class.getName());
- /* Creates MapDispatcher with empty URL mapped to "/".
- **/
+ /**
+ * Constructor creates MapDispatcher with empty URL mapped to "/".
+ */
public MapDispatcher() {
m_notFoundHandler = JSPApplicationDispatcher.getInstance();
}
diff --git a/ccm-core/src/com/arsdigita/loader/CoreLoader.java b/ccm-core/src/com/arsdigita/loader/CoreLoader.java
index 989625e74..e1b743a1a 100755
--- a/ccm-core/src/com/arsdigita/loader/CoreLoader.java
+++ b/ccm-core/src/com/arsdigita/loader/CoreLoader.java
@@ -454,36 +454,35 @@ public class CoreLoader extends PackageLoader {
private void loadWebDev() {
// Add the package type to the database
-/*
- PackageType packType = PackageType.create
- ("webdev-support", "WebDeveloper Support", "WebDeveloper Supports",
- "http://arsdigita.com/webdev-support");
- // Add the node and the package instance on that node.
- SiteNode node = SiteNode.createSiteNode("ds");
- // Specify the URL stub for this package instance.
- node.mountPackage(packType.createInstance("webdev-support"));
-
- // Map the package type to a dispatcher class
- packType.setDispatcherClass("com.arsdigita.webdevsupport.Dispatcher");
-*/
+ /* LEGACY COMPATIBLE application type */
ApplicationType webDevType = ApplicationType
.createApplicationType("webdev-support",
"WebDeveloper Support",
WebDevSupport.BASE_DATA_OBJECT_TYPE);
webDevType.setDispatcherClass("com.arsdigita.webdevsupport.Dispatcher");
+
+ /* LEGACY FREE application type */
+// ApplicationType webDevType =
+// new ApplicationType("WebDev Support",
+// WebDevSupport.BASE_DATA_OBJECT_TYPE );
+
+
+ /* Legacy free / compatible INDEPENDENT application properties */
webDevType.setDescription("WebDeveloper Support application");
webDevType.save();
+
+ // create application instance as a legacy free or legacy comp. app.
+ // Whether a legacy compatible or a legacy free application is
+ // created depends on the type of ApplicationType above. No need to
+ // modify anything here
Application webDev = Application.createApplication(webDevType,
"ds",
"WebDeveloper Support",
null);
webDev.setDescription("The default WEB developer service instance.");
webDev.save();
-
-
-
}
diff --git a/ccm-core/src/com/arsdigita/web/BaseServlet.java b/ccm-core/src/com/arsdigita/web/BaseServlet.java
index 1528b5cc7..3aa3ccaa7 100755
--- a/ccm-core/src/com/arsdigita/web/BaseServlet.java
+++ b/ccm-core/src/com/arsdigita/web/BaseServlet.java
@@ -63,13 +63,17 @@ public abstract class BaseServlet extends HttpServlet {
private static Logger s_log = Logger.getLogger(BaseServlet.class);
- /**
- * The name of the request attribute used to store the originally
- * requested URL.
- */
+ /** The name of the request attribute used to store the originally
+ * requested URL. */
public static final String REQUEST_URL_ATTRIBUTE =
- BaseServlet.class.getName() + ".request_url";
+ BaseServlet.class.getName() + ".request_url";
+ /**
+ * Initializer uses parent class's initializer to setup the servlet request /
+ * response and application context. Usually a user of this class will not
+ * overwrite this method but the user extension point doInit to perform
+ * local initialization tasks.
+ */
@Override
public void init(final ServletConfig sconfig) throws ServletException {
if (s_log.isInfoEnabled()) {
diff --git a/ccm-core/src/com/arsdigita/webdevsupport/Dispatcher.java b/ccm-core/src/com/arsdigita/webdevsupport/Dispatcher.java
index 30df7ae0e..9c448a961 100755
--- a/ccm-core/src/com/arsdigita/webdevsupport/Dispatcher.java
+++ b/ccm-core/src/com/arsdigita/webdevsupport/Dispatcher.java
@@ -64,15 +64,18 @@ import com.arsdigita.web.URL;
import com.arsdigita.webdevsupport.log4j.CategoryPanel;
import com.arsdigita.webdevsupport.config.ConfigList;
import com.arsdigita.xml.Element;
+
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.ListIterator;
import java.util.Map;
+
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
+
import org.apache.log4j.Logger;
/**
@@ -83,22 +86,23 @@ import org.apache.log4j.Logger;
*
* @author Joseph A. Bank (jbank@alum.mit.edu)
* @version 1.0
- **/
+ * @version $Id: Dispatcher.java 1460 2007-03-02 14:36:38Z sskracic $
+ */
public class Dispatcher extends BebopMapDispatcher {
+
private static final Logger s_log =
- Logger.getLogger(Dispatcher.class.getName());
+ Logger.getLogger(Dispatcher.class.getName());
public static final String APP_NAME = "ds";
- public static final String versionId =
- "$Id: Dispatcher.java 1460 2007-03-02 14:36:38Z sskracic $" +
- " by $Author: sskracic $, " +
- "$DateTime: 2004/08/16 18:10:38 $";
-
private static boolean s_showDSPages = false;
private final static String CACHE_TABLE_URL = "cache-table";
+ /**
+ * Constructor builds the various Developer Support Pages and creates the
+ * corresponding URL - Page mapping.
+ */
public Dispatcher() {
super();
Map m = new HashMap();
@@ -115,20 +119,35 @@ public class Dispatcher extends BebopMapDispatcher {
m.put("config", buildConfigPage());
m.put(CACHE_TABLE_URL, buildCacheTablePage());
- /** The rest of the URLs go here with more
+ /* The rest of the URLs go here with more
* m_put statements.
*/
+
+ /* Make the created map operational */
setMap(m);
}
+ /**
+ * Overwrites parent's dispatch method to add permission checking. If
+ * required permissions are granted dispatching is delegatged to parent.
+ * @param req
+ * @param resp
+ * @param ctx
+ * @throws IOException
+ * @throws ServletException
+ */
+ @Override
public void dispatch(HttpServletRequest req,
HttpServletResponse resp,
RequestContext ctx)
- throws IOException, ServletException {
+ throws IOException, ServletException {
+
+ /* Determine access priviledge, only logged in users may access DS */
Party party = Kernel.getContext().getParty();
if (party == null) {
throw new LoginSignal(req);
}
+
SiteNode node = ((SiteNodeRequestContext)ctx).getSiteNode();
PermissionDescriptor admin = new PermissionDescriptor
(PrivilegeDescriptor.ADMIN, node, party);
@@ -144,6 +163,10 @@ public class Dispatcher extends BebopMapDispatcher {
}
+ /**
+ *
+ * @return index Page object
+ */
private Page buildIndexPage() {
Page p = PageFactory.buildPage(APP_NAME, "Web Developer Support");
@@ -154,11 +177,13 @@ public class Dispatcher extends BebopMapDispatcher {
links.add(new Link("Cache Table Browser", CACHE_TABLE_URL));
ActionLink enable = new ActionLink("Enable request logging") {
- public boolean isVisible(PageState state) {
- return !DeveloperSupport.containsListener(WebDevSupportListener.getInstance())
+ @Override
+ public boolean isVisible(PageState state) {
+ return !DeveloperSupport.containsListener(
+ WebDevSupportListener.getInstance())
&& super.isVisible(state);
- }
- };
+ }
+ };
enable.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
DeveloperSupport.addListener(WebDevSupportListener.getInstance());
@@ -171,11 +196,14 @@ public class Dispatcher extends BebopMapDispatcher {
ActionLink disable = new ActionLink("Disable request logging") {
- public boolean isVisible(PageState state) {
- return DeveloperSupport.containsListener(WebDevSupportListener.getInstance())
- && super.isVisible(state);
- }
- };
+ @Override
+ public boolean isVisible(PageState state) {
+ return DeveloperSupport.containsListener(
+ WebDevSupportListener.getInstance())
+ && super.isVisible(state);
+ }
+ };
+
disable.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
DeveloperSupport.removeListener(WebDevSupportListener.getInstance());
@@ -225,6 +253,10 @@ public class Dispatcher extends BebopMapDispatcher {
return p;
}
+ /**
+ *
+ * @return log4j Page object
+ */
private Page buildLog4jPage() {
Page p = PageFactory.buildPage(APP_NAME, "Log4j Logger Adjuster");
p.add(new CategoryPanel());
@@ -232,6 +264,10 @@ public class Dispatcher extends BebopMapDispatcher {
return p;
}
+ /**
+ *
+ * @return config Page object
+ */
private Page buildConfigPage() {
Page p = PageFactory.buildPage(APP_NAME, "Registry Config");
p.add(new ConfigList());
@@ -247,6 +283,11 @@ public class Dispatcher extends BebopMapDispatcher {
}
private ParameterModel m_request_id = new IntegerParameter("request_id");
+
+ /**
+ *
+ * @return info Page object
+ */
private Page buildInfoPage() {
Page p = PageFactory.buildPage(APP_NAME, "Request Information");
p.addGlobalStateParam(m_request_id);
@@ -258,6 +299,12 @@ public class Dispatcher extends BebopMapDispatcher {
private ParameterModel m_query_id = new IntegerParameter("query_id");
private ParameterModel m_query_request_id = new IntegerParameter("request_id");
+
+ /**
+ *
+ *
+ * @return queryInfo Page object
+ */
private Page buildQueryInfoPage() {
Page p = PageFactory.buildPage(APP_NAME, "Query Information");
p.addGlobalStateParam(m_query_request_id);
@@ -268,11 +315,12 @@ public class Dispatcher extends BebopMapDispatcher {
}
private RequestLocal m_scoreboard = new RequestLocal() {
- protected Object initialValue(PageState state) {
- // queryInfo.getID() => {textRepeats, queryRepeats}
- return new HashMap();
- }
- };
+ @Override
+ protected Object initialValue(PageState state) {
+ // queryInfo.getID() => {textRepeats, queryRepeats}
+ return new HashMap();
+ }
+ };
// Used below in makeDatabaseRequestComponent to generate a query
// scoreboard.
@@ -561,10 +609,24 @@ public class Dispatcher extends BebopMapDispatcher {
return sb.toString();
}
+ /**
+ *
+ */
class RequestInfoComponent extends com.arsdigita.bebop.SimpleComponent {
+
+ /**
+ * Constructor
+ */
public RequestInfoComponent() {
super();
}
+
+ /**
+ *
+ * @param state
+ * @param parent
+ */
+ @Override
public void generateXML(PageState state, Element parent) {
Integer request_id = (Integer)state.getValue(m_request_id);
RequestInfo ri =
@@ -671,10 +733,24 @@ public class Dispatcher extends BebopMapDispatcher {
}
}
+ /**
+ * Class
+ */
class QueryInfoComponent extends com.arsdigita.bebop.SimpleComponent {
+
+ /**
+ *
+ */
public QueryInfoComponent() {
super();
}
+
+ /**
+ *
+ * @param state
+ * @param parent
+ */
+ @Override
public void generateXML(PageState state, Element parent) {
Integer request_id = (Integer)state.getValue(m_query_request_id);
Integer query_id = (Integer)state.getValue(m_query_id);
diff --git a/ccm-core/src/com/arsdigita/webdevsupport/QueryLog.java b/ccm-core/src/com/arsdigita/webdevsupport/QueryLog.java
index c5fb6b5be..b6fd1c29c 100755
--- a/ccm-core/src/com/arsdigita/webdevsupport/QueryLog.java
+++ b/ccm-core/src/com/arsdigita/webdevsupport/QueryLog.java
@@ -53,7 +53,7 @@ import com.arsdigita.bebop.parameters.IntegerParameter;
*
* @author Daniel Berrange (berrange@redhat.com)
* @version 1.0
- **/
+ */
public class QueryLog implements com.arsdigita.dispatcher.Dispatcher {
private static final Logger s_log =
Logger.getLogger(QueryLog.class.getName());
diff --git a/ccm-core/src/com/arsdigita/webdevsupport/WebDevSupport.java b/ccm-core/src/com/arsdigita/webdevsupport/WebDevSupport.java
index 98ce825b2..101d794b2 100755
--- a/ccm-core/src/com/arsdigita/webdevsupport/WebDevSupport.java
+++ b/ccm-core/src/com/arsdigita/webdevsupport/WebDevSupport.java
@@ -46,7 +46,7 @@ public class WebDevSupport extends Application {
public static final String BASE_DATA_OBJECT_TYPE =
"com.arsdigita.webdevsupport.WebDevSupport";
- public WebDevSupport(DataObject obj) {
+ public WebDevSupport(final DataObject obj) {
super(obj);
}
@@ -66,5 +66,10 @@ public class WebDevSupport extends Application {
return BASE_DATA_OBJECT_TYPE;
}
+ @Override
+ public String getServletPath() {
+ return "/webdevsupport";
+ }
+
}
diff --git a/ccm-core/src/com/arsdigita/webdevsupport/WebDevSupportServlet.java b/ccm-core/src/com/arsdigita/webdevsupport/WebDevSupportServlet.java
new file mode 100644
index 000000000..9ea7b9ee9
--- /dev/null
+++ b/ccm-core/src/com/arsdigita/webdevsupport/WebDevSupportServlet.java
@@ -0,0 +1,876 @@
+/*
+ * Copyright (C) 2012 Peter Boy All Rights Reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+package com.arsdigita.webdevsupport;
+
+import com.arsdigita.bebop.ActionLink;
+import com.arsdigita.bebop.BoxPanel;
+import com.arsdigita.bebop.ColumnPanel;
+import com.arsdigita.bebop.Component;
+import com.arsdigita.bebop.Container;
+import com.arsdigita.bebop.ControlLink;
+import com.arsdigita.bebop.Label;
+import com.arsdigita.bebop.Link;
+import com.arsdigita.bebop.ListPanel;
+import com.arsdigita.bebop.Page;
+import com.arsdigita.bebop.PageFactory;
+import com.arsdigita.bebop.PageState;
+import com.arsdigita.bebop.RequestLocal;
+import com.arsdigita.bebop.Table;
+import com.arsdigita.bebop.event.ActionEvent;
+import com.arsdigita.bebop.event.ActionListener;
+import com.arsdigita.bebop.event.TableActionAdapter;
+import com.arsdigita.bebop.event.TableActionEvent;
+import com.arsdigita.bebop.parameters.IntegerParameter;
+import com.arsdigita.bebop.parameters.ParameterModel;
+import com.arsdigita.bebop.table.AbstractTableModelBuilder;
+import com.arsdigita.bebop.table.DefaultTableCellRenderer;
+import com.arsdigita.bebop.table.TableModel;
+import com.arsdigita.bebop.table.TableModelBuilder;
+import com.arsdigita.developersupport.DeveloperSupport;
+import com.arsdigita.dispatcher.AccessDeniedException;
+import com.arsdigita.dispatcher.DispatcherHelper;
+import com.arsdigita.kernel.Kernel;
+import com.arsdigita.kernel.Party;
+import com.arsdigita.kernel.permissions.PermissionDescriptor;
+import com.arsdigita.kernel.permissions.PermissionService;
+import com.arsdigita.kernel.permissions.PrivilegeDescriptor;
+import com.arsdigita.templating.PresentationManager;
+import com.arsdigita.templating.Templating;
+import com.arsdigita.util.Assert;
+import com.arsdigita.util.StringUtils;
+import com.arsdigita.web.Application;
+import com.arsdigita.web.BaseApplicationServlet;
+import com.arsdigita.web.LoginSignal;
+import com.arsdigita.web.ParameterMap;
+import com.arsdigita.web.RedirectSignal;
+import com.arsdigita.web.URL;
+import com.arsdigita.webdevsupport.config.ConfigList;
+import com.arsdigita.webdevsupport.log4j.CategoryPanel;
+import com.arsdigita.xml.Document;
+import com.arsdigita.xml.Element;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.ListIterator;
+import java.util.Map;
+
+import javax.servlet.ServletException;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.apache.log4j.Logger;
+
+/**
+ * Web Developer Support Application Servlet class, central entry point to
+ * create and process the applications UI.
+ *
+ * We should have subclassed BebopApplicationServlet but couldn't overwrite
+ * doService() method to add permission checking. So we use our own page
+ * mapping. The general logic is the same as for BebopApplicationServlet.
+ * {@see com.arsdigita.bebop.page.BebopApplicationServlet}
+ *
+ * @author pb
+ */
+public class WebDevSupportServlet extends BaseApplicationServlet {
+
+ private static final Logger s_log =
+ Logger.getLogger(Dispatcher.class.getName());
+
+ public static final String APP_NAME = "ds";
+
+ private static boolean s_showDSPages = false;
+ /** URL (pathinfo) -> Page object mapping. Based on it (and the http
+ * request url) the doService method to selects a page to display */
+ private final Map m_pages = new HashMap();
+
+
+ /**
+ * User extension point, overwrite this method to setup a URL - page mapping
+ *
+ * @throws ServletException
+ */
+ @Override
+ public void doInit() throws ServletException {
+
+ addPage("/", buildIndexPage()); // index page at address ~/ds
+ // addPage("/index.jsp", buildIndexPage()); // index page at address ~/ds
+
+ addPage("/log4j", buildLog4jPage()); // Logger Adjuster at addr. ~/ds/log4j
+ addPage("/config", buildConfigPage()); // config browser @ ~/ds/config
+ // cache table browser @ ~/ds/cache-table
+ addPage("/cache-table", buildCacheTablePage());
+
+// XXXX!!
+// QueryLog is a class of its own in webdevsupport, based upon dispatcher.Disp
+// and prints out all queries in a request
+// put("query-log", new QueryLog());
+
+ addPage("/request-info", buildInfoPage());
+ addPage("/query-info", buildQueryInfoPage());
+ addPage("/query-plan", buildQueryPlanPage());
+
+ }
+
+
+ /**
+ * Central service method, checks for required permission, determines the
+ * requested page and passes the page object to PresentationManager.
+ */
+ public final void doService(HttpServletRequest sreq,
+ HttpServletResponse sresp,
+ Application app)
+ throws ServletException, IOException {
+
+ // /////// Some preparational steps ///////////////
+
+ /* Determine access privilege: only logged in users may access DS */
+ Party party = Kernel.getContext().getParty();
+ if (party == null) {
+ throw new LoginSignal(sreq);
+ }
+ /* Determine access privilege: Admin privileges must be granted */
+ PermissionDescriptor admin = new PermissionDescriptor
+ (PrivilegeDescriptor.ADMIN, app, party);
+ if (!PermissionService.checkPermission(admin)) {
+ throw new AccessDeniedException("User is not an administrator");
+ }
+ /* Want ds to always show the latest stuff... */
+ DispatcherHelper.cacheDisable(sresp);
+
+
+ // /////// Everything OK here - DO IT ///////////////
+
+ String pathInfo = sreq.getPathInfo();
+ Assert.exists(pathInfo, "String pathInfo");
+ if (pathInfo.length() > 1 && pathInfo.endsWith("/")) {
+ /* NOTE: ServletAPI specifies, pathInfo may be empty or will
+ * start with a '/' character. It currently carries a
+ * trailing '/' if a "virtual" page, i.e. not a real jsp, but
+ * result of a servlet mapping. But Application requires url
+ * NOT to end with a trailing '/' for legacy free applications. */
+ pathInfo = pathInfo.substring(0, pathInfo.length()-1);
+ }
+
+ final Page page = (Page) m_pages.get(pathInfo);
+
+ if (page != null) {
+
+ final Document doc = page.buildDocument(sreq, sresp);
+
+ PresentationManager pm = Templating.getPresentationManager();
+ pm.servePage(doc, sreq, sresp);
+
+ } else {
+ sresp.sendError(404, "No such page for path " + pathInfo);
+ // throw new IllegalStateException("No such page for path " + pathInfo);
+ }
+
+ }
+
+
+ /**
+ * Adds one Url-Page mapping to the internal mapping table.
+ *
+ * @param pathInfo url stub for a page to display
+ * @param page Page object to display
+ */
+ private void addPage(final String pathInfo, final Page page) {
+
+ Assert.exists(pathInfo, String.class);
+ Assert.exists(page, Page.class);
+ // Current Implementation requires pathInfo to start with a leading '/'
+ // SUN Servlet API specifies: "PathInfo *may be empty* or will start
+ // with a '/' character."
+ Assert.isTrue(pathInfo.startsWith("/"), "path starts not with '/'");
+
+ m_pages.put(pathInfo, page);
+ }
+
+ /**
+ *
+ * @return index Page object
+ */
+ private Page buildIndexPage() {
+
+ Page p = PageFactory.buildPage(APP_NAME, "Web Developer Support");
+
+ BoxPanel links = new BoxPanel(BoxPanel.VERTICAL);
+
+ links.add(new Link("Log4j Logger Adjuster", "/ds/log4j"));
+ links.add(new Link("Config Browser", "/ds/config"));
+ links.add(new Link("Cache Table Browser", "/ds/cache-table"));
+
+ ActionLink enable = new ActionLink("Enable request logging") {
+ @Override
+ public boolean isVisible(PageState state) {
+ return !DeveloperSupport.containsListener(
+ WebDevSupportListener.getInstance())
+ && super.isVisible(state);
+ }
+ };
+ enable.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ DeveloperSupport.addListener(WebDevSupportListener.getInstance());
+ throw new RedirectSignal(URL.request(e.getPageState().getRequest(),
+ null), true);
+ }
+
+ });
+ links.add(enable);
+
+ ActionLink disable = new ActionLink("Disable request logging") {
+ @Override
+ public boolean isVisible(PageState state) {
+ return DeveloperSupport.containsListener(
+ WebDevSupportListener.getInstance())
+ && super.isVisible(state);
+ }
+ };
+
+ disable.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ DeveloperSupport.removeListener(WebDevSupportListener.getInstance());
+ WebDevSupportListener.getInstance().clearRequestHistory();
+ throw new RedirectSignal(URL.request(e.getPageState().getRequest(),
+ null), true);
+ }
+ });
+ links.add(disable);
+
+ BoxPanel logs = new BoxPanel(BoxPanel.VERTICAL) {
+ @Override
+ public boolean isVisible(PageState state) {
+ return DeveloperSupport
+ .containsListener(WebDevSupportListener.getInstance())
+ && super.isVisible(state);
+ }
+ };
+
+ logs.add(new Label("") {
+ @Override
+ public String getLabel(PageState ps) {
+ return "Currently storing the last " +
+ WebDevSupportListener.getInstance().getMaxRequests() +
+ " requests";
+ }
+ });
+
+ Label toggle = new Label("") {
+ @Override
+ public String getLabel(PageState ps) {
+ return s_showDSPages ? "Hide hits to developer support" :
+ "Show hits to developer support";
+ }
+ };
+ ControlLink cl = new ControlLink(toggle) {
+ @Override
+ public void respond(PageState s) {
+ s_showDSPages = !s_showDSPages;
+ }
+ @Override
+ public void setControlEvent(PageState s) {
+ s.setControlEvent(this);
+ }
+ };
+
+
+ logs.add(cl);
+ logs.add(new Label("Available Request Information
", false));
+ logs.add(makeRequestTable());
+
+ p.add(links);
+ p.add(logs);
+ p.lock();
+ return p;
+ }
+
+
+ /**
+ *
+ * @return log4j Page object
+ */
+ private Page buildLog4jPage() {
+ Page p = PageFactory.buildPage(APP_NAME, "Log4j Logger Adjuster");
+ p.add(new CategoryPanel());
+ p.lock();
+ return p;
+ }
+
+ /**
+ *
+ * @return config Page object
+ */
+ private Page buildConfigPage() {
+ Page p = PageFactory.buildPage(APP_NAME, "Registry Config");
+ p.add(new ConfigList());
+ p.lock();
+ return p;
+ }
+
+ private Page buildCacheTablePage() {
+ Page p = PageFactory.buildPage(APP_NAME, "Cache Table Browser");
+ p.add(new CacheTableBrowser());
+ p.lock();
+ return p;
+ }
+
+
+ private ParameterModel m_request_id = new IntegerParameter("request_id");
+
+ /**
+ *
+ * @return info Page object
+ */
+ private Page buildInfoPage() {
+ Page p = PageFactory.buildPage(APP_NAME, "Request Information");
+ p.addGlobalStateParam(m_request_id);
+ // p.add(new RequestInfoComponent());
+ p.add(makeDatabaseRequestComponent());
+ p.lock();
+ return p;
+ }
+
+ private ParameterModel m_query_id = new IntegerParameter("query_id");
+ private ParameterModel m_query_request_id = new IntegerParameter("request_id");
+
+ /**
+ *
+ *
+ * @return queryInfo Page object
+ */
+ private Page buildQueryInfoPage() {
+ Page p = PageFactory.buildPage(APP_NAME, "Query Information");
+ p.addGlobalStateParam(m_query_request_id);
+ p.addGlobalStateParam(m_query_id);
+ p.add(new QueryInfoComponent());
+ p.lock();
+ return p;
+ }
+
+
+ private RequestLocal m_scoreboard = new RequestLocal() {
+ @Override
+ protected Object initialValue(PageState state) {
+ // queryInfo.getID() => {textRepeats, queryRepeats}
+ return new HashMap();
+ }
+ };
+
+ // Used below in makeDatabaseRequestComponent to generate a query
+ // scoreboard.
+ private void incrementScore(final PageState state, final QueryInfo key,
+ final int index) {
+ Map map = (Map) m_scoreboard.get(state);
+
+ Integer[] row = (Integer[]) map.get(new Integer(key.getID()));
+
+ if (row == null) {
+ row = new Integer[] { new Integer(0), new Integer(0) };
+ map.put(new Integer(key.getID()), row);
+ }
+
+ row[index] = new Integer(row[index].intValue() + 1);
+ }
+
+
+ private Component makeDatabaseRequestComponent() {
+ final String[] headers =
+ { "ID", "Duration (execution)", "Conn", "Command", "Exception" };
+
+ TableModelBuilder b = new AbstractTableModelBuilder() {
+ public TableModel makeModel(Table t, final PageState s) {
+ Integer request_id = (Integer)s.getValue(m_request_id);
+ RequestInfo ri =
+ WebDevSupportListener.getInstance().getRequest(request_id.intValue());
+ final Iterator iter = (ri == null) ? new ArrayList().iterator() :
+ ri.getQueries();
+
+ return new TableModel() {
+ private HashMap seenQueryAndValues = new HashMap();
+ private HashMap seenQuery = new HashMap();
+ private boolean duplicateQueryAndValues = false;
+ private boolean duplicateQuery = false;
+ private QueryInfo current = null;
+
+ public int getColumnCount() {
+ return 5;
+ }
+
+ public boolean nextRow() {
+ if (iter.hasNext()) {
+ current = (QueryInfo) iter.next();
+
+ duplicateQueryAndValues =
+ seenQueryAndValues.containsKey(current);
+ duplicateQuery =
+ seenQuery.containsKey(current.getQuery());
+
+ if (!duplicateQueryAndValues) {
+ seenQueryAndValues.put(current, current);
+ }
+
+ if (!duplicateQuery) {
+ seenQuery.put(current.getQuery(), current);
+ }
+
+ incrementScore
+ (s, (QueryInfo) seenQueryAndValues.get(current),
+ 0);
+ incrementScore
+ (s, (QueryInfo) seenQuery.get(current.getQuery()),
+ 1);
+
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ public Object getElementAt(int columnIndex) {
+ switch (columnIndex) {
+ case 0: {
+ StringBuffer result = new StringBuffer();
+ result.append(current.getID());
+
+ if (duplicateQueryAndValues) {
+ result.append(
+ " duplicates #" +
+ ((QueryInfo)seenQueryAndValues.get(
+ current)).getID()
+ );
+ }
+
+ if (duplicateQuery) {
+ result.append(
+ " duplicates text of #" +
+ ((QueryInfo)seenQuery.get(
+ current.getQuery())).getID()
+ );
+ }
+
+ return result.toString();
+ }
+ case 1: return (current.isClosed() ?
+ current.getTotalTime() + " ms" :
+ "unknown")
+ + "
(" + current.getTime() + " ms)";
+ case 2: return current.getConnectionID();
+ case 3: return "" +
+ current.getQuery() +
+ "
BINDS: " +
+ current.getBindvars() +
+ "
";
+ case 4: return current.getSQLE();
+ default: return null;
+ }
+ }
+
+ public Object getKeyAt(int columnIndex) {
+ return new Integer(current.getID());
+ }
+ };
+ }
+ };
+
+ Table table = new Table(b, headers);
+ table.setBorder("1");
+ table.getColumn(1).setCellRenderer(new NonEscapedTableCellRenderer(false));
+ table.getColumn(3).setCellRenderer(new NonEscapedTableCellRenderer());
+ table.getColumn(0).setCellRenderer(new
+ DefaultTableCellRenderer(true));
+ table.addTableActionListener(new TableActionAdapter() {
+ @Override
+ public void cellSelected(TableActionEvent e) {
+ PageState s = e.getPageState();
+ Integer request_id = (Integer) s.getValue(m_query_request_id);
+
+ final ParameterMap params = new ParameterMap();
+
+ params.setParameter("request_id", request_id);
+ params.setParameter("query_id", e.getRowKey());
+
+ if (e.getColumn().intValue() == 0) {
+ throw new RedirectSignal(URL.getDispatcherPath() +
+ "/ds/query-info" + params, true);
+ } else {
+ throw new RedirectSignal(URL.getDispatcherPath() +
+ "/ds/query-log" + params, true);
+ }
+ }
+ });
+
+ Label l = new Label("None");
+ l.setFontWeight(Label.ITALIC);
+ l.setStyleAttr("padding-left: 3em");
+ table.setEmptyView(l);
+ table.setWidth("100%");
+
+ BoxPanel panel = new BoxPanel(BoxPanel.VERTICAL);
+ panel.add(table);
+
+ TableModelBuilder scoreBuilder = new AbstractTableModelBuilder() {
+ public TableModel makeModel(Table t, final PageState state) {
+ return new TableModel() {
+ Map map = (Map) m_scoreboard.get(state);
+ Iterator iter = map.keySet().iterator();
+ Integer key;
+ Integer[] row;
+
+ public int getColumnCount() {
+ return 3;
+ }
+
+ public boolean nextRow() {
+ if (iter.hasNext()) {
+ key = (Integer) iter.next();
+ row = (Integer[]) map.get(key);
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ public Object getElementAt(int column) {
+ switch (column) {
+ case 0:
+ return key;
+ case 1:
+ return row[0];
+ case 2:
+ return row[1];
+ default:
+ return null;
+ }
+ }
+
+ public Object getKeyAt(int column) {
+ return key;
+ }
+ };
+ }
+ };
+
+ final String[] scoreHeaders = {"ID", "Duplicates", "Text-Duplicates"};
+
+ Table scoreTable = new Table(scoreBuilder, scoreHeaders);
+ panel.add(scoreTable);
+
+ return panel;
+ }
+
+
+ private Table makeRequestTable() {
+ final String[] headers = { "Time", "Duration", "Queries", "IP",
+ "Request", "Extra" };
+
+ TableModelBuilder b = new AbstractTableModelBuilder() {
+ public TableModel makeModel(Table t, PageState s) {
+ return new TableModel() {
+ ListIterator iter =
+ WebDevSupportListener.getInstance().getRequestsReverse();
+ private RequestInfo current = null;
+
+ public int getColumnCount() {
+ return 6;
+ }
+
+ public boolean nextRow() {
+ while (iter.hasPrevious()) {
+ current = (RequestInfo) iter.previous();
+ boolean isdevsupp = current.isDevSupportRequest();
+ if (s_showDSPages || !isdevsupp) {
+ return true;
+ }
+ }
+ return false;
+ }
+ static final int MAXSTR = 35;
+ public Object getElementAt(int columnIndex) {
+ switch (columnIndex) {
+ case 0: return current.getTime();
+ case 1: return current.getDuration();
+ case 2: return current.getNumQueries()+"";
+ case 3: return current.getIP();
+ case 4: {
+ String req = current.getRequest();
+ if (req.length() > MAXSTR) {
+ return req.substring(0, MAXSTR)+"...";
+ } else {
+ return req;
+ }
+ }
+ case 5:
+ return "[query log]";
+ default: return null;
+ }
+ }
+
+ public Object getKeyAt(int columnIndex) {
+ return new Integer(current.getID());
+ }
+ };
+ }
+ };
+
+ Table result = new Table(b, headers);
+ result.getColumn(4).setCellRenderer(new
+ DefaultTableCellRenderer(true));
+ result.getColumn(5).setCellRenderer(new
+ DefaultTableCellRenderer(true));
+ result.addTableActionListener(new TableActionAdapter() {
+ @Override
+ public void cellSelected(TableActionEvent e) {
+ final ParameterMap params = new ParameterMap();
+ params.setParameter("request_id", e.getRowKey());
+
+ if (e.getColumn().intValue() == 4) {
+ throw new RedirectSignal(URL.getDispatcherPath() +
+ "/ds/request-info" + params, true);
+ } else if (e.getColumn().intValue() == 5) {
+ throw new RedirectSignal(URL.getDispatcherPath() +
+ "/ds/query-log" + params, true);
+ }
+ }
+ });
+ Label l = new Label("None");
+ l.setFontWeight(Label.ITALIC);
+ l.setStyleAttr("padding-left: 3em");
+ result.setEmptyView(l);
+ result.setWidth("100%");
+ return result;
+ }
+
+ String dashes(int depth) {
+ StringBuilder sb = new StringBuilder();
+ while (depth-- > 0) sb.append("--");
+ return sb.toString();
+ }
+
+
+
+ private ParameterModel m_query_p_id = new IntegerParameter("query_id");
+ private ParameterModel m_query_p_request_id =
+ new IntegerParameter("request_id");
+
+ private Page buildQueryPlanPage() {
+ Page p = PageFactory.buildPage(APP_NAME, "Query Execution Plan");
+ p.addGlobalStateParam(m_query_p_request_id);
+ p.addGlobalStateParam(m_query_p_id);
+ p.add(new QueryPlanComponent());
+ p.lock();
+ return p;
+ }
+
+ /**
+ *
+ */
+ class RequestInfoComponent extends com.arsdigita.bebop.SimpleComponent {
+
+ /**
+ * Constructor
+ */
+ public RequestInfoComponent() {
+ super();
+ }
+
+ /**
+ *
+ * @param state
+ * @param parent
+ */
+ @Override
+ public void generateXML(PageState state, Element parent) {
+ Integer request_id = (Integer)state.getValue(m_request_id);
+ RequestInfo ri =
+ WebDevSupportListener.getInstance().getRequest(request_id.intValue());
+ if (ri != null) {
+ Container param_list;
+ Container form_list;
+ Container comment_list;
+ Container headers_list;
+ Container stages_list;
+ BoxPanel bp = new BoxPanel();
+ bp.add(new Label("Parameters
", false));
+ bp.add(param_list = new ColumnPanel(2));
+ param_list.add(new Label("Request Start Time:"));
+ param_list.add(new Label(ri.getTime()));
+ param_list.add(new Label("Request Completion Time: "));
+ param_list.add(new Label(ri.getEndTime()));
+ param_list.add(new Label("Request Duration: "));
+ param_list.add(new Label(ri.getDuration()));
+ param_list.add(new Label("IP: "));
+ param_list.add(new Label(ri.getIP()));
+ param_list.add(new Label("Method: "));
+ param_list.add(new Label(ri.getMethod()));
+ param_list.add(new Label("URL: "));
+ param_list.add(new Label(ri.getURL()));
+ param_list.add(new Label("Query: "));
+ param_list.add(new Label(StringUtils.quoteHtml( ri.getQuery())));
+ param_list.add(new Label("Request Parameters: "));
+ param_list.add(form_list = new ColumnPanel(2));
+ Iterator iter = ri.getParameterNames();
+ while (iter.hasNext()) {
+ String param = (String)iter.next();
+ form_list.add(new Label(param + ":"));
+ form_list.add(new Label(ri.getParameter(param)));
+ }
+
+ bp.add(new Label("Headers
", false));
+ bp.add(headers_list = new ColumnPanel(2));
+ iter = ri.headerKeys();
+ while (iter.hasNext()) {
+ String header = (String)iter.next();
+ headers_list.add(new Label(header +": "));
+ headers_list.add(new Label(ri.getHeader(header)));
+ }
+ //Doesn't appear to be possible to get the output headers
+ //bp.add(new Label("Output Headers"));
+ if (ri.numComments() > 0) {
+ bp.add(new Label("Comments
", false));
+ bp.add(comment_list = new ListPanel(false));
+ iter = ri.getComments();
+ while (iter.hasNext()) {
+ comment_list.add(new Label((String)iter.next()));
+ }
+ }
+ if (ri.numStages() > 0) {
+ bp.add(new Label("Stages
", false));
+ bp.add(stages_list = new ColumnPanel(4));
+ iter = ri.getStages();
+ stages_list.add(new Label("Stage"));
+ stages_list.add(new Label("Time"));
+ stages_list.add(new Label("Queries"));
+ stages_list.add(new Label("Processing"));
+ while (iter.hasNext()) {
+ StageInfo si = (StageInfo)iter.next();
+ String leaf = si.leaf() ? "*" : "";
+ int numqueries = si.numQueries();
+ long time = si.time();
+ stages_list.add(new Label(dashes(si.depth()) + si.getName()));
+ stages_list.add(new Label(time + " ms" + leaf));
+ if (numqueries != 0) {
+ long queryTime = si.queryTime(ri.getQueries());
+ long non_queryTime = time-queryTime;
+ stages_list.add(new Label(queryTime + " ms" + leaf +
+ " ("+numqueries+" queries)"));
+ stages_list.add(new Label(non_queryTime + " ms" + leaf));
+ } else {
+ stages_list.add(new Label(""));
+ stages_list.add(new Label(time + " ms"+ leaf));
+ }
+ }
+ }
+ bp.add(new Label("Database Requests
", false));
+ int query_count = 0;
+ long total_time = 0;
+ long total_execute = 0;
+ int unclosed_count = 0;
+ iter = ri.getQueries();
+ while (iter.hasNext()) {
+ QueryInfo qi = (QueryInfo)iter.next();
+ query_count++;
+ total_execute += qi.getTime();
+ total_time += qi.getTotalTime();
+ if (!qi.isClosed()) {
+ unclosed_count++;
+ }
+ }
+ bp.add(new Label("Total Queries: " + query_count +
+ (unclosed_count > 0 ? " (" + unclosed_count +
+ " unclosed)" : "")));
+ bp.add(new Label("Total Time: " + total_time +
+ " ms (execution: " + total_execute + " ms)"));
+ bp.generateXML(state, parent);
+ }
+ }
+ }
+
+ /**
+ * Class
+ */
+ class QueryInfoComponent extends com.arsdigita.bebop.SimpleComponent {
+
+ /**
+ *
+ */
+ public QueryInfoComponent() {
+ super();
+ }
+
+ /**
+ *
+ * @param state
+ * @param parent
+ */
+ @Override
+ public void generateXML(PageState state, Element parent) {
+ Integer request_id = (Integer)state.getValue(m_query_request_id);
+ Integer query_id = (Integer)state.getValue(m_query_id);
+ RequestInfo ri =
+ WebDevSupportListener.getInstance().getRequest(request_id.intValue());
+ if (ri != null) {
+ QueryInfo qi = ri.getQuery(query_id.intValue());
+ if (qi != null) {
+ ListPanel info_list;
+ BoxPanel bp = new BoxPanel();
+ bp.add(info_list = new ListPanel(false));
+ info_list.add(new Label("Total query duration: " +
+ qi.getTotalTime() + " ms"));
+ info_list.add(new Label("Query execution time: " +
+ qi.getTime() + " ms"));
+ info_list.add(new Label("Statement closed: " +
+ qi.isClosed()));
+ info_list.add(new Label("Connection ID: " +
+ qi.getConnectionID()));
+ info_list.add(new Label("Type: " +
+ qi.getType()));
+ info_list.add(new Label("Query:
" +
+ StringUtils.quoteHtml(
+ qi.getQuery() ) + "
", false));
+ info_list.add(new Label("Bindvars: " +
+ qi.getBindvars()));
+
+ Link l = new Link ("Query Execution Plan",
+ "../query-plan/");
+ l.setVar("request_id", request_id.toString());
+ l.setVar("query_id", query_id.toString());
+ info_list.add(l);
+
+
+ info_list.add(new Label("Exception: " +
+ qi.getSQLE()));
+ info_list.add(new Label("StackTrace:
" +
+ StringUtils.quoteHtml(qi.getStackTrace()) +
+ "
", false));
+ bp.generateXML(state, parent);
+ }
+ }
+ }
+ }
+
+
+// We need NonEscapedTableCellRenderer
+// Currently part of (thisPackage) Dispatcher.java
+// When the class will we deleted we must copy first!
+
+
+}
diff --git a/ccm-core/web/WEB-INF/web.ccm-core.xml b/ccm-core/web/WEB-INF/web.ccm-core.xml
index 076a33871..97a86e5bd 100644
--- a/ccm-core/web/WEB-INF/web.ccm-core.xml
+++ b/ccm-core/web/WEB-INF/web.ccm-core.xml
@@ -83,6 +83,11 @@
com.arsdigita.versioning.VersioningServlet
+
+ webdevsupport
+ com.arsdigita.webdevsupport.WebDevSupportServlet
+
+
oid-redirect
com.arsdigita.web.OIDRedirectServlet
@@ -176,6 +181,11 @@
/versioning/*
+
+ webdevsupport
+ /webdevsupport/*
+
+
oid-redirect
/redirect/*
diff --git a/ccm-docrepo/src/com/arsdigita/docrepo/ApplicationAuthenticationListener.java b/ccm-docrepo/src/com/arsdigita/docrepo/ApplicationAuthenticationListener.java
index 5e111ff40..4cd2ccad7 100644
--- a/ccm-docrepo/src/com/arsdigita/docrepo/ApplicationAuthenticationListener.java
+++ b/ccm-docrepo/src/com/arsdigita/docrepo/ApplicationAuthenticationListener.java
@@ -83,9 +83,9 @@ public class ApplicationAuthenticationListener
PrivilegeDescriptor.get(m_privilegeName);
PermissionDescriptor permDescriptor = new PermissionDescriptor
- (privDescriptor,
- Web.getContext().getApplication(),
- Kernel.getContext().getParty());
+ (privDescriptor,
+ Web.getContext().getApplication(),
+ Kernel.getContext().getParty());
if (!PermissionService.checkPermission(permDescriptor)) {
denyRequest(state);
diff --git a/ccm-forum/src/com/arsdigita/forum/ForumServlet.java b/ccm-forum/src/com/arsdigita/forum/ForumServlet.java
index ae00a140b..827b8d449 100755
--- a/ccm-forum/src/com/arsdigita/forum/ForumServlet.java
+++ b/ccm-forum/src/com/arsdigita/forum/ForumServlet.java
@@ -53,9 +53,10 @@ public class ForumServlet extends BebopApplicationServlet
@Override
public void init() throws ServletException {
super.init();
- s_log.debug("creating forum page");
+ s_log.debug("creating forum page");
final Page forum = ForumPageFactory.getPage(ForumPageFactory.FORUM_PAGE);
+
s_log.debug("creating thread page");
final Page thread = ForumPageFactory.getPage(ForumPageFactory.THREAD_PAGE);
diff --git a/ccm-sci-bundle/bundles/devel/cfg/web-sci.xml b/ccm-sci-bundle/bundles/devel/cfg/web-sci.xml
index 74f9d554a..86049f46c 100644
--- a/ccm-sci-bundle/bundles/devel/cfg/web-sci.xml
+++ b/ccm-sci-bundle/bundles/devel/cfg/web-sci.xml
@@ -122,6 +122,11 @@
com.arsdigita.versioning.VersioningServlet
+
+ webdevsupport
+ com.arsdigita.webdevsupport.WebDevSupportServlet
+
+
oid-redirect
com.arsdigita.web.OIDRedirectServlet
@@ -439,6 +444,11 @@
/versioning/*
+
+ webdevsupport
+ /webdevsupport/*
+
+
oid-redirect
/redirect/*