Programmers can often come across a servlet application that connects to a company's database and provides customers with a specific service that is protected by a powerful authentication mechanism that thousands of customers worldwide use. The question now arises: How will you provide user access to the database from the application when the application is outside the company's firewall? You know, a network administrator is not going to open a special port specifically for your application to connect to the database.
HTTP tunneling Technology and XML
How to connect to a client/server application over a firewall this problem has been bothering programmers for a long time. In most cases, a company's firewall always opens the port as little as possible. In general, the only port you can use is 80, which is the port used by the Web.
The way to solve this problem is to use the HTTP Tunneling Technique (HTTP tunneling). This method first wraps the request in an HTTP POST request, and then the request is processed by a CGI application (such as a servlet) on a Web server inside the firewall.
The servlet restores the original request, executes it, and then inserts the result into the HTTP response stream. The firewall interprets this interaction as a general request for a Web page and allows it to continue processing. This is like a Trojan horse: it looks like a normal request, but it hides the unexpected load.
The next question is how do you format the request? Of course, using XML is the best choice to send these loads to an HTTP request. The idea is ingenious, and the XML above HTTP is a hot emerging field, and some new rules are being written to become standard communication protocols for distributed applications in the future. Among them, simple Object Access Protocol (SOAP) is the most recognized.
Unfortunately, there is no stable soap for us to use, and the best we can find now is from the Apache group, but it only supports simple return types. Therefore, it is of no use to our project. But that's also good, which means we can propose our own XML protocol on HTTP and learn the concepts it contains.
Concept
Let's now build a simple framework structure that takes XML on HTTP as a basic communication strategy, allowing us to create a set of services that can be accessed from desktop applications that are distributed all over the Internet.
First, we need to establish the syntax for common requests and responses. The request looks like this:
<?xml version= ' 1.0 ' encoding= ' utf-8 '?>
<requestType>
[Type of request]
</requestType>
<request>
[Application specific request. This is an XML elment]
</request>
The response looks like this:
<?xml version= ' 1.0 ' encoding= ' utf-8 '?>
<response>
<responseMessage>
[The response message]
</responseCode>
<responseCode>
[A application specific return code.]
</responseCode>
<response>
[Application specific request. This'll is an XML Element]
</response>
To understand the concept behind this framework structure, we want to write an application service routine: A simple database service that handles any SQL statement and returns the result as a vector. The details of the request, which are the XML tags contained in the request element, are simple as follows:
<sql-statement>
[The SQL statement to is executed]
</sql-statement >
The response result is this:
<result-set>
</result-count>
[The number of rows in the ' result set]
</result-count>
<row>
<col name= ' name ' >
[The value]
</col>
?
</row>
?
<result-set>
Httpservice is a servlet that responds to a POST request, restores the XML payload, and uses it to create a servicerequest example. Then, depending on the type of request, the request is given to a specific subclass of the Httpservicehandler abstract class. The handler class executes the request, stores the result in an example of Serviceresponse, and sends the result back to the client application.
By following the convention of naming service processor classes, we can use the image function of Java to create an example of the processor class, which is based solely on the service type attributes of the Servicerequest object. This cancels the dependency between Httpservice and all service processor classes, which means that when we add a new service, we no longer need to change the Httpservice class.
In our case, the type of service is dbservice, so we'll create a subclass of Httpservicehandler, called Dbservicehandler. In Httpservice, we use the following code:
A httpservicehandler subclass needs to execute a method ProcessRequest (), it takes a Servicerequest object, and then returns a Serviceresponse object. This method is invoked by subclasses during the HandleRequest method:
This is a typical example of using the template (template) method pattern, in which an abstract superclass invokes a method executed in a subclass.
The Servicerequest class stores service-specific data as an XML document. This class is used by the visitor to set up and get the type of request, and it has methods to handle the details of the request. The Getrequest () method returns the XML node contained in the request token, and the Setrequest () method overwrites the original request with a newly generated request. This class also uses two factory methods to create new elements and text nodes that allow developers to generate new requests. The Serviceresponse class handles the details of the request in a very simple way.
Although these two classes allow us to process all types of requests and responses, developers must also understand the special syntax for each request. The developer cannot make any acknowledgement of the requested format correctly.
To simplify this process, we'll create subclasses of Servicerequest and Serviceresponse, which are called dbservicerequest and Dbserviceresponse, each with a way to handle service-specific details. For example, Dbservicerequest has a way to set up and get SQL statements, while dbserviceresponse a way to set and get results and to set vectors for values and results.
Services are accessed using the Httpserviceclient class. In the client application, you have the following code:
Httpserviceclient client = new Httpserviceclient (serviceurl);
Dbservicerequest request = new Dbservicerequest ();
Request.setsqlstatement (statement);
Dbserviceresponse response = new Dbserviceresponse (Request) (client.executerequest);
The URL of the service is this:
Http://myHost/servlet/httpservice.HttpService.
Details
We've seen all the elements in the frame structure, and now look at the interesting details. First, let's take a look at the protocol layer. How should we create an HTTP POST request that wraps an XML payload? What should we do with HTTP responses?
The HTTP request is a standardized, ASCII-based, socket communication with a Web server. Here's an example:
The following code shows the execution of the Httpserviceclient class, which handles all the details of the HTTP request. As you can see, once you understand the exact format of those requests, this is a very simple process:
public class Httpserviceclient
{
private static final String http_version = "1.0";
private static final String http_post_request = "POST";
private static final String Header_host = "HOST";
private static final String Header_content_type = "Content-type";
private static final String Xml_mime_type = "Text/xml";
private static Final String
Header_content_length = "Content-length";
private static final int default_port = 80;
I'll ignore all the headers and keep reading
Until I get a empty line
Try
{
String headerline = null;
while ((Headerline = In.readline ())!= null)
{
if (headerline.length () = = 0)
Break
}
}
catch (Exception ex)
{
throw new Httpserviceexception ("Error reading HTTP headers:" + ex.getmessage ());
}
What remains of the input Stream is my payload
Responsepayload = in;
}
private void Parsestatusline (String statusline)
Throws Exception
{
StringTokenizer st =
New StringTokenizer (Statusline);
This is the HTTP Version
St.nexttoken ();
ReturnCode = Integer.parseint (St.nexttoken ());
StringBuffer retmessage = new StringBuffer ();
while (St.hasmoretokens ())
{
Retmessage.append (St.nexttoken ());
if (St.hasmoretokens ())
{
Retmessage.append ("");
}
}
ReturnMessage = Retmessage.tostring ();
}
}
When the Web server accepts an HTTP request, it creates a new example of the Httpservice servlet, and then calls the Dopost () method, which is passed in the HttpServletRequest and HttpServletResponse objects. The servlet then restores the XML payload and creates an example of the Servicerequest class, and finally forwards it to the correct processor:
The following code shows the execution of the Dbservicehandler class, which creates a database connection, executes a query, and generates a Dbserviceresponse object. Also, note that this process is very simple, because many complex problems are hidden behind the Serviceresponse and Servicerequest classes and subclasses:
public class Dbservicehandler extends
Httpservicehandler
{
Public Serviceresponse ProcessRequest (servicerequest req)
Throws Httpserviceexception
{
Dbservicerequest request = new Dbservicerequest (req);
String res = response.serializeresponsetostring ("Utf-8");
System.out.println (RES);
return response;
}
?
}
In the following code, you can see the execution of Servicerequest and dbservicerequest. Note that as Dbservice-request also provides an additional constructor, it will servicerequest as an argument. This allows us to use the original requested XML document as the current document, thus providing an additional application-specific interface to the existing data:
public class Servicerequest
{
Public final static String request_type_tag_name = "RequestType";
Public final static String request_tag_name = "REQUEST";
Public final static String root_tag_name = "Http-request";
Protected Document dom;
Public servicerequest (Document request)
{
dom = Request;
}
Public Servicerequest ()
{
Dom = new Documentimpl ();
InitializeRequest ();
}
Initializes an empty request
private void initializerequest ()
{
Element root = dom.createelement (root_tag_name);
Dom.appendchild (root);
Element Erequesttype =
Dom.createelement (Request_type_tag_name);
Erequesttype.appendchild (Dom.createtextnode (""));
Root.appendchild (Erequesttype);
Element erequest =
Dom.createelement (Request_tag_name);
Root.appendchild (erequest);
}
public class Dbservicerequest extends Servicerequest
{
Public final static String service_name = "Dbservice";
Public final static String sql_statement_tag_name = "Sql-statement";
Public Dbservicerequest ()
{
Super ();
Initializeparameters ();
}
Public dbservicerequest (Document request)
{
Super (Request);
}
Public dbservicerequest (Servicerequest request)
{
dom = Request.dom;
}
public void setsqlstatement (String sql)
{
Settextattribute (Sql_statement_tag_name,sql);
}
Public String getsqlstatement ()
{
Return Gettextattribute (Sql_statement_tag_name);
}
private void Initializeparameters ()
{
Element edbrequest = null;
Not very nice but this should never fail
Extended frame Structure
We can extend this framework to handle any type of service. To create a new service, you must first define the syntax for the XML request and response. Then, you create a subclass of Servicerequest and Serviceresponse that will help with service-specific data. Finally, create a new subclass of the Http-servicehandler that will process the request and generate the appropriate response. This is the whole process.
Although this framework contains some features, it is not practical in practical applications without adding more functionality. I deliberately omitted these features to keep the frame structure simple and focused on the most important details. For the sake of completeness, let's briefly analyze some of the limitations of the framework and how to overcome these limitations.
First, this framework structure does not restrict access to services. This means that everyone who knows how to access the service is able to access it. Before you allow access to a service, ask for some kind of proof to solve the problem. You can use this same framework to create a certification service that confirms the user and generates a unique ID that will be required when the user accesses any other service. The system stores this ID in certain types of access lists, and requires that for a limited period of time, each request must pass a valid ID to access the service.
In addition, each time a user accesses a service, the service creates a connection to the database. Combining the service with a connection pooling framework solves this problem by assigning an existing connection to the request instead of creating a new connection each time.
The last limitation is the lack of session management. Because we are accessing the servlet directly through a socket, we cannot use particularly useful httpsession objects. Because the session cookie is not generated on the client computer during the interaction between the Web server and the browser, automatic sessions cannot be managed. To overcome this limitation, we can perform our own session management. A session can be an object that is associated with a unique ID, and it can store other objects. For example, you can have a context hash signal table in which to store the session object, using a unique ID as the keyword. The session object can also contain a hash signal table that stores each object that you want to stick with in the session.
This framework leverages HTTP tunneling technology to allow a desktop application to access services behind the firewall, extending it to provide easy access to other types of services.
The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion;
products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the
content of the page makes you feel confusing, please write us an email, we will handle the problem
within 5 days after receiving your email.
If you find any instances of plagiarism from the community, please send an email to:
info-contact@alibabacloud.com
and provide relevant evidence. A staff member will contact you within 5 working days.