Chapter 11 Servlet Collaboration Servlet Context 1 2
Chapter 11 Servlet Collaboration
Servlet. Context • 1. 2. 3. 4. A servlet retrieves its context with get. Servlet. Context() call. A servlet may use the context like a Hashable or Map: void SC. set. Attribute(String name, Object o) Object SC. get. Attribute(String name) Enumeration SC. get. Attribute. Names() Void SC. remove. Attribute(String name)
set. Attribute(String name, Object o) • • • set. Attribute(String name, Object o) binds an object under a given name and replaces any existing binding of that name. Attribute names should follow package name conventions to avoid conflicts. java, javax, com. sun are reserved package names.
get. Attribute(String name) • • get. Attribute(String name) retrieves the object bound under the name or null. This could include server-specific hard-coded attributes (like context. tempdir)
get. Attribute. Names • get. Attribute. Names returns an enumeration which contains the names of all the bound attributes or an empty enumeration.
remove. Attribute() • remove. Attribute() removes the object bound under the given attribute (or does nothing if there is none).
Setting & displaying the special of the day
Getting the special from servlet context
Setting the special public class Special. Setter extends Http. Servlet { public void do. Get(Http. Servlet. Request req, Http. Servlet. Response res) throws Servlet. Exception, IOException { res. set. Content. Type("text/plain"); Print. Writer out = res. get. Writer(); Servlet. Context context = get. Servlet. Context(); context. set. Attribute("com. costena. special. burrito", "Pollo Adobado"); context. set. Attribute("com. costena. special. day", new Date()); out. println("The burrito special has been set. "); } }
Getting the special of the day public class Special. Getter extends Http. Servlet { public void do. Get(Http. Servlet. Request req, Http. Servlet. Response res) throws Servlet. Exception, IOException { res. set. Content. Type("text/html"); Print. Writer out = res. get. Writer(); Servlet. Context context = get. Servlet. Context(); String burrito = (String) context. get. Attribute("com. costena. special. burrito"); Date day = (Date) context. get. Attribute("com. costena. special. day"); Date. Format df = Date. Format. get. Date. Instance(Date. Format. MEDIUM); String today = df. format(day); out. println("Our burrito special today (" + today + ") is: " + burrito); } }
Another example: primegetter • You can get information which is continuously “posted” to your context by other servlets that are currently running.
primegetter import java. io. *; import java. text. *; import java. util. *; import javax. servlet. http. *; public class Prime. Getter extends Http. Servlet { public void do. Get(Http. Servlet. Request req, Http. Servlet. Response res) throws Servlet. Exception, IOException { res. set. Header("Refresh", "10"); //using client pull again res. set. Content. Type("text/html"); Print. Writer out = res. get. Writer(); Servlet. Context context = get. Servlet. Context(); String prime = (String) context. get. Attribute("prime"); Date day = (Date) context. get. Attribute("prime. date"); Date. Format df = Date. Format. get. Date. Instance(Date. Format. MEDIUM); String today = df. format(day); out. println("Current primes (" + today + ") is: " + prime); }}
Changes to threaded primes public void run() { // QTTTBBBMMMTTTOOO long candidate = 100000001 L; // one quadrillion and one // Begin loop searching for primes while (true) { // search forever if (is. Prime(candidate)) { lastprime = candidate; // new prime lastprime. Modified = new Date(); // new "prime time" Servlet. Context context = get. Servlet. Context(); context. set. Attribute("prime", ""+candidate); context. set. Attribute("prime. date", new Date()); } candidate += 2; // evens aren't prime // Between candidates take a 0. 2 second break. // Another way to be a good citizen with system resources. try { searcher. sleep(200); } catch (Interrupted. Exception ignored) { } } }
Sharing with another servlet context • A servlet can obtain the handle to another context on the same server using the get. Context() method of its own context: public Servlet. Context. get. Context(String uripath) The path must be absolute and start with / (at server’s document root). In J 2 EE or a secure environment, the server may return null for all such requests.
Sharing with another servlet context • If the special of the day was at uri /burritostore we might retrieve it as follows: Servlet. Context mycontext=get. Servlet. Context(); Servlet. Context othercontext=mycontext. get. Context(“/burritostore/index. html”); String burrito=othercontext. get. Attribute(“com. costena. special. burrito”); Day date=(Date)othercontext. get. Attribute(com. costena. special. day”); This won’t work if they move the burrito store, and the only way currently to do the lookup is via the path.
Sharing with another servlet context • There are some loader issues. Each context has its own loader. And the class loader from one web app can’t locate the classes of another’s. Consequently, if the class file can only be located in the classes directory of one web application it won’t be easy to use from another application. A No. Class. Def. Found. Error will result from trying to cast the object to its proper type.
Sharing with another servlet context • You can’t solve this by copying a class file into both classes directories, either, since classes loaded by different loaders are regarded as different classes. You’ll get the previously noted error plus Class. Cast. Exception.
Sharing with another servlet context: workarounds • Put classes into Tomcat/root/classes so the loader shared by all webapps would load it. • Avoid casting the returned object and access methods by reflection (a mechanism by which an object can inspect and manipulate itself at runtime. ) • Cast the returned object but to a shared interface that has the desired methods. Put the interface in the server’s standard classpath. Other classes can reside with separate web apps.
Sharing control with another servlet • A servlet can forward a request to another servlet, perhaps after some preliminary processing. • A servlet can include in its response some of the content generated by another component, a “server-side include”. • A servlet can construct a response as a collection of content generated by a number of other web components. • This is important for JSP, where a servlet might build a response and then pass off to a JSP for completion.
Request dispatcher • The javax. servlet. Request. Dispatcher interface of the 2. 2 API supports delegation. • A servlet gets a request dispatcher instance by calling request. get. Request. Dispatcher() • The dispatcher can dispatch to the component at a given URI • The URI may be relative but can’t extend outside the context path. • Using get. Context() (previous discussion) you can dispatch to components outside the current context.
Request dispatcher • It is not possible to dispatch to a context on another server. • A path starting with / is interpreted as relative to the current context root. • If the path contains a query string the parameters are added to the requested component’s parameter set. • The method returns null if the container can’t return a request dispatcher.
Request dispatcher • There is also a method get. Request. Dispatcher() in Servlet. Context class, which was introduced in API 2. 1. This method accepts only absolute URLs (beginning with a /) while the version in servlet request accepts both absolute and relative URLs. • This method is not deprecated but is inferior to the method available in servlet request.
Request dispatcher • It is also possible to get a dispatcher by naming a resource instead of providing a path using get. Named. Dispatcher() on the class Servlet. Context. • This would allow dispatching to resources which are not publicly available. • The named resource might be specified in web. xml
Request dispatcher • Request. Dispatcher has two methods, forward() and include(). • forward() hands off control to another component while include() includes another components response with this servlet’s, leaving the current servlet in control.
Request dispatcher: dispatching a forward • Unlike send. Redirect() which we have used before, a forward is invisible to the client. • A forward is also limited to this server. • By passing a query string or setting attributes on the request, information can be passed to the delegate. • Text example using servlets should be revisited using a servlet and a JSP, it includes a search engine back and front-end.
html
Search. Logic sends info to a servlet to display results
Search logic servlet public void do. Get(Http. Servlet. Request req, Http. Servlet. Response res) throws Servlet. Exception, IOException { // We don't set the content type or get a writer // Get the string to search for String search = req. get. Parameter("search"); // Calculate the URLs containing the string String[] results = get. Results(search); // Specify the results as a request attribute req. set. Attribute("results", results); // Forward to a display page String display = "/Search. View"; Request. Dispatcher dispatcher = req. get. Request. Dispatcher(display); dispatcher. forward(req, res); } // In real use this method would call actual search engine logic // and return more information about each result than a URL String[] get. Results(String search) { return new String[] { "http: //www. employees. oneonta. edu/higgindm", "http: //www. javasoft. com" }; }
Search. View servlet public void do. Get(Http. Servlet. Request req, Http. Servlet. Response res) throws Servlet. Exception, IOException { res. set. Content. Type("text/plain"); Print. Writer out = res. get. Writer(); // Get the search results from a request attribute String[] results = (String[]) req. get. Attribute("results"); if (results == null) { out. println("No results. "); out. println("Did you accidentally access this servlet directly? "); } else { out. println("Results: "); for (int i = 0; i < results. length; i++) { out. println(results[i]); } } out. println(); out. println("Request URI: " + req. get. Request. URI()); out. println("Context Path: " + req. get. Context. Path()); out. println("Servlet Path: " + req. get. Servlet. Path()); out. println("Path Info: " + req. get. Path. Info()); out. println("Query String: " + req. get. Query. String()); }
Rules forwarding servlet • It may set attributes, headers, status code, but may not send body of response to the client. • forward() must happen before response is committed. • If response has been committed the forward() throws an Illegal. State. Exception • If forward is called with content in the buffer, then the buffer is cleared. • New response and request objects can’t be substituted for the original. Forward() must be called in the same handler thread.
Another Example: Forward. It servlet public class Forward. It extends Http. Servlet { public void do. Get(Http. Servlet. Request req, Http. Servlet. Response res) throws Servlet. Exception, IOException { // if you set content type or get a writer it will be cleared res. set. Content. Type("text/plain"); Print. Writer out = res. get. Writer(); out. println("this part should not appear"); String[] message ={" this is a bunch", "of text that I want", "the target servlet to display"}; // Specify the message as a request attribute req. set. Attribute("message", message); // Forward to a display page String display = "/Result. View"; Request. Dispatcher dispatcher = req. get. Request. Dispatcher(display); dispatcher. forward(req, res); }}
Example continued: Result. View servlet public class Result. View extends Http. Servlet { public void do. Get(Http. Servlet. Request req, Http. Servlet. Response res) throws Servlet. Exception, IOException { res. set. Content. Type("text/plain"); Print. Writer out = res. get. Writer(); // Get the search results from a request attribute String[] results = (String[]) req. get. Attribute("message"); if (results == null) { out. println("No results. "); out. println("Did you accidentally access this servlet directly? "); } else { out. println("Content from forwarding servlet: "); for (int i = 0; i < results. length; i++) { out. println(results[i]); } } out. println("Content from this servlet: "); out. println("Request URI: " + req. get. Request. URI()); out. println("Context Path: " + req. get. Context. Path()); out. println("Servlet Path: " + req. get. Servlet. Path()); out. println("Path Info: " + req. get. Path. Info()); out. println("Query String: " + req. get. Query. String()); }}
Forward. It sends strings to be displayed by Result. View, content it wrote was discarded
A minor error in the text? • Instructions on dispatching by name in the text are wrong. Earlier the author correctly says you can get a dispatcher by name from the servlet context. • Instruction pg 374 will generate a compile error. • you can’t get a dispatcher by name from the request as in req. get. Named. Dispatcher(some. Name)
Forward by name does work: note different servlet path
Code in Forward. It to get dispatcher and forward String display = "Result. View"; Servlet. Context context = get. Servlet. Context(); Request. Dispatcher dispatcher = context. get. Named. Dispatcher(display); dispatcher. forward(req, res);
Why use get. Dispatcher by name? • If the servlet you are directing to is already available to the client in the webapp, she might just go there herself. Removing it from /servlet invoker logic and invoking by name would mean it is only accessible via forward.
Why forward() instead of send. Redirect()? • Forward works best if one component implements processing/logic and other generates display. • forward() is faster than send. Redirect() since it operates within your server so it is tempting to use it to redirect the client. • A forward doesn’t let the client know about redirection, so relative links within the forwarded page will be broken, as text example shows.
Home. Page. Forward servlet import java. io. *; import javax. servlet. http. *; public class Home. Page. Forward extends Http. Servlet { public void do. Get(Http. Servlet. Request req, Http. Servlet. Response res) throws Servlet. Exception, IOException { Request. Dispatcher dispatcher = req. get. Request. Dispatcher("/index. html"); dispatcher. forward(req, res); } }
Home. Page. Forward servlet example, continued • Relative (img, for example) links in the index page won’t work anymore, because the client thinks it wants images from the originally requested page. • Text advises to use send. Redirect() whenever possible and use forward() only when necessary.
Notice missing chickadee background?
Dispatching an include • Dispatcher. include() serves to include content from another web component into the generated content of the current servlet.
Includes servlet public class Includes extends Http. Servlet { public void do. Get(Http. Servlet. Request req, Http. Servlet. Response res) throws Servlet. Exception, IOException { res. set. Header("Refresh", "10"); res. set. Content. Type("text/html"); Print. Writer out = res. get. Writer(); out. println("<HTML><HEAD><TITLE>Welcome to this interesting site</TITLE></HEAD>"); out. println("<BODY>"); String[] message ={" this is a bunch", "of text that I want", "the target servlet to display"}; // Specify the message as a request attribute req. set. Attribute("message", message); out. println("<p>From Result. View"); Request. Dispatcher dispatcher = req. get. Request. Dispatcher("/Result. View"); dispatcher. include(req, res); // Remove the "message" attribute after use req. remove. Attribute("message"); out. println("<p>From primes"); //new Request. Dispatcher dispatcher = req. get. Request. Dispatcher("/primes"); // Show primes dispatcher. include(req, res); out. println("</BODY></HTML>"); }}
Get content from Result. View and Primes, throw in clientpull for good measure
Text example • The text example was good, showed how to put items into a page a commercial site might want to use to market to a specific customer.
Servlet to display a book as included content import java. io. *; import javax. servlet. http. *; public class Nile. Item extends Http. Servlet { public void do. Get(Http. Servlet. Request req, Http. Servlet. Response res) throws Servlet. Exception, IOException { // We do not set the content type Print. Writer out = res. get. Writer(); Book book = (Book) req. get. Attribute("item"); out. println("<BR>"); if (book != null) { out. println("<I>" + book. get. Title() + "</I>"); out. println(" by " + book. get. Author()); } else { out. println("<I>No book record found</I>"); } out. println("<BR>"); }}
Servlet that calls the item for inclusion public class Nile. Books extends Http. Servlet { public void do. Get(Http. Servlet. Request req, Http. Servlet. Response res) throws Servlet. Exception, IOException { res. set. Content. Type("text/html"); Print. Writer out = res. get. Writer(); out. println("<HTML><HEAD><TITLE>Welcome to Nile</TITLE></HEAD>"); out. println("<BODY>"); // Show items in an online catalog Request. Dispatcher dispatcher = req. get. Request. Dispatcher("/Nile. Item"); out. println("Feast your eyes on this beauty: "); req. set. Attribute("item", Book. get. Book("156592391 X")); dispatcher. include(req, res); // Remove the "item" attribute after use req. remove. Attribute("item"); out. println("Or how about this one: "); req. set. Attribute("item", Book. get. Book("0395282659")); dispatcher. include(req, res); out. println("And, since I like you, they're all 20% off!"); out. println("</BODY></HTML>"); }}
a book class hardcodes some volumes which would normally be gleaned from a database class Book { String isbn; String title; String author; private static Book JSERVLET = new Book("156592391 X", "Java Servlet Programming", "Hunter"); private static Book HOBBIT = new Book("0395282659", "The Hobbit", "Tolkien"); // Here we simulate a database lookup public static Book get. Book(String isbn) { if (JSERVLET. get. ISBN(). equals(isbn)) { return JSERVLET; } else if (HOBBIT. get. ISBN(). equals(isbn)) { return HOBBIT; } else { return null; } } private Book(String isbn, String title, String author) { this. isbn = isbn; this. title = title; this. author = author; } public String get. ISBN() { return isbn; } public String get. Title() { return title; } public String get. Author() { return author; }}
Getting path info from caller and callee • To get path info and query string for the included servlet you will not be able to use request. get. XXX() • You must use server-assigned attributes like javax. servlet. include. XXXX
Getting path info from caller and callee
Originating servlet import java. io. *; import javax. servlet. http. *; public class Include. Paths extends Http. Servlet { public void do. Get(Http. Servlet. Request req, Http. Servlet. Response res) throws Servlet. Exception, IOException { res. set. Header("Refresh", "10"); res. set. Content. Type("text/html"); Print. Writer out = res. get. Writer(); out. println("<HTML><HEAD><TITLE>Welcome to another includes example</TITLE></HEAD>"); out. println("<BODY>"); Request. Dispatcher dispatcher = req. get. Request. Dispatcher("/Include. View"); dispatcher. include(req, res); out. println("</BODY></HTML>"); } }
Included servlet import java. io. *; import javax. servlet. http. *; public class Include. View extends Http. Servlet { public void do. Get(Http. Servlet. Request req, Http. Servlet. Response res) throws Servlet. Exception, IOException { res. set. Content. Type("text/plain"); Print. Writer out = res. get. Writer(); // Get the path info for this servlet String uri = (String) req. get. Attribute("javax. servlet. include. request_uri"); String contextpath = (String) req. get. Attribute("javax. servlet. include. context_path"); String query_string = (String) req. get. Attribute("javax. servlet. include. query_string"); String servletpath = (String) req. get. Attribute("javax. servlet. include. servlet_path"); String path_info = (String) req. get. Attribute("javax. servlet. include. path_info"); out. println("path elements and params from caller: "); out. println("Request URI: " + req. get. Request. URI()); out. println("Context Path: " + req. get. Context. Path()); out. println("Servlet Path: " + req. get. Servlet. Path()); out. println("Path Info: " + req. get. Path. Info()); out. println("Query String: " + req. get. Query. String()); out. println("path elements and params from here: "); out. println("uri: "+uri); out. println("contextpath: "+contextpath); out. println("querystring: "+query_string); out. println("servletpath: "+servletpath); out. println("pathinfo: "+path_info); }}
- Slides: 52