| |||||||||||||
The package formerly known as KDF has been repackaged and renamed Theseus. Theseus also adds the ability to use multiple config files to break up the action mappings into one (or more) config files for easier management of the mappings.
We discovered a bug within a fix we did the other day. We added the ability to send request parameters to the JSP page being read in for dynamic XML output. This occurrs when using XSL. In order to process the JSP to get dynamic XML output, it has to be read in via a URL connection as if a web browser was connecting to the page. The bug is, if you have too many request parameters it throws a server exception. We noticed this when we were working on a worksheet project which is using XSL. The worksheet consists of about 30 to 70 columns (dynamically created), and many hundreds of rows can be added. In adding about 10 rows, we kept getting this server exception when the server was sending the response. Turns out the error was the request was too large. We tried submitting the request as a POST method, but that didn't seem to work.
So, we have to retract the fix we did the other day and explain how to overcome the limitation. Normally, you can submit a link like so:
<a href="/MyAction.do?command=MethodName¶m1=value1¶m2=value2">
In the ControllerServlet, it would read in these parameters and values, then pass them on as part of the request to the JSP page being read in, but if you had a form being submitted with 100's of request parameters and values, it would be too large for the GET method to handle. All HREF links are sent via the GET method. Forms are submitted based on the method you specify, but POST is most common. In creating our own URLConnection, we for some reason are not able to set the method to POST. To overcome this, since you generally associate a JavaBean with the action class, you can thus just use the JavaBean on every JSP page, including the JSP page being read in for dynamic XML output. Now, instead of passing some parameters via request, you simply add some methods to the javabean (getter/setter and an instance variable). Since you generally store the state of a user in the HttpSession javabean anyways, you may as well make use of the javabean to pass special parameters around to multiple pages. This has one main benefit that we see, a less cluttered request line. It may add some methods to your javabean, but again, the javabean is then the only HttpSession object needed for the specific module (unless you are using others on the pages).
This may be a little confusing so please feel free to ask some questions if you are not sure how this works. We apologize for the bug and we hope nobody has been inconvenienced.
Thanks.
A minor fix was added to the latest code. If you are using XSL pages that
don't have the
A fix was in order and we apologize for not testing this before. Apparently the action methods no longer get a bean reference. We believe we have fixed it with this as our test program works again.
As always, make sure you have the latest xerces.jar (1.3.0 just came out) and saxon.jar (6.2) in your WEB-INF/lib folders along with the theseus.jar.
This latest version adds a fix and a new feature. When using XSL, the ControllerServlet reads in each JSP via a URL connection to the JSP page. In doing so, we was originally not passing the request parameters to the JSP page, so when it was being processed by the JSP engine, any request parameters passed to the action class Request object were NOT being seen by the JSP page. We may have fixed this before, but we noticed the latest version didn't have it in there. That is because we am maintaining two copies of the source, one at home and one at work. This should hopefully sync us up and the fix should remain.
The second is a new feature allowing a "hook" if you will, into the ControllerServlet. Its easy enough with the source to modify the ControllerServlet for your own needs. None the less, right before the action class method is called, we call a hook.securityCheck() method. This method allows you to do anything you want, the main purpose being the ability to check for an existing HttpSession object in the case where a user tries to access a given /path and should not be able to unless they are logged in. The method returns a String which if is not null and is > 0 in length, becomes the forwarding result to look up. If the return value is null or "" (empty String), then execution continues in the ControllerServlet, thus the action class method is called and so on.
A new interface, ControllerHook has been added, and the ControllerServlet implements this class incase you do not provide your own class that implements the securityCheck() method behavior. The ControllerServlet implementation simply returns null, thus ignoring the use of the hook. You can implement this method in your own class, and add that class name to the web.xml init params like so:
<init-param>
<param-name>Hook</param-name>
<param-value>com.mycompany.package.MyHook</param-value>
</init-param>
Add the above in the ControllerServlet mapping area of web.xml and make sure your class is set up like so:
import org.theseus.servlets.*;
import org.theseus.actions.*;
public class MyHook
implements ControllerHook
{
public String securityCheck(ActionContext context)
{
return "";
}
}
Just have the return value be a
We hope this adds a little more functionality. We am debating if this is a useful feature or not and would love to hear back from anyone if they would use it.
Yet another update! We discovered that when using XSL, the process of retrieving dynamic XML from JSP has a major problem, it does not pass on request parameters/attributes to the JSP page that is to return XML. This can be disasterous if your action method puts some things in the request scope for the JSP page to use, which then never gets to see them.
We also found one strange problem that we think has to do with our router, but none the less we added a potential fix (potential being that it may revert if the problem is not common and a bug crops up in the new change. Basically, when retrieving a JSP page in the framework, it establishes a URL to the page. To simplify life, we have written the framework to accept relative paths to those pages. Maybe there is a way to make this work, but when we use the URL object, we have to construct a complete URL, which is http://host/path/page.jsp. The problem is, it resolves the HOST part to the server name, which if a DNS server is present (or maybe not), it changes the name into an IP. The problem we have is that our simple cable-model router will NOT allow us to access our own site via the IP address because that IP is now part of the router (in order to allow multiple connections to one cable/dsl you need to do this). So we noticed the the /path/page.jsp became http://xxx.xxx.xxx.xxxx/path/page.jsp which was not good. So since the process is in code, we can use http://localhost every time, and it works for now, meaning the outside world can see our pages again (our development box pages that is, which we generally don't share).
The latest release (2/27/01) of Theseus has some changes related specifically to the use of XSL in the framework. We am fairly new to XSL so Theseus may not be the most effecient XSL tool to use. However, we find that it does simplify the process of using XSL withing the Model 2 MVC architecture. So long as you supply the latest xerces.jar and saxon.jar (should work with xalan.jar too) in your /WEB-INF/lib dir, you should be all set to do XSL processing with Theseus.
The changes we made are not finalized yet. Consider them beta at this point. We tested them out and they work, but we still need to perform load testing which we won't get around to doing for a short while yet. Earlier versions used some code that we believe was not thread safe. Also, every single XSL request would read the XSL page off the disk, EVERY TIME. While small sites can get away with this, large sites would probably choke at reading off the HD for every XSL page for every request. So we added what we think is an effective caching system that should be thread safe. Using a HashMap (which is NOT thread-safe but offers quite a bit better performance if only used to read from it, making it thread-safe), we have added two init parameters that well allow you to USE caching (or ignore it completely, thus reading off the hd), as well as Pre-load ALL xsl pages mapped in the theseus-config.xml configuration file. If you do NOT want to pre-load XSL pages, but DO want to employ the caching, you can do so by setting the init parameter to use caching, but not to pre-load. Here are the web.xml init settings for the framework:
<?xml version="1.0"?>
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application
2.3//EN" "http://java.sun.com/j2ee/dtds/web-app_2_3.dtd">
<web-app>
<servlet>
<servlet-name>Controller</servlet-name>
<display-name>Controller</display-name>
<servlet-class>org.theseus.servlets.ControllerServlet</servlet-class>
<init-param>
<param-name>PopulateAttributeName</param-name>
<param-value>auto-populate</param-value>
</init-param>
<init-param>
<param-name>PopulateClassName</param-name>
<param-value>org.theseus.beans.BeanUtils</param-value>
</init-param>
<init-param>
<param-name>Debug</param-name>
<param-value>2</param-value>
</init-param>
<init-param>
<param-name>UseXSLCache</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>PreloadXSL</param-name>
<param-value>true</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Controller</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
<session-config>
<session-timeout>30</session-timeout>
</session-config>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
You should be able to copy/paste the above into your existing app. There are now 5 init parameters. The Debug one is used just to display messages as things are happening, but we haven't used it in a multi-level way yet (where by when you put in 1, only some messages come out, where as 2, 3 or 4 may provide different sets of output messages or ALL of them). The UseXSLCache indicates to use the cache, even if they XSL pages are not preloaded. By using cache, every time a request comes in it first checks the cache Hashmap if the XSL page is available (stored by keyname of the XSL path in the Forward tags of theseus-config.xml). If it is there, it uses that XSL page. If it is not, it loads the XSL page, then stores it in the Hashmap cache. From then on, future requests use the cached version. PreloadXSL is the last init arg added which simply instructs the application server to load all XSL pages into cache when the app server starts up (or is restarted, hot-swap restarted, etc). This is much like compiling all JSP pages before using them, it saves the hassle of reading the XSL pages off the HD at request time for your clients, a good thing to have for production use.
There are two things to be aware of that we know of, possibly more. First, we syncronize the little bit of code that reads in the XSL page at run-time then stores it in the Hashmap. Because Hashmap is not thread-safe, we don't want the possibility of a second thread trying to read in the same XSL page and putting the XSL page into the cache while a first one is in the process. More importantly to keep in mind is that if you alter XSL pages, you need to restart your app server for them to take affect. Therefore, we suggest you do NOT use XSL caching during development, but only in production use. This way as you develop XSL pages, you can see the changes as they are developed. When going to production, turn on caching and pre-loading, and your all set. If you run a site where you upload changes daily but don't restart your server that often, such as middle-of-the-day text changes to pages, then your plumb out of luck right now. Ideally your application should be hot-swappable anyways and restarting the app server (or reloading the app) should not affect the site too much. Oh, on the syncronized() code bit, this ONLY occurs at run time requests, if you preload the XSL pages, the application server is in control of the app at that point and there is no reason to syncronize access to the loading XSL routine at that point.
As always, ask questions if you like, and we welcome any input on a better way of doing this if you know how. Please look at the source and modify it as you wish. If you have a better alternative, share the wealth.
Thanks.The latest framework update has made some changes that should be taken note of. Primarily, we determined there was no reason to have the xslControllerServlet as a separate class, as well as a separate extension mapped to use XSL specific functionality. Therefore, in your web.xml file (or your server specific config file), please remove the 2nd extension or path mapped to org.theseus.servlets.xslControllerServlet as it no longer exists. If you are using the old framework where by you mapped a 2nd extension to the xslControllerServlet, simply remove the xsl so that the 2nd extension (or path) maps to the org.theseus.servlets.ControllerServlet and you should not see a break of your site. However, it wouldn't harm you to rework the 2nd extension in your links to use the same one as the non-XSL extension. A simple file search/replace should do the trick.
So, in order to use XSL, its even simpler. If a
Please keep your eyes pealed to this page as soon we will put up the new name of this framework with a very detailed explanation of how to use it in many scenarios, as well as detailed information on what clustering, fail-over (session level), 24/7 up-time, and scalability is really all about, and how you can use this framework to achieve these and other goals for your organization.
Some things have changed in the MVC framework which should not affect existing use of the framework.
The theseus-config.xml no longer uses the <redirect/> declaration. From now on, because Servlet 2.1 and later ALL support the RequestDispatcher.forward() mechanism, the framework will always FORWARD to a resource and no longer attempt to redirect. If you must REDIRECT (which adds two additional network steps and is thus slower, definitely not a good thing for high-traffic sites), then do so in an action class method and return an empty String as the return type from the method. For example:
-------
theseus-config.xml
-------
<action name="MyAction" class="com.mycompany.package.MyAction">
<redirect/>
<forward name="DefaultOK" resource="/path/page.jsp"/>
</action>
public String executeCommandMyAction(ActionContext context)
{
return "DefaultOK"
}
becomes:
<action name="MyAction" class="com.mycompany.package.MyAction">
<forward name="DefaultOK" resource="/path/page.jsp"/>
</action>
public String executeCommandMyAction(ActionContext context)
{
context.getResponse().sendRedirect("http://mysite.com/path/page.jsp");
return "";
}
The old theseus.jar used to throw an exception (in the output window of your
running app server or servlet engine) IF you forwarded or redirected in your
code, but still returned a value. This is because of two reasons. One, if
the value you returned is NOT a valid mapping (
Therefore, if you need to redirect (perhaps to another site entirely, which should be a Redirect and not a Forward), do it in your code directly. If you are in your own site, the theseus.jar will ALWAYS forward to that page, saving the extra two network steps of going back to the server and returning a page.
In case your not sure of the differences between redirection and forwarding, its really quite simple. When you use response.sendRedirect() you are sending a "request" back to the browser to tell it to go fetch another page. When you forward, you simply grab the page while still on the server-side BEFORE going back to the browser, in effect including the output of the page you forwarded to as part of the response that will go back to the browser. The redirect sends a response to the browser, which then sends a request to the server to send a response back (3 steps). The forward sends a response to the browser WITH the page it needed (forwarded to) as part of its request.
Because of the way the Servlet engine works, as long as you do NOT open an output stream in the response BEFORE forwarding, you can always use forwarding. As soon as you initiate a stream buffer for the output of a response, your can no longer "forward" to a resource (usually a jsp page) without it being "included" as part of the current output. For example, if in your code you get a response.getWriter() call, add some HTML to it, then try to forward, the html you put in the buffer yourself AND the forwarded page will all be part of the response. This may give undesirable results in the final displayed page. However, this is perfectly ok to do and may infact be what you want to do. It is the same process as servlet chaining, where you call one servlet, fill in the output of the response with that servlet, then call upon another servlet to add some more to the output, and so on, .until its full. It is then flushed and becomes the response back to the browser.
To participate in discussion of Theseus, send email to theseus-user-subscribe@yahoogroups.com.
the following is a "hit counter" only