代写网络程序,聊天程序FTP, Http Proxy, P2P等,语言可以是Java C C++.
page 1
Chapter 13: Web Service Callbacks
and Polling: the Chat Example
The obvious next step we need to take is to rebuild the chat example that we used to illustrate sockets and rmi
using web services. But there are problems...
13.1. Asynchronous Web Services: Polling and Callbacks
As you might expect, web services do provide mechanisms for asynchronous service invocation - using either
polling or callbacks - and we will look at these below. However, they don't help us very much. This is because
the mechanisms are designed to enable clients to call services and then continue without blocking for their
results. Furthermore, it's not easy to build our own version (at least not for callbacks - we can do polling
straightforwardly enough) because the architectural focus for asynchronous service calls is entirely on the
client: that is, a web service is built regardless of how it will be invoked and the choice of how to invoke it -
synchronous (blocking) or ansynchronous (non blocking) is made solely at the client. And there is no mechanism
to allow a service to contact a client except in response to an invocation from a client. Therefore, we can't use
this mechanism to implement the chat application.
As an aside, this may seem silly - but you need to remember that a fair amount of the point of web services
is that they are published by an organization and then used by clients as the see fit: there is no strong ('tightly
coupled') link between the service provider and the client. A variety of large organizations now expose access
to (parts of) the business logic of their systems: for example Google, Amazon and eBay. You can fairly easily
track down and look at the published WSDL descriptions for these services, that are made available to potential
clients. To make it easier, Amazon's is at:
http://soap.amazon.com/schemas2/AmazonWebServices.wsdl
You may have to select 'view source' in your browser to see anything sensible. Notice complex the Amazon
WSDL is - representing a real, useable service - much more complex than the small examples we are going to
see.)
Can we build our own mechanism - which is more or less what we had to do in RMI - to enable a client to call a
service and pass a reference to another service of its own (technically called an endpoint) that the client can then
call? Well, possibly, but not easily. This is because the code to communicate with services is generated, from the
WSDL, statically at compile time. Since the WSDL for each client will be slightly different (the service URL
will not be the same), it's not obvious how we can do this. In fact, there are mechanisms to enable this but they
are not something we have the time to go into.
The slightly better news is that although we cannot use the built-in mechanisms for callbacks or polling, it's
pretty easy for us to build our own polling mechanism - which is what we will do.But first we will look at the the
asynchronous tools that are provided - even though they are not useful for our application, they are important.
13.1.1. Polling
Polling is the easiest mechanism to understand, just as in RMI, and centers around an interface called
java.util.concurrent.Future:
public interface Future<V>{
boolean cancel(boolean mayInterruptIfRunning);
boolean isCancelled();
boolean isDone();
V get() throws InterruptedException, ExecutionException;
V get(long timeout, TimeUnit unit) throws
InterruptedException,ExecutionException,TimeoutException;
}
JAX-WS provides mechanisms for generating objects implementing Future which are passed back to the
client when a service is invoked asychronously. This object allows the client to monitor, control and retrieve the
result of the service. Most of the methods should be pretty obvious: get() retrieves the result if the service has
finished and waits otherwise; the other get waits for a specified period of time.
13.1.2. Callbacks
This is a bit more complicated in practice, though in principle is quite straightforward: you create an instance of
a class called AsyncHandlerService linking it to the service you are calling, and a method to be invoked
when that service has finished.page 2
13.2. WSChat with Polling
Since we can't easily use callbacks (or at least we don't have time to built a callback version - and it would be
sufficiently complex that it would not be easy to see what's going on) we will build a polling version instead.
Because the 'built in' mechanisms to handle polling won't work for us, we need to build our own: our client
will simply use a thread to periodically call the service and retrieve any waiting messages. This is not quite as
completely basic as it sounds: the service needs to keep a queue of unretrieved messages for each client. Our
Web Service chat application is less complex than the previous ones, because you're going to build a better one
for your coursework.
13.2.1. The Service
The code for the web service is very simple - though it suffers a serious drawback that would be a problem in a
real application.
package wschatserver;
import javax.jws.WebService;
import javax.jws.WebMethod;
import java.util.*;
@WebService
public class ChatServer {
private static ArrayList<String> serverList = new ArrayList<String>();
@WebMethod
public int join(){
synchronized (serverList) {
int id = serverList.size();
serverList.add("");
return id;
}
}
@WebMethod
public void talk(String message){
synchronized(serverList){
for(int i = 0;i < serverList.size();i++){
String val = serverList.get(i) + message;
serverList.set(i,val);
}
}
}
@WebMethod
public String listen(int id) {
synchronized(serverList){
String temp = serverList.get(id);
serverList.set(id,"");
return temp;
}
}
@WebMethod
public void leave(int ID){
/* Do nothing! */
}page 3
}
·
join. When a new client calls join it is assigned a slot in a (static) ArrayList and its position in that
list becomes its identifier.
·
talk. When a client sends text, that text is added to all the ArrayList elements. Obviously a bit
inefficient since we are copying text - but we do not know when clients will poll to collect messages, so
we cannot just keep one copy.
·
listen. When a client polls to collect messages, all text stored in the ArrayList at the location
corresponding to its identifier is sent back, and then deleted.
·
leave. This currently does nothing - because if we actually deleted elements from the ArrayList we
would mess up the ordering of identifiers - this is obviously a significant disadvantage which we won't
bother to fix here. It's not actually that hard to fix - we just need to 'decouple' client identifiers from their
position in the arraylist. We could do this with an arraylist of objects that include a name as well as the
string of as-yet-unread messages instead of just an arraylist of strings.
Unlike the previous versions, we will not attempt to improve the efficiency of the server by reducing the amount
of synchronized code - though we could if we chose to.
You can download the code.
13.2.2. The Client
The client is very similar to the socket based version (and was edited from that in fact). The only significant
change is that the thread waiting for incoming messages now actively polls the service.
Again you can download the code - the version below leaves out the various GUI bits..
package wschatclient2;
import java.io.*;
import java.net.*;
import javax.swing.*;
import java.awt.event.*;
import java.awt.*;
import javax.xml.ws.WebServiceRef;
import wschatserver2.ChatServer2Service;
import wschatserver2.ChatServer2;
public class ChatClient2 {
@WebServiceRef(wsdlLocation="http://localhost:8080/WSChatServer2/
ChatServer2Service?wsdl")
...
private ChatServer2Service service;
private ChatServer2 port;
private int id;
private void initComponents(String host) {
...
try {
service = new wschatserver2.ChatServer2Service();
port = service.getChatServer2Port();
id = port.join();
otherTextThread = new TextThread(otherText, id, port);
otherTextThread.start();page 4
frame.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
try {
port.leave(id);
}
catch (Exception ex) {
otherText.append("Exit failed.");
}
System.exit(0);
}
});
}
catch (Exception ex) {
otherText.append("Failed to connect to server.");
}
}
private void textTyped(java.awt.event.KeyEvent evt) {
char c = evt.getKeyChar();
if (c == '\n'){
try {
String waiting = port.talk(textString,id);
otherText.append(waiting + "\n");
}
catch (Exception ie) {
otherText.append("Failed to send message.");
}
textString = "";
} else {
textString = textString + c;
}
}
public static void main(String[] args) {
/*if (args.length < 1) {
System.out.println("Usage: AppChatClient host");
return;
}*/
//TODO put CLI for host back in
final String host = "localhost";
javax.swing.SwingUtilities.invokeLater(new Runnable() {
ChatClient2 client = new ChatClient2();
public void run() {
client.initComponents(host);
}
});
}
}
class TextThread extends Thread {page 5
ObjectInputStream in;
JTextArea otherText;
int id;
ChatServer2 port;
TextThread(JTextArea other, int id, ChatServer2 port) throws
IOException
{
otherText = other;
this.id = id;
this.port = port;
}
public void run() {
while (true) {
try {
String newText = port.listen(id);
if (!newText.equals("")) {
otherText.append(newText + "\n");
}
Thread.sleep(1000);
}
catch (Exception e) {
otherText.append("Error reading from server.");
//return;
}
}
}
}
When the client starts up, it calls join and gets an ID; when the user types return, it calls talk to send any text
collected; when the window closes it calls leave (even though the service does not handle it, there is no need to
build the client assuming that it doesn't); and the thread TextThread polls the service every second to collect
messages.
13.3. A Slight Improvement
Although the polling is not great, in practice the only place you notice an issue is the delay between typing
some text and its appearance in your own client. We could probably actually extend the polling interval (and
save resources) quite happily except for this. The obvious change to make is to get the talk method to return
any waiting messages - sort of like picking up your mail when you're passing. There are only a few significant
changes to the service and client, which are shown below.
@WebMethod
public String talk(String message, int id){
synchronized(serverList){
for(int i = 0;i < serverList.size();i++){
String val = serverList.get(i) + message;
serverList.set(i,val);
}
}
return listen(id);
}
And the client:
private void textTyped(java.awt.event.KeyEvent evt) {page 6
char c = evt.getKeyChar();
if (c == '\n'){
try {
String waiting = port.talk(textString,id);
otherText.append(waiting + "\n");
}
catch (Exception ie) {
otherText.append("Failed to send message.");
}
textString = "";
} else {
textString = textString + c;
}
}