![]() ![]() ![]() |
||||||||||||||||||||||||||||||
![]()
Building Java Servlets with JBuilder NOTE: The views and information expressed in this document represent those of its author(s) who is solely responsible for its content. Borland does not make or give any representation or warranty with respect such content.
![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]()
Java Client Technology With DHTML, interactive, dynamic applications can be created simply through the use of HTML and a collection of new DHTML tags. Applications with tabbed interfaces, drag-and-drop, menus, forms, and a host of other features, are all possible with DHTML. Because traditional server-side processing like database connectivity and legacy system access, is, by definition, performed at the server, DHTML is an extremely viable solution for client-side applications. The server can still perform its required tasks, but the client application can be created more quickly, can be more compatible across platforms, and can be modified and deployed in shorter time.
Targeted Deployment
Server-Side Java
Table 1.1 In most respects, server-side Java is the complete opposite of client-side Java. With server-side Java, many, if not all, of the limitations of applets are gone. As a developer, you now have complete control over your Java virtual machine, what features and capabilities you can use, and on what platforms you run your server-side application. Server-side Java allows you to completely realize JavaÆs "write once, run anywhere" goal. The full capabilities of the language are available to you with no limitations or restrictions imposed. If you want to use MicrosoftÆs J/Direct technology or SunÆs RMI technology, you can do so without concern for your clientsÆ ability to run these applications. Since these applications run on the server, you are also in complete control over the platform, the operating system, and every aspect of the environment your application runs in. Java on the server is a true unifying solution for your application development. Because you now have complete control over your environment, you can now develop on one platform and deploy on another, with great confidence that it will run properly on both platforms. Because the interface is now handled with DHTML or a similar technology, you now have a truly portable system. With all of the power server-side Java affords you, including all of the inherent benefits of Java û object-oriented, strong type-checking, large set of built-in classes, etc. û there is still one shortcoming, a consistent server-side API. Why is this important? It is not enough to say that Java on the server will answer all of your problems. More importantly, we want to use Java as the development platform across all servers. Additionally, we want to be able to combine multiple applications together into a larger system. There is an answer to this problem, Java Servlets. Java Servlets are platform-independent, server-side programs written using the Java Servlet API, part of the Java Platform. Java Servlets bring the power of "write once, run anywhere," Safe Network Delivery, and Smart Card to Supercomputer scalibility to server-side applications. Traditional server-side applications and extensions written in Perl, C/C++, or other languages, can be replaced through the use of Servlets. By using Servlets as an extensible, well-defined cross-platform environment, sophisticated applications for the server become easier to develop, faster to deploy, and more cost effective to maintain. By using existing mature server technology, such as Web servers, in combination with Servlets, sophisticated applications can be written that hide the complexities and platform-specific behaviors of the particular server implementation. Using Servlets, the developer doesn't have to worry about the inner workings of the server. Form data, server headers, cookies, etc. are all handled for you by the Servlet API's underlying classes. One of the biggest differences between todayÆs server-side applications and Servlets is performance. There's a single Java virtual machine running on the server and the servlet is loaded once when it is called. It's not loaded again until the servlet changes, and a modified servlet can be re-loaded without restarting the server or application. The servlet stays resident in memory and it's very fast. Static or persistent information can be shared across multiple invocations of the servlet, allowing you to share information between multiple users. Servlets are also modular; each servlet can perform a specific task and then you can tie them together. In short, Servlets can talk to each other. What does a servlet look like? Figure 1 shows the complete source code to a functioning servlet. import java.io.*; import javax.servlet.*; public class SimpleServlet extends GenericServlet { public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException { ServletOutputStream out = res.getOutputStream(); out.println("<HEAD><TITLE> SimpleServlet Output "); out.println("</TITLE></HEAD><BODY>"); out.println("<h1> SimpleServlet Output </h1>"); out.println("</BODY>"); } }
Figure 1
A Server Component Model Servlet chaining is the ability for one ServletÆs output to be æpipedÆ in to the input of another Servlet. This can continue for multiple servlets. This simple yet powerful notion means that different servlets, perhaps from different vendors, can work together seemlessly. For example, let say you had a spelling checker servlet. This servlet could be chained after any other servlet to spell check any of the ouput being sent to the client. This model for server-side application invocation means that a greater capability can be harnessed from the combination of lesser-capability servlets. Another example might be a language translation servlet. A servlet that perhaps converts English to German could be used to dynamically convert all of our servletsÆ output into a different language. For example, you could chain your servlet to the spell checker servlet then to the German translator servlet. Each servlet need not know about the capabilities of the other, yet they can all work seemlessly together. This is possible because of the use of a consistant server-side API, the Servlet API. The other powerful capability of Servlets is their ability to "communicate" with each other. That is, one servlet can request the services of another servlet. For example, let say you created a servlet that handled database connectivity. You could have that servlet loaded on Web server startup, then any subsequent servlet could use the facilities of the database servlet without implementing database code directly. Figure 2 shows a portion of code that exemplifies such a behavior.
DatabaseServlet dbServlet = (DatabaseServlet) context.getServlet ("DatabaseServlet"); ResultSet rs = dbServlet.executeQuery("SELECT * From Table"); Figure 2 shows how a servlet could request another servlet by name then execute a method, in this case, an SQL query that returns a ResultSet object. Any servlet could make use of that method.
One of the main problems with traditional CGI applications is performance. A new process is created each time a CGI application is requested by the client. This can lead to performance problems at popular web sites that handle requests from multiple users. Servlets, on the other hand, handle this process more efficiently. The first time a servlet is requested, it is loaded into the web serverÆs memory space. Subsequent client requests for the servlet result in calls to the servlet instance in memory. Also, servlets can use threading to process multiple requests efficiently if the JVM embedded in the web server offers thread support, whereas CGI programs are not threaded. Another benefit of servlets over CGI is that servlets are platform-independent, whereas CGI programs are platform-dependent. Another difference between servlets and CGI is the maintenance of state information. A servlet maintains memory of its state once it is loaded by the server. The JVM running on the web server loads the servlet when it is called. CGI programs are stateless because they result in the creation of a new process each time a request is serviced.
Servers with built-in Servlet Support
Adding Servlet Support to Existing Web Servers Example: Creating a Database Servlet using Data Express The purpose of this topic is to help you become acquainted with using the Java Servlet API along with using JBuilderÆs Data Express to connect a servlet to a database server. By using the Java Servlet API as the middleware to the database server, users will be able to connect to a database using the HTTP transport. A simple example is used to illustrate the steps on creating this model. There will be three tiers to this model. The first tier is the web browser. The second tier is the servlet implementation, which we will call SimpleServlet. The third tier is the back-end database server. Following examples will provide illustrations on how to expand the database servlet (SimpleServlet) to be accessible via remote method calls. One example will explain how to expand SimpleServlet to be an RMI server. The other example will explain how to expand SimpleServlet to be a CORBA server. Adding such functionality to SimpleServlet will eventually make the servlet accessible not only through HTTP, but through the RMI, or CORBA transport. The development of these examples will be illustrated using JBuilderÆs built-in RMI and CORBA support respectively.
See also:
A Simple Database Servlet
The completed files for this example is in <LOCATION OF SimplServlet>. This directory contains the project named SimpleServlet.jpr and three java files called SimpleServlet.java, IDataTable.java, DataTableImpl.java.
Creating the SimpleServlet in JBuilder
The generated code should resemble the following:
Example 1-1: SimpleServlet.java package JBExamples.database.servlets.simple; import javax.servlet.*; import javax.servlet.http.*; import java.io.*; import java.util.*; public class SimpleServlet extends HttpServlet { //Initialize global variables public void init(ServletConfig config) throws ServletException { super.init(config); } //Service the request public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String dbQuery = ""; try { dbQuery = request.getParameter("dbQuery"); } catch (Exception e) { e.printStackTrace(); } } //Get Servlet information public String getServletInfo() { return "JBExamples.database.servlets.simple.SimpleServlet Information"; } } The init method for now contains a single call to the super classeÆs init method, this will later be modified to accomadate the next step, which will involve adding database connectivity functionality.
Adding database functionality to SimpleServlet Adding a Database component to SimpleServlet To add the Database component to SimpleServlet,
Database database1 = new Database();
For the Database object to be usable, you need to set its connection properties. The ConnectionDescriptor object holds the values to these properties. You can access this object programmatically or set connection properties through the user interface by the following steps:
Adding a QueryDataSet component to the SimpleServlet To add a QueryDataSet component, follow the same steps as with adding a Database component. Once the QueryDataSet has been added set the query properties of the QueryDataSet component from the Inspector as follows:
Lets try a test query. To do this, type a valid sql query in the SQL statement text box. Click Test Query to ensure that the query is runnable. When the gray area beneath the button indicates Success. Delete the query statement from the text box. For this example we want to dynamically generate the query. In the Create ResourceBundle dialog, select Cancel. For a discussion of this option, see Dissecting the query dialog The code generated by this step is: queryDataSet1.setQuery(new borland.sql.dataset.QueryDescriptor(database1, "", null, true, Load.ALL)); SimpleServlet should look something like the following:
Example 1-2: SimpleServlet.java package JBExamples.database.servlets.simple; import javax.servlet.*; import javax.servlet.http.*; import java.io.*; import java.util.*; import borland.sql.dataset.*; public class SimpleServlet extends HttpServlet { Database database1 = new Database(); QueryDataSet queryDataSet1 = new QueryDataSet(); //Initialize global variables public void init(ServletConfig config) throws ServletException { super.init(config); } //Service the request public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //A database query String dbQuery = ""; try { dbQuery = request.getParameter("dbQuery"); } catch (Exception e) { e.printStackTrace(); } } //Get Servlet information public String getServletInfo() { return "JBExamples.database.servlets.simple.SimpleServlet Information"; } public SimpleServlet() { try { jbInit(); } catch (Exception e) { e.printStackTrace(); } } private void jbInit() throws Exception { database1.setConnection(new borland.sql.dataset.ConnectionDescriptor( "jdbc:odbc:DemoDatabase", "", "", false, "sun.jdbc.odbc.JdbcOdbcDriver")); queryDataSet1.setQuery(new borland.sql.dataset.QueryDescriptor(database1, "", null, true, Load.ALL)); } }
Preparing SimpleServlet for multi-threaded situations
Example 1-3: SimpleServlet.java package JBExamples.database.servlets.simple; import javax.servlet.*; import javax.servlet.http.*; import java.io.*; import java.util.*; import borland.sql.dataset.*; import borland.jbcl.dataset.DataSetException; public class SimpleServlet extends HttpServlet { Database database1 = new Database(); //Initialize global variables public void init(ServletConfig config) throws ServletException{ super.init(config); try { jbInit(); } catch(Exception e) { e.printStackTrace(); } } //Close the database connection public void destroy() { try { System.out.println("SimpleServlet closing database connection."); database1.closeConnection(); } catch(DataSetException e) { System.out.println("Error in closing the database connection"); } } /** * Returns a data table containing the results of */ public IDataTable getResultTable(String dbQuery) { IDataTable dataTable = null; try { QueryDataSet queryDataSet = new QueryDataSet(); queryDataSet.setQuery(new borland.sql.dataset.QueryDescriptor(database1, dbQuery, null, true, Load.ALL)); //Query will be executed when QueryDataSet is open queryDataSet.open(); dataTable = new DataTableImpl(queryDataSet); queryDataSet.close(); } catch(Exception e) { e.printStackTrace(); } return dataTable; } //Service the request public void service(HttpServletRequest request, HttpServletResponse
The Changes to SimpleServlet The getResultTable method executes the given query string by simply calling the open method of the QueryDataSet instance. Once opened, the QueryDataSet instance is passed to a constructor of a convenience class DataTableImpl. DataTableImpl is responsible for formatting the query results into an HTML formatted table, or, a tab/newline delimited string. An implementation of the destroy method has also been added. The destroy method is used to close the database connection. The service method now has the code to get the results of the database query back to the client. The method call req.setContentType("text/html") tells the client browser to expect html text. This gives the client browser a chance to interpret the data as specified. A stream to the client is obtained by a call to out.getWriter(). A PrintWriter object is returned and eventually used to write the results of the query in an HTML formatted table which is obtained through the method call dataTable.toHTMLString(). The code for DataTableImpl and its interface IDataTable is shown below. Example 1-4a: DataTableImpl..java
package JBExamples.database.servlets.simple; import borland.sql.dataset.QueryDataSet; import borland.jbcl.dataset.DataRow; import borland.jbcl.dataset.DataSetException; import java.util.Vector; public class DataTableImpl implements IDataTable { /* * A convenience class that copies the data from QueryDataSet * into a table of Strings. */ private String[] columnNames; private Vector tableData; private int columnCount; private int rowCount; public DataTableImpl() { super(); } public DataTableImpl(QueryDataSet queryDataSet) throws DataSetException { DataRow dataRow = new DataRow(queryDataSet); this.columnCount = dataRow.getColumnCount(); this.rowCount = queryDataSet.getRowCount(); this.columnNames = dataRow.getColumnNames( columnCount ); copyQueryData(queryDataSet, dataRow); } private void copyQueryData(QueryDataSet queryDataSet, DataRow dataRow) throws DataSetException { tableData = new Vector(); for(int i=0; i<rowCount; i++) { String[] data = new String[columnCount]; queryDataSet.getDataRow(i, dataRow); for(int j=0; j<columnCount; j++) { data[j] = dataRow.format(j); } tableData.addElement(data); } } public String[] getColumnNames() { return this.columnNames; } public int getRowCount() { return this.rowCount; } public int getColumnCount() { return this.columnCount; } public String getDataAt(int row, int column) { return ((String[])tableData.elementAt(row))[column]; } //Return a tab/newline delimited String public String toTableString() { return toTableString("","\n","","\n","","\t"); } //Return an HTML String representation of this object public String toHTMLString() { return toTableString( "<table width=\"80%\" height=\"80%\"border=\"1\">", "</table>\n","<tr align=center>","</tr>\n","<td>","</td>"); } // Convert the table data of this object to a String using the given table // delimiters. private String toTableString(String tblStart, String tblEnd, String rowStart, String rowEnd, String dataStart, String dataEnd ) { StringBuffer results = new StringBuffer(); try { results.append(tblStart); //Column names results.append(rowStart); for(int i=0; i Example 1-4b: IDataTable.java package JBExamples.database.servlets.simple; public interface IDataTable { public String[] getColumnNames(); public int getRowCount(); public int getColumnCount(); public String getDataAt(int row, int column); public String toHTMLString(); public String toTableString(); } In DataTableImpl, an instance of DataRow is used to get the column names, and the column count of the QueryDataSet object. The row count is obtained from the QueryDataSet object. The method copyQueryData, does just that. It traverses through the QueryDataSet and copies them into a Vector. Each element of the Vector will consist of a String[] containing the data of one row. The row data is obtained from the method call queryDataSet.getDataRow(i, dataRow). This populates dataRow with the data at the specified row i, in queryDataSet. The method call dataRow.format(j) returns the data at the specified column j as a String type. Right about now you are probably wondering, why we need an interface for DataTableImpl. The reason for this will become clear in the next section when we expand SimpleServlet to be an RMI server.
Invoking SimpleServlet Note: If youÆve created SimpleServlet within a package, you will have to create your directory structure that maps to the package name under the servlets directory. So if SimpleServlet belonged to a package foo.bar. You will have to create the directory structure foo/bar under the servlets directory, and copy your SimpleServlet class files into the bar directory. In short: Class name: foo.bar.SimpleServlet Location: Before the servlet is to be envoked, the servlet runner has to know the location of Borland specific classes. This is done by setting the user classpath in the servlet runnerÆs admin. The Borland specifi class files used in SimpleServlet are in the jar file called jbcl2.0.jar under the <jbuilder_root>/lib directory. The following screen shot illustrates where the path to the jar file should be set in JRunÆs Admin. The field, User Classpath:
In order for this change to take affect, you will have to stop and restart your web server. If this classpath was already specified, there is no need to stop and restart your web server. Once youÆve done this, the next step is to call the SimpleServlet. Since SimpleServlet handles requests through its service method, you can call SimpleServlet in four popular ways.
Calling SimpleServlet through the <servlet> tag Follow the steps to call a servlet with the servlet tag.
<HTML> <BODY> <servlet code=SimpleServlet> <param name="dbQuery" value="select * from mydatabase"> </servlet> </BODY> </HTML>
For cases where you have created SimpleServlet in a package, the full class name is required in the <servlet> tag. So, if SimpleServlet belonged to a package foo.bar, the servlet tag would be <servlet code=foo.bar.SimpleServlet>.
Calling SimpleServlet through POST To call SimpleServlet using post, try the following.
<HTML> <BODY> <FORM method="post" action="/servlet/SimpleServlet "> <PRE> Enter Query: <input type="text" size=80 name="dbQuery"> <input type="submit" value="Execute Query"> </PRE> </FORM> </BODY> </HTML>
For cases where you have created SimpleServlet in a package, the full class name is required in the <FORM> tag. So, if SimpleServlet belonged to a package foo.bar, the <FORM> tag would be <FORM method="post" action="/servlet/foo.bar.SimpleServlet">. Calling SimpleServlet through GET To call SimpleServlet with get, try the following.
http://localhost/servlet/SimpleServlet?dbQuery=select+*+from+mydatabase Calling SimpleServlet from an applet The following class is responsible for connecting to and querying a database servlet. package JBExamples.database.applets; import java.util.*; import java.net.*; import java.io.*; public class HTTPTransportClient implements ITransportClient { String servletLoc; public HTTPTransportClient(String servletLoc) { this.servletLoc = servletLoc; } public String executeQuery(String queryString) throws Exception { StringBuffer results = new StringBuffer(); //The value of dbquery has to be urlencoded to be passed through //http. String postData = "dbquery="+URLEncoder.encode(queryString); URL action = new URL(servletLoc); URLConnection url = action.openConnection(); url.setDoInput(true); url.setDoOutput(true); url.setUseCaches(false); url.setRequestProperty("Content-type", "application/x-www-form-urlencoded"); url.setRequestProperty("Content-length", "" + postData.length()); // Write out post data DataOutputStream out = new DataOutputStream(url.getOutputStream()); out.writeBytes(postData); out.flush(); out.close(); // Get response DataInputStream in = new DataInputStream(new BufferedInputStream(url.getInputStream())); String line; while(null != ((line = in.readLine()))) results.append(line + "\n"); return results.toString(); } } The executeQuery() method illustrates how to communicate to the JDBC servlet using the HTTP transport. This method also illustrates how general applet to servlet communication can be carried out. A simple test applet in later examples will illustrate using the above class to display the results of a query. Continue to Part II Back To TopHome Page |