500 likes | 555 Views
Servlets Chapter 10. Applet/servlet communication (with a database twist). Why would you want applet-servlet communication?. At the time the text was written, jsp had not established itself as THE way to do UI. (It still hasn’t but that’s another story).
E N D
Servlets Chapter 10 Applet/servlet communication (with a database twist)
Why would you want applet-servlet communication? • At the time the text was written, jsp had not established itself as THE way to do UI. (It still hasn’t but that’s another story). • Flex wasn’t around yet as a presentation option. • The java api is big and you can build nice UI with it, although it is not always quick or easy.
What is involved in applet-servlet communication? • The original applet specification prohibited applets from modifying files on the client’s machine, basically treating the applet as “untrusted”. • Later specifications allowed for “trusted” applets, making possible an applet to configure Java Web Server, or an applet to provide UI for a Blog/Chat. • Typically, an “untrusted” applet would need to communicate with the server to present information in a nice format, to the user.
Why the need for applet-servlet communication? • An applet handling chat, or providing dow-jones info would need to communicate with the server for updates.
Before JDK1.1 • Establish http connection to CGI program on server. The applet acts like a browser. • Have applet establish a raw socket connection to a non-http server. This program would listen on some port and communicate using a custom protocol.
Choice 1: pros • Choice 1 is easy to write. Applet can use java.net.URL and .URLConnection classes to manage the channel, and the CGI program can be written as usual. • Even if the applet is behind a firewall it should work. (Firewalls typically allow http connections but disallow raw sockets). • The server-side programming can be done in any language. • It works for jdk1.0 and so works for all java-enabled browsers. • Using https, it would be possible to establish a secure connection. • The same CGI program could be accessed by browsers as well.
Choice 1: cons • It is slow. The two programs can’t communicate interactively. There’s also a delay when the CGI launches. • Requests are typically an array of name/value pairs. • Responses must be formatted in a previously devised manner. • Only the applet can initiate the communication.
Choice 2 pros & cons • Allows bi-directional sustained communication. • The server-side program can be more efficient. • BUT • It fails if the applet is behind a firewall. • The server-side code may be complicated to write. Some process must be listening at a port. • A custom protocol may need to be developed. • The server-side program probably can’t be used by browers.
Servlets and object serialization • Servlets have made much headway replacing slow CGI programs. • Java object serialization simplifies the applet-servlet communication model. • A dedicated communication “object” can be passed back and forth.
JDBC, CORBA, RMI • Jdk1.1 added jdbc and rmi to the java api. • In theory, an applet could use jdbc to directly connect to a database server, although in general, going through a servlet is better. To look up something in the database, an applet might pass information to a servlet which connects to the db and then passes back an object (which might just be a string.)
RMI • Remote method invocation allows an applet to invoke methods of a java object on a server and possibly allows that server object to invoke applet methods, too. • It allows applets and server objects to communicate using a high-level o-o paradigm. • With RMI there is no “request” or “response” – those are http terms. Instead, there are method invokations. An applet might get a stock’s daily high by calling stockserver.dailyhigh(…) • The server can make “call backs” to methods of an applet, maybe indicating a stock price has changed by calling theapplet.update(…) • It can work through a firewall using a technique called tunneling (a non-trivial activity, or RMI could use the HTTP protocol.) HTTP does not support call-backs and has other overhead cost. RMI
RMI • RMI uses “stubs” and “skeleton” classes, because the caller needs to know something about the way the callee looks, works and is called. • RMI uses a naming registry so clients can obtain references to remote objects. • Netscape $.X and up supported RMI (I think Firefox does but haven’t tried it) IE does not support RMI. • RMI requires a java client.
CORBA • Common-object-request-broker-architecture is similar to RMI. • It allows for distributed objects written in different languages. • With CORBA and IIOP communication, a C++ client might communicate with a servlet. This is not covered in the Hunter text.
Hunter’s chapter 10 example(s) • Hunter develops an applet/servlet that use All-of-the-above (as available) for communication. Ie., try RMI, if not supported by the browser, try a socket, but if the applet is behind a firewall, try HTTP. • Hunter’s example is the Daytime Server. It sends time of day. The applet has a GUI in which the time, retrieved via different mechanisms from the servlet, is displayed. • Some methods used are deprecated. • I don’t remember if I tried this in Netscape at the time I built it, but I would have to revisit it to see if it works in Firefox. (See next screen where RMI does NOT come up in IE).
The applet slide#1 import java.io.*; import java.net.*; import java.rmi.*; import java.rmi.registry.*; import java.util.*; import javax.swing.*; import java.awt.event.*; import java.awt.*; import com.oreilly.servlet.HttpMessage; public class DaytimeApplet extends JApplet implements ActionListener{ static final int DEFAULT_PORT = 1313; TextField httpText, httpObject, socketText, socketObject, RMIObject; Button refresh; public void init() { // Construct the user interface Container c=getContentPane(); c.setLayout(new BorderLayout()); // On the left create labels for the various communication // mechanisms Panel west = new Panel(); west.setLayout(new GridLayout(5, 1)); west.add(new Label("HTTP text: ", Label.RIGHT)); west.add(new Label("HTTP object: ", Label.RIGHT)); west.add(new Label("Socket text: ", Label.RIGHT)); west.add(new Label("Socket object: ", Label.RIGHT)); west.add(new Label("RMI object: ", Label.RIGHT)); c.add("West", west);
Applet slide 2 // On the right create text fields to display the retrieved time values Panel center = new Panel(); center.setLayout(new GridLayout(5, 1)); httpText = new TextField(); httpText.setEditable(false); center.add(httpText); httpObject = new TextField(); httpObject.setEditable(false); center.add(httpObject); socketText = new TextField(); socketText.setEditable(false); center.add(socketText); socketObject = new TextField(); socketObject.setEditable(false); center.add(socketObject); RMIObject = new TextField(); RMIObject.setEditable(false); center.add(RMIObject); c.add("Center", center); // On the bottom create a button to update the times Panel south = new Panel(); refresh = new Button("Refresh"); south.add(refresh); c.add("South", south); refresh(); refresh.addActionListener(this); }
Applet slide 3 private void refresh() { // Fetch and display the time values httpText.setText(getDateUsingHttpText()); httpObject.setText(getDateUsingHttpObject()); // socketText.setText(getDateUsingSocketText()); // socketObject.setText(getDateUsingSocketObject()); // RMIObject.setText(getDateUsingRMIObject()); } private String getDateUsingHttpText() { try { // Construct a URL referring to the servlet URL url = new URL( "http://CSCI345.oneonta.edu:8080/myexamples/DaytimeServlet"); // Create a com.oreilly.servlet.HttpMessage to communicate with that URL HttpMessage msg = new HttpMessage(url); // Send a GET message to the servlet, with no query string // Get the response as an InputStream InputStream in = msg.sendGetMessage(); // Wrap the InputStream with a DataInputStream DataInputStream result = new DataInputStream(new BufferedInputStream(in)); // Read the first line of the response, which should be // a string representation of the current time String date = result.readLine(); // Close the InputStream in.close(); // Return the retrieved time return date; } catch (Exception e) { // If there was a problem, print to System.out // (typically the Java console) and return null e.printStackTrace(); return null; } }
Applet slide 4 private String getDateUsingHttpObject() { try { // Construct a URL referring to the servlet URL url = new URL( "http://CSCI345.oneonta.edu:8080/myexamples/DaytimeServlet"); // Create a com.oreilly.servlet.HttpMessage to communicate with that URL HttpMessage msg = new HttpMessage(url); // Construct a Properties list to say format=object Properties props = new Properties(); props.put("format", "object"); // Send a GET message to the servlet, passing "props" as a query string // Get the response as an ObjectInputStream InputStream in = msg.sendGetMessage(props); ObjectInputStream result = new ObjectInputStream(in); // Read the Date object from the stream Object obj = result.readObject(); Date date = (Date)obj; // Return the string representation of the Date return date.toString(); } catch (Exception e) { // If there was a problem, print to System.out // (typically the Java console) and return null e.printStackTrace(); return null; } }
Applet slide 5 private int getSocketPort() { try { return Integer.parseInt(getParameter("socketPort")); } catch (NumberFormatException e) { return DEFAULT_PORT; } } private String getDateUsingSocketText() { InputStream in = null; try { // Establish a socket connection with the servlet Socket socket = new Socket(getCodeBase().getHost(), getSocketPort()); // Print an empty line, indicating we want the time as plain text PrintStream out = new PrintStream(socket.getOutputStream()); out.println(); out.flush(); // Read the first line of the response // It should contain the current time in = socket.getInputStream(); DataInputStream result = new DataInputStream(new BufferedInputStream(in)); String date = result.readLine(); // Return the retrieved string return date; } catch (Exception e) { // If there was a problem, print to System.out // (typically the Java console) and return null e.printStackTrace(); return null; } finally { // Always close the connection // This code executes no matter how the try block completes if (in != null) { try { in.close(); } catch (IOException ignored) { } } } }
Applet slide6 private String getDateUsingSocketObject() { InputStream in = null; try { // Establish a socket connection with the servlet Socket socket = new Socket(getCodeBase().getHost(), getSocketPort()); // Print a line saying "object", indicating we want the time as // a serialized Date object PrintStream out = new PrintStream(socket.getOutputStream()); out.println("object"); out.flush(); // Create an ObjectInputStream to read the response in = socket.getInputStream(); ObjectInputStream result = new ObjectInputStream(new BufferedInputStream(in)); // Read an object, and cast it to be a Date Object obj = result.readObject(); Date date = (Date)obj; // Return a string representation of the retrieved Date return date.toString(); } catch (Exception e) { // If there was a problem, print to System.out // (typically the Java console) and return null e.printStackTrace(); return null; } finally { // Always close the connection // This code executes no matter how the try block completes if (in != null) { try { in.close(); } catch (IOException ignored) { } } } }
Applet slide 7 private String getRegistryHost() { return getCodeBase().getHost(); } private int getRegistryPort() { try { return Integer.parseInt(getParameter("registryPort")); } catch (NumberFormatException e) { return Registry.REGISTRY_PORT; } } private String getRegistryName() { String name = getParameter("registryName"); if (name == null) { name = "DaytimeServlet"; // default } return name; } private String getDateUsingRMIObject() { try { Registry registry = LocateRegistry.getRegistry(getRegistryHost(), getRegistryPort()); DaytimeServer daytime = (DaytimeServer)registry.lookup(getRegistryName()); return daytime.getDate().toString(); } catch (ClassCastException e) { System.out.println("Retrieved object was not a DaytimeServer: " + e.getMessage()); } catch (NotBoundException e) { System.out.println(getRegistryName() + " not bound: " + e.getMessage()); } catch (RemoteException e) { System.out.println("Hit remote exception: " + e.getMessage()); } catch (Exception e) { System.out.println("Problem getting DaytimeServer reference: " + e.getClass().getName() + ": " + e.getMessage()); } return null; } public void actionPerformed(ActionEvent event) { refresh(); }}
You’ll have to fix some stuff • I put the applet’s html and class file in the tomcat/webapps/root directory. • I changed the URL in it so the servlet could be with my other servlets in myexamples. • I upgraded the applet to JApplet with ActionListener from jdk1.1 • Still other deprecated stuff in it I didn’t change.
A class used by the server for RMI import java.util.Date; import java.rmi.Remote; import java.rmi.RemoteException; public interface DaytimeServer extends Remote { public Date getDate() throws RemoteException; }
The servlet slide 1 import java.io.*; import java.net.*; import java.util.*; import javax.servlet.*; import javax.servlet.http.*; import com.oreilly.servlet.RemoteDaemonHttpServlet; public class DaytimeServlet extends RemoteDaemonHttpServlet implements DaytimeServer { public Date getDate() { return new Date(); } public void init(ServletConfig config) throws ServletException { // As before, if you override init() you have to call super.init() super.init(config); }
Servlet slide 2 public void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { // If the client says "format=object" then // send the Date as a serialized object if ("object".equals(req.getParameter("format"))) { ObjectOutputStream out = new ObjectOutputStream(res.getOutputStream()); out.writeObject(getDate()); } // Otherwise send the Date as a normal ASCII string else { PrintWriter out = res.getWriter(); out.println(getDate().toString()); } } public void doPost(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { doGet(req, res); } public void destroy() { // If you override destroy() you also have to call super.destroy() super.destroy(); } // Handle a client's socket connection by spawning a DaytimeConnection // thread. public void handleClient(Socket client) { new DaytimeConnection(this, client).start(); }}
Server slide 3 class DaytimeConnection extends Thread { DaytimeServlet servlet; Socket client; DaytimeConnection(DaytimeServlet servlet, Socket client) { this.servlet = servlet; this.client = client; setPriority(NORM_PRIORITY - 1); } public void run() { try { // Read the first line sent by the client DataInputStream in = new DataInputStream( new BufferedInputStream( client.getInputStream())); String line = in.readLine(); // If it was "object" then return the Date as a serialized object if ("object".equals(line)) { ObjectOutputStream out = new ObjectOutputStream(client.getOutputStream()); out.writeObject(servlet.getDate()); out.close(); } // Otherwise, send the Date as a normal string else { // Wrap a PrintStream around the Socket's OutputStream PrintStream out = new PrintStream(client.getOutputStream()); out.println(servlet.getDate().toString()); out.close(); } // Be sure to close the connection client.close(); } catch (IOException e) { servlet.getServletContext() .log(e, "IOException while handling client request"); } catch (Exception e) { servlet.getServletContext() .log("Exception while handling client request"); } }}
Changes to servlet • The RMI part is not working ---I need to look at the registry stuff to see why. • One thing… jdk1.5 no longer provides skeleton parts of the rmic method for the server side, only stubs are provided, so I might try doing this one in jdk1.4 (to match what the text describes).
MySQL • Click on the administration tool • It will put a ryg stoplight in the bottom corner of your screen • Select this icon (show me) • Start the server standalone
MySQL • MySQL is a real database tool, with security, and an administrative program. (MySQLAdministrator) It can manipulate a number of databases.
In mysqlcc directory • Run mysqlcc • Select query then select insert (or delete) record. • You can also edit the table itself and change the field datatypes.
A servlet that looks into the test database import javax.servlet.*; import javax.servlet.http.*; import java.io.*; import java.util.*; import java.sql.*; public class Servlet2 extends HttpServletJXGB { //Initialize global variables public void init(ServletConfig config) throws ServletException { super.init(config); } //Process the HTTP Get request public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html"); PrintWriter out = new PrintWriter (response.getOutputStream()); Connection conn=myBroker.getConnection(); Statement stmt=null; String query=""; out.println("<html>"); out.println("<head><title>Servlet1</title></head>"); out.println("<body>"); //out.println("Hello World...Servlet2 is running!<BR>");
A servlet that looks into the test database: slide 2 try { stmt = conn.createStatement (); query="select name,grade,age from table1"; out.println("Query: "+query+"<BR>"); ResultSet rset=stmt.executeQuery( query ); while (rset.next ()){ out.prinln("name: "+rset.getString(1)); out.println("grade: "+rset.getString(2)); out.println("age: "+rset.getString(3)); out.println("<BR>"); } }catch (SQLException e2) { System.out.println("SQLException: "+e2); }finally { try{if(stmt != null) {stmt.close();}} catch(SQLException e1){System.out.println("SQLException: "+e1);} myBroker.freeConnection(conn); // Release connection back to pool } out.println("Querying a MySQL table!<BR>"); out.println("</body></html>"); out.close(); } //Process the HTTP Post request public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { } public String getServletInfo() { return "Servlet 2"; } }
HttpServletJXGB: a special database broker class /** * Database Global Broker Superclass. * @version 1.0.0 7/28/99 * @author Marc A. Mnich */ import java.sql.*; import java.io.*; import javax.servlet.*; import javax.servlet.http.*; import sun.misc.*; import java.util.*; import com.javaexchange.dbConnectionBroker.*; import org.gjt.mm.mysql.*; /** * Creates a two-tier database connection pool that can be shared * by many servlets through inheritance. * @version 1.0.0 7/28/99 * @author Marc A. Mnich */ public class HttpServletJXGB extends HttpServlet { protected static DbConnectionBroker myBroker;
HttpServletJXGB slide 2 public void init (ServletConfig config) throws ServletException { super.init(config); if(myBroker == null) { // Only created by first servlet to call Properties p = new Properties(); try { p.load(new FileInputStream("p:\\classes\\JXGBconfig.dat")); String dbDriver = (String) p.get("dbDriver"); String dbServer = (String) p.get("dbServer"); String dbLogin = (String) p.get("dbLogin"); String dbPassword = (String) p.get("dbPassword"); int minConns = Integer.parseInt((String) p.get("minConns")); int maxConns = Integer.parseInt((String) p.get("maxConns")); String logFileString = (String) p.get("logFileString"); double maxConnTime = (new Double((String)p.get("maxConnTime"))).doubleValue(); myBroker = new DbConnectionBroker(dbDriver,dbServer,dbLogin,dbPassword, minConns,maxConns,logFileString,maxConnTime); } catch (FileNotFoundException f) {System.out.println("File not found "+f.toString());} catch (IOException e) {System.out.println("IO Exception "+e.toString());} } } }
.dat file in p:/classes specifies database used by broker dbDriver=org.gjt.mm.mysql.Driver dbServer=jdbc:mysql://localhost/test dbLogin= dbPassword= minConns=10 maxConns=20 logFileString=p:\\classes\\connections.log maxConnTime=2
Applet uses sendPostMessage from HttpMessage class to upload a serailizable object public InputStream sendPostMessage(Serializable obj) throws IOException{ URLConnection con=servlet.openConnection(); con.setDoInput(true); con.setDoOutput(true); con.setUseCaches(false); con.setRequestProperty("Content-Type","application/x-java-serailizable-object"); ObjectOutputStream out= new ObjectOutputStream(con.getOutputStream()); out.writeObject(obj); out.flush(); out.close(); return con.getInputStream(); }
The applet passing & receiving (string) objects from servlet
Object applet: refresh display button code private void refresh() { // Fetch and display the time values String s=httpSendObject.getText(); try{ if(s.length()>0){ URL url = new URL( "http://CSCI345.oneonta.edu:8080/myexamples/ObjectServlet"); HttpMessage msg=new HttpMessage(url); InputStream in=msg.sendPostMessage(s); ObjectInputStream br=new ObjectInputStream (in); String tmp=(String) br.readObject(); httpSendObject.setText("msg"+tmp); }//if httpReceiveObject.setText(getHttpObject()); }//try catch(IOException e){} catch(ClassNotFoundException e){} }
ObjectServlet public String getMessage(){ct++;return message+ct;} public void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { // If the client says "format=object" then // send the Date as a serialized object if ("object".equals(req.getParameter("format"))) { ObjectOutputStream out = new ObjectOutputStream(res.getOutputStream()); out.writeObject(getMessage()); } else{ PrintWriter out = res.getWriter(); out.println(getMessage()); }
ObjectServlet public void doPost(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { ObjectInputStream objin= new ObjectInputStream(req.getInputStream()); try{ String s= (String) objin.readObject(); System.out.println("from applet"+s); } catch(ClassNotFoundException e){System.out.println("class not found ex line 37");} }//method
ObjectServlet public void handleClient(Socket client) { new ObjectConnection(this, client).start(); }} class ObjectConnection extends Thread { ObjectServlet servlet; Socket client;
ObjectServlet, thread continued public ObjectConnection(ObjectServlet servlet, Socket client) { this.servlet = servlet; this.client = client; setPriority(NORM_PRIORITY - 1); } public void run() { try { // Read the first line sent by the client ObjectInputStream in = new ObjectInputStream( client.getInputStream()); String line = (String) in.readLine(); // If it was "object" then return the message as a serialized object if ("object".equals(line)) { ObjectOutputStream out = new ObjectOutputStream(client.getOutputStream()); out.writeObject(servlet.getMessage()); out.close(); } // Be sure to close the connection client.close(); } catch (IOException e) { servlet.getServletContext() .log(e, "IOException while handling client request"); } catch (Exception e) { servlet.getServletContext() .log("Exception while handling client request"); } }}