changed folder name
This commit is contained in:
parent
4503531ec9
commit
80c6a52c69
73 changed files with 0 additions and 0 deletions
504
src/zutil/net/FTPClient.java
Normal file
504
src/zutil/net/FTPClient.java
Normal file
|
|
@ -0,0 +1,504 @@
|
|||
package zutil.net;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.PrintStream;
|
||||
import java.net.Socket;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedList;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import javax.security.auth.login.AccountException;
|
||||
|
||||
import zutil.io.MultiPrintStream;
|
||||
|
||||
/**
|
||||
* A simple FTP client class
|
||||
*
|
||||
* @author Ziver
|
||||
*
|
||||
* http://en.wikipedia.org/wiki/List_of_FTP_commands
|
||||
* http://en.wikipedia.org/wiki/List_of_FTP_server_return_codes
|
||||
* http://www.ietf.org/rfc/rfc959.txt
|
||||
*
|
||||
* TODO: file info, rename, Active mode
|
||||
*/
|
||||
public class FTPClient extends Thread{
|
||||
public static boolean DEBUG = true;
|
||||
|
||||
public static final int FTP_ACTIVE = 0;
|
||||
public static final int FTP_PASSIVE = 1;
|
||||
public static final int FTP_PORT = 21;
|
||||
public static final int FTP_DATA_PORT = 20;
|
||||
public static final int FTP_NOOP_INT = 120;
|
||||
|
||||
//************** FTP Return Codes ******************
|
||||
public static final int FTPC_USER_OK = 331;
|
||||
public static final int FTPC_NEED_PASS = 331;
|
||||
public static final int FTPC_LOGIN_NO = 530;
|
||||
public static final int FTPC_LOGIN_OK = 230;
|
||||
|
||||
public static final int FTPC_ENTERING_PASSIVE = 227;
|
||||
public static final int FTPC_FILE_ACTION_OK = 250;
|
||||
public static final int FTPC_PATH_CREATED = 257;
|
||||
//***************************************************
|
||||
|
||||
private BufferedReader in;
|
||||
private PrintStream out;
|
||||
private Socket socket;
|
||||
private long last_sent;
|
||||
|
||||
public static void main(String[] args){
|
||||
try {
|
||||
FTPClient client = new FTPClient("213.180.86.135", 21, "administrator", "geineZ2K", FTP_PASSIVE);
|
||||
/*
|
||||
client.createDir("./ziver/lol");
|
||||
client.removeDir("./ziver/lol");
|
||||
|
||||
MultiPrintStream.out.dump(client.getFileList("./ziver"));
|
||||
client.sendFile("./ziver/test.txt", "lol");
|
||||
MultiPrintStream.out.dump(client.getFileList("./ziver"));
|
||||
|
||||
MultiPrintStream.out.dump(client.getFile("./ziver/test.txt"));
|
||||
client.readCommand(DEBUG);
|
||||
|
||||
MultiPrintStream.out.println(client.getFileInfo("./ziver/test.txt"));
|
||||
|
||||
MultiPrintStream.out.dump(client.getFileList("./ziver"));
|
||||
client.removeFile("./ziver/test.txt");
|
||||
MultiPrintStream.out.dump(client.getFileList("./ziver"));
|
||||
*/
|
||||
ArrayList<String[]> tmp = client.getFileInfo("");
|
||||
MultiPrintStream.out.println("****************");
|
||||
MultiPrintStream.out.dump(tmp);
|
||||
MultiPrintStream.out.println(tmp.size());
|
||||
MultiPrintStream.out.println("****************");
|
||||
client.close();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a FTP connection and logs in
|
||||
*
|
||||
* @param url The address to server
|
||||
* @param port Port number
|
||||
* @param user User name
|
||||
* @param pass Password
|
||||
* @param connection_type Pasive or Active
|
||||
*/
|
||||
public FTPClient(String url, int port, String user, String pass, int connection_type) throws UnknownHostException, IOException, AccountException{
|
||||
socket = new Socket(url, port);
|
||||
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
|
||||
out = new PrintStream(socket.getOutputStream());
|
||||
|
||||
readCommand(DEBUG);
|
||||
sendCommand("USER "+user);
|
||||
sendNoReplyCommand("PASS "+pass, DEBUG);
|
||||
if(DEBUG)System.out.println("PASS ***");
|
||||
String tmp = readMultipleCommands(DEBUG);
|
||||
if(parseReturnCode(tmp) == FTPC_LOGIN_NO){
|
||||
close();
|
||||
throw new AccountException(tmp);
|
||||
}
|
||||
|
||||
start();
|
||||
}
|
||||
|
||||
//**************************************************************************************
|
||||
//**************************************************************************************
|
||||
//********************************* Command channel ************************************
|
||||
|
||||
/**
|
||||
* Sends the given line to the server and returns a status integer
|
||||
*
|
||||
* @param cmd The command to send
|
||||
* @return The return code from the server
|
||||
* @throws IOException
|
||||
*/
|
||||
public synchronized int sendCommand(String cmd) throws IOException{
|
||||
return parseReturnCode(sendCommand(cmd, DEBUG));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the given line to the server and returns the last line
|
||||
*
|
||||
* @param cmd The command to send
|
||||
* @param print To print out the received lines
|
||||
* @return Last String line from the server
|
||||
* @throws IOException
|
||||
*/
|
||||
private synchronized String sendCommand(String cmd, boolean print) throws IOException{
|
||||
sendNoReplyCommand(cmd, print);
|
||||
return readCommand(print);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a given command and don't cares about the reply
|
||||
*
|
||||
* @param cmd The command
|
||||
* @param print If it should print to System.out
|
||||
* @throws IOException
|
||||
*/
|
||||
private synchronized void sendNoReplyCommand(String cmd, boolean print) throws IOException{
|
||||
out.println(cmd);
|
||||
last_sent = System.currentTimeMillis();
|
||||
if(print)System.out.println(cmd);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads on line from the command channel
|
||||
*
|
||||
* @param print If the method should print the input line
|
||||
* @return The input line
|
||||
* @throws IOException
|
||||
*/
|
||||
public synchronized String readCommand(boolean print) throws IOException{
|
||||
String tmp = in.readLine();
|
||||
if(print)System.out.println(tmp);
|
||||
if(parseReturnCode(tmp) >= 400 ) throw new IOException(tmp);
|
||||
|
||||
return tmp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads from the command channel until there are nothing
|
||||
* left to read and returns the last line
|
||||
*
|
||||
* @param print To print out the received lines
|
||||
* @return The last received line
|
||||
* @throws IOException
|
||||
*/
|
||||
private synchronized String readMultipleCommands(boolean print) throws IOException{
|
||||
String tmp = readCommand(print);
|
||||
while(!tmp.substring(3, 4).equalsIgnoreCase(" ")){
|
||||
tmp = readCommand(print);
|
||||
}
|
||||
|
||||
/*
|
||||
String tmp = in.readLine();
|
||||
if(print)System.out.println(tmp);
|
||||
try{ Thread.sleep(500); }catch(Exception e){}
|
||||
while(in.ready()){
|
||||
tmp = in.readLine();
|
||||
if(print)System.out.println(tmp);
|
||||
try{ Thread.sleep(500); }catch(Exception e){}
|
||||
}
|
||||
*/
|
||||
return tmp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the return line from the server and returns the status code
|
||||
*
|
||||
* @param msg The message from the server
|
||||
* @return The status code
|
||||
* @throws IOException
|
||||
*/
|
||||
private synchronized int parseReturnCode(String msg){
|
||||
return Integer.parseInt(msg.substring(0, 3));
|
||||
}
|
||||
|
||||
//**************************************************************************************
|
||||
//**************************************************************************************
|
||||
//****************************** File system actions ************************************
|
||||
|
||||
/**
|
||||
* Returns a LinkedList with the names of all the files in the directory
|
||||
*
|
||||
* @param path Path to the files to be listed
|
||||
* @return LinkedList whit filenames
|
||||
* @throws IOException
|
||||
*/
|
||||
public LinkedList<String> getFileList(String path) throws IOException{
|
||||
LinkedList<String> list = new LinkedList<String>();
|
||||
|
||||
BufferedReader data_in = getDataInputStream();
|
||||
sendCommand("NLST "+path, DEBUG);
|
||||
|
||||
String tmp = "";
|
||||
while((tmp = data_in.readLine()) != null){
|
||||
list.add(tmp);
|
||||
}
|
||||
|
||||
data_in.close();
|
||||
readCommand(DEBUG);
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns information about the file or directory
|
||||
*
|
||||
* @deprecated
|
||||
* @param path The path and filename of a file or a directory
|
||||
* @return A List of Strings with information
|
||||
* @throws IOException
|
||||
*/
|
||||
public ArrayList<String[]> getFileInfo(String path) throws IOException{
|
||||
Pattern regex = Pattern.compile("\\s{1,}");
|
||||
ArrayList<String[]> info = new ArrayList<String[]>();
|
||||
|
||||
BufferedReader data_in = getDataInputStream();
|
||||
sendCommand("LIST "+path, DEBUG);
|
||||
|
||||
String tmp = "";
|
||||
while((tmp = data_in.readLine()) != null){
|
||||
System.err.println(tmp);
|
||||
info.add(regex.split(tmp));
|
||||
}
|
||||
|
||||
data_in.close();
|
||||
readCommand(DEBUG);
|
||||
return info;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a file at the server with the given data
|
||||
*
|
||||
* @param path The path and filename
|
||||
* @param data The data to put in the file
|
||||
* @throws IOException
|
||||
*/
|
||||
public void sendFile(String path, String data) throws IOException{
|
||||
PrintStream data_out = getDataOutputStream();
|
||||
sendCommand("STOR "+path, DEBUG);
|
||||
data_out.println(data);
|
||||
data_out.close();
|
||||
readCommand(DEBUG);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a directory at the server
|
||||
*
|
||||
* @param path The path to the directory
|
||||
* @throws IOException
|
||||
*/
|
||||
public boolean createDir(String path) throws IOException{
|
||||
if(sendCommand("MKD "+path) == FTPC_PATH_CREATED)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a BufferedReader with the file data
|
||||
* WARNING: you must run readCommand(true); after you close the stream
|
||||
*
|
||||
* @param path The path and filename
|
||||
* @return Stream with the file
|
||||
* @throws IOException
|
||||
*/
|
||||
private BufferedReader getFile(String path) throws IOException{
|
||||
BufferedReader ret = getDataInputStream();
|
||||
sendCommand("RETR "+path, DEBUG);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Downloads a file from the FTP server to a local file
|
||||
*
|
||||
* @param source The source file on the server
|
||||
* @param destination The local file to save to
|
||||
* @throws IOException
|
||||
*/
|
||||
public void getFile(String source, String destination) throws IOException{
|
||||
BufferedReader file_in = getFile(source);
|
||||
PrintStream file_out = new PrintStream(new File(destination));
|
||||
|
||||
String tmp = "";
|
||||
while((tmp = file_in.readLine()) != null){
|
||||
file_out.println(tmp);
|
||||
}
|
||||
readCommand(DEBUG);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a file from the FTP server
|
||||
*
|
||||
* @param path The path and filename of the file to be deleted
|
||||
* @return True if the command was successful or false otherwise
|
||||
* @throws IOException
|
||||
*/
|
||||
public boolean removeFile(String path) throws IOException{
|
||||
if(sendCommand("DELE "+path) == FTPC_FILE_ACTION_OK)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a directory from the FTP server
|
||||
*
|
||||
* @param path The path of the directory to be deleted
|
||||
* @return True if the command was successful or false otherwise
|
||||
* @throws IOException
|
||||
*/
|
||||
public boolean removeDir(String path) throws IOException{
|
||||
if(sendCommand("RMD "+path) == FTPC_FILE_ACTION_OK)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
//**************************************************************************************
|
||||
//**************************************************************************************
|
||||
//******************************** Data Connection *************************************
|
||||
|
||||
/**
|
||||
* Starts a connection to the server. It automatically handles
|
||||
* passive or active mode
|
||||
*
|
||||
* @return The PrintStream for the channel
|
||||
* @throws IOException
|
||||
*/
|
||||
public synchronized PrintStream getDataOutputStream() throws IOException{
|
||||
int port = getDataConnectionPortType();
|
||||
if(port < 0){ // Active Mode
|
||||
port *= -1;
|
||||
return getActiveDataOutputStream(port);
|
||||
}
|
||||
else{
|
||||
System.out.println("port: "+port);
|
||||
return getPassiveDataOutputStream(port);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts a connection to the server. It automatically handles
|
||||
* passive or active mode
|
||||
*
|
||||
* @return The BufferedReader for the channel
|
||||
* @throws IOException
|
||||
*/
|
||||
public synchronized BufferedReader getDataInputStream() throws IOException{
|
||||
int port = getDataConnectionPortType();
|
||||
if(port < 0){ // Active Mode
|
||||
port *= -1;
|
||||
return getActiveDataInputStream(port);
|
||||
}
|
||||
else{
|
||||
return getPassiveDataInputStream(port);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method chooses the appropriate data connection type
|
||||
* to the server (Passive or Active) and returns the port number
|
||||
*
|
||||
* @return A port number. If port > 0 = Passive AND port < 0 Active
|
||||
* @throws IOException
|
||||
*/
|
||||
private int getDataConnectionPortType() throws IOException{
|
||||
return setPassiveMode();
|
||||
}
|
||||
|
||||
/**
|
||||
* Connects to the data port on the server and returns the InputStream
|
||||
*
|
||||
* @param port The port to connect to
|
||||
* @return The InputStream for the data channel
|
||||
* @throws IOException
|
||||
*/
|
||||
private BufferedReader getPassiveDataInputStream(int port) throws IOException{
|
||||
Socket data_socket = new Socket(socket.getInetAddress().getHostAddress(), port);
|
||||
BufferedReader data_in = new BufferedReader(new InputStreamReader(data_socket.getInputStream()));
|
||||
|
||||
return data_in;
|
||||
}
|
||||
|
||||
/**
|
||||
* Connects to the data port on the server and returns the OutputStream
|
||||
*
|
||||
* @param port The port to connect to
|
||||
* @return The OutputStream for the data channel
|
||||
* @throws IOException
|
||||
*/
|
||||
private PrintStream getPassiveDataOutputStream(int port) throws IOException{
|
||||
Socket data_socket = new Socket(socket.getInetAddress().getHostAddress(), port);
|
||||
PrintStream data_out = new PrintStream(data_socket.getOutputStream());
|
||||
|
||||
return data_out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Listens on a local port for a connection from the server
|
||||
* and returns with the InputStream of the connection from the server
|
||||
*
|
||||
* @param port The port to listen to
|
||||
* @return The InputStream for the data channel
|
||||
* @throws IOException
|
||||
*/
|
||||
private BufferedReader getActiveDataInputStream(int port) throws IOException{
|
||||
// TODO:
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Listens on a local port for a connection from the server
|
||||
* and returns with the OutputStream of the connection from the server
|
||||
*
|
||||
* @param port The port to listen to
|
||||
* @return The OutputStream for the data channel
|
||||
* @throws IOException
|
||||
*/
|
||||
private PrintStream getActiveDataOutputStream(int port) throws IOException{
|
||||
// TODO:
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets Passive mode at the server and returns the port number
|
||||
* for the data channel
|
||||
*
|
||||
* @return Port number for data channel
|
||||
* @throws IOException
|
||||
*/
|
||||
private int setPassiveMode() throws IOException{
|
||||
String tmp = sendCommand("PASV", DEBUG);
|
||||
if(parseReturnCode(tmp) != FTPC_ENTERING_PASSIVE){
|
||||
throw new IOException(tmp);
|
||||
}
|
||||
tmp = tmp.substring(tmp.indexOf('(')+1, tmp.indexOf(')'));
|
||||
String[] tmpArray = tmp.split("[,]");
|
||||
|
||||
if(tmpArray.length <= 1)
|
||||
return Integer.parseInt(tmpArray[0]);
|
||||
else
|
||||
return Integer.parseInt(tmpArray[4])*256 + Integer.parseInt(tmpArray[5]);
|
||||
}
|
||||
|
||||
//**************************************************************************************
|
||||
//**************************************************************************************
|
||||
|
||||
/**
|
||||
* To keep the connection alive
|
||||
*/
|
||||
public void run(){
|
||||
try {
|
||||
while(true){
|
||||
if(last_sent > System.currentTimeMillis() + FTP_NOOP_INT*1000){
|
||||
sendCommand("NOOP");
|
||||
}
|
||||
try{ Thread.sleep(5000); }catch(Exception e){}
|
||||
}
|
||||
} catch (IOException e1) {
|
||||
e1.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the FTP connection
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public void close() throws IOException{
|
||||
sendCommand("QUIT", DEBUG);
|
||||
in.close();
|
||||
out.close();
|
||||
socket.close();
|
||||
this.stop();
|
||||
}
|
||||
}
|
||||
317
src/zutil/net/POP3Client.java
Normal file
317
src/zutil/net/POP3Client.java
Normal file
|
|
@ -0,0 +1,317 @@
|
|||
package zutil.net;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.PrintStream;
|
||||
import java.net.Socket;
|
||||
import java.net.UnknownHostException;
|
||||
|
||||
import javax.net.SocketFactory;
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
|
||||
/**
|
||||
* A simple class that connects and logs in to a POP3
|
||||
* server and then can read and delete messages.
|
||||
* INFO: http://pages.prodigy.net/michael_santovec/pop3telnet.htm
|
||||
*
|
||||
* @author Ziver
|
||||
*
|
||||
*/
|
||||
public class POP3Client {
|
||||
public static boolean DEBUG = false;
|
||||
public static final int POP3_PORT = 110;
|
||||
public static final int POP3_SSL_PORT = 995;
|
||||
|
||||
private BufferedReader in;
|
||||
private PrintStream out;
|
||||
private Socket socket;
|
||||
|
||||
/**
|
||||
* Connect to a POP3 server without username
|
||||
*
|
||||
* @param host The hostname of the server
|
||||
* @throws UnknownHostException
|
||||
* @throws IOException
|
||||
*/
|
||||
public POP3Client(String host) throws UnknownHostException, IOException{
|
||||
this(host, POP3_PORT, null, null, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect to a POP3 server with username and password
|
||||
*
|
||||
* @param host The hostname of the server
|
||||
* @param user The username
|
||||
* @param password the password
|
||||
* @throws UnknownHostException
|
||||
* @throws IOException
|
||||
*/
|
||||
public POP3Client(String host, String user, String password) throws UnknownHostException, IOException{
|
||||
this(host, POP3_PORT, user, password, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Connects to a POP3 server with username and password and SSL
|
||||
*
|
||||
* @param host The hostname of the server
|
||||
* @param user The username
|
||||
* @param password the password
|
||||
* @param ssl If SSL should be used
|
||||
* @throws UnknownHostException
|
||||
* @throws IOException
|
||||
*/
|
||||
public POP3Client(String host, String user, String password, boolean ssl) throws UnknownHostException, IOException{
|
||||
this(host, (ssl ? POP3_SSL_PORT : POP3_PORT), user, password, ssl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Connects to a POP3 server with username and password and
|
||||
* SSL and with costume port.
|
||||
*
|
||||
* @param host The hostname of the server
|
||||
* @param port The port number to connect to on the server
|
||||
* @param user The username
|
||||
* @param password the password
|
||||
* @param ssl If SSL should be used
|
||||
* @throws UnknownHostException
|
||||
* @throws IOException
|
||||
*/
|
||||
public POP3Client(String host, int port, String user, String password, boolean ssl) throws UnknownHostException, IOException{
|
||||
if(ssl) connectSSL(host, port);
|
||||
else connect(host, port);
|
||||
|
||||
if(user != null){
|
||||
login(user, password);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Connects to the server
|
||||
*
|
||||
* @param host The hostname of the server
|
||||
* @param port The port to connect to on the server
|
||||
* @throws UnknownHostException
|
||||
* @throws IOException
|
||||
*/
|
||||
private void connect(String host, int port) throws UnknownHostException, IOException{
|
||||
socket = new Socket(host, port);
|
||||
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
|
||||
out = new PrintStream(socket.getOutputStream());
|
||||
|
||||
readCommand(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Connects to the server with SSL.
|
||||
* http://www.exampledepot.com/egs/javax.net.ssl/Client.html
|
||||
*
|
||||
* @param host The hostname of the server
|
||||
* @param port The port to connect to on the server
|
||||
* @throws UnknownHostException
|
||||
* @throws IOException
|
||||
*/
|
||||
private void connectSSL(String host, int port) throws UnknownHostException, IOException{
|
||||
SocketFactory socketFactory = SSLSocketFactory.getDefault();
|
||||
socket = socketFactory.createSocket(host, port);
|
||||
|
||||
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
|
||||
out = new PrintStream(socket.getOutputStream());
|
||||
|
||||
readCommand(DEBUG);
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs in to the POP3 server with the username and password if the password is set
|
||||
*
|
||||
* @param user The user name
|
||||
* @param password The password or null if no password is required
|
||||
* @throws IOException
|
||||
*/
|
||||
private void login(String user, String password) throws IOException{
|
||||
sendCommand("USER "+user);
|
||||
if(password != null){
|
||||
sendNoReplyCommand("PASS "+password, false);
|
||||
if(DEBUG)System.out.println("PASS ***");
|
||||
readCommand(DEBUG);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of messages that is on the server
|
||||
*
|
||||
* @return Message count
|
||||
* @throws IOException
|
||||
*/
|
||||
public int getMessageCount() throws IOException{
|
||||
String msg = sendCommand("STAT", DEBUG);
|
||||
return Integer.parseInt(
|
||||
msg.substring(
|
||||
msg.indexOf(' ')+1,
|
||||
msg.indexOf(' ', msg.indexOf(' ')+1)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the message with the given id.
|
||||
*
|
||||
* @param id The id of the message to get
|
||||
* @return The message
|
||||
* @throws IOException
|
||||
*/
|
||||
public String getMessage(int id) throws IOException{
|
||||
sendCommand("RETR "+id);
|
||||
return readMultipleLines(DEBUG);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the title of the message
|
||||
*
|
||||
* @param id The message id
|
||||
* @return The title
|
||||
* @throws IOException
|
||||
*/
|
||||
public String getMessageTitle(int id) throws IOException{
|
||||
String tmp = getMessageHeader(id);
|
||||
String tmp2 = tmp.toLowerCase();
|
||||
if(tmp2.contains("subject:")){
|
||||
return tmp.substring(
|
||||
tmp2.indexOf("subject:")+8,
|
||||
tmp2.indexOf('\n',
|
||||
tmp2.indexOf("subject:")));
|
||||
}
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the header of the given message id.
|
||||
*
|
||||
* @param id The id of the message to get
|
||||
* @return The message
|
||||
* @throws IOException
|
||||
*/
|
||||
public String getMessageHeader(int id) throws IOException{
|
||||
sendCommand("TOP "+id+" 0");
|
||||
return readMultipleLines(DEBUG);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the message with the given id
|
||||
*
|
||||
* @param id The id of the message to be deleted
|
||||
* @throws IOException
|
||||
*/
|
||||
public void delete(int id) throws IOException{
|
||||
sendCommand("DELE "+id);
|
||||
}
|
||||
|
||||
|
||||
//*********************** IO Stuff *********************************************
|
||||
|
||||
/**
|
||||
* Sends the given line to the server and returns a status integer
|
||||
*
|
||||
* @param cmd The command to send
|
||||
* @return The return code from the server
|
||||
* @throws IOException if the cmd fails
|
||||
*/
|
||||
private boolean sendCommand(String cmd) throws IOException{
|
||||
return parseReturnCode(sendCommand(cmd, DEBUG));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the given line to the server and returns the last line
|
||||
*
|
||||
* @param cmd The command to send
|
||||
* @param print To print out the received lines
|
||||
* @return Last String line from the server
|
||||
* @throws IOException
|
||||
*/
|
||||
private String sendCommand(String cmd, boolean print) throws IOException{
|
||||
sendNoReplyCommand(cmd, print);
|
||||
return readCommand(print);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a given command and don't cares about the reply
|
||||
*
|
||||
* @param cmd The command
|
||||
* @param print If it should print to System.out
|
||||
* @throws IOException
|
||||
*/
|
||||
private void sendNoReplyCommand(String cmd, boolean print) throws IOException{
|
||||
out.println(cmd);
|
||||
if(print)System.out.println(cmd);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads on line from the command channel
|
||||
*
|
||||
* @param print If the method should print the input line
|
||||
* @return The input line
|
||||
* @throws IOException if the server returns a error code
|
||||
*/
|
||||
private String readCommand(boolean print) throws IOException{
|
||||
String tmp = in.readLine();
|
||||
if(print)System.out.println(tmp);
|
||||
if( !parseReturnCode(tmp) ) throw new IOException(tmp);
|
||||
|
||||
return tmp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads from the server until there are a line with
|
||||
* only one '.'
|
||||
*
|
||||
* @param print To print out the received lines
|
||||
* @return String with the text
|
||||
* @throws IOException
|
||||
*/
|
||||
private String readMultipleLines(boolean print) throws IOException{
|
||||
StringBuffer msg = new StringBuffer();
|
||||
String tmp = in.readLine();
|
||||
while(!tmp.equals(".")){
|
||||
msg.append(tmp);
|
||||
msg.append('\n');
|
||||
tmp = in.readLine();
|
||||
if(print)System.out.println(tmp);
|
||||
}
|
||||
|
||||
return msg.toString();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parses the return line from the server and returns the status code
|
||||
*
|
||||
* @param msg The message from the server
|
||||
* @return Returns true if return code is OK false if it is ERR
|
||||
* @throws IOException
|
||||
*/
|
||||
private boolean parseReturnCode(String msg){
|
||||
int endpos = (msg.indexOf(' ')<0 ? msg.length() : msg.indexOf(' '));
|
||||
return msg.substring(0, endpos).equals("+OK");
|
||||
}
|
||||
|
||||
//*********************************************************************************
|
||||
|
||||
/**
|
||||
* All the delete marked messages are unmarkt
|
||||
* @throws IOException
|
||||
*/
|
||||
public void reset() throws IOException{
|
||||
sendCommand("RSET", DEBUG);
|
||||
}
|
||||
|
||||
/**
|
||||
* All the changes(DELETE) are performed and then the connection is closed
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
public void close() throws IOException{
|
||||
sendCommand("QUIT", DEBUG);
|
||||
in.close();
|
||||
out.close();
|
||||
socket.close();
|
||||
}
|
||||
}
|
||||
154
src/zutil/net/SMTPClient.java
Normal file
154
src/zutil/net/SMTPClient.java
Normal file
|
|
@ -0,0 +1,154 @@
|
|||
package zutil.net;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.PrintStream;
|
||||
import java.net.Socket;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* A simple class that connects and logs in to a SMTP
|
||||
* server and then send emails.
|
||||
* INFO: http://cr.yp.to/smtp/client.html
|
||||
*
|
||||
* @author Ziver
|
||||
*
|
||||
*/
|
||||
public class SMTPClient {
|
||||
public static boolean DEBUG = false;
|
||||
|
||||
private BufferedReader in;
|
||||
private PrintStream out;
|
||||
private Socket socket;
|
||||
private String url;
|
||||
private int port;
|
||||
|
||||
public SMTPClient(String url){
|
||||
this(url, 25);
|
||||
}
|
||||
|
||||
public SMTPClient(String url, int port){
|
||||
this.url = url;
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
/**
|
||||
* Connects to the server.
|
||||
* Sends the message.
|
||||
* Closes the connection
|
||||
*
|
||||
* @param from The destination email address
|
||||
* @param to The recipients email address
|
||||
* @param subj The subject of the message
|
||||
* @param msg The message
|
||||
* @throws IOException
|
||||
*/
|
||||
public synchronized void send(String from, String to, String subj, String msg) throws IOException{
|
||||
try{
|
||||
connect();
|
||||
// FROM and TO
|
||||
sendCommand("MAIL FROM:"+from);
|
||||
sendCommand("RCPT TO:"+to);
|
||||
sendCommand("DATA");
|
||||
// The Message
|
||||
sendNoReplyCommand("Date: "+(new Date()), DEBUG);
|
||||
sendNoReplyCommand("From: "+from, DEBUG);
|
||||
sendNoReplyCommand("To: "+to, DEBUG);
|
||||
sendNoReplyCommand("Subject: "+subj, DEBUG);
|
||||
sendNoReplyCommand(" ", DEBUG);
|
||||
sendNoReplyCommand(msg, DEBUG);
|
||||
sendCommand(".", DEBUG);
|
||||
|
||||
close();
|
||||
}catch(IOException e){
|
||||
close();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Connects to the server
|
||||
*
|
||||
* @throws UnknownHostException
|
||||
* @throws IOException
|
||||
*/
|
||||
public void connect() throws UnknownHostException, IOException{
|
||||
socket = new Socket(url, port);
|
||||
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
|
||||
out = new PrintStream(socket.getOutputStream());
|
||||
|
||||
readCommand(DEBUG);
|
||||
sendCommand("HELO "+url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the given line to the server and returns a status integer
|
||||
*
|
||||
* @param cmd The command to send
|
||||
* @return The return code from the server
|
||||
* @throws IOException if the cmd fails
|
||||
*/
|
||||
private int sendCommand(String cmd) throws IOException{
|
||||
return parseReturnCode(sendCommand(cmd, DEBUG));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the given line to the server and returns the last line
|
||||
*
|
||||
* @param cmd The command to send
|
||||
* @param print To print out the received lines
|
||||
* @return Last String line from the server
|
||||
* @throws IOException
|
||||
*/
|
||||
private String sendCommand(String cmd, boolean print) throws IOException{
|
||||
sendNoReplyCommand(cmd, print);
|
||||
return readCommand(print);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a given command and don't cares about the reply
|
||||
*
|
||||
* @param cmd The command
|
||||
* @param print If it should print to System.out
|
||||
* @throws IOException
|
||||
*/
|
||||
private void sendNoReplyCommand(String cmd, boolean print) throws IOException{
|
||||
out.println(cmd);
|
||||
if(print)System.out.println(cmd);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads on line from the command channel
|
||||
*
|
||||
* @param print If the method should print the input line
|
||||
* @return The input line
|
||||
* @throws IOException if the server returns a error code
|
||||
*/
|
||||
private String readCommand(boolean print) throws IOException{
|
||||
String tmp = in.readLine();
|
||||
if(print)System.out.println(tmp);
|
||||
if(parseReturnCode(tmp) >= 400 ) throw new IOException(tmp);
|
||||
|
||||
return tmp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the return line from the server and returns the status code
|
||||
*
|
||||
* @param msg The message from the server
|
||||
* @return The status code
|
||||
* @throws IOException
|
||||
*/
|
||||
private synchronized int parseReturnCode(String msg){
|
||||
return Integer.parseInt(msg.substring(0, 3));
|
||||
}
|
||||
|
||||
public void close() throws IOException{
|
||||
sendCommand("QUIT", DEBUG);
|
||||
in.close();
|
||||
out.close();
|
||||
socket.close();
|
||||
}
|
||||
}
|
||||
73
src/zutil/net/ServerFind.java
Normal file
73
src/zutil/net/ServerFind.java
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
package zutil.net;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.DatagramPacket;
|
||||
import java.net.DatagramSocket;
|
||||
import java.net.InetAddress;
|
||||
import java.net.MulticastSocket;
|
||||
|
||||
import zutil.io.MultiPrintStream;
|
||||
|
||||
/**
|
||||
* This class broadcast its address in the LAN so that
|
||||
* the ServerFindClient can get the server IP
|
||||
*
|
||||
* @author Ziver
|
||||
*
|
||||
*/
|
||||
public class ServerFind extends Thread {
|
||||
public String broadcastAddress = "230.0.0.1";
|
||||
|
||||
private InetAddress group;
|
||||
private MulticastSocket Msocket;
|
||||
|
||||
private boolean avsluta;
|
||||
private int port;
|
||||
|
||||
/**
|
||||
* Creates a ServerFind Thread an the specified port
|
||||
*
|
||||
* @param port The port to run the ServerFind Server on
|
||||
* @throws IOException
|
||||
*/
|
||||
public ServerFind (int port) throws IOException {
|
||||
this.port = port;
|
||||
avsluta = false;
|
||||
group = InetAddress.getByName(broadcastAddress);
|
||||
Msocket = new MulticastSocket(port);
|
||||
Msocket.joinGroup(group);
|
||||
|
||||
start();
|
||||
}
|
||||
|
||||
public void run (){
|
||||
byte[] buf = new byte[256];
|
||||
DatagramPacket packet;
|
||||
DatagramSocket lan_socket = null;
|
||||
|
||||
while (!avsluta){
|
||||
try {
|
||||
packet = new DatagramPacket(buf, buf.length);
|
||||
Msocket.receive(packet);
|
||||
|
||||
lan_socket = new DatagramSocket(port , packet.getAddress());
|
||||
packet = new DatagramPacket(buf, buf.length, group, port);
|
||||
lan_socket.send(packet);
|
||||
lan_socket.close();
|
||||
} catch (Exception e) {
|
||||
MultiPrintStream.out.println("Error Establishing ServerFind Connection!!!\n" + e);
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the broadcast socket
|
||||
*/
|
||||
public void close(){
|
||||
avsluta = true;
|
||||
Msocket.close();
|
||||
}
|
||||
}
|
||||
50
src/zutil/net/ServerFindClient.java
Normal file
50
src/zutil/net/ServerFindClient.java
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
package zutil.net;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.DatagramPacket;
|
||||
import java.net.DatagramSocket;
|
||||
import java.net.InetAddress;
|
||||
import java.net.MulticastSocket;
|
||||
|
||||
/**
|
||||
* This class is the client for ServerFind that receives the server IP
|
||||
*
|
||||
* @author Ziver
|
||||
*
|
||||
*/
|
||||
public class ServerFindClient{
|
||||
public String broadcastAddress = "230.0.0.1";
|
||||
|
||||
private int port;
|
||||
|
||||
/**
|
||||
* Creates a ServerFind Client
|
||||
*
|
||||
* @param port The port to contact the server on
|
||||
*/
|
||||
public ServerFindClient(int port){
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests IP from server
|
||||
*
|
||||
* @return The address of the server
|
||||
* @throws IOException
|
||||
*/
|
||||
public InetAddress find() throws IOException{
|
||||
InetAddress group = InetAddress.getByName(broadcastAddress);
|
||||
DatagramSocket lan_socket = new DatagramSocket();
|
||||
MulticastSocket Msocket = new MulticastSocket(port);
|
||||
Msocket.joinGroup(group);
|
||||
|
||||
byte[] buf = new byte[256];
|
||||
DatagramPacket packet = new DatagramPacket(buf, buf.length, group, port);
|
||||
lan_socket.send(packet);
|
||||
|
||||
packet = new DatagramPacket(buf, buf.length);
|
||||
Msocket.receive(packet);
|
||||
|
||||
return packet.getAddress();
|
||||
}
|
||||
}
|
||||
134
src/zutil/net/UpdateClient.java
Normal file
134
src/zutil/net/UpdateClient.java
Normal file
|
|
@ -0,0 +1,134 @@
|
|||
package zutil.net;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.net.Socket;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import zutil.ProgressListener;
|
||||
import zutil.io.MultiPrintStream;
|
||||
import zutil.io.file.FileUtil;
|
||||
|
||||
/**
|
||||
* This class connects to a update server and updates a path
|
||||
* with the servers
|
||||
*
|
||||
* @author Ziver
|
||||
*
|
||||
*/
|
||||
public class UpdateClient{
|
||||
private ArrayList<FileHash> clientFileList;
|
||||
private Socket socket;
|
||||
private String path;
|
||||
private ProgressListener progress;
|
||||
private int speed;
|
||||
private long totalReceived;
|
||||
|
||||
/**
|
||||
* Creates a UpdateClient
|
||||
*
|
||||
* @param address Address to the UpdateServer
|
||||
* @param port The port on the server
|
||||
* @param path Path to the files to update
|
||||
* @throws Exception
|
||||
*/
|
||||
public UpdateClient(String address, int port, String path) throws Exception{
|
||||
clientFileList = UpdateServer.getFileList(path);
|
||||
socket = new Socket(address, port);
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
public void setProgressListener(ProgressListener p){
|
||||
progress = p;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the files
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public void update() throws Exception{
|
||||
ObjectOutputStream out = new ObjectOutputStream(socket.getOutputStream());
|
||||
ObjectInputStream in = new ObjectInputStream(socket.getInputStream());
|
||||
|
||||
// send client file list
|
||||
out.writeObject(clientFileList);
|
||||
out.flush();
|
||||
|
||||
// receive file updates
|
||||
FileHash fileInfo = (FileHash)in.readObject();
|
||||
File tmpPath = FileUtil.find(path);
|
||||
while(!fileInfo.path.isEmpty()){
|
||||
MultiPrintStream.out.println("Updating: "+path+fileInfo.path);
|
||||
// reading new file data
|
||||
File file = new File(tmpPath.getAbsolutePath()+fileInfo.path);
|
||||
File tmpFile = File.createTempFile(file.getName(), ".tmp", tmpPath);
|
||||
tmpFile.getParentFile().mkdirs();
|
||||
tmpFile.deleteOnExit();
|
||||
|
||||
FileOutputStream fileOut = new FileOutputStream(tmpFile);
|
||||
byte[] buffer = new byte[socket.getReceiveBufferSize()];
|
||||
|
||||
int bytesReceived = 0;
|
||||
totalReceived = 0;
|
||||
long time = System.currentTimeMillis();
|
||||
long timeTotalRecived = 0;
|
||||
|
||||
while((bytesReceived = in.read(buffer)) > 0) {
|
||||
fileOut.write(buffer, 0, bytesReceived);
|
||||
|
||||
if(time+1000 < System.currentTimeMillis()){
|
||||
time = System.currentTimeMillis();
|
||||
speed = (int)(totalReceived - timeTotalRecived);
|
||||
timeTotalRecived = totalReceived;
|
||||
}
|
||||
|
||||
totalReceived += bytesReceived;
|
||||
if(progress != null) progress.progressUpdate(this, fileInfo, (double)totalReceived/fileInfo.size*100);
|
||||
}
|
||||
fileOut.close();
|
||||
speed = 0;
|
||||
|
||||
// delete old file and replace whit new
|
||||
file.delete();
|
||||
if(!tmpFile.renameTo(file)){
|
||||
throw new Exception("Cannot update file: "+file.getAbsolutePath());
|
||||
}
|
||||
// read new message
|
||||
fileInfo = (FileHash)in.readObject();
|
||||
}
|
||||
|
||||
MultiPrintStream.out.println("Update Done!!");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the speed of the transfer
|
||||
*
|
||||
* @return The speed in bytes/s
|
||||
*/
|
||||
public int speed(){
|
||||
return speed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the total amount of data received for the
|
||||
* current file
|
||||
*
|
||||
* @return The speed in bytes/s
|
||||
*/
|
||||
public long totalReceived(){
|
||||
return totalReceived;
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the connection
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
public void close() throws IOException{
|
||||
socket.close();
|
||||
}
|
||||
}
|
||||
166
src/zutil/net/UpdateServer.java
Normal file
166
src/zutil/net/UpdateServer.java
Normal file
|
|
@ -0,0 +1,166 @@
|
|||
package zutil.net;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.io.Serializable;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
import java.net.URISyntaxException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import zutil.Hasher;
|
||||
import zutil.io.MultiPrintStream;
|
||||
import zutil.io.file.FileUtil;
|
||||
|
||||
public class UpdateServer extends Thread{
|
||||
private ArrayList<FileHash> fileList;
|
||||
private ServerSocket server;
|
||||
private boolean close;
|
||||
private String path;
|
||||
|
||||
/**
|
||||
* Creates a UpdateServer Thread
|
||||
*
|
||||
* @param path The path to sync the clients with
|
||||
* @throws IOException
|
||||
* @throws URISyntaxException
|
||||
* @throws NoSuchAlgorithmException
|
||||
*/
|
||||
public UpdateServer(int port, String path) throws Exception{
|
||||
fileList = getFileList(path);
|
||||
server = new ServerSocket(port);
|
||||
close = false;
|
||||
this.path = path;
|
||||
|
||||
this.start();
|
||||
MultiPrintStream.out.println("Update Server Online!!!");
|
||||
}
|
||||
|
||||
public void run(){
|
||||
while (!close){
|
||||
try {
|
||||
new UpdateServerThread(server.accept()).start();
|
||||
MultiPrintStream.out.println("Update Server: Client Connected!!!");
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles all the connecting clients
|
||||
*
|
||||
* @author Ziver
|
||||
*/
|
||||
class UpdateServerThread extends Thread{
|
||||
private ObjectOutputStream out;
|
||||
private ObjectInputStream in;
|
||||
private Socket client;
|
||||
|
||||
/**
|
||||
* Creates a UpdateServerThread
|
||||
* @param client The socket to the client
|
||||
* @throws IOException
|
||||
*/
|
||||
public UpdateServerThread(Socket c) throws IOException {
|
||||
client = c;
|
||||
out = new ObjectOutputStream(client.getOutputStream());
|
||||
in = new ObjectInputStream(client.getInputStream());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void run(){
|
||||
try {
|
||||
// receive the clients filelist
|
||||
ArrayList<FileHash> clientFileList = (ArrayList<FileHash>)in.readObject();
|
||||
File tmpPath = FileUtil.find(path);
|
||||
|
||||
for(FileHash file : fileList){
|
||||
if(!clientFileList.contains(file)){
|
||||
// send new file to client
|
||||
out.writeObject(file);
|
||||
out.flush();
|
||||
|
||||
// send file data
|
||||
FileInputStream input = new FileInputStream(tmpPath.getAbsolutePath()+file.path);
|
||||
byte[] nextBytes = new byte[client.getSendBufferSize()];
|
||||
int bytesRead = 0;
|
||||
while((bytesRead = input.read(nextBytes)) > 0){
|
||||
out.write(nextBytes,0,bytesRead);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// send update done message
|
||||
out.writeObject(new FileHash("","",0));
|
||||
out.flush();
|
||||
|
||||
out.close();
|
||||
in.close();
|
||||
client.close();
|
||||
} catch (Exception e) {
|
||||
MultiPrintStream.out.println("Update Server: Client Error!!! "+e.getMessage());
|
||||
} finally {
|
||||
MultiPrintStream.out.println("Update Server: Client Update Done!!!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a ArrayList with all the files in the specified folder and there
|
||||
* MD5 hashes
|
||||
*
|
||||
* @param path The path to search
|
||||
* @return A ArrayList with all the files in the path
|
||||
* @throws URISyntaxException
|
||||
* @throws NoSuchAlgorithmException
|
||||
* @throws IOException
|
||||
*/
|
||||
public static ArrayList<FileHash> getFileList(String path) throws Exception{
|
||||
ArrayList<FileHash> fileHash = new ArrayList<FileHash>();
|
||||
|
||||
List<File> files = FileUtil.search(FileUtil.find(path));
|
||||
for(File file : files){
|
||||
fileHash.add(new FileHash(
|
||||
FileUtil.relativePath(file, path),
|
||||
Hasher.hash(file, "MD5"),
|
||||
file.length()));
|
||||
}
|
||||
|
||||
return fileHash;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This class is used to store the files
|
||||
* and there hashes
|
||||
*
|
||||
* @author Ziver
|
||||
*/
|
||||
class FileHash implements Serializable{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public String path;
|
||||
public String hash;
|
||||
public long size;
|
||||
|
||||
public FileHash(String p, String h, long s){
|
||||
path = p;
|
||||
hash = h;
|
||||
size = s;
|
||||
}
|
||||
|
||||
public boolean equals(Object comp){
|
||||
FileHash tmp = (FileHash)comp;
|
||||
return path.equals(tmp.path) && hash.equals(tmp.hash);
|
||||
}
|
||||
|
||||
public String toString(){
|
||||
return path;
|
||||
}
|
||||
}
|
||||
232
src/zutil/net/Zupdater.java
Normal file
232
src/zutil/net/Zupdater.java
Normal file
|
|
@ -0,0 +1,232 @@
|
|||
/*
|
||||
* Zupdater.java
|
||||
*
|
||||
* Created on den 27 juli 2008, 23:32
|
||||
*/
|
||||
|
||||
package zutil.net;
|
||||
|
||||
import java.awt.Dimension;
|
||||
|
||||
import zutil.ProgressListener;
|
||||
import zutil.StringUtil;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Ziver
|
||||
*/
|
||||
public class Zupdater extends javax.swing.JFrame implements ProgressListener{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** Creates new form Zupdater */
|
||||
public Zupdater() {
|
||||
super("Zupdater");
|
||||
initComponents();
|
||||
centerScreen();
|
||||
setVisible(true);
|
||||
}
|
||||
|
||||
public void centerScreen(){
|
||||
Dimension screen = getToolkit().getScreenSize();
|
||||
this.setBounds(
|
||||
(screen.width-getWidth())/2,
|
||||
(screen.height-getHeight())/2,
|
||||
getWidth(),
|
||||
getHeight() );
|
||||
}
|
||||
|
||||
public void progressUpdate(Object source, Object info, double percent) {
|
||||
if(info instanceof FileHash){
|
||||
FileHash fileHash = (FileHash) info;
|
||||
fileLabel.setText(fileHash.toString());
|
||||
fileProgressBar.setValue((int)percent);
|
||||
percentLabel.setText((int)percent+"%");
|
||||
|
||||
speedLabel.setText(StringUtil.formatBytesToString(((UpdateClient)source).speed())+"/s");
|
||||
transferedLabel.setText(StringUtil.formatBytesToString(((UpdateClient)source).totalReceived())+
|
||||
" / "+StringUtil.formatBytesToString(fileHash.size));
|
||||
}
|
||||
}
|
||||
|
||||
/** This method is called from within the constructor to
|
||||
* initialize the form.
|
||||
* WARNING: Do NOT modify this code. The content of this method is
|
||||
* always regenerated by the Form Editor.
|
||||
*/
|
||||
// <editor-fold defaultstate="collapsed" desc="Generated Code">
|
||||
private void initComponents() {
|
||||
|
||||
jSeparator1 = new javax.swing.JSeparator();
|
||||
cancelButton = new javax.swing.JButton();
|
||||
jPanel1 = new javax.swing.JPanel();
|
||||
jLabel1 = new javax.swing.JLabel();
|
||||
totalProgressBar = new javax.swing.JProgressBar();
|
||||
jLabel2 = new javax.swing.JLabel();
|
||||
fileProgressBar = new javax.swing.JProgressBar();
|
||||
fileLabel = new javax.swing.JLabel();
|
||||
totalProgressLabel = new javax.swing.JLabel();
|
||||
speedLabel = new javax.swing.JLabel();
|
||||
percentLabel = new javax.swing.JLabel();
|
||||
transferedLabel = new javax.swing.JLabel();
|
||||
etaLabel = new javax.swing.JLabel();
|
||||
jLabel3 = new javax.swing.JLabel();
|
||||
|
||||
setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
|
||||
|
||||
cancelButton.setText("Cancel");
|
||||
cancelButton.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
cancel();
|
||||
}
|
||||
});
|
||||
|
||||
jPanel1.setBorder(javax.swing.BorderFactory.createTitledBorder("Update"));
|
||||
|
||||
jLabel1.setFont(new java.awt.Font("Tahoma", 1, 11));
|
||||
jLabel1.setText("Total Progress:");
|
||||
|
||||
totalProgressBar.setIndeterminate(true);
|
||||
|
||||
jLabel2.setFont(new java.awt.Font("Tahoma", 1, 11));
|
||||
jLabel2.setText("File: ");
|
||||
|
||||
fileLabel.setFont(new java.awt.Font("Tahoma", 0, 11));
|
||||
fileLabel.setText("file");
|
||||
|
||||
totalProgressLabel.setFont(new java.awt.Font("Tahoma", 0, 11));
|
||||
totalProgressLabel.setText("totalProgress");
|
||||
|
||||
speedLabel.setFont(new java.awt.Font("Tahoma", 1, 11));
|
||||
speedLabel.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT);
|
||||
speedLabel.setText("speed");
|
||||
|
||||
percentLabel.setFont(new java.awt.Font("Tahoma", 1, 11));
|
||||
percentLabel.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
|
||||
percentLabel.setText("0%");
|
||||
|
||||
transferedLabel.setFont(new java.awt.Font("Tahoma", 2, 11));
|
||||
transferedLabel.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT);
|
||||
transferedLabel.setText("transfer");
|
||||
|
||||
etaLabel.setFont(new java.awt.Font("Tahoma", 0, 11));
|
||||
etaLabel.setHorizontalAlignment(javax.swing.SwingConstants.LEFT);
|
||||
etaLabel.setText("eta");
|
||||
|
||||
jLabel3.setFont(new java.awt.Font("Tahoma", 1, 11));
|
||||
jLabel3.setText("ETA:");
|
||||
|
||||
javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1);
|
||||
jPanel1.setLayout(jPanel1Layout);
|
||||
jPanel1Layout.setHorizontalGroup(
|
||||
jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(jPanel1Layout.createSequentialGroup()
|
||||
.addContainerGap()
|
||||
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(jPanel1Layout.createSequentialGroup()
|
||||
.addComponent(jLabel1)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(totalProgressLabel, javax.swing.GroupLayout.DEFAULT_SIZE, 464, Short.MAX_VALUE))
|
||||
.addComponent(totalProgressBar, javax.swing.GroupLayout.DEFAULT_SIZE, 555, Short.MAX_VALUE)
|
||||
.addComponent(fileProgressBar, javax.swing.GroupLayout.DEFAULT_SIZE, 555, Short.MAX_VALUE)
|
||||
.addGroup(jPanel1Layout.createSequentialGroup()
|
||||
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(jLabel2)
|
||||
.addComponent(jLabel3))
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(jPanel1Layout.createSequentialGroup()
|
||||
.addComponent(etaLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 175, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(percentLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 130, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(transferedLabel, javax.swing.GroupLayout.DEFAULT_SIZE, 207, Short.MAX_VALUE))
|
||||
.addGroup(jPanel1Layout.createSequentialGroup()
|
||||
.addComponent(fileLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 399, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(speedLabel, javax.swing.GroupLayout.DEFAULT_SIZE, 119, Short.MAX_VALUE)))))
|
||||
.addContainerGap())
|
||||
);
|
||||
jPanel1Layout.setVerticalGroup(
|
||||
jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(jPanel1Layout.createSequentialGroup()
|
||||
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
||||
.addComponent(jLabel1)
|
||||
.addComponent(totalProgressLabel))
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(totalProgressBar, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
|
||||
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
||||
.addComponent(jLabel2)
|
||||
.addComponent(fileLabel)
|
||||
.addComponent(speedLabel))
|
||||
.addGap(6, 6, 6)
|
||||
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
||||
.addComponent(jLabel3)
|
||||
.addComponent(etaLabel)
|
||||
.addComponent(transferedLabel)
|
||||
.addComponent(percentLabel))
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(fileProgressBar, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
|
||||
);
|
||||
|
||||
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
|
||||
getContentPane().setLayout(layout);
|
||||
layout.setHorizontalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addContainerGap()
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(jSeparator1, javax.swing.GroupLayout.DEFAULT_SIZE, 587, Short.MAX_VALUE)
|
||||
.addComponent(jPanel1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addComponent(cancelButton, javax.swing.GroupLayout.Alignment.TRAILING))
|
||||
.addContainerGap())
|
||||
);
|
||||
layout.setVerticalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addContainerGap()
|
||||
.addComponent(jPanel1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(jSeparator1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(cancelButton)
|
||||
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
|
||||
);
|
||||
|
||||
pack();
|
||||
}// </editor-fold>
|
||||
|
||||
public void cancel(){
|
||||
System.exit(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param args the command line arguments
|
||||
*/
|
||||
public static void main(String args[]) {
|
||||
java.awt.EventQueue.invokeLater(new Runnable() {
|
||||
public void run() {
|
||||
new Zupdater().setVisible(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Variables declaration - do not modify
|
||||
private javax.swing.JButton cancelButton;
|
||||
private javax.swing.JLabel etaLabel;
|
||||
private javax.swing.JLabel fileLabel;
|
||||
private javax.swing.JProgressBar fileProgressBar;
|
||||
private javax.swing.JLabel jLabel1;
|
||||
private javax.swing.JLabel jLabel2;
|
||||
private javax.swing.JLabel jLabel3;
|
||||
private javax.swing.JPanel jPanel1;
|
||||
private javax.swing.JSeparator jSeparator1;
|
||||
private javax.swing.JLabel percentLabel;
|
||||
private javax.swing.JLabel speedLabel;
|
||||
private javax.swing.JProgressBar totalProgressBar;
|
||||
private javax.swing.JLabel totalProgressLabel;
|
||||
private javax.swing.JLabel transferedLabel;
|
||||
// End of variables declaration
|
||||
|
||||
}
|
||||
266
src/zutil/net/http/HTTPHeaderParser.java
Normal file
266
src/zutil/net/http/HTTPHeaderParser.java
Normal file
|
|
@ -0,0 +1,266 @@
|
|||
package zutil.net.http;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Scanner;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class HTTPHeaderParser {
|
||||
// Some Cached regex's
|
||||
private static final Pattern colonPattern = Pattern.compile(":");
|
||||
private static final Pattern equalPattern = Pattern.compile("=");
|
||||
private static final Pattern andPattern = Pattern.compile("&");
|
||||
private static final Pattern semiColonPattern = Pattern.compile(";");
|
||||
|
||||
// HTTP info
|
||||
private String type;
|
||||
private String url;
|
||||
private HashMap<String, String> url_attr;
|
||||
private float version;
|
||||
private int httpCode;
|
||||
|
||||
// Parameters
|
||||
private HashMap<String, String> headers;
|
||||
private HashMap<String, String> cookies;
|
||||
|
||||
/**
|
||||
* Parses the HTTP header information from the stream
|
||||
*
|
||||
* @param in is the stream
|
||||
* @throws IOException
|
||||
*/
|
||||
public HTTPHeaderParser(BufferedReader in) throws IOException{
|
||||
url_attr = new HashMap<String, String>();
|
||||
headers = new HashMap<String, String>();
|
||||
cookies = new HashMap<String, String>();
|
||||
|
||||
String tmp = null;
|
||||
if( (tmp=in.readLine()) != null && !tmp.isEmpty() ){
|
||||
parseStatusLine( tmp );
|
||||
while( (tmp=in.readLine()) != null && !tmp.isEmpty() ){
|
||||
parseLine( tmp );
|
||||
}
|
||||
}
|
||||
parseCookies();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the HTTP header information from an String
|
||||
*
|
||||
* @param in is the string
|
||||
*/
|
||||
public HTTPHeaderParser(String in){
|
||||
url_attr = new HashMap<String, String>();
|
||||
headers = new HashMap<String, String>();
|
||||
cookies = new HashMap<String, String>();
|
||||
|
||||
Scanner sc = new Scanner(in);
|
||||
sc.useDelimiter("\n");
|
||||
String tmp = null;
|
||||
if( sc.hasNext() && !(tmp=sc.next()).isEmpty() ){
|
||||
parseStatusLine( tmp );
|
||||
while( sc.hasNext() && !(tmp=sc.next()).isEmpty() ){
|
||||
parseLine( tmp );
|
||||
}
|
||||
}
|
||||
parseCookies();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the first header line and ads the values to
|
||||
* the map and returns the file name and path
|
||||
*
|
||||
* @param header The header String
|
||||
* @param map The HashMap to put the variables to
|
||||
* @return The path and file name as a String
|
||||
*/
|
||||
protected void parseStatusLine(String line){
|
||||
// Server Response
|
||||
if( line.startsWith("HTTP/") ){
|
||||
version = Float.parseFloat( line.substring( 5 , 8) );
|
||||
httpCode = Integer.parseInt( line.substring( 9, 12 ));
|
||||
}
|
||||
// Client Request
|
||||
else{
|
||||
type = (line.substring(0, line.indexOf(" "))).trim();
|
||||
version = Float.parseFloat( line.substring(line.lastIndexOf("HTTP/")+5 , line.length()).trim() );
|
||||
line = (line.substring(type.length()+1, line.lastIndexOf("HTTP/"))).trim();
|
||||
|
||||
// parse URL and attributes
|
||||
int index = line.indexOf('?');
|
||||
if(index > -1){
|
||||
url = line.substring(0, index );
|
||||
line = line.substring( index+1, line.length());
|
||||
parseUrlAttributes(line, url_attr);
|
||||
}
|
||||
else{
|
||||
url = line;
|
||||
}
|
||||
|
||||
url = url.replaceAll("//", "/");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a String with variables from a get or post
|
||||
* that was sent from a client and puts the data into a HashMap
|
||||
*
|
||||
* @param attributes is the String containing all the attributes
|
||||
*/
|
||||
public static HashMap<String, String> parseUrlAttributes( String attributes ){
|
||||
HashMap<String, String> map = new HashMap<String, String>();
|
||||
parseUrlAttributes(attributes, map);
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a String with variables from a get or post
|
||||
* that was sent from a client and puts the data into a HashMap
|
||||
*
|
||||
* @param attributes is the String containing all the attributes
|
||||
* @param map is the HashMap to put all the values into
|
||||
*/
|
||||
public static void parseUrlAttributes(String attributes, HashMap<String, String> map){
|
||||
String[] tmp;
|
||||
// get the variables
|
||||
String[] data = andPattern.split( attributes );
|
||||
for(String element : data){
|
||||
tmp = equalPattern.split(element, 2);
|
||||
map.put(
|
||||
tmp[0].trim(), // Key
|
||||
(tmp.length>1 ? tmp[1] : "").trim()); //Value
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the rest of the header
|
||||
*
|
||||
* @param line is the next line in the header
|
||||
*/
|
||||
protected void parseLine(String line){
|
||||
String[] data = colonPattern.split( line, 2 );
|
||||
headers.put(
|
||||
data[0].trim().toUpperCase(), // Key
|
||||
(data.length>1 ? data[1] : "").trim()); //Value
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the attribute "Cookie" and returns a HashMap
|
||||
* with the values
|
||||
*
|
||||
* @return a HashMap with cookie values
|
||||
*/
|
||||
protected void parseCookies(){
|
||||
if( headers.containsKey("COOKIE") ){
|
||||
String[] tmp = semiColonPattern.split( headers.get("COOKIE") );
|
||||
String[] tmp2;
|
||||
for(String cookie : tmp){
|
||||
tmp2 = equalPattern.split(cookie, 2);
|
||||
cookies.put(
|
||||
tmp2[0].trim(), // Key
|
||||
(tmp2.length>1 ? tmp2[1] : "").trim()); //Value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the HTTP message type( ex. GET,POST...)
|
||||
*/
|
||||
public String getRequestType(){
|
||||
return type;
|
||||
}
|
||||
/**
|
||||
* @return the HTTP version of this header
|
||||
*/
|
||||
public float getHTTPVersion(){
|
||||
return version;
|
||||
}
|
||||
/**
|
||||
* @return the HTTP Return Code from a Server
|
||||
*/
|
||||
public float getHTTPCode(){
|
||||
return httpCode;
|
||||
}
|
||||
/**
|
||||
* @return the URL that the client sent the server
|
||||
*/
|
||||
public String getRequestURL(){
|
||||
return url;
|
||||
}
|
||||
/**
|
||||
* Returns the URL attribute value of the given name.
|
||||
*
|
||||
* returns null if there is no such attribute
|
||||
*/
|
||||
public String getURLAttribute(String name){
|
||||
return url_attr.get( name );
|
||||
}
|
||||
/**
|
||||
* Returns the HTTP attribute value of the given name.
|
||||
*
|
||||
* returns null if there is no such attribute
|
||||
*/
|
||||
public String getHeader(String name){
|
||||
return headers.get( name.toUpperCase() );
|
||||
}
|
||||
/**
|
||||
* Returns the cookie value of the given name.
|
||||
*
|
||||
* returns null if there is no such attribute
|
||||
*/
|
||||
public String getCookie(String name){
|
||||
return cookies.get( name );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return a map of the parsed cookies
|
||||
*/
|
||||
public HashMap<String, String> getCookies(){
|
||||
return cookies;
|
||||
}
|
||||
/**
|
||||
* @return a map of the parsed URL attributes
|
||||
*/
|
||||
public HashMap<String, String> getURLAttributes(){
|
||||
return url_attr;
|
||||
}
|
||||
/**
|
||||
* @return a map of the parsed headers
|
||||
*/
|
||||
public HashMap<String, String> getHeaders(){
|
||||
return headers;
|
||||
}
|
||||
|
||||
|
||||
public String toString(){
|
||||
StringBuffer tmp = new StringBuffer();
|
||||
tmp.append("Type: ").append(type);
|
||||
tmp.append("\nHTTP Version: HTTP/").append(version);
|
||||
tmp.append("\nURL: ").append(url);
|
||||
|
||||
for( String key : url_attr.keySet() ){
|
||||
tmp.append("\nURL Attr: ");
|
||||
tmp.append(key);
|
||||
tmp.append("=");
|
||||
tmp.append( url_attr.get(key) );
|
||||
}
|
||||
|
||||
for( String key : headers.keySet() ){
|
||||
tmp.append("\nHeader: ");
|
||||
tmp.append(key);
|
||||
tmp.append("=");
|
||||
tmp.append( headers.get(key) );
|
||||
}
|
||||
|
||||
for( String key : cookies.keySet() ){
|
||||
tmp.append("\nCookie: ");
|
||||
tmp.append(key);
|
||||
tmp.append("=");
|
||||
tmp.append( cookies.get(key) );
|
||||
}
|
||||
|
||||
return tmp.toString();
|
||||
}
|
||||
}
|
||||
28
src/zutil/net/http/HttpPage.java
Normal file
28
src/zutil/net/http/HttpPage.java
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
package zutil.net.http;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* This is a interface for a ordinary page for the HttpServer
|
||||
*
|
||||
* @author Ziver
|
||||
*
|
||||
*/
|
||||
public interface HttpPage{
|
||||
/**
|
||||
* This method has to be implemented for every page.
|
||||
* This method is called when a client wants a response
|
||||
* from this specific page.
|
||||
*
|
||||
* @param out is the PrintStream to the client
|
||||
* @param client_info is information about the client
|
||||
* @param session is session values for the client
|
||||
* @param cookie is cookie information from the client
|
||||
* @param request is POST and GET requests from the client
|
||||
*/
|
||||
public abstract void respond(HttpPrintStream out,
|
||||
Map<String,String> client_info,
|
||||
Map<String,Object> session,
|
||||
Map<String,String> cookie,
|
||||
Map<String,String> request);
|
||||
}
|
||||
268
src/zutil/net/http/HttpPrintStream.java
Normal file
268
src/zutil/net/http/HttpPrintStream.java
Normal file
|
|
@ -0,0 +1,268 @@
|
|||
package zutil.net.http;
|
||||
|
||||
import java.io.OutputStream;
|
||||
import java.io.PrintStream;
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* This PrintStream is written for HTTP use
|
||||
* It has buffer capabilities and cookie management.
|
||||
*
|
||||
* @author Ziver
|
||||
*
|
||||
*/
|
||||
public class HttpPrintStream extends PrintStream{
|
||||
// Defines the type of message
|
||||
public enum HTTPMessageType{
|
||||
REQUEST,
|
||||
RESPONSE
|
||||
}
|
||||
|
||||
// This defines the type of message that will be generated
|
||||
private HTTPMessageType message_type;
|
||||
// The status code of the message, ONLY for response
|
||||
private Integer res_status_code;
|
||||
// The request type of the message ONLY for request
|
||||
private String req_type;
|
||||
// The requesting url ONLY for request
|
||||
private String req_url;
|
||||
// An Map of all the header values
|
||||
private HashMap<String, String> header;
|
||||
// An Map of all the cookies
|
||||
private HashMap<String, String> cookie;
|
||||
// The buffered header
|
||||
private StringBuffer buffer;
|
||||
// If the header buffering is enabled
|
||||
private boolean buffer_enabled;
|
||||
|
||||
/**
|
||||
* Creates an new instance of HttpPrintStream with
|
||||
* message type of RESPONSE and buffering disabled.
|
||||
*
|
||||
* @param out is the OutputStream to send the message
|
||||
*/
|
||||
public HttpPrintStream(OutputStream out) {
|
||||
this( out, HTTPMessageType.RESPONSE );
|
||||
}
|
||||
/**
|
||||
* Creates an new instance of HttpPrintStream with
|
||||
* message type buffering disabled.
|
||||
*
|
||||
* @param out is the OutputStream to send the message
|
||||
* @param type is the type of message
|
||||
*/
|
||||
public HttpPrintStream(OutputStream out, HTTPMessageType type) {
|
||||
super(out);
|
||||
|
||||
this.message_type = type;
|
||||
res_status_code = 0;
|
||||
header = new HashMap<String, String>();
|
||||
cookie = new HashMap<String, String>();
|
||||
buffer = new StringBuffer();
|
||||
buffer_enabled = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable the buffering capability of the PrintStream.
|
||||
* Nothing will be sent to the client when buffering
|
||||
* is enabled until you close or flush the stream.
|
||||
* This function will flush the stream if buffering is
|
||||
* disabled.
|
||||
*
|
||||
* @param b
|
||||
*/
|
||||
public void enableBuffering(boolean b){
|
||||
buffer_enabled = b;
|
||||
if(!buffer_enabled) flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a cookie that will be sent to the client
|
||||
*
|
||||
* @param key is the name of the cookie
|
||||
* @param value is the value of the cookie
|
||||
* @throws Exception Throws exception if the header has already been sent
|
||||
*/
|
||||
public void setCookie(String key, String value) throws RuntimeException{
|
||||
if(cookie == null)
|
||||
throw new RuntimeException("Header already sent!!!");
|
||||
cookie.put(key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an header value
|
||||
*
|
||||
* @param key is the header name
|
||||
* @param value is the value of the header
|
||||
* @throws Exception Throws exception if the header has already been sent
|
||||
*/
|
||||
public void setHeader(String key, String value) throws RuntimeException{
|
||||
if(header == null)
|
||||
throw new RuntimeException("Header already sent!!!");
|
||||
header.put(key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the status code of the message, ONLY available in HTTP RESPONSE
|
||||
*
|
||||
* @param code the code from 100 up to 599
|
||||
* @throws RuntimeException if the header has already been sent or the message type is wrong
|
||||
*/
|
||||
public void setStatusCode(int code) throws RuntimeException{
|
||||
if( res_status_code == null )
|
||||
throw new RuntimeException("Header already sent!!!");
|
||||
if( message_type != HTTPMessageType.RESPONSE )
|
||||
throw new RuntimeException("Status Code is only available in HTTP RESPONSE!!!");
|
||||
res_status_code = code;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the request type of the message, ONLY available in HTTP REQUEST
|
||||
*
|
||||
* @param req_type is the type of the message, e.g. GET, POST...
|
||||
* @throws RuntimeException if the header has already been sent or the message type is wrong
|
||||
*/
|
||||
public void setRequestType(String req_type) throws RuntimeException{
|
||||
if( req_type == null )
|
||||
throw new RuntimeException("Header already sent!!!");
|
||||
if( message_type != HTTPMessageType.REQUEST )
|
||||
throw new RuntimeException("Request Message Type is only available in HTTP REQUEST!!!");
|
||||
this.req_type = req_type;
|
||||
}
|
||||
/**
|
||||
* Sets the requesting URL of the message, ONLY available in HTTP REQUEST
|
||||
*
|
||||
* @param req_url is the URL
|
||||
* @throws RuntimeException if the header has already been sent or the message type is wrong
|
||||
*/
|
||||
public void setRequestURL(String req_url) throws RuntimeException{
|
||||
if( req_url == null )
|
||||
throw new RuntimeException("Header already sent!!!");
|
||||
if( message_type != HTTPMessageType.REQUEST )
|
||||
throw new RuntimeException("Request URL is only available in HTTP REQUEST!!!");
|
||||
this.req_url = req_url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints with a new line
|
||||
*/
|
||||
public void println(String s){
|
||||
printOrBuffer(s+"\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints an string
|
||||
*/
|
||||
public void print(String s){
|
||||
printOrBuffer(s);
|
||||
}
|
||||
|
||||
/**
|
||||
* prints to all
|
||||
*/
|
||||
private void printOrBuffer(String s){
|
||||
if(buffer_enabled){
|
||||
buffer.append(s);
|
||||
}
|
||||
else{
|
||||
if(res_status_code != null){
|
||||
if( message_type==HTTPMessageType.REQUEST )
|
||||
super.print(req_type+" "+req_url+" HTTP/1.1");
|
||||
else
|
||||
super.print("HTTP/1.1 "+res_status_code+" "+getStatusString(res_status_code));
|
||||
super.println();
|
||||
res_status_code = null;
|
||||
req_type = null;
|
||||
req_url = null;
|
||||
}
|
||||
if(header != null){
|
||||
for(String key : header.keySet()){
|
||||
super.print(key+": "+header.get(key));
|
||||
super.println();
|
||||
}
|
||||
header = null;
|
||||
}
|
||||
if(cookie != null){
|
||||
if( !cookie.isEmpty() ){
|
||||
if( message_type==HTTPMessageType.REQUEST ){
|
||||
super.print("Cookie: ");
|
||||
for(String key : cookie.keySet()){
|
||||
super.print(key+"="+cookie.get(key)+"; ");
|
||||
}
|
||||
super.println();
|
||||
}
|
||||
else{
|
||||
for(String key : cookie.keySet()){
|
||||
super.print("Set-Cookie: "+key+"="+cookie.get(key)+";");
|
||||
super.println();
|
||||
}
|
||||
}
|
||||
}
|
||||
super.println();
|
||||
cookie = null;
|
||||
}
|
||||
super.print(s);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends out the buffer and clears it
|
||||
*/
|
||||
public void flush(){
|
||||
if(buffer_enabled){
|
||||
buffer_enabled = false;
|
||||
printOrBuffer(buffer.toString());
|
||||
buffer.delete(0, buffer.length());
|
||||
buffer_enabled = true;
|
||||
}
|
||||
else if(res_status_code != null || header != null || cookie != null){
|
||||
printOrBuffer("");
|
||||
}
|
||||
super.flush();
|
||||
}
|
||||
|
||||
public void close(){
|
||||
flush();
|
||||
super.close();
|
||||
}
|
||||
|
||||
public void println(){ println("");}
|
||||
public void println(boolean x){ println(String.valueOf(x));}
|
||||
public void println(char x){ println(String.valueOf(x));}
|
||||
public void println(char[] x){ println(new String(x));}
|
||||
public void println(double x){ println(String.valueOf(x));}
|
||||
public void println(float x){ println(String.valueOf(x));}
|
||||
public void println(int x){ println(String.valueOf(x));}
|
||||
public void println(long x){ println(String.valueOf(x));}
|
||||
public void println(Object x){ println(String.valueOf(x));}
|
||||
|
||||
public void print(boolean x){ printOrBuffer(String.valueOf(x));}
|
||||
public void print(char x){ printOrBuffer(String.valueOf(x));}
|
||||
public void print(char[] x){ printOrBuffer(new String(x));}
|
||||
public void print(double x){ printOrBuffer(String.valueOf(x));}
|
||||
public void print(float x){ printOrBuffer(String.valueOf(x));}
|
||||
public void print(int x){ printOrBuffer(String.valueOf(x));}
|
||||
public void print(long x){ printOrBuffer(String.valueOf(x));}
|
||||
public void print(Object x){ printOrBuffer(String.valueOf(x));}
|
||||
|
||||
/*
|
||||
public void write(int b) { print((char)b);}
|
||||
public void write(byte buf[], int off, int len){
|
||||
print(new String(buf, off, len));}
|
||||
*/
|
||||
private String getStatusString(int type){
|
||||
switch(type){
|
||||
case 100: return "Continue";
|
||||
case 200: return "OK";
|
||||
case 301: return "Moved Permanently";
|
||||
case 307: return "Temporary Redirect";
|
||||
case 400: return "Bad Request";
|
||||
case 401: return "Unauthorized";
|
||||
case 403: return "Forbidden";
|
||||
case 404: return "Not Found";
|
||||
case 500: return "Internal Server Error";
|
||||
case 501: return "Not Implemented";
|
||||
default: return "";
|
||||
}
|
||||
}
|
||||
}
|
||||
263
src/zutil/net/http/HttpServer.java
Normal file
263
src/zutil/net/http/HttpServer.java
Normal file
|
|
@ -0,0 +1,263 @@
|
|||
package zutil.net.http;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.Socket;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import zutil.log.LogUtil;
|
||||
import zutil.net.threaded.ThreadedTCPNetworkServer;
|
||||
import zutil.net.threaded.ThreadedTCPNetworkServerThread;
|
||||
|
||||
|
||||
/**
|
||||
* A simple web server that handles both cookies and
|
||||
* sessions for all the clients
|
||||
*
|
||||
* @author Ziver
|
||||
*/
|
||||
public class HttpServer extends ThreadedTCPNetworkServer{
|
||||
public static final Logger logger = LogUtil.getLogger();
|
||||
public static final String SERVER_VERSION = "Ziver HttpServer 1.0";
|
||||
public static final int COOKIE_TTL = 200;
|
||||
public static final int SESSION_TTL = 10*60*1000; // in milliseconds
|
||||
|
||||
public final String server_url;
|
||||
public final int server_port;
|
||||
|
||||
private HashMap<String,HttpPage> pages;
|
||||
private HttpPage defaultPage;
|
||||
private Map<String,Map<String,Object>> sessions;
|
||||
private int nextSessionId;
|
||||
|
||||
/**
|
||||
* Creates a new instance of the sever
|
||||
*
|
||||
* @param url The address to the server
|
||||
* @param port The port that the server should listen to
|
||||
*/
|
||||
public HttpServer(String url, int port){
|
||||
this(url, port, null, null);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new instance of the sever
|
||||
*
|
||||
* @param url The address to the server
|
||||
* @param port The port that the server should listen to
|
||||
* @param sslCert If this is not null then the server will use SSL connection with this keyStore file path
|
||||
* @param sslCert If this is not null then the server will use a SSL connection with the given certificate
|
||||
*/
|
||||
public HttpServer(String url, int port, File keyStore, String keyStorePass){
|
||||
super( port, keyStore, keyStorePass );
|
||||
this.server_url = url;
|
||||
this.server_port = port;
|
||||
|
||||
pages = new HashMap<String,HttpPage>();
|
||||
sessions = Collections.synchronizedMap(new HashMap<String,Map<String,Object>>());
|
||||
nextSessionId = 0;
|
||||
|
||||
Timer timer = new Timer();
|
||||
timer.schedule(new GarbageCollector(), 0, SESSION_TTL / 2);
|
||||
|
||||
logger.info("HTTP"+(keyStore==null?"":"S")+" Server ready!");
|
||||
}
|
||||
|
||||
/**
|
||||
* This class acts as an garbage collector that
|
||||
* removes old sessions from the session HashMap
|
||||
*
|
||||
* @author Ziver
|
||||
*/
|
||||
private class GarbageCollector extends TimerTask {
|
||||
public void run(){
|
||||
Object[] keys = sessions.keySet().toArray();
|
||||
for(Object key : keys){
|
||||
Map<String,Object> client_session = sessions.get(key);
|
||||
|
||||
// Check if session is still valid
|
||||
if((Long)client_session.get("ttl") < System.currentTimeMillis()){
|
||||
sessions.remove(key);
|
||||
logger.fine("Removing Session: "+key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a HttpPage to a specific URL
|
||||
*
|
||||
* @param name The URL or name of the page
|
||||
* @param page The page itself
|
||||
*/
|
||||
public void setPage(String name, HttpPage page){
|
||||
pages.put(name, page);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a default page that will be shown
|
||||
* if there is no other matching page,
|
||||
*
|
||||
* @param page The HttpPage that will be shown
|
||||
*/
|
||||
public void setDefaultPage(HttpPage page){
|
||||
defaultPage = page;
|
||||
}
|
||||
|
||||
protected ThreadedTCPNetworkServerThread getThreadInstance( Socket s ){
|
||||
try {
|
||||
return new HttpServerThread( s );
|
||||
} catch (IOException e) {
|
||||
logger.log(Level.SEVERE, "Could not start new Thread", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal class that handles all the requests
|
||||
*
|
||||
* @author Ziver
|
||||
*
|
||||
*/
|
||||
protected class HttpServerThread implements ThreadedTCPNetworkServerThread{
|
||||
private HttpPrintStream out;
|
||||
private BufferedReader in;
|
||||
private Socket socket;
|
||||
|
||||
public HttpServerThread(Socket socket) throws IOException{
|
||||
out = new HttpPrintStream(socket.getOutputStream());
|
||||
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
|
||||
this.socket = socket;
|
||||
logger.fine("New Connection!!! "+socket.getInetAddress().getHostName());
|
||||
}
|
||||
|
||||
public void run(){
|
||||
String tmp = null;
|
||||
|
||||
HashMap<String,String> client_info = new HashMap<String,String>();
|
||||
HashMap<String,String> cookie = new HashMap<String,String>();
|
||||
HashMap<String,String> request = new HashMap<String,String>();
|
||||
|
||||
//**************************** REQUEST *********************************
|
||||
try {
|
||||
logger.finer("Reciving Http Request!!!");
|
||||
|
||||
HTTPHeaderParser parser = new HTTPHeaderParser(in);
|
||||
logger.finest(parser.toString());
|
||||
client_info = parser.getHeaders();
|
||||
request = parser.getURLAttributes();
|
||||
cookie = parser.getCookies();
|
||||
|
||||
|
||||
//******* Read in the post data if available
|
||||
if( parser.getHeader("Content-Length")!=null ){
|
||||
// Reads the post data size
|
||||
tmp = parser.getHeader("Content-Length");
|
||||
int post_data_length = Integer.parseInt( tmp );
|
||||
// read the data
|
||||
StringBuffer tmpb = new StringBuffer();
|
||||
// read the data
|
||||
for(int i=0; i<post_data_length ;i++){
|
||||
tmpb.append((char)in.read());
|
||||
}
|
||||
|
||||
tmp = parser.getHeader("Content-Type");
|
||||
if( tmp.contains("application/x-www-form-urlencoded") ){
|
||||
// get the variables
|
||||
HTTPHeaderParser.parseUrlAttributes( tmpb.toString(), request );
|
||||
}
|
||||
else if( tmp.contains("application/soap+xml" ) ||
|
||||
tmp.contains("text/xml") ||
|
||||
tmp.contains("text/plain") ){
|
||||
// save the variables
|
||||
request.put( "" , tmpb.toString() );
|
||||
}
|
||||
else if( tmp.contains("multipart/form-data") ){
|
||||
// TODO: File upload
|
||||
throw new Exception( "\"multipart-form-data\" Not implemented!!!" );
|
||||
}
|
||||
}
|
||||
|
||||
//**************************** HANDLE REQUEST *********************************
|
||||
// Get the client session or create one
|
||||
Map<String, Object> client_session;
|
||||
long ttl_time = System.currentTimeMillis()+SESSION_TTL;
|
||||
if( cookie.containsKey("session_id") && sessions.containsKey(cookie.get("session_id")) ){
|
||||
client_session = sessions.get( cookie.get("session_id") );
|
||||
// Check if session is still valid
|
||||
if( (Long)client_session.get("ttl") < System.currentTimeMillis() ){
|
||||
int session_id = (Integer)client_session.get("session_id");
|
||||
client_session = Collections.synchronizedMap(new HashMap<String, Object>());
|
||||
client_session.put( "session_id", session_id);
|
||||
sessions.put( ""+session_id, client_session);
|
||||
}
|
||||
// renew the session TTL
|
||||
client_session.put( "ttl", ttl_time );
|
||||
}
|
||||
else{
|
||||
client_session = Collections.synchronizedMap(new HashMap<String, Object>());
|
||||
client_session.put( "session_id", nextSessionId );
|
||||
client_session.put( "ttl", ttl_time );
|
||||
sessions.put( ""+nextSessionId, client_session );
|
||||
nextSessionId++;
|
||||
}
|
||||
// Debug
|
||||
if(logger.isLoggable(Level.FINE)){
|
||||
logger.finest( "# page_url: "+parser.getRequestURL() );
|
||||
logger.finest( "# cookie: "+cookie );
|
||||
logger.finest( "# client_session: "+client_session );
|
||||
logger.finest( "# client_info: "+client_info );
|
||||
logger.finest( "# request: "+request );
|
||||
}
|
||||
//**************************** RESPONSE ************************************
|
||||
logger.finer("Sending Http Response!!!");
|
||||
out.setStatusCode(200);
|
||||
out.setHeader( "Server", SERVER_VERSION );
|
||||
out.setHeader( "Content-Type", "text/html" );
|
||||
out.setCookie( "session_id", ""+client_session.get("session_id") );
|
||||
|
||||
if( !parser.getRequestURL().isEmpty() && pages.containsKey(parser.getRequestURL()) ){
|
||||
pages.get(parser.getRequestURL()).respond(out, client_info, client_session, cookie, request);
|
||||
}
|
||||
else if( defaultPage != null ){
|
||||
defaultPage.respond(out, client_info, client_session, cookie, request);
|
||||
}
|
||||
else{
|
||||
out.setStatusCode( 404 );
|
||||
out.println( "404 Page Not Found: "+parser.getRequestURL() );
|
||||
logger.fine( "404 Page Not Found: "+parser.getRequestURL() );
|
||||
}
|
||||
|
||||
//********************************************************************************
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.WARNING, "500 Internal Server Error", e);
|
||||
try {
|
||||
out.setStatusCode( 500 );
|
||||
} catch (Exception e1) {}
|
||||
if(e.getMessage() != null)
|
||||
out.println( "500 Internal Server Error: "+e.getMessage() );
|
||||
else{
|
||||
out.println( "500 Internal Server Error: "+e.getCause().getMessage() );
|
||||
}
|
||||
}
|
||||
|
||||
try{
|
||||
logger.fine("Conection Closed!!!");
|
||||
out.close();
|
||||
in.close();
|
||||
socket.close();
|
||||
} catch( Exception e ) {
|
||||
logger.log(Level.WARNING, "Could not close connection", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
51
src/zutil/net/http/multipart/MultipartField.java
Normal file
51
src/zutil/net/http/multipart/MultipartField.java
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
package zutil.net.http.multipart;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
|
||||
import zutil.ProgressListener;
|
||||
|
||||
|
||||
/**
|
||||
* A class for handling multipart field
|
||||
*
|
||||
* @author Ziver
|
||||
*/
|
||||
public class MultipartField{
|
||||
protected long received;
|
||||
protected long length;
|
||||
protected String contentType;
|
||||
|
||||
protected String fieldname;
|
||||
protected String value;
|
||||
|
||||
|
||||
protected MultipartField(){
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the amount of data received for this field
|
||||
*/
|
||||
public long getReceivedBytes(){
|
||||
return received;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the fieldname
|
||||
*/
|
||||
public String getFieldname(){
|
||||
return fieldname;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the value of the field
|
||||
*/
|
||||
public String getValue(){
|
||||
return value;
|
||||
}
|
||||
|
||||
protected void parse(){
|
||||
|
||||
}
|
||||
}
|
||||
63
src/zutil/net/http/multipart/MultipartFile.java
Normal file
63
src/zutil/net/http/multipart/MultipartFile.java
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
package zutil.net.http.multipart;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
|
||||
import zutil.ProgressListener;
|
||||
|
||||
|
||||
/**
|
||||
* A class for handling multipart files
|
||||
*
|
||||
* @author Ziver
|
||||
*/
|
||||
public class MultipartFile extends MultipartField{
|
||||
protected String filename;
|
||||
protected File file;
|
||||
|
||||
|
||||
protected MultipartFile(File tempFile){
|
||||
this.file = tempFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the amount of data received for this field
|
||||
*/
|
||||
public long getReceivedBytes(){
|
||||
return received;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the value of the field
|
||||
*/
|
||||
public String getValue(){
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the filename
|
||||
*/
|
||||
public String getFilename(){
|
||||
return filename;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the File class that points to the received file
|
||||
*/
|
||||
public File getFile(){
|
||||
return file;
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves this file
|
||||
*
|
||||
* @param new_file is the new location to move the file to
|
||||
* @return if the move was successful
|
||||
*/
|
||||
public boolean moveFile(File new_file){
|
||||
boolean success = file.renameTo(new_file);
|
||||
if(success)
|
||||
file = new_file;
|
||||
return success;
|
||||
}
|
||||
}
|
||||
194
src/zutil/net/http/multipart/MultipartParser.java
Normal file
194
src/zutil/net/http/multipart/MultipartParser.java
Normal file
|
|
@ -0,0 +1,194 @@
|
|||
package zutil.net.http.multipart;
|
||||
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import zutil.ProgressListener;
|
||||
import zutil.net.http.HTTPHeaderParser;
|
||||
|
||||
/**
|
||||
* Parses a multipart/form-data http request,
|
||||
* saves files to temporary location.
|
||||
*
|
||||
* http://www.ietf.org/rfc/rfc1867.txt
|
||||
*
|
||||
* @author Ziver
|
||||
*
|
||||
*/
|
||||
public class MultipartParser {
|
||||
/** This is the temporary directory for the received files */
|
||||
private File tempDir;
|
||||
/** This is the delimiter that will separate the fields */
|
||||
private String delimiter;
|
||||
/** The length of the HTTP Body */
|
||||
private long contentLength;
|
||||
/** This is the input stream */
|
||||
private BufferedReader in;
|
||||
|
||||
/** This is the listener that will listen on the progress */
|
||||
private ProgressListener<MultipartField> listener;
|
||||
|
||||
|
||||
|
||||
public MultipartParser(BufferedReader in, HTTPHeaderParser header){
|
||||
this.in = in;
|
||||
|
||||
String cotype = header.getHeader("Content-type");
|
||||
cotype = cotype.split(" *; *")[1];
|
||||
delimiter = cotype.split(" *= *")[1];
|
||||
|
||||
contentLength = Long.parseLong( header.getHeader("Content-Length") );
|
||||
}
|
||||
|
||||
public MultipartParser(BufferedReader in, HttpServletRequest req){
|
||||
this.in = in;
|
||||
|
||||
String cotype = req.getHeader("Content-type");
|
||||
cotype = cotype.split(" *; *")[1];
|
||||
delimiter = cotype.split(" *= *")[1];
|
||||
|
||||
contentLength = req.getContentLength();
|
||||
}
|
||||
|
||||
public MultipartParser(BufferedReader in, String delimiter, long length){
|
||||
this.in = in;
|
||||
this.delimiter = delimiter;
|
||||
this.contentLength = length;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param listener is the listener that will be called for progress
|
||||
*/
|
||||
public void setListener(ProgressListener<MultipartField> listener){
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the HTTP Body and returns a list of fields
|
||||
*
|
||||
* @return A list of FormField
|
||||
*/
|
||||
public List<MultipartField> parse() throws IOException{
|
||||
ArrayList<MultipartField> list = new ArrayList<MultipartField>();
|
||||
// TODO: parse(list, delimiter);
|
||||
return list;
|
||||
}
|
||||
|
||||
// TODO:
|
||||
/*
|
||||
private void parse(List<MultipartField> list, String delimiter) throws IOException{
|
||||
String line = "";
|
||||
MultipartField field = null;
|
||||
delimiter = "--"+delimiter;
|
||||
String endDelimiter = delimiter+"--";
|
||||
BufferedWriter out = null;
|
||||
// Parsing the stream
|
||||
while(line != null){
|
||||
line = in.readLine();
|
||||
// Skip empty lines
|
||||
if(line == null || line.trim().isEmpty())
|
||||
continue;
|
||||
// End of field
|
||||
else if(line.equals( endDelimiter )){
|
||||
list.add(field);
|
||||
if(out != null) out.close();
|
||||
field.length = field.file.length();
|
||||
out = null;
|
||||
field = null;
|
||||
continue;
|
||||
}
|
||||
// New field
|
||||
else if(line.equals( delimiter )){
|
||||
if(field != null){
|
||||
list.add(field);
|
||||
if(out != null) out.close();
|
||||
field.length = field.file.length();
|
||||
out = null;
|
||||
field = null;
|
||||
}
|
||||
// Read the content-disposition
|
||||
line = in.readLine();
|
||||
if(line.toLowerCase().startsWith("content-disposition")){
|
||||
line = line.split(":", 2)[1];
|
||||
String[] fieldData = line.split(" *; *");
|
||||
//String type = fieldData[0].toLowerCase();
|
||||
field = new MultipartField();
|
||||
field.type = MultipartField.FieldType.Field;
|
||||
|
||||
// Parse content-disposition parameters
|
||||
for(String param : fieldData){
|
||||
String[] temp = param.split(" *= *");
|
||||
if(temp[0].equalsIgnoreCase("name"))
|
||||
field.fieldname = temp[1];
|
||||
else if(temp[0].equalsIgnoreCase("filename")){
|
||||
field.filename = temp[1];
|
||||
field.file = createTempFile();
|
||||
out = new BufferedWriter(new FileWriter(field.file));
|
||||
field.type = MultipartField.FieldType.File;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
throw new IOException("MultipartForm parse error unrecognized line: "+line);
|
||||
}
|
||||
// Read field data
|
||||
else if(field != null){
|
||||
if(field.type == MultipartField.FieldType.File){
|
||||
out.append(line);
|
||||
}
|
||||
else{
|
||||
field.value += line;
|
||||
}
|
||||
field.received += line.length();
|
||||
}
|
||||
}
|
||||
|
||||
if(field != null)
|
||||
throw new IOException("MultipartForm parse error stream ended prematurely");
|
||||
}
|
||||
*/
|
||||
|
||||
/**
|
||||
* Creates a temporary file in either the system
|
||||
* temporary folder or by the setTempDir() function
|
||||
*
|
||||
* @return the temporary file
|
||||
*/
|
||||
protected File createTempFile() throws IOException{
|
||||
if(tempDir != null)
|
||||
return File.createTempFile("upload", ".part", tempDir.getAbsoluteFile());
|
||||
else
|
||||
return File.createTempFile("upload", ".part");
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the initial delimiter
|
||||
*
|
||||
* @param delimiter is the new delimiter
|
||||
*/
|
||||
public void setDelimiter(String delimiter){
|
||||
this.delimiter = delimiter;
|
||||
}
|
||||
|
||||
public void setTempDir(File dir){
|
||||
if(!dir.isDirectory())
|
||||
throw new RuntimeException("\""+dir.getAbsolutePath()+"\" is not a directory!");
|
||||
if(!dir.canWrite())
|
||||
throw new RuntimeException("\""+dir.getAbsolutePath()+"\" is not writable!");
|
||||
tempDir = dir;
|
||||
}
|
||||
|
||||
public long getContentLength(){
|
||||
return contentLength;
|
||||
}
|
||||
}
|
||||
79
src/zutil/net/http/soap/SOAPClientFactory.java
Normal file
79
src/zutil/net/http/soap/SOAPClientFactory.java
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
package zutil.net.http.soap;
|
||||
|
||||
import javax.wsdl.WSDLException;
|
||||
|
||||
import javassist.CannotCompileException;
|
||||
import javassist.ClassPool;
|
||||
import javassist.CtClass;
|
||||
import javassist.CtMethod;
|
||||
import javassist.CtNewMethod;
|
||||
import javassist.NotFoundException;
|
||||
import zutil.net.ws.WSInterface;
|
||||
import zutil.net.ws.WSMethodDef;
|
||||
import zutil.net.ws.WebServiceDef;
|
||||
|
||||
/**
|
||||
* This is an factory that generates clients for web services
|
||||
*
|
||||
* @author Ziver
|
||||
*/
|
||||
public class SOAPClientFactory {
|
||||
|
||||
/**
|
||||
* Generates a Client Object for the web service.
|
||||
*
|
||||
* @param <T> is the class of the web service definition
|
||||
* @param intf is the class of the web service definition
|
||||
* @return a client Object
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> T getClient(Class<T> intf) throws InstantiationException, IllegalAccessException, CannotCompileException, NotFoundException, WSDLException{
|
||||
if( !WSInterface.class.isAssignableFrom( intf )){
|
||||
throw new ClassCastException("The Web Service class is not a subclass of WSInterface!");
|
||||
}
|
||||
return getClient( intf, new WebServiceDef((Class<? extends WSInterface>)intf) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a Client Object for the web service.
|
||||
*
|
||||
* @param <T> is the class of the web service definition
|
||||
* @param intf is the class of the web service definition
|
||||
* @param wsDef is the web service definition of the intf parameter
|
||||
* @return a client Object
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> T getClient(Class<T> intf, WebServiceDef wsDef) throws InstantiationException, IllegalAccessException, CannotCompileException, NotFoundException{
|
||||
if( !WSInterface.class.isAssignableFrom( intf )){
|
||||
throw new ClassCastException("The Web Service class is not a subclass of WSInterface!");
|
||||
}
|
||||
|
||||
// Generate the class
|
||||
ClassPool pool = ClassPool.getDefault();
|
||||
CtClass cc = pool.makeClass(intf.getName()+"Impl_"+Math.random());
|
||||
|
||||
CtClass intfClass = pool.get( intf.getName() );
|
||||
|
||||
// Is intf an interface
|
||||
if( intf.isInterface() ){
|
||||
cc.addInterface( intfClass );
|
||||
}
|
||||
// or a class
|
||||
else{
|
||||
cc.setSuperclass( intfClass );
|
||||
}
|
||||
|
||||
// Generate the methods
|
||||
// TODO:
|
||||
for(WSMethodDef methodDef : wsDef.getMethods()){
|
||||
CtMethod method = CtNewMethod.make("public int m(int i){}", cc);
|
||||
method.insertBefore("System.out.println(\"Hello.say():\");");
|
||||
}
|
||||
|
||||
// Initiate the class
|
||||
Class<T> c = cc.toClass();
|
||||
T obj = c.newInstance();
|
||||
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
15
src/zutil/net/http/soap/SOAPException.java
Normal file
15
src/zutil/net/http/soap/SOAPException.java
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
package zutil.net.http.soap;
|
||||
|
||||
/**
|
||||
* This generates an client fault message
|
||||
* when used with SOAPHttpPage
|
||||
*
|
||||
* @author Ziver
|
||||
*/
|
||||
public class SOAPException extends Exception{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public SOAPException(String string) {
|
||||
super(string);
|
||||
}
|
||||
}
|
||||
860
src/zutil/net/http/soap/SOAPHttpPage.java
Normal file
860
src/zutil/net/http/soap/SOAPHttpPage.java
Normal file
|
|
@ -0,0 +1,860 @@
|
|||
package zutil.net.http.soap;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Array;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.wsdl.Binding;
|
||||
import javax.wsdl.BindingInput;
|
||||
import javax.wsdl.BindingOperation;
|
||||
import javax.wsdl.BindingOutput;
|
||||
import javax.wsdl.Definition;
|
||||
import javax.wsdl.Fault;
|
||||
import javax.wsdl.Import;
|
||||
import javax.wsdl.Input;
|
||||
import javax.wsdl.Message;
|
||||
import javax.wsdl.Operation;
|
||||
import javax.wsdl.Output;
|
||||
import javax.wsdl.Part;
|
||||
import javax.wsdl.Port;
|
||||
import javax.wsdl.PortType;
|
||||
import javax.wsdl.Service;
|
||||
import javax.wsdl.WSDLException;
|
||||
import javax.wsdl.extensions.soap.SOAPAddress;
|
||||
import javax.wsdl.extensions.soap.SOAPBinding;
|
||||
import javax.wsdl.extensions.soap.SOAPBody;
|
||||
import javax.wsdl.extensions.soap.SOAPHeader;
|
||||
import javax.wsdl.extensions.soap.SOAPOperation;
|
||||
import javax.wsdl.factory.WSDLFactory;
|
||||
import javax.wsdl.xml.WSDLWriter;
|
||||
import javax.xml.namespace.QName;
|
||||
|
||||
import org.dom4j.Document;
|
||||
import org.dom4j.DocumentException;
|
||||
import org.dom4j.DocumentHelper;
|
||||
import org.dom4j.Element;
|
||||
import org.dom4j.io.OutputFormat;
|
||||
import org.dom4j.io.XMLWriter;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
import zutil.converters.Converter;
|
||||
import zutil.io.StringOutputStream;
|
||||
import zutil.log.LogUtil;
|
||||
import zutil.net.http.HttpPage;
|
||||
import zutil.net.http.HttpPrintStream;
|
||||
import zutil.net.ws.WSInterface;
|
||||
import zutil.net.ws.WSObject;
|
||||
import zutil.net.ws.WSReturnValueList;
|
||||
import zutil.net.ws.WSInterface.WSDocumentation;
|
||||
import zutil.net.ws.WSInterface.WSParamDocumentation;
|
||||
import zutil.net.ws.WSObject.WSFieldName;
|
||||
|
||||
import com.ibm.wsdl.extensions.PopulatedExtensionRegistry;
|
||||
import com.ibm.wsdl.extensions.soap.SOAPConstants;
|
||||
import com.sun.org.apache.xerces.internal.dom.DocumentImpl;
|
||||
|
||||
/**
|
||||
* This is an HTTPPage for the HTTPServer that
|
||||
* handles soap messages.
|
||||
*
|
||||
* TODO: Header should be variables not methods
|
||||
* TODO: Read SOAPObjects as input parameter
|
||||
* TODO: Ability to have multiple arrays of same SOAPObject
|
||||
*
|
||||
* Features:
|
||||
* Input:
|
||||
* <br>-int
|
||||
* <br>-double
|
||||
* <br>-float
|
||||
* <br>-char
|
||||
* <br>-String
|
||||
* <br>-byte[]
|
||||
* <br>-And the Wrappers except byte
|
||||
*
|
||||
* Output:
|
||||
* <br>-SOAPObjects
|
||||
* <br>-SOAPReturnObjectList
|
||||
* <br>-byte[]
|
||||
* <br>-int
|
||||
* <br>-double
|
||||
* <br>-float
|
||||
* <br>-char
|
||||
* <br>-String
|
||||
* <br>-Arrays of Output
|
||||
* <br>-And the Wrappers except byte
|
||||
*
|
||||
* @author Ziver
|
||||
*/
|
||||
public class SOAPHttpPage implements HttpPage{
|
||||
public static final Logger logger = LogUtil.getLogger();
|
||||
|
||||
// valid methods for this soap page
|
||||
private HashMap<String, MethodCache> methods;
|
||||
// contains an method and the names for the parameters
|
||||
private class MethodCache{
|
||||
String[] paramName;
|
||||
boolean[] paramOptional;
|
||||
String[] returnName;
|
||||
Class<?>[] returnClass;
|
||||
Method method;
|
||||
boolean header;
|
||||
|
||||
MethodCache(Method m){
|
||||
method = m;
|
||||
paramName = new String[method.getParameterTypes().length];
|
||||
paramOptional = new boolean[method.getParameterTypes().length];
|
||||
header = false;
|
||||
|
||||
Class<?> tmp = m.getReturnType();
|
||||
if( WSReturnValueList.class.isAssignableFrom( tmp )){
|
||||
returnName = new String[ tmp.getFields().length ];
|
||||
returnClass = new Class<?>[ tmp.getFields().length ];
|
||||
}
|
||||
else if( !tmp.isAssignableFrom( void.class )){
|
||||
returnName = new String[1];
|
||||
returnClass = new Class<?>[1];
|
||||
}
|
||||
else{
|
||||
returnName = new String[0];
|
||||
returnClass = new Class<?>[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
// The object that the functions will be invoked from
|
||||
private WSInterface interf;
|
||||
// The WSDL document
|
||||
private Definition wsdl;
|
||||
// The WSDL Type part
|
||||
private Document wsdlType;
|
||||
// the URL to this soap page
|
||||
private String url;
|
||||
// Session enabled
|
||||
private boolean session_enabled;
|
||||
|
||||
public SOAPHttpPage(String url, WSInterface interf) throws WSDLException{
|
||||
//if(!SOAPInterface.class.isAssignableFrom(interf) )
|
||||
// throw new ClassCastException("Class does not implement SOAPInterface!");
|
||||
this.url = url;
|
||||
this.interf = interf;
|
||||
this.session_enabled = false;
|
||||
methods = new HashMap<String, MethodCache>();
|
||||
|
||||
for(Method m : interf.getClass().getDeclaredMethods()){
|
||||
// check for public methods
|
||||
if((m.getModifiers() & Modifier.PUBLIC) > 0 &&
|
||||
!m.isAnnotationPresent(WSInterface.WSDisabled.class)){
|
||||
MethodCache chasch = new MethodCache(m);
|
||||
StringBuffer tmp = new StringBuffer(m.getName()+"(");
|
||||
|
||||
// Get the parameter names
|
||||
Annotation[][] paramAnnotation = m.getParameterAnnotations();
|
||||
|
||||
for(int i=0; i<paramAnnotation.length ;i++){
|
||||
for(Annotation annotation : paramAnnotation[i]){
|
||||
if(annotation instanceof WSInterface.WSParamName){
|
||||
WSInterface.WSParamName paramName = (WSInterface.WSParamName) annotation;
|
||||
chasch.paramName[i] = paramName.value();
|
||||
chasch.paramOptional[i] = paramName.optional();
|
||||
}
|
||||
}
|
||||
// if no name was found then use default
|
||||
if(chasch.paramName[i] == null)
|
||||
chasch.paramName[i] = "args"+i;
|
||||
|
||||
tmp.append(m.getParameterTypes()[i].getSimpleName()+" "+chasch.paramName[i]);
|
||||
if( i<paramAnnotation.length-1 ) tmp.append(", ");
|
||||
}
|
||||
tmp.append(") => ");
|
||||
|
||||
// the return parameter name
|
||||
WSInterface.WSReturnName returnName = m.getAnnotation(WSInterface.WSReturnName.class);
|
||||
if( WSReturnValueList.class.isAssignableFrom( m.getReturnType() ) ){
|
||||
Class<?> retClass = m.getReturnType();
|
||||
for(int i=0; i<retClass.getFields().length ;i++){
|
||||
if(i!=0) tmp.append(", ");
|
||||
WSReturnValueList.WSValueName retValName = retClass.getFields()[i]
|
||||
.getAnnotation( WSReturnValueList.WSValueName.class );
|
||||
if(retValName != null) chasch.returnName[i] = retValName.value();
|
||||
else chasch.returnName[i] = retClass.getFields()[i].getName();
|
||||
chasch.returnClass[i] = retClass.getFields()[i].getType();
|
||||
tmp.append(chasch.returnClass[i].getSimpleName()+" "+chasch.returnName[i]);
|
||||
}
|
||||
}
|
||||
else if( chasch.returnName.length>0 ){
|
||||
if(returnName != null) chasch.returnName[0] = returnName.value();
|
||||
else chasch.returnName[0] = "return";
|
||||
chasch.returnClass[0] = m.getReturnType();
|
||||
tmp.append(chasch.returnClass[0].getSimpleName()+" "+chasch.returnName[0]);
|
||||
}
|
||||
|
||||
// SOAP header?
|
||||
if(m.getAnnotation(WSInterface.WSHeader.class) != null)
|
||||
chasch.header = true;
|
||||
|
||||
// save in HashMap
|
||||
logger.info("New SOAP Method Registered: "+tmp);
|
||||
methods.put(m.getName(), chasch);
|
||||
}
|
||||
}
|
||||
|
||||
generateWSDL();
|
||||
|
||||
if(logger.isLoggable(Level.INFO)){
|
||||
try {
|
||||
// WSDL
|
||||
StringOutputStream out = new StringOutputStream();
|
||||
WSDLFactory factory = WSDLFactory.newInstance();
|
||||
WSDLWriter writer = factory.newWSDLWriter();
|
||||
writer.writeWSDL(wsdl, out);
|
||||
logger.info(out.toString());
|
||||
// WSDL Type
|
||||
out.clear();
|
||||
OutputFormat format = OutputFormat.createPrettyPrint();
|
||||
XMLWriter xmlWriter = new XMLWriter( out, format );
|
||||
xmlWriter.write( wsdlType );
|
||||
logger.info(out.toString());
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables session support, if enabled then a new instance
|
||||
* of the SOAPInterface will be created, if disabled then
|
||||
* only the given object will be used as an static interface
|
||||
*
|
||||
* @param enabled is if session should be enabled
|
||||
*/
|
||||
public void enableSession(boolean enabled){
|
||||
this.session_enabled = enabled;
|
||||
}
|
||||
|
||||
|
||||
public void respond(HttpPrintStream out,
|
||||
Map<String, String> client_info,
|
||||
Map<String, Object> session,
|
||||
Map<String, String> cookie,
|
||||
Map<String, String> request) {
|
||||
|
||||
try {
|
||||
out.setHeader("Content-Type", "text/xml");
|
||||
out.flush();
|
||||
|
||||
if(request.containsKey("wsdl")){
|
||||
WSDLWriter writer = WSDLFactory.newInstance().newWSDLWriter();
|
||||
writer.writeWSDL(wsdl, out);
|
||||
}
|
||||
else if(request.containsKey("type")){
|
||||
OutputFormat format = OutputFormat.createPrettyPrint();
|
||||
XMLWriter writer = new XMLWriter( out, format );
|
||||
writer.write( wsdlType );
|
||||
}
|
||||
else{
|
||||
WSInterface obj = null;
|
||||
if(session_enabled){
|
||||
if( session.containsKey("SOAPInterface"))
|
||||
obj = (WSInterface)session.get("SOAPInterface");
|
||||
else{
|
||||
obj = interf.getClass().newInstance();
|
||||
session.put("SOAPInterface", obj);
|
||||
}
|
||||
}
|
||||
else{
|
||||
obj = interf;
|
||||
}
|
||||
|
||||
Document document = genSOAPResponse( request.get(""), obj);
|
||||
|
||||
OutputFormat format = OutputFormat.createCompactFormat();
|
||||
XMLWriter writer = new XMLWriter( out, format );
|
||||
writer.write( document );
|
||||
|
||||
|
||||
// DEBUG
|
||||
OutputFormat format2 = OutputFormat.createPrettyPrint();
|
||||
System.err.println("********** Request");
|
||||
System.err.println(request);
|
||||
System.out.println("********** Response");
|
||||
writer = new XMLWriter( System.out, format2 );
|
||||
writer.write( document );
|
||||
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.WARNING, "Unhandled request", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a soap response for the given XML
|
||||
* @param xml is the XML request
|
||||
* @return a Document with the response
|
||||
*/
|
||||
public Document genSOAPResponse(String xml){
|
||||
try {
|
||||
WSInterface o = null;
|
||||
if(session_enabled) o = interf.getClass().newInstance();
|
||||
else o = interf;
|
||||
|
||||
return genSOAPResponse(xml, o );
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.WARNING, "Exception in SOAP generation", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
protected Document genSOAPResponse(String xml, WSInterface obj){
|
||||
Document document = DocumentHelper.createDocument();
|
||||
Element envelope = document.addElement("soap:Envelope");
|
||||
try {
|
||||
envelope.addNamespace("soap", "http://schemas.xmlsoap.org/soap/envelope/");
|
||||
envelope.addNamespace("xsi", "http://www.w3.org/2001/XMLSchema-instance");
|
||||
envelope.addNamespace("xsd", "http://www.w3.org/2001/XMLSchema");
|
||||
|
||||
Element body = envelope.addElement( "soap:Body" );
|
||||
try{
|
||||
Element request = getXMLRoot(xml);
|
||||
if(request == null) return document;
|
||||
// Header
|
||||
if( request.element("Header") != null){
|
||||
Element header = envelope.addElement( "soap:Header" );
|
||||
prepareInvoke( obj, request.element("Header"), header );
|
||||
}
|
||||
|
||||
// Body
|
||||
if( request.element("Body") != null){
|
||||
prepareInvoke( obj, request.element("Body"), body );
|
||||
}
|
||||
}catch(Throwable e){
|
||||
body.clearContent();
|
||||
Element fault = body.addElement("soap:Fault");
|
||||
// The fault source
|
||||
if(e instanceof SOAPException || e instanceof SAXException || e instanceof DocumentException)
|
||||
fault.addElement("faultcode").setText( "soap:Client" );
|
||||
else
|
||||
fault.addElement("faultcode").setText( "soap:Server" );
|
||||
// The fault message
|
||||
if( e.getMessage() == null || e.getMessage().isEmpty())
|
||||
fault.addElement("faultstring").setText( ""+e.getClass().getSimpleName() );
|
||||
else
|
||||
fault.addElement("faultstring").setText( ""+e.getMessage() );
|
||||
logger.log(Level.WARNING, "Caught exception from SOAP Class", e);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.WARNING, "Exception in SOAP generation", e);
|
||||
}
|
||||
|
||||
return document;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an String XML to an Element
|
||||
*
|
||||
* @param msg is the string XML
|
||||
* @return the XML root Element
|
||||
*/
|
||||
private Element getXMLRoot(String xml) throws Exception {
|
||||
if(xml != null && !xml.isEmpty()){
|
||||
Document document = DocumentHelper.parseText(xml);
|
||||
return document.getRootElement();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes an XML Element and invokes all the
|
||||
* Child Elements as methods.
|
||||
*
|
||||
* @param obj is the object that the methods will be called from
|
||||
* @param requestRoot is the Element where the children lies
|
||||
* @param responseRoot is the root element of the response
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private void prepareInvoke(WSInterface obj, Element requestRoot, Element responseRoot) throws Throwable{
|
||||
Iterator<Element> it = requestRoot.elementIterator();
|
||||
while( it.hasNext() ){
|
||||
Element e = it.next();
|
||||
if(methods.containsKey(e.getQName().getName())){
|
||||
MethodCache m = methods.get(e.getQName().getName());
|
||||
Object[] params = new Object[m.paramName.length];
|
||||
|
||||
// Get the parameter values
|
||||
for(int i=0; i<m.paramName.length ;i++){
|
||||
if(e.element(m.paramName[i]) != null)
|
||||
params[i] = Converter.fromString(
|
||||
e.element(m.paramName[i]).getTextTrim(),
|
||||
m.method.getParameterTypes()[i]);
|
||||
}
|
||||
// MultiPrintStream.out.println("invoking: "+m.method.getName()+" "+MultiPrintStream.out.dumpToString(params));
|
||||
// Invoke
|
||||
Object ret = invoke(obj, m.method, params);
|
||||
|
||||
// generate response XML
|
||||
if( m.returnClass.length>0 ){
|
||||
WSInterface.WSNamespace namespace = m.method.getAnnotation(WSInterface.WSNamespace.class);
|
||||
Element response = responseRoot.addElement("");
|
||||
if( namespace != null )
|
||||
response.addNamespace("m", namespace.value());
|
||||
else
|
||||
response.addNamespace("m", url+""+m.method.getName());
|
||||
response.setName("m:"+m.method.getName()+"Response");
|
||||
if( ret instanceof WSReturnValueList ){
|
||||
Field[] f = ret.getClass().getFields();
|
||||
for(int i=0; i<m.returnName.length ;i++ ){
|
||||
generateReturnXML(response,((WSReturnValueList)ret).getValue(f[i]) , m.returnName[i], m);
|
||||
}
|
||||
}
|
||||
else{
|
||||
generateReturnXML(response, ret, m.returnName[0], m);
|
||||
}
|
||||
}
|
||||
}
|
||||
else{
|
||||
throw new Exception("No such method: "+e.getQName().getName()+"!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes a specified method
|
||||
*
|
||||
* @param m is the function
|
||||
* @param params a vector with arguments
|
||||
* @throws Throwable
|
||||
*/
|
||||
protected Object invoke(Object obj, Method m, Object[] params) throws Throwable{
|
||||
try {
|
||||
return m.invoke(obj, params );
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new SOAPException("Arguments missing for "+m.getName()+"!");
|
||||
} catch (IllegalAccessException e) {
|
||||
throw e;
|
||||
} catch (InvocationTargetException e) {
|
||||
throw e.getCause();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates an return XML Element. This function can
|
||||
* handle return values as XML Elements, SOAPObject and the
|
||||
* Java basic data types.
|
||||
*
|
||||
* @param root is the parent Element
|
||||
* @param ret is the object that is the return value
|
||||
* @param ename is the name of the parent Element
|
||||
* @param m is the method that returned the ret value
|
||||
*/
|
||||
private void generateReturnXML(Element root, Object ret, String ename, MethodCache m) throws IllegalArgumentException, IllegalAccessException{
|
||||
if(ret == null) return;
|
||||
if(byte[].class.isAssignableFrom(ret.getClass())){
|
||||
Element valueE = root.addElement( ename );
|
||||
valueE.addAttribute("type", "xsd:"+getClassSOAPName(ret.getClass()));
|
||||
String tmp = new sun.misc.BASE64Encoder().encode((byte[])ret);
|
||||
tmp = tmp.replaceAll("\\s", "");
|
||||
valueE.setText(tmp);
|
||||
}
|
||||
// return an array
|
||||
else if(ret.getClass().isArray()){
|
||||
Element array = root.addElement( (ename.equals("element") ? "Array" : ename) );
|
||||
String arrayType = "xsd:"+getClassSOAPName(ret.getClass());
|
||||
arrayType = arrayType.replaceFirst("\\[\\]", "["+Array.getLength(ret)+"]");
|
||||
|
||||
array.addAttribute("type", "soap:Array");
|
||||
array.addAttribute("soap:arrayType", arrayType);
|
||||
for(int i=0; i<Array.getLength(ret) ;i++){
|
||||
generateReturnXML(array, Array.get(ret, i), "element", m);
|
||||
}
|
||||
}
|
||||
else{
|
||||
Element objectE = root.addElement( ename ); //getClassSOAPName(ret.getClass())
|
||||
if(ret instanceof Element)
|
||||
objectE.add( (Element)ret );
|
||||
else if(ret instanceof WSObject){
|
||||
Field[] fields = ret.getClass().getFields();
|
||||
for(int i=0; i<fields.length ;i++){
|
||||
WSFieldName tmp = fields[i].getAnnotation(WSObject.WSFieldName.class);
|
||||
String name;
|
||||
if(tmp != null) name = tmp.value();
|
||||
else name = "field"+i;
|
||||
generateReturnXML(objectE, fields[i].get(ret), name, m);
|
||||
}
|
||||
}
|
||||
else {
|
||||
objectE.addAttribute("type", "xsd:"+getClassSOAPName(ret.getClass()));
|
||||
objectE.addText( ""+ret );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private String getClassSOAPName(Class<?> c){
|
||||
Class<?> cTmp = getClass(c);
|
||||
if(byte[].class.isAssignableFrom(c)){
|
||||
return "base64Binary";
|
||||
}
|
||||
else if( WSObject.class.isAssignableFrom(cTmp) ){
|
||||
return c.getSimpleName();
|
||||
}
|
||||
else{
|
||||
String ret = c.getSimpleName().toLowerCase();
|
||||
|
||||
if(cTmp == Integer.class) ret = ret.replaceAll("integer", "int");
|
||||
else if(cTmp == Character.class)ret = ret.replaceAll("character", "char");
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
//********************************************************
|
||||
//********* WSDL Generation *************************
|
||||
|
||||
/**
|
||||
* Generates an WSDL document for the class
|
||||
*
|
||||
* @throws WSDLException
|
||||
*/
|
||||
private void generateWSDL() throws WSDLException{
|
||||
ArrayList<Class<?>> types = new ArrayList<Class<?>>();
|
||||
|
||||
String tns = url+"?wsdl";
|
||||
String xsd = "http://www.w3.org/2001/XMLSchema";
|
||||
String soap = "http://schemas.xmlsoap.org/wsdl/soap/";
|
||||
String wsdln = "http://schemas.xmlsoap.org/wsdl/";
|
||||
String td = url+"?type";
|
||||
|
||||
PopulatedExtensionRegistry extReg = new PopulatedExtensionRegistry();
|
||||
WSDLFactory factory = WSDLFactory.newInstance();
|
||||
String portTypeName = this.interf.getClass().getSimpleName()+"PortType";
|
||||
|
||||
wsdl = factory.newDefinition();
|
||||
wsdl.setQName(new QName(tns, this.interf.getClass().getSimpleName()));
|
||||
wsdl.setTargetNamespace(tns);
|
||||
wsdl.addNamespace("tns", tns);
|
||||
wsdl.addNamespace("xsd", xsd);
|
||||
wsdl.addNamespace("soap", soap);
|
||||
wsdl.addNamespace("wsdl", wsdln);
|
||||
wsdl.addNamespace("td", td);
|
||||
|
||||
Message exception = wsdl.createMessage();
|
||||
exception.setQName(new QName(tns, "exception"));
|
||||
exception.setUndefined(true);
|
||||
Part epart = wsdl.createPart();
|
||||
epart.setName("message");
|
||||
epart.setTypeName(new QName(xsd, "string"));
|
||||
exception.addPart(epart);
|
||||
wsdl.addMessage(exception);
|
||||
|
||||
Message empty = wsdl.createMessage();
|
||||
empty.setQName(new QName(tns, "empty"));
|
||||
empty.setUndefined(false);
|
||||
epart = wsdl.createPart();
|
||||
epart.setName("empty");
|
||||
epart.setTypeName(new QName(td, "empty"));
|
||||
empty.addPart(epart);
|
||||
wsdl.addMessage(empty);
|
||||
|
||||
// Types import
|
||||
Import imp = wsdl.createImport();
|
||||
imp.setNamespaceURI(td);
|
||||
imp.setLocationURI(td);
|
||||
wsdl.addImport(imp);
|
||||
|
||||
// PortType
|
||||
PortType portType = wsdl.createPortType();
|
||||
portType.setQName(new QName(tns, portTypeName));
|
||||
portType.setUndefined(false);
|
||||
for(MethodCache m : methods.values()){
|
||||
Operation operation = wsdl.createOperation();
|
||||
//********* Request Messages
|
||||
if(m.paramName.length > 0){
|
||||
Message msgIn = wsdl.createMessage();
|
||||
msgIn.setQName(new QName(tns, m.method.getName()+"Request"));
|
||||
msgIn.setUndefined(false);
|
||||
|
||||
//***** Documentation
|
||||
WSParamDocumentation tmpParamDoc = m.method.getAnnotation(WSInterface.WSParamDocumentation.class);
|
||||
if(tmpParamDoc != null){
|
||||
org.w3c.dom.Document xmldoc= new DocumentImpl();
|
||||
org.w3c.dom.Element paramDoc = xmldoc.createElement("wsdl:documentation");
|
||||
paramDoc.setTextContent(tmpParamDoc.value());
|
||||
msgIn.setDocumentationElement(paramDoc);
|
||||
}
|
||||
|
||||
// Parameters
|
||||
for(int i=0; i<m.paramName.length ;i++){
|
||||
// Parts
|
||||
Part part = wsdl.createPart();
|
||||
part.setName(m.paramName[i]);
|
||||
part.setTypeName(new QName( xsd,
|
||||
getClassSOAPName(m.method.getParameterTypes()[i])));
|
||||
if(m.paramOptional[i])
|
||||
part.getExtensionAttribute(new QName("minOccurs", "0"));
|
||||
msgIn.addPart(part);
|
||||
}
|
||||
wsdl.addMessage(msgIn);
|
||||
Input input = wsdl.createInput();
|
||||
input.setMessage(msgIn);
|
||||
operation.setInput(input);
|
||||
}
|
||||
else{
|
||||
Input input = wsdl.createInput();
|
||||
input.setMessage(empty);
|
||||
operation.setInput(input);
|
||||
}
|
||||
//********** Response Message
|
||||
if( m.returnName.length>0 ){
|
||||
Message msgOut = wsdl.createMessage();
|
||||
msgOut.setQName(new QName(tns, m.method.getName()+"Response"));
|
||||
msgOut.setUndefined(false);
|
||||
|
||||
for( int i=0; i<m.returnName.length ;i++ ){
|
||||
Class<?> retClass = m.returnClass[i];
|
||||
//MultiPrintStream.out.println(m.method.getName()+"=>"+m.returnName[i]+"="+retClass);
|
||||
|
||||
// Parts
|
||||
Part part = wsdl.createPart();
|
||||
part.setName( m.returnName[i] );
|
||||
msgOut.addPart(part);
|
||||
|
||||
Class<?> cTmp = getClass( retClass );
|
||||
// is an binary array
|
||||
if(byte[].class.isAssignableFrom( retClass )){
|
||||
part.setTypeName(new QName(xsd, "base64Binary"));
|
||||
}
|
||||
// is an array?
|
||||
else if( retClass.isArray()){
|
||||
part.setTypeName(new QName(td,
|
||||
"ArrayOf"+getClassSOAPName( retClass ).replaceAll("[\\[\\]]", "")));
|
||||
// add to type generation list
|
||||
if(!types.contains( retClass ))
|
||||
types.add( retClass );
|
||||
}
|
||||
else if( WSObject.class.isAssignableFrom(cTmp) ){
|
||||
// its an SOAPObject
|
||||
part.setTypeName(new QName(td, getClassSOAPName( retClass )));
|
||||
// add to type generation list
|
||||
if(!types.contains(cTmp))
|
||||
types.add(cTmp);
|
||||
}
|
||||
else{// its an Object
|
||||
part.setTypeName(new QName(xsd, getClassSOAPName( retClass )));
|
||||
}
|
||||
}
|
||||
|
||||
wsdl.addMessage(msgOut);
|
||||
Output output = wsdl.createOutput();
|
||||
output.setMessage(msgOut);
|
||||
operation.setOutput(output);
|
||||
}
|
||||
//************* Exceptions
|
||||
if(m.method.getExceptionTypes().length <= 0){
|
||||
Fault fault = wsdl.createFault();
|
||||
fault.setMessage(exception);
|
||||
operation.addFault(fault);
|
||||
}
|
||||
//************* Operations
|
||||
operation.setName(m.method.getName());
|
||||
operation.setUndefined(false);
|
||||
|
||||
//***** Documentation
|
||||
WSDocumentation tmpDoc = m.method.getAnnotation(WSInterface.WSDocumentation.class);
|
||||
if(tmpDoc != null){
|
||||
// <!-- example -->
|
||||
org.w3c.dom.Document xmldoc= new DocumentImpl();
|
||||
org.w3c.dom.Element doc = xmldoc.createElement("wsdl:documentation");
|
||||
doc.setTextContent(tmpDoc.value());
|
||||
operation.setDocumentationElement(doc);
|
||||
}
|
||||
|
||||
portType.addOperation(operation);
|
||||
}
|
||||
wsdl.addPortType(portType);
|
||||
|
||||
// Binding
|
||||
Binding binding = wsdl.createBinding();
|
||||
binding.setQName(new QName(tns, interf.getClass().getSimpleName()+"Binding"));
|
||||
binding.setPortType(portType);
|
||||
binding.setUndefined(false);
|
||||
|
||||
SOAPBinding soapBinding = (SOAPBinding)extReg.createExtension(Binding.class, SOAPConstants.Q_ELEM_SOAP_BINDING);
|
||||
soapBinding.setStyle("rpc");
|
||||
//soapBinding.setRequired(true);
|
||||
soapBinding.setTransportURI("http://schemas.xmlsoap.org/soap/http");
|
||||
binding.addExtensibilityElement(soapBinding);
|
||||
|
||||
for(MethodCache m : methods.values()){
|
||||
BindingOperation operation = wsdl.createBindingOperation();
|
||||
operation.setName(m.method.getName());
|
||||
|
||||
SOAPOperation soapOperation = (SOAPOperation)extReg.createExtension(BindingOperation.class, SOAPConstants.Q_ELEM_SOAP_OPERATION);
|
||||
soapOperation.setSoapActionURI(url+""+m.method.getName());
|
||||
operation.addExtensibilityElement(soapOperation);
|
||||
|
||||
// input
|
||||
BindingInput input = wsdl.createBindingInput();
|
||||
// Header
|
||||
if(m.header){
|
||||
SOAPHeader soapHeader = (SOAPHeader)extReg.createExtension(BindingInput.class, SOAPConstants.Q_ELEM_SOAP_HEADER);
|
||||
soapHeader.setUse("literal");
|
||||
soapHeader.setNamespaceURI(url+""+m.method.getName());
|
||||
input.addExtensibilityElement(soapHeader);
|
||||
}// Body
|
||||
else{
|
||||
SOAPBody soapBody = (SOAPBody)extReg.createExtension(BindingInput.class, SOAPConstants.Q_ELEM_SOAP_BODY);
|
||||
soapBody.setUse("literal");
|
||||
soapBody.setNamespaceURI(url+""+m.method.getName());
|
||||
input.addExtensibilityElement(soapBody);
|
||||
}
|
||||
operation.setBindingInput(input);
|
||||
|
||||
// output
|
||||
if(!m.method.getReturnType().equals( void.class )){
|
||||
BindingOutput output = wsdl.createBindingOutput();
|
||||
// Header
|
||||
if(m.header){
|
||||
SOAPHeader soapHeader = (SOAPHeader)extReg.createExtension(BindingInput.class, SOAPConstants.Q_ELEM_SOAP_HEADER);
|
||||
soapHeader.setUse("literal");
|
||||
soapHeader.setNamespaceURI(url+""+m.method.getName());
|
||||
output.addExtensibilityElement(soapHeader);
|
||||
}// Body
|
||||
else{
|
||||
SOAPBody soapBody = (SOAPBody)extReg.createExtension(BindingInput.class, SOAPConstants.Q_ELEM_SOAP_BODY);
|
||||
soapBody.setUse("literal");
|
||||
soapBody.setNamespaceURI(url+""+m.method.getName());
|
||||
output.addExtensibilityElement(soapBody);
|
||||
}
|
||||
operation.setBindingOutput(output);
|
||||
}
|
||||
|
||||
binding.addBindingOperation(operation);
|
||||
}
|
||||
wsdl.addBinding(binding);
|
||||
|
||||
// Service
|
||||
Port port = wsdl.createPort();
|
||||
port.setName( interf.getClass().getSimpleName()+"Port" );
|
||||
port.setBinding(binding);
|
||||
SOAPAddress addr = (SOAPAddress)extReg.createExtension(Port.class, SOAPConstants.Q_ELEM_SOAP_ADDRESS);
|
||||
addr.setLocationURI(url);
|
||||
port.addExtensibilityElement(addr);
|
||||
|
||||
Service ser = wsdl.createService();
|
||||
ser.setQName(new QName(tns, interf.getClass().getSimpleName()+"Service"));
|
||||
ser.addPort(port);
|
||||
wsdl.addService(ser);
|
||||
|
||||
// generate the complexTypes
|
||||
generateWSDLType(types);
|
||||
}
|
||||
|
||||
/**
|
||||
* This function generates the Type part of the WSDL.
|
||||
* Should be cabled after generateWSDL has finished.
|
||||
*
|
||||
*/
|
||||
private void generateWSDLType(ArrayList<Class<?>> types){
|
||||
wsdlType = DocumentHelper.createDocument();
|
||||
Element definitions = wsdlType.addElement( "wsdl:definitions" );
|
||||
definitions.addAttribute("targetNamespace", url);
|
||||
definitions.addNamespace("tns", url+"?type");
|
||||
definitions.addNamespace("xsd", "http://www.w3.org/2001/XMLSchema");
|
||||
definitions.addNamespace("wsdl", "http://schemas.xmlsoap.org/wsdl/");
|
||||
definitions.addNamespace("SOAP-ENC", "http://schemas.xmlsoap.org/soap/encoding/");
|
||||
|
||||
Element typeE = definitions.addElement("wsdl:types");
|
||||
Element schema = typeE.addElement("xsd:schema");
|
||||
schema.addAttribute("targetNamespace", url+"?type");
|
||||
|
||||
// empty type
|
||||
Element empty = schema.addElement("xsd:complexType");
|
||||
empty.addAttribute("name", "empty");
|
||||
empty.addElement("xsd:sequence");
|
||||
|
||||
for(int n=0; n<types.size() ;n++){
|
||||
Class<?> c = types.get(n);
|
||||
// Generate Array type
|
||||
if(c.isArray()){
|
||||
Class<?> ctmp = getClass(c);
|
||||
|
||||
Element type = schema.addElement("xsd:complexType");
|
||||
type.addAttribute("name",
|
||||
"ArrayOf"+getClassSOAPName(c).replaceAll("[\\[\\]]", ""));
|
||||
|
||||
/*// .Net can't handle this code
|
||||
Element complexContent = type.addElement("complexContent");
|
||||
|
||||
Element restriction = complexContent.addElement("restriction");
|
||||
restriction.addAttribute("base", "SOAP-ENC:Array");
|
||||
|
||||
Element attribute = restriction.addElement("attribute");
|
||||
attribute.addAttribute("ref", "SOAP-ENC:arrayType");
|
||||
attribute.addAttribute("wsdl:arrayType", "tns:"+getClassSOAPName(c));
|
||||
*/
|
||||
|
||||
Element sequence = type.addElement("xsd:sequence");
|
||||
|
||||
Element element = sequence.addElement("xsd:element");
|
||||
element.addAttribute("minOccurs", "0");
|
||||
element.addAttribute("maxOccurs", "unbounded");
|
||||
element.addAttribute("name", "element");
|
||||
element.addAttribute("nillable", "true");
|
||||
if(WSObject.class.isAssignableFrom(ctmp))
|
||||
element.addAttribute("type", "tns:"+getClassSOAPName(c).replace("[]", ""));
|
||||
else
|
||||
element.addAttribute("type", "xsd:"+getClassSOAPName(c).replace("[]", ""));
|
||||
|
||||
if(!types.contains(ctmp))
|
||||
types.add(ctmp);
|
||||
}
|
||||
// Generate SOAPObject type
|
||||
else if(WSObject.class.isAssignableFrom(c)){
|
||||
Element type = schema.addElement("xsd:complexType");
|
||||
type.addAttribute("name", getClassSOAPName(c));
|
||||
|
||||
Element sequence = type.addElement("xsd:sequence");
|
||||
|
||||
Field[] fields = c.getFields();
|
||||
for(int i=0; i<fields.length ;i++){
|
||||
WSFieldName tmp = fields[i].getAnnotation(WSObject.WSFieldName.class);
|
||||
String name;
|
||||
if(tmp != null) name = tmp.value();
|
||||
else name = "field"+i;
|
||||
|
||||
Element element = sequence.addElement("xsd:element");
|
||||
element.addAttribute("name", name);
|
||||
|
||||
// Check if the object is an SOAPObject
|
||||
Class<?> cTmp = getClass(fields[i].getType());
|
||||
if(WSObject.class.isAssignableFrom(cTmp)){
|
||||
element.addAttribute("type", "tns:"+getClassSOAPName(cTmp));
|
||||
if(!types.contains(cTmp))
|
||||
types.add(cTmp);
|
||||
}
|
||||
else{
|
||||
element.addAttribute("type", "xsd:"+getClassSOAPName(fields[i].getType()));
|
||||
}
|
||||
// Is the Field optional
|
||||
if(tmp != null && tmp.optional())
|
||||
element.addAttribute("minOccurs", "0");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Class<?> getClass(Class<?> c){
|
||||
if(c!=null && c.isArray()){
|
||||
return getClass(c.getComponentType());
|
||||
}
|
||||
return c;
|
||||
}
|
||||
}
|
||||
|
||||
57
src/zutil/net/nio/NioClient.java
Normal file
57
src/zutil/net/nio/NioClient.java
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
package zutil.net.nio;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.nio.channels.Selector;
|
||||
import java.nio.channels.SocketChannel;
|
||||
import java.nio.channels.spi.SelectorProvider;
|
||||
|
||||
import zutil.net.nio.message.Message;
|
||||
import zutil.net.nio.message.type.ResponseRequestMessage;
|
||||
import zutil.net.nio.response.ResponseEvent;
|
||||
|
||||
|
||||
public class NioClient extends NioNetwork{
|
||||
private SocketChannel serverSocket;
|
||||
|
||||
/**
|
||||
* Creates a NioClient that connects to a server
|
||||
*
|
||||
* @param hostAddress The server address
|
||||
* @param port The port to listen on
|
||||
*/
|
||||
public NioClient(InetAddress serverAddress, int port) throws IOException {
|
||||
super(InetAddress.getLocalHost(), port, NetworkType.CLIENT);
|
||||
serverSocket = initiateConnection(new InetSocketAddress(serverAddress, port));
|
||||
Thread thread = new Thread(this);
|
||||
thread.setDaemon(false);
|
||||
thread.start();
|
||||
}
|
||||
|
||||
protected Selector initSelector() throws IOException {
|
||||
// Create a new selector
|
||||
return SelectorProvider.provider().openSelector();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a Message to the default server
|
||||
*
|
||||
* @param data The data to be sent
|
||||
* @throws IOException Something got wrong
|
||||
*/
|
||||
public void send(Message data) throws IOException {
|
||||
send(serverSocket, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is for the Client to send a message to the server
|
||||
*
|
||||
* @param handler The response handler
|
||||
* @param data The data to send
|
||||
* @throws IOException
|
||||
*/
|
||||
public void send(ResponseEvent handler, ResponseRequestMessage data) throws IOException {
|
||||
send(serverSocket, handler, data);
|
||||
}
|
||||
}
|
||||
452
src/zutil/net/nio/NioNetwork.java
Normal file
452
src/zutil/net/nio/NioNetwork.java
Normal file
|
|
@ -0,0 +1,452 @@
|
|||
package zutil.net.nio;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.SelectionKey;
|
||||
import java.nio.channels.Selector;
|
||||
import java.nio.channels.ServerSocketChannel;
|
||||
import java.nio.channels.SocketChannel;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import zutil.Encrypter;
|
||||
import zutil.converters.Converter;
|
||||
import zutil.io.DynamicByteArrayStream;
|
||||
import zutil.io.MultiPrintStream;
|
||||
import zutil.log.LogUtil;
|
||||
import zutil.net.nio.message.type.ResponseRequestMessage;
|
||||
import zutil.net.nio.message.type.SystemMessage;
|
||||
import zutil.net.nio.response.ResponseEvent;
|
||||
import zutil.net.nio.server.ChangeRequest;
|
||||
import zutil.net.nio.server.ClientData;
|
||||
import zutil.net.nio.worker.SystemWorker;
|
||||
import zutil.net.nio.worker.Worker;
|
||||
|
||||
|
||||
public abstract class NioNetwork implements Runnable {
|
||||
private static Logger logger = LogUtil.getLogger();
|
||||
public static enum NetworkType {SERVER, CLIENT};
|
||||
|
||||
private NetworkType type;
|
||||
|
||||
// The host:port combination to listen on
|
||||
protected InetAddress address;
|
||||
protected int port;
|
||||
|
||||
// The channel on which we'll accept connections
|
||||
protected ServerSocketChannel serverChannel;
|
||||
// The selector we'll be monitoring
|
||||
private Selector selector;
|
||||
// The buffer into which we'll read data when it's available
|
||||
private ByteBuffer readBuffer = ByteBuffer.allocate(8192);
|
||||
protected Worker worker;
|
||||
protected SystemWorker systemWorker;
|
||||
|
||||
// This map contains all the clients that are conncted
|
||||
protected Map<InetSocketAddress, ClientData> clients = new HashMap<InetSocketAddress, ClientData>();
|
||||
|
||||
// A list of PendingChange instances
|
||||
private List<ChangeRequest> pendingChanges = new LinkedList<ChangeRequest>();
|
||||
// Maps a SocketChannel to a list of ByteBuffer instances
|
||||
private Map<SocketChannel, List<ByteBuffer>> pendingWriteData = new HashMap<SocketChannel, List<ByteBuffer>>();
|
||||
private Map<SocketChannel, DynamicByteArrayStream> pendingReadData = new HashMap<SocketChannel, DynamicByteArrayStream>();
|
||||
// The encrypter
|
||||
private Encrypter encrypter;
|
||||
|
||||
/**
|
||||
* Create a nio network class
|
||||
*
|
||||
* @param hostAddress The host address
|
||||
* @param port The port
|
||||
* @param type The type of network host
|
||||
* @throws IOException
|
||||
*/
|
||||
public NioNetwork(InetAddress address, int port, NetworkType type) throws IOException {
|
||||
this.port = port;
|
||||
this.address = address;
|
||||
this.type = type;
|
||||
this.selector = initSelector();
|
||||
this.systemWorker = new SystemWorker(this);
|
||||
}
|
||||
|
||||
protected abstract Selector initSelector() throws IOException;
|
||||
|
||||
/**
|
||||
* Sets the Worker for the network messages
|
||||
*
|
||||
* @param worker The worker that handles the incoming messages
|
||||
*/
|
||||
public void setDefaultWorker(Worker worker){
|
||||
this.worker = worker;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the encrypter to use in the network
|
||||
*
|
||||
* @param enc The encrypter to use or null fo no encryption
|
||||
*/
|
||||
public void setEncrypter(Encrypter enc){
|
||||
encrypter = enc;
|
||||
MultiPrintStream.out.println("Network Encryption "+
|
||||
(encrypter != null ? "Enabled("+encrypter.getAlgorithm()+")" : "Disabled")+"!!");
|
||||
}
|
||||
|
||||
public void send(SocketChannel socket, Object data) throws IOException{
|
||||
send(socket, Converter.toBytes(data));
|
||||
}
|
||||
|
||||
public void send(InetSocketAddress address, Object data) throws IOException{
|
||||
send(address, Converter.toBytes(data));
|
||||
}
|
||||
|
||||
public void send(InetSocketAddress address, byte[] data){
|
||||
send(getSocketChannel(address), data);
|
||||
}
|
||||
|
||||
public void send(SocketChannel socket, ResponseEvent handler, ResponseRequestMessage data) throws IOException {
|
||||
// Register the response handler
|
||||
systemWorker.addResponseHandler(handler, data);
|
||||
|
||||
queueSend(socket,Converter.toBytes(data));
|
||||
}
|
||||
|
||||
/**
|
||||
* This method sends data true the given socket
|
||||
*
|
||||
* @param socket The socket
|
||||
* @param data The data to send
|
||||
*/
|
||||
public void send(SocketChannel socket, byte[] data) {
|
||||
queueSend(socket,data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Queues the message to be sent and wakeups the selector
|
||||
*
|
||||
* @param socket The socet to send the message thrue
|
||||
* @param data The data to send
|
||||
*/
|
||||
protected void queueSend(SocketChannel socket, byte[] data){
|
||||
logger.finest("Sending Queue...");
|
||||
// And queue the data we want written
|
||||
synchronized (pendingWriteData) {
|
||||
List<ByteBuffer> queue = pendingWriteData.get(socket);
|
||||
if (queue == null) {
|
||||
queue = new ArrayList<ByteBuffer>();
|
||||
pendingWriteData.put(socket, queue);
|
||||
}
|
||||
//encrypts
|
||||
if(encrypter != null)
|
||||
queue.add(ByteBuffer.wrap(encrypter.encrypt(data)));
|
||||
else queue.add(ByteBuffer.wrap(data));
|
||||
}
|
||||
// Changing the key state to write
|
||||
synchronized (pendingChanges) {
|
||||
// Indicate we want the interest ops set changed
|
||||
pendingChanges.add(new ChangeRequest(socket, ChangeRequest.CHANGEOPS, SelectionKey.OP_WRITE));
|
||||
}
|
||||
logger.finest("selector.wakeup();");
|
||||
// Finally, wake up our selecting thread so it can make the required changes
|
||||
selector.wakeup();
|
||||
}
|
||||
|
||||
public void run() {
|
||||
logger.fine("NioNetwork Started!!!");
|
||||
while (true) {
|
||||
try {
|
||||
// Process any pending changes
|
||||
synchronized (pendingChanges) {
|
||||
Iterator<ChangeRequest> changes = pendingChanges.iterator();
|
||||
while (changes.hasNext()) {
|
||||
ChangeRequest change = changes.next();
|
||||
switch (change.type) {
|
||||
case ChangeRequest.CHANGEOPS:
|
||||
SelectionKey key = change.socket.keyFor(selector);
|
||||
key.interestOps(change.ops);
|
||||
logger.finest("change.ops "+change.ops);
|
||||
break;
|
||||
case ChangeRequest.REGISTER:
|
||||
change.socket.register(selector, change.ops);
|
||||
logger.finest("register socket ");
|
||||
break;
|
||||
}
|
||||
}
|
||||
pendingChanges.clear();
|
||||
}
|
||||
|
||||
// Wait for an event one of the registered channels
|
||||
selector.select();
|
||||
logger.finest("selector is awake");
|
||||
|
||||
// Iterate over the set of keys for which events are available
|
||||
Iterator<SelectionKey> selectedKeys = selector.selectedKeys().iterator();
|
||||
while (selectedKeys.hasNext()) {
|
||||
SelectionKey key = (SelectionKey) selectedKeys.next();
|
||||
selectedKeys.remove();
|
||||
logger.finest("KeyOP: "+key.interestOps()+" isAcceptable: "+SelectionKey.OP_ACCEPT+" isConnectable: "+SelectionKey.OP_CONNECT+" isWritable: "+SelectionKey.OP_WRITE+" isReadable: "+SelectionKey.OP_READ);
|
||||
|
||||
if (key.isValid()) {
|
||||
// Check what event is available and deal with it
|
||||
if (key.isAcceptable()) {
|
||||
logger.finest("Accepting Connection!!");
|
||||
accept(key);
|
||||
}
|
||||
else if (key.isConnectable()) {
|
||||
logger.finest("Finnishing Connection!!");
|
||||
finishConnection(key);
|
||||
}
|
||||
else if (key.isWritable()) {
|
||||
logger.finest("Writing");
|
||||
write(key);
|
||||
}
|
||||
else if (key.isReadable()) {
|
||||
logger.finest("Reading");
|
||||
read(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Server
|
||||
*/
|
||||
private void accept(SelectionKey key) throws IOException {
|
||||
// For an accept to be pending the channel must be a server socket channel.
|
||||
ServerSocketChannel serverSocketChannel = (ServerSocketChannel) key.channel();
|
||||
|
||||
// Accept the connection and make it non-blocking
|
||||
SocketChannel socketChannel = serverSocketChannel.accept();
|
||||
socketChannel.socket().setReuseAddress(true);
|
||||
socketChannel.configureBlocking(false);
|
||||
|
||||
// Register the new SocketChannel with our Selector, indicating
|
||||
// we'd like to be notified when there's data waiting to be read
|
||||
socketChannel.register(selector, SelectionKey.OP_READ);
|
||||
|
||||
// adds the client to the clients list
|
||||
InetSocketAddress remoteAdr = (InetSocketAddress) socketChannel.socket().getRemoteSocketAddress();
|
||||
if(!clients.containsValue(remoteAdr)){
|
||||
clients.put(remoteAdr, new ClientData(socketChannel));
|
||||
logger.fine("New Connection("+remoteAdr+")!!! Count: "+clients.size());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Client and Server
|
||||
*/
|
||||
private void read(SelectionKey key) throws IOException {
|
||||
SocketChannel socketChannel = (SocketChannel) key.channel();
|
||||
InetSocketAddress remoteAdr = (InetSocketAddress) socketChannel.socket().getRemoteSocketAddress();
|
||||
|
||||
// Clear out our read buffer so it's ready for new data
|
||||
readBuffer.clear();
|
||||
|
||||
// Attempt to read off the channel
|
||||
int numRead;
|
||||
try {
|
||||
numRead = socketChannel.read(readBuffer);
|
||||
} catch (IOException e) {
|
||||
// The remote forcibly closed the connection, cancel
|
||||
// the selection key and close the channel.
|
||||
key.cancel();
|
||||
socketChannel.close();
|
||||
clients.remove(remoteAdr);
|
||||
pendingReadData.remove(socketChannel);
|
||||
pendingWriteData.remove(socketChannel);
|
||||
logger.fine("Connection Forced Close("+remoteAdr+")!!! Connection Count: "+clients.size());
|
||||
if(type == NetworkType.CLIENT)
|
||||
throw new IOException("Server Closed The Connection!!!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (numRead == -1) {
|
||||
// Remote entity shut the socket down cleanly. Do the
|
||||
// same from our end and cancel the channel.
|
||||
key.channel().close();
|
||||
key.cancel();
|
||||
clients.remove(remoteAdr);
|
||||
pendingReadData.remove(socketChannel);
|
||||
pendingWriteData.remove(socketChannel);
|
||||
logger.fine("Connection Close("+remoteAdr+")!!! Connection Count: "+clients.size());
|
||||
if(type == NetworkType.CLIENT)
|
||||
throw new IOException("Server Closed The Connection!!!");
|
||||
return;
|
||||
}
|
||||
|
||||
// Make a correctly sized copy of the data before handing it
|
||||
// to the client
|
||||
byte[] rspByteData = new byte[numRead];
|
||||
System.arraycopy(readBuffer.array(), 0, rspByteData, 0, numRead);
|
||||
if(encrypter != null)// Encryption
|
||||
rspByteData = encrypter.decrypt(rspByteData);
|
||||
|
||||
// Message Count 1m: 36750
|
||||
// Message Count 1s: 612
|
||||
if(!pendingReadData.containsKey(socketChannel)){
|
||||
pendingReadData.put(socketChannel, new DynamicByteArrayStream());
|
||||
}
|
||||
DynamicByteArrayStream dynBuf = pendingReadData.get(socketChannel);
|
||||
dynBuf.add(rspByteData);
|
||||
|
||||
|
||||
Object rspData = null;
|
||||
try{
|
||||
//rspData = Converter.toObject(rspByteData);
|
||||
rspData = Converter.toObject(dynBuf);
|
||||
handleRecivedMessage(socketChannel, rspData);
|
||||
dynBuf.clear();
|
||||
}catch(Exception e){
|
||||
e.printStackTrace();
|
||||
dynBuf.reset();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Client and Server
|
||||
*/
|
||||
private void write(SelectionKey key) throws IOException {
|
||||
SocketChannel socketChannel = (SocketChannel) key.channel();
|
||||
|
||||
synchronized (pendingWriteData) {
|
||||
List<ByteBuffer> queue = pendingWriteData.get(socketChannel);
|
||||
if(queue == null){
|
||||
queue = new ArrayList<ByteBuffer>();
|
||||
pendingWriteData.put(socketChannel, queue);
|
||||
}
|
||||
|
||||
// Write until there's not more data ...
|
||||
while (!queue.isEmpty()) {
|
||||
ByteBuffer buf = queue.get(0);
|
||||
socketChannel.write(buf);
|
||||
if (buf.remaining() > 0) {
|
||||
// ... or the socket's buffer fills up
|
||||
logger.finest("Write Buffer Full!!");
|
||||
break;
|
||||
}
|
||||
queue.remove(0);
|
||||
}
|
||||
|
||||
if (queue.isEmpty()) {
|
||||
// We wrote away all data, so we're no longer interested
|
||||
// in writing on this socket. Switch back to waiting for
|
||||
// data.
|
||||
logger.finest("No more Data to write!!");
|
||||
key.interestOps(SelectionKey.OP_READ);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void handleRecivedMessage(SocketChannel socketChannel, Object rspData){
|
||||
logger.finer("Handling incomming message...");
|
||||
|
||||
if(rspData instanceof SystemMessage){
|
||||
if(systemWorker != null){
|
||||
logger.finest("System Message!!!");
|
||||
systemWorker.processData(this, socketChannel, rspData);
|
||||
}
|
||||
else{
|
||||
logger.finer("Unhandled System Message!!!");
|
||||
}
|
||||
}
|
||||
else{
|
||||
// Hand the data off to our worker thread
|
||||
if(worker != null){
|
||||
logger.finest("Worker Message!!!");
|
||||
worker.processData(this, socketChannel, rspData);
|
||||
}
|
||||
else{
|
||||
logger.fine("Unhandled Worker Message!!!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes a socket to a server
|
||||
*/
|
||||
protected SocketChannel initiateConnection(InetSocketAddress address) throws IOException {
|
||||
// Create a non-blocking socket channel
|
||||
SocketChannel socketChannel = SocketChannel.open();
|
||||
socketChannel.socket().setReuseAddress(true);
|
||||
socketChannel.configureBlocking(false);
|
||||
logger.fine("Connecting to: "+address);
|
||||
|
||||
// Kick off connection establishment
|
||||
socketChannel.connect(address);
|
||||
|
||||
// Queue a channel registration since the caller is not the
|
||||
// selecting thread. As part of the registration we'll register
|
||||
// an interest in connection events. These are raised when a channel
|
||||
// is ready to complete connection establishment.
|
||||
synchronized(this.pendingChanges) {
|
||||
pendingChanges.add(new ChangeRequest(socketChannel, ChangeRequest.REGISTER, SelectionKey.OP_CONNECT));
|
||||
}
|
||||
|
||||
return socketChannel;
|
||||
}
|
||||
|
||||
protected SocketChannel getSocketChannel(InetSocketAddress address){
|
||||
return clients.get(address).getSocketChannel();
|
||||
}
|
||||
|
||||
/**
|
||||
* Client
|
||||
*/
|
||||
private void finishConnection(SelectionKey key){
|
||||
SocketChannel socketChannel = (SocketChannel) key.channel();
|
||||
|
||||
// Finish the connection. If the connection operation failed
|
||||
// this will raise an IOException.
|
||||
try {
|
||||
socketChannel.finishConnect();
|
||||
} catch (IOException e) {
|
||||
// Cancel the channel's registration with our selector
|
||||
e.printStackTrace();
|
||||
key.cancel();
|
||||
return;
|
||||
}
|
||||
|
||||
// Register an interest in writing on this channel
|
||||
key.interestOps(SelectionKey.OP_WRITE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Client
|
||||
* @throws IOException
|
||||
*/
|
||||
protected void closeConnection(SocketChannel socketChannel) throws IOException{
|
||||
socketChannel.close();
|
||||
socketChannel.keyFor(selector).cancel();
|
||||
}
|
||||
|
||||
/**
|
||||
* Client
|
||||
* @throws IOException
|
||||
*/
|
||||
protected void closeConnection(InetSocketAddress address) throws IOException{
|
||||
closeConnection(getSocketChannel(address));
|
||||
}
|
||||
|
||||
/*
|
||||
public void close() throws IOException{
|
||||
if(serverChannel != null){
|
||||
serverChannel.close();
|
||||
serverChannel.keyFor(selector).cancel();
|
||||
}
|
||||
selector.close();
|
||||
}*/
|
||||
|
||||
public NetworkType getType(){
|
||||
return type;
|
||||
}
|
||||
}
|
||||
68
src/zutil/net/nio/NioServer.java
Normal file
68
src/zutil/net/nio/NioServer.java
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
package zutil.net.nio;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.nio.channels.SelectionKey;
|
||||
import java.nio.channels.Selector;
|
||||
import java.nio.channels.ServerSocketChannel;
|
||||
import java.nio.channels.spi.SelectorProvider;
|
||||
import java.util.Iterator;
|
||||
|
||||
public class NioServer extends NioNetwork{
|
||||
|
||||
/**
|
||||
* Creates a NioServer object which listens on localhost
|
||||
*
|
||||
* @param port The port to listen to
|
||||
*/
|
||||
public NioServer(int port) throws IOException {
|
||||
this(null, port);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a NioServer object which listens to a specific address
|
||||
*
|
||||
* @param address The address to listen to
|
||||
* @param port The port to listen to
|
||||
*/
|
||||
public NioServer(InetAddress address, int port) throws IOException {
|
||||
super(address, port, NetworkType.SERVER);
|
||||
new Thread(this).start();
|
||||
}
|
||||
|
||||
protected Selector initSelector() throws IOException {
|
||||
// Create a new selector
|
||||
Selector socketSelector = SelectorProvider.provider().openSelector();
|
||||
|
||||
// Create a new non-blocking server socket channel
|
||||
serverChannel = ServerSocketChannel.open();
|
||||
serverChannel.socket().setReuseAddress(true);
|
||||
serverChannel.configureBlocking(false);
|
||||
|
||||
// Bind the server socket to the specified address and port
|
||||
InetSocketAddress isa = new InetSocketAddress(address, port);
|
||||
serverChannel.socket().bind(isa);
|
||||
|
||||
// Register the server socket channel, indicating an interest in
|
||||
// accepting new connections
|
||||
serverChannel.register(socketSelector, SelectionKey.OP_ACCEPT);
|
||||
|
||||
return socketSelector;
|
||||
}
|
||||
|
||||
/**
|
||||
* Broadcasts the message to all the connected clients
|
||||
*
|
||||
* @param data The data to broadcast
|
||||
*/
|
||||
public void broadcast(byte[] data){
|
||||
synchronized(clients){
|
||||
Iterator<InetSocketAddress> it = clients.keySet().iterator();
|
||||
while(it.hasNext()){
|
||||
send(it.next(), data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
45
src/zutil/net/nio/message/ChatMessage.java
Normal file
45
src/zutil/net/nio/message/ChatMessage.java
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
package zutil.net.nio.message;
|
||||
|
||||
public class ChatMessage extends Message{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public static enum ChatMessageType {REGISTER, UNREGISTER, MESSAGE};
|
||||
|
||||
public ChatMessageType type;
|
||||
public String msg;
|
||||
public String room;
|
||||
|
||||
/**
|
||||
* Registers the user to the main chat
|
||||
*
|
||||
* @param name Name of user
|
||||
*/
|
||||
public ChatMessage(){
|
||||
this("", "", ChatMessageType.REGISTER);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the user to the given room
|
||||
*
|
||||
* @param room The room to register to
|
||||
*/
|
||||
public ChatMessage(String room){
|
||||
this("", room, ChatMessageType.REGISTER);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a message to the given room
|
||||
*
|
||||
* @param msg The message
|
||||
* @param room The room
|
||||
*/
|
||||
public ChatMessage(String msg, String room){
|
||||
this(msg, room, ChatMessageType.MESSAGE);
|
||||
}
|
||||
|
||||
public ChatMessage(String msg, String room, ChatMessageType type){
|
||||
this.msg = msg;
|
||||
this.room = room;
|
||||
this.type = type;
|
||||
}
|
||||
}
|
||||
31
src/zutil/net/nio/message/GraphicsSyncMessage.java
Normal file
31
src/zutil/net/nio/message/GraphicsSyncMessage.java
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
package zutil.net.nio.message;
|
||||
|
||||
|
||||
public class GraphicsSyncMessage extends SyncMessage{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public float locX;
|
||||
public float locY;
|
||||
public float locZ;
|
||||
|
||||
public float rotX;
|
||||
public float rotY;
|
||||
public float rotZ;
|
||||
public float rotW;
|
||||
|
||||
public GraphicsSyncMessage(String id){
|
||||
this.type = MessageType.SYNC;
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public boolean equals(Object obj){
|
||||
if(obj instanceof GraphicsSyncMessage){
|
||||
GraphicsSyncMessage tmp = (GraphicsSyncMessage)obj;
|
||||
return (tmp.locX == locX && tmp.locY == locY &&
|
||||
tmp.locZ == locZ && tmp.rotX == rotX &&
|
||||
tmp.rotY == rotY && tmp.rotZ == rotZ &&
|
||||
tmp.rotW == rotW);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
84
src/zutil/net/nio/message/GridMessage.java
Normal file
84
src/zutil/net/nio/message/GridMessage.java
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
package zutil.net.nio.message;
|
||||
|
||||
public class GridMessage<T> extends Message{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
// Client type messages
|
||||
/** Computation job return right answer **/
|
||||
public static final int COMP_SUCCESSFUL = 1; //
|
||||
/** Initial static data **/
|
||||
public static final int COMP_INCORRECT = 2; //
|
||||
/** Computation job return wrong answer **/
|
||||
public static final int COMP_ERROR = 3; //
|
||||
/** There was an error computing **/
|
||||
public static final int REGISTER = 4; //
|
||||
/** Register at the server **/
|
||||
public static final int UNREGISTER = 5; //
|
||||
/** Request new computation data **/
|
||||
public static final int NEW_DATA = 6; //
|
||||
|
||||
// Server type messages
|
||||
/** Sending initial static data **/
|
||||
public static final int INIT_DATA = 100;
|
||||
/** Sending new dynamic data **/
|
||||
public static final int COMP_DATA = 101;
|
||||
|
||||
|
||||
private int type;
|
||||
private int jobID;
|
||||
private T data;
|
||||
|
||||
/**
|
||||
* Creates a new GridMessage
|
||||
*
|
||||
* @param type is the type of message
|
||||
* @param jobID is the id of the job
|
||||
*/
|
||||
public GridMessage(int type){
|
||||
this(type, 0, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new GridMessage
|
||||
*
|
||||
* @param type is the type of message
|
||||
* @param jobID is the id of the job
|
||||
*/
|
||||
public GridMessage(int type, int jobID){
|
||||
this(type, jobID, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new GridMessage
|
||||
*
|
||||
* @param type is the type of message
|
||||
* @param jobID is the id of the job
|
||||
* @param data is the data to send with this message
|
||||
*/
|
||||
public GridMessage(int type, int jobID, T data){
|
||||
this.type = type;
|
||||
this.jobID = jobID;
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the type of message
|
||||
*/
|
||||
public int messageType(){
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the job id for this message
|
||||
*/
|
||||
public int getJobQueueID(){
|
||||
return jobID;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the data in this message, may not always carry any data.
|
||||
*/
|
||||
public T getData(){
|
||||
return data;
|
||||
}
|
||||
}
|
||||
16
src/zutil/net/nio/message/KeepAliveMessage.java
Normal file
16
src/zutil/net/nio/message/KeepAliveMessage.java
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
package zutil.net.nio.message;
|
||||
|
||||
import zutil.net.nio.message.type.SystemMessage;
|
||||
|
||||
/**
|
||||
* Tells the destination that the
|
||||
* source is still online
|
||||
*
|
||||
* @author Ziver
|
||||
*
|
||||
*/
|
||||
public class KeepAliveMessage extends Message implements SystemMessage{
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
}
|
||||
9
src/zutil/net/nio/message/Message.java
Normal file
9
src/zutil/net/nio/message/Message.java
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
package zutil.net.nio.message;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
public class Message implements Serializable{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
|
||||
}
|
||||
34
src/zutil/net/nio/message/StringMessage.java
Normal file
34
src/zutil/net/nio/message/StringMessage.java
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
package zutil.net.nio.message;
|
||||
|
||||
import zutil.net.nio.message.type.EchoMessage;
|
||||
import zutil.net.nio.message.type.ResponseRequestMessage;
|
||||
|
||||
|
||||
|
||||
public class StringMessage extends EchoMessage implements ResponseRequestMessage{
|
||||
private static final long serialVersionUID = 1L;
|
||||
private double responseId;
|
||||
|
||||
private String msg;
|
||||
|
||||
public StringMessage(String msg){
|
||||
this.msg = msg;
|
||||
responseId = Math.random();
|
||||
}
|
||||
|
||||
public String getString(){
|
||||
return msg;
|
||||
}
|
||||
|
||||
public void setString(String msg){
|
||||
this.msg = msg;
|
||||
}
|
||||
|
||||
public String toString(){
|
||||
return getString();
|
||||
}
|
||||
|
||||
public double getResponseId() {
|
||||
return responseId;
|
||||
}
|
||||
}
|
||||
13
src/zutil/net/nio/message/SyncMessage.java
Normal file
13
src/zutil/net/nio/message/SyncMessage.java
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
package zutil.net.nio.message;
|
||||
|
||||
import zutil.net.nio.message.type.SystemMessage;
|
||||
|
||||
public class SyncMessage extends Message implements SystemMessage{
|
||||
private static final long serialVersionUID = 1L;
|
||||
public static enum MessageType { REQUEST_ID, NEW, REMOVE, SYNC };
|
||||
|
||||
// type of message
|
||||
public MessageType type;
|
||||
// id of the Object
|
||||
public String id;
|
||||
}
|
||||
34
src/zutil/net/nio/message/type/EchoMessage.java
Normal file
34
src/zutil/net/nio/message/type/EchoMessage.java
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
package zutil.net.nio.message.type;
|
||||
|
||||
import zutil.net.nio.message.Message;
|
||||
|
||||
/**
|
||||
* The reciver will echo out this message to the sender
|
||||
*
|
||||
* @author Ziver
|
||||
*/
|
||||
public abstract class EchoMessage extends Message implements SystemMessage{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private boolean echo;
|
||||
|
||||
public EchoMessage(){
|
||||
echo = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns if the message should be echoed
|
||||
* @return If the message should be echoed
|
||||
*/
|
||||
public boolean echo() {
|
||||
return echo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by the reciver to disable looping of the message
|
||||
*
|
||||
*/
|
||||
public void recived() {
|
||||
echo = false;
|
||||
}
|
||||
}
|
||||
18
src/zutil/net/nio/message/type/ResponseRequestMessage.java
Normal file
18
src/zutil/net/nio/message/type/ResponseRequestMessage.java
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
package zutil.net.nio.message.type;
|
||||
|
||||
/**
|
||||
* This interface means that the sender
|
||||
* wants a reply from the destination
|
||||
*
|
||||
* @author Ziver
|
||||
*
|
||||
*/
|
||||
public interface ResponseRequestMessage {
|
||||
|
||||
/**
|
||||
* The id of the response to identify the response event
|
||||
* @return Response id
|
||||
*/
|
||||
public double getResponseId();
|
||||
|
||||
}
|
||||
12
src/zutil/net/nio/message/type/SystemMessage.java
Normal file
12
src/zutil/net/nio/message/type/SystemMessage.java
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
package zutil.net.nio.message.type;
|
||||
|
||||
/**
|
||||
* A message that implements this will be
|
||||
* handeld internaly by the network engine
|
||||
*
|
||||
* @author Ziver
|
||||
*
|
||||
*/
|
||||
public interface SystemMessage {
|
||||
|
||||
}
|
||||
12
src/zutil/net/nio/response/PrintRsp.java
Normal file
12
src/zutil/net/nio/response/PrintRsp.java
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
package zutil.net.nio.response;
|
||||
|
||||
import zutil.io.MultiPrintStream;
|
||||
|
||||
public class PrintRsp extends ResponseEvent{
|
||||
|
||||
@Override
|
||||
protected void responseEvent(Object rsp) {
|
||||
MultiPrintStream.out.println(rsp);
|
||||
}
|
||||
|
||||
}
|
||||
43
src/zutil/net/nio/response/ResponseEvent.java
Normal file
43
src/zutil/net/nio/response/ResponseEvent.java
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
package zutil.net.nio.response;
|
||||
|
||||
|
||||
public abstract class ResponseEvent {
|
||||
private Object rsp = null;
|
||||
|
||||
public synchronized boolean handleResponse(Object rsp) {
|
||||
this.rsp = rsp;
|
||||
notify();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Blocks the Thread until there is a response
|
||||
*/
|
||||
public synchronized void waitForResponse() {
|
||||
while(!gotResponse()) {
|
||||
try {
|
||||
this.wait();
|
||||
} catch (InterruptedException e) {}
|
||||
}
|
||||
|
||||
responseEvent(rsp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the response
|
||||
*/
|
||||
public void handleResponse(){
|
||||
if(gotResponse()){
|
||||
responseEvent(rsp);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return If there is an response
|
||||
*/
|
||||
public boolean gotResponse(){
|
||||
return (rsp != null);
|
||||
}
|
||||
|
||||
protected abstract void responseEvent(Object rsp);
|
||||
}
|
||||
41
src/zutil/net/nio/response/ResponseHandler.java
Normal file
41
src/zutil/net/nio/response/ResponseHandler.java
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
package zutil.net.nio.response;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
public abstract class ResponseHandler implements Runnable{
|
||||
private List<ResponseEvent> queue = new LinkedList<ResponseEvent>();
|
||||
|
||||
public ResponseHandler(){
|
||||
|
||||
}
|
||||
|
||||
public synchronized void addResponseEvent(ResponseEvent re){
|
||||
queue.add(re);
|
||||
notify();
|
||||
}
|
||||
|
||||
public synchronized void removeResponseEvent(ResponseEvent re){
|
||||
queue.remove(re);
|
||||
}
|
||||
|
||||
public void run() {
|
||||
while(true) {
|
||||
try {
|
||||
this.wait();
|
||||
} catch (InterruptedException e) {}
|
||||
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void update(){
|
||||
while(!queue.isEmpty()){
|
||||
queue.get(0).handleResponse();
|
||||
if(queue.get(0).gotResponse()){
|
||||
queue.remove(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
18
src/zutil/net/nio/server/ChangeRequest.java
Normal file
18
src/zutil/net/nio/server/ChangeRequest.java
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
package zutil.net.nio.server;
|
||||
|
||||
import java.nio.channels.SocketChannel;
|
||||
|
||||
public class ChangeRequest {
|
||||
public static final int REGISTER = 1;
|
||||
public static final int CHANGEOPS = 2;
|
||||
|
||||
public SocketChannel socket;
|
||||
public int type;
|
||||
public int ops;
|
||||
|
||||
public ChangeRequest(SocketChannel socket, int type, int ops) {
|
||||
this.socket = socket;
|
||||
this.type = type;
|
||||
this.ops = ops;
|
||||
}
|
||||
}
|
||||
29
src/zutil/net/nio/server/ClientData.java
Normal file
29
src/zutil/net/nio/server/ClientData.java
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
package zutil.net.nio.server;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.nio.channels.SocketChannel;
|
||||
|
||||
public class ClientData {
|
||||
private SocketChannel socketChannel;
|
||||
private long lastMessageReceived;
|
||||
|
||||
public ClientData(SocketChannel socketChannel){
|
||||
this.socketChannel = socketChannel;
|
||||
}
|
||||
|
||||
public SocketChannel getSocketChannel(){
|
||||
return socketChannel;
|
||||
}
|
||||
|
||||
public InetSocketAddress getAddress(){
|
||||
return (InetSocketAddress) socketChannel.socket().getRemoteSocketAddress();
|
||||
}
|
||||
|
||||
public void setLastMessageReceived(long time){
|
||||
lastMessageReceived = time;
|
||||
}
|
||||
|
||||
public long getLastMessageReceived(){
|
||||
return lastMessageReceived;
|
||||
}
|
||||
}
|
||||
25
src/zutil/net/nio/service/NetworkService.java
Normal file
25
src/zutil/net/nio/service/NetworkService.java
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
package zutil.net.nio.service;
|
||||
|
||||
import java.nio.channels.SocketChannel;
|
||||
|
||||
import zutil.net.nio.NioNetwork;
|
||||
import zutil.net.nio.message.Message;
|
||||
|
||||
public abstract class NetworkService {
|
||||
protected static NetworkService instance;
|
||||
protected NioNetwork nio;
|
||||
|
||||
public NetworkService(NioNetwork nio){
|
||||
instance = this;
|
||||
this.nio = nio;
|
||||
}
|
||||
|
||||
public abstract void handleMessage(Message message, SocketChannel socket);
|
||||
|
||||
/**
|
||||
* @return A instance of this class
|
||||
*/
|
||||
public static NetworkService getInstance(){
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
10
src/zutil/net/nio/service/chat/ChatListener.java
Normal file
10
src/zutil/net/nio/service/chat/ChatListener.java
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
package zutil.net.nio.service.chat;
|
||||
|
||||
/**
|
||||
* Tis is a listener class for new chat messages
|
||||
* @author Ziver
|
||||
*
|
||||
*/
|
||||
public interface ChatListener {
|
||||
public void messageAction(String msg, String room);
|
||||
}
|
||||
133
src/zutil/net/nio/service/chat/ChatService.java
Normal file
133
src/zutil/net/nio/service/chat/ChatService.java
Normal file
|
|
@ -0,0 +1,133 @@
|
|||
package zutil.net.nio.service.chat;
|
||||
|
||||
import java.nio.channels.SocketChannel;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import zutil.log.LogUtil;
|
||||
import zutil.net.nio.NioNetwork;
|
||||
import zutil.net.nio.message.ChatMessage;
|
||||
import zutil.net.nio.message.Message;
|
||||
import zutil.net.nio.service.NetworkService;
|
||||
|
||||
/**
|
||||
* A simple chat service with users and rooms
|
||||
*
|
||||
* @author Ziver
|
||||
*/
|
||||
public class ChatService extends NetworkService{
|
||||
private static Logger logger = LogUtil.getLogger();
|
||||
private HashMap<String,LinkedList<SocketChannel>> rooms;
|
||||
private ChatListener listener;
|
||||
|
||||
public ChatService(NioNetwork nio){
|
||||
super(nio);
|
||||
rooms = new HashMap<String,LinkedList<SocketChannel>>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleMessage(Message message, SocketChannel socket) {
|
||||
try {
|
||||
// New message
|
||||
if(message instanceof ChatMessage){
|
||||
ChatMessage chatmessage = (ChatMessage)message;
|
||||
//is this a new message
|
||||
if(chatmessage.type == ChatMessage.ChatMessageType.MESSAGE){
|
||||
// Is this the server
|
||||
if(nio.getType() == NioNetwork.NetworkType.SERVER){
|
||||
if(rooms.containsKey(chatmessage.room)){
|
||||
LinkedList<SocketChannel> tmpList = rooms.get(chatmessage.room);
|
||||
|
||||
// Broadcast the message
|
||||
for(SocketChannel s : tmpList){
|
||||
if(s.isConnected()){
|
||||
nio.send(s, chatmessage);
|
||||
}
|
||||
else{
|
||||
unRegisterUser(chatmessage.room, s);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
logger.finer("New Chat Message: "+chatmessage.msg);
|
||||
listener.messageAction(chatmessage.msg, chatmessage.room);
|
||||
}
|
||||
// register to a room
|
||||
else if(chatmessage.type == ChatMessage.ChatMessageType.REGISTER){
|
||||
registerUser(chatmessage.room, socket);
|
||||
}
|
||||
// unregister to a room
|
||||
else if(chatmessage.type == ChatMessage.ChatMessageType.UNREGISTER){
|
||||
unRegisterUser(chatmessage.room, socket);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a user to the main room
|
||||
*
|
||||
* @param socket The socket to the user
|
||||
*/
|
||||
public void registerUser(SocketChannel socket){
|
||||
registerUser("", socket);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the given user to a specific room
|
||||
*
|
||||
* @param room The room
|
||||
* @param socket The socket to the user
|
||||
*/
|
||||
public void registerUser(String room, SocketChannel socket){
|
||||
addRoom(room);
|
||||
logger.fine("New Chat User: "+socket);
|
||||
rooms.get(room).add(socket);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters a user from a room and removes the room if its empty
|
||||
*
|
||||
* @param room The room
|
||||
* @param socket The socket to the user
|
||||
*/
|
||||
public void unRegisterUser(String room, SocketChannel socket){
|
||||
if(rooms.containsKey(room)){
|
||||
logger.fine("Remove Chat User: "+socket);
|
||||
rooms.get(room).remove(socket);
|
||||
removeRoom(room);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a room into the list
|
||||
*
|
||||
* @param room The name of the room
|
||||
*/
|
||||
public void addRoom(String room){
|
||||
if(!rooms.containsKey(room)){
|
||||
logger.fine("New Chat Room: "+room);
|
||||
rooms.put(room, new LinkedList<SocketChannel>());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the given room if its empty
|
||||
*
|
||||
* @param room The room
|
||||
*/
|
||||
public void removeRoom(String room){
|
||||
if(rooms.get(room).isEmpty()){
|
||||
logger.fine("Remove Chat Room: "+room);
|
||||
rooms.remove(room);
|
||||
}
|
||||
}
|
||||
|
||||
public static ChatService getInstance(){
|
||||
return (ChatService)instance;
|
||||
}
|
||||
}
|
||||
28
src/zutil/net/nio/service/sync/ObjectSync.java
Normal file
28
src/zutil/net/nio/service/sync/ObjectSync.java
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
package zutil.net.nio.service.sync;
|
||||
|
||||
import zutil.net.nio.message.SyncMessage;
|
||||
|
||||
public abstract class ObjectSync {
|
||||
public String id;
|
||||
|
||||
public ObjectSync(String id){
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends sync message if the object has bean changed
|
||||
*/
|
||||
public abstract void sendSync();
|
||||
|
||||
/**
|
||||
* Applies the SyncMessage to the object
|
||||
* @param message
|
||||
* @param object
|
||||
*/
|
||||
public abstract void syncObject(SyncMessage message);
|
||||
|
||||
/**
|
||||
* Called when the object is removed from the sync list
|
||||
*/
|
||||
public abstract void remove();
|
||||
}
|
||||
60
src/zutil/net/nio/service/sync/SyncService.java
Normal file
60
src/zutil/net/nio/service/sync/SyncService.java
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
package zutil.net.nio.service.sync;
|
||||
|
||||
import java.nio.channels.SocketChannel;
|
||||
import java.util.HashMap;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import zutil.log.LogUtil;
|
||||
import zutil.net.nio.NioNetwork;
|
||||
import zutil.net.nio.message.Message;
|
||||
import zutil.net.nio.message.SyncMessage;
|
||||
import zutil.net.nio.service.NetworkService;
|
||||
|
||||
public class SyncService extends NetworkService{
|
||||
private static Logger logger = LogUtil.getLogger();
|
||||
// list of objects to sync
|
||||
private HashMap<String, ObjectSync> sync;
|
||||
|
||||
public SyncService(NioNetwork nio){
|
||||
super(nio);
|
||||
sync = new HashMap<String, ObjectSync>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a SyncObject to the sync list
|
||||
* @param os The object to sync
|
||||
*/
|
||||
public void addSyncObject(ObjectSync os){
|
||||
sync.put(os.id, os);
|
||||
logger.fine("New Sync object: "+os);
|
||||
}
|
||||
|
||||
public void handleMessage(Message message, SocketChannel socket){
|
||||
if(message instanceof SyncMessage){
|
||||
SyncMessage syncMessage = (SyncMessage)message;
|
||||
if(syncMessage.type == SyncMessage.MessageType.SYNC){
|
||||
ObjectSync obj = sync.get(syncMessage.id);
|
||||
if(obj != null){
|
||||
logger.finer("Syncing Message...");
|
||||
obj.syncObject(syncMessage);
|
||||
}
|
||||
}
|
||||
else if(syncMessage.type == SyncMessage.MessageType.REMOVE){
|
||||
sync.remove(syncMessage.id).remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Syncs all the objects whit the server
|
||||
*/
|
||||
public void sync(){
|
||||
for(String id : sync.keySet()){
|
||||
sync.get(id).sendSync();
|
||||
}
|
||||
}
|
||||
|
||||
public static SyncService getInstance(){
|
||||
return (SyncService)instance;
|
||||
}
|
||||
}
|
||||
22
src/zutil/net/nio/worker/EchoWorker.java
Normal file
22
src/zutil/net/nio/worker/EchoWorker.java
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
package zutil.net.nio.worker;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import zutil.io.MultiPrintStream;
|
||||
|
||||
public class EchoWorker extends ThreadedEventWorker {
|
||||
|
||||
@Override
|
||||
public void messageEvent(WorkerDataEvent dataEvent) {
|
||||
try {
|
||||
// Return to sender
|
||||
MultiPrintStream.out.println("Recived Msg: "+dataEvent.data);
|
||||
dataEvent.network.send(dataEvent.socket, dataEvent.data);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
129
src/zutil/net/nio/worker/SystemWorker.java
Normal file
129
src/zutil/net/nio/worker/SystemWorker.java
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
package zutil.net.nio.worker;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import zutil.log.LogUtil;
|
||||
import zutil.net.nio.NioNetwork;
|
||||
import zutil.net.nio.message.ChatMessage;
|
||||
import zutil.net.nio.message.Message;
|
||||
import zutil.net.nio.message.SyncMessage;
|
||||
import zutil.net.nio.message.type.EchoMessage;
|
||||
import zutil.net.nio.message.type.ResponseRequestMessage;
|
||||
import zutil.net.nio.response.ResponseEvent;
|
||||
import zutil.net.nio.service.NetworkService;
|
||||
import zutil.net.nio.service.chat.ChatService;
|
||||
import zutil.net.nio.service.sync.SyncService;
|
||||
|
||||
|
||||
public class SystemWorker extends ThreadedEventWorker {
|
||||
private static Logger logger = LogUtil.getLogger();
|
||||
private NioNetwork nio;
|
||||
// Maps a SocketChannel to a RspHandler
|
||||
private Map<Double, ResponseEvent> rspEvents = new HashMap<Double, ResponseEvent>();
|
||||
// Difren services listening on specific messages
|
||||
private Map<Class<?>, NetworkService> services = new HashMap<Class<?>, NetworkService>();
|
||||
/**
|
||||
* Creates a new SystemWorker
|
||||
* @param nio The Network
|
||||
*/
|
||||
public SystemWorker(NioNetwork nio){
|
||||
this.nio = nio;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void messageEvent(WorkerDataEvent event) {
|
||||
try {
|
||||
logger.finer("System Message: "+event.data.getClass().getName());
|
||||
if(event.data instanceof Message){
|
||||
if(event.data instanceof EchoMessage && ((EchoMessage)event.data).echo()){
|
||||
// Echos back the recived message
|
||||
((EchoMessage)event.data).recived();
|
||||
logger.finer("Echoing Message: "+event.data);
|
||||
nio.send(event.socket, event.data);
|
||||
}
|
||||
else if(event.data instanceof ResponseRequestMessage &&
|
||||
rspEvents.get(((ResponseRequestMessage)event.data).getResponseId()) != null){
|
||||
// Handle the response
|
||||
handleResponse(((ResponseRequestMessage)event.data).getResponseId(), event.data);
|
||||
logger.finer("Response Request Message: "+event.data);
|
||||
}
|
||||
else{
|
||||
//Services
|
||||
if(services.containsKey(event.data.getClass()) ||
|
||||
!services.containsKey(event.data.getClass()) && defaultServices(event.data)){
|
||||
services.get(event.data.getClass()).handleMessage((Message)event.data, event.socket);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a Service to a specific message
|
||||
*
|
||||
* @param c The Message class
|
||||
* @param ns The service
|
||||
*/
|
||||
public void registerService(Class<?> c, NetworkService ns){
|
||||
services.put(c, ns);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters a service
|
||||
*
|
||||
* @param c The class
|
||||
*/
|
||||
public void unregisterService(Class<?> c){
|
||||
services.remove(c);
|
||||
}
|
||||
|
||||
/**
|
||||
* Connects a ResponseHandler to a specific message
|
||||
* @param handler The Handler
|
||||
* @param data The Message
|
||||
*/
|
||||
public void addResponseHandler(ResponseEvent handler, ResponseRequestMessage data){
|
||||
rspEvents.put(data.getResponseId(), handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Client And Server ResponseEvent
|
||||
*/
|
||||
private void handleResponse(double responseId, Object rspData){
|
||||
// Look up the handler for this channel
|
||||
ResponseEvent handler = rspEvents.get(responseId);
|
||||
// And pass the response to it
|
||||
handler.handleResponse(rspData);
|
||||
|
||||
rspEvents.remove(responseId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the default services in the engin e
|
||||
* if the message needs one of them
|
||||
*
|
||||
* @param o The message
|
||||
*/
|
||||
private boolean defaultServices(Object o){
|
||||
if(o instanceof SyncMessage){
|
||||
if(SyncService.getInstance() == null)
|
||||
registerService(o.getClass(), new SyncService(nio));
|
||||
else
|
||||
registerService(o.getClass(), SyncService.getInstance());
|
||||
return true;
|
||||
}
|
||||
else if(o instanceof ChatMessage){
|
||||
if(ChatService.getInstance() == null)
|
||||
registerService(o.getClass(), new ChatService(nio));
|
||||
else
|
||||
registerService(o.getClass(), ChatService.getInstance());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
34
src/zutil/net/nio/worker/ThreadedEventWorker.java
Normal file
34
src/zutil/net/nio/worker/ThreadedEventWorker.java
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
package zutil.net.nio.worker;
|
||||
|
||||
public abstract class ThreadedEventWorker extends Worker{
|
||||
private Thread thread;
|
||||
|
||||
public ThreadedEventWorker(){
|
||||
thread = new Thread(this);
|
||||
thread.start();
|
||||
}
|
||||
|
||||
public void update() {
|
||||
WorkerDataEvent dataEvent;
|
||||
|
||||
while(true) {
|
||||
try{
|
||||
// Wait for data to become available
|
||||
synchronized(getEventQueue()) {
|
||||
while(getEventQueue().isEmpty()) {
|
||||
try {
|
||||
getEventQueue().wait();
|
||||
} catch (InterruptedException e) {}
|
||||
}
|
||||
dataEvent = (WorkerDataEvent) getEventQueue().remove(0);
|
||||
}
|
||||
messageEvent(dataEvent);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public abstract void messageEvent(WorkerDataEvent e);
|
||||
|
||||
}
|
||||
52
src/zutil/net/nio/worker/Worker.java
Normal file
52
src/zutil/net/nio/worker/Worker.java
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
package zutil.net.nio.worker;
|
||||
|
||||
import java.nio.channels.SocketChannel;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import zutil.net.nio.NioNetwork;
|
||||
|
||||
|
||||
public abstract class Worker implements Runnable {
|
||||
private LinkedList<WorkerDataEvent> queue = new LinkedList<WorkerDataEvent>();
|
||||
|
||||
public void processData(NioNetwork server, SocketChannel socket, Object data) {
|
||||
synchronized(queue) {
|
||||
queue.add(new WorkerDataEvent(server, socket, data));
|
||||
queue.notify();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The event queue
|
||||
*/
|
||||
protected List<WorkerDataEvent> getEventQueue(){
|
||||
return queue;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return If there is a event in the queue
|
||||
*/
|
||||
protected boolean hasEvent(){
|
||||
return !queue.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Polls a event from the list or waits until there is a event
|
||||
* @return The next event
|
||||
*/
|
||||
protected WorkerDataEvent pollEvent(){
|
||||
while(queue.isEmpty()) {
|
||||
try {
|
||||
this.wait();
|
||||
} catch (InterruptedException e) {}
|
||||
}
|
||||
return queue.poll();
|
||||
}
|
||||
|
||||
public void run(){
|
||||
update();
|
||||
}
|
||||
|
||||
public abstract void update();
|
||||
}
|
||||
18
src/zutil/net/nio/worker/WorkerDataEvent.java
Normal file
18
src/zutil/net/nio/worker/WorkerDataEvent.java
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
package zutil.net.nio.worker;
|
||||
|
||||
import java.nio.channels.SocketChannel;
|
||||
|
||||
import zutil.net.nio.NioNetwork;
|
||||
|
||||
|
||||
public class WorkerDataEvent {
|
||||
public NioNetwork network;
|
||||
public SocketChannel socket;
|
||||
public Object data;
|
||||
|
||||
public WorkerDataEvent(NioNetwork server, SocketChannel socket, Object data) {
|
||||
this.network = server;
|
||||
this.socket = socket;
|
||||
this.data = data;
|
||||
}
|
||||
}
|
||||
114
src/zutil/net/nio/worker/grid/GridClient.java
Normal file
114
src/zutil/net/nio/worker/grid/GridClient.java
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
package zutil.net.nio.worker.grid;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Queue;
|
||||
|
||||
import zutil.io.MultiPrintStream;
|
||||
import zutil.net.nio.NioClient;
|
||||
import zutil.net.nio.message.GridMessage;
|
||||
import zutil.net.nio.worker.ThreadedEventWorker;
|
||||
import zutil.net.nio.worker.WorkerDataEvent;
|
||||
|
||||
/**
|
||||
* This class is the client part of the grid.
|
||||
* It connects to a grid server and requests new job.
|
||||
* And then sends back the result to the server.
|
||||
*
|
||||
* @author Ziver
|
||||
*/
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
public class GridClient extends ThreadedEventWorker {
|
||||
private static LinkedList<GridJob> jobQueue;
|
||||
private static GridThread thread;
|
||||
private static NioClient network;
|
||||
|
||||
/**
|
||||
* Creates a new GridClient object and registers itself at the server
|
||||
* and sets itself as a worker in NioClient
|
||||
*
|
||||
* @param thread the Thread interface to run for the jobs
|
||||
* @param network the NioClient to use to communicate to the server
|
||||
*/
|
||||
public GridClient(GridThread thread, NioClient network){
|
||||
jobQueue = new LinkedList<GridJob>();
|
||||
GridClient.thread = thread;
|
||||
GridClient.network = network;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts up the client and a couple of GridThreads.
|
||||
* And registers itself as a worker in NioClient
|
||||
* @throws IOException
|
||||
*/
|
||||
public void initiate() throws IOException{
|
||||
network.setDefaultWorker(this);
|
||||
network.send(new GridMessage(GridMessage.REGISTER));
|
||||
|
||||
for(int i=0; i<Runtime.getRuntime().availableProcessors() ;i++){
|
||||
Thread t = new Thread(thread);
|
||||
t.start();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void messageEvent(WorkerDataEvent e) {
|
||||
// ignores other messages than GridMessage
|
||||
if(e.data instanceof GridMessage){
|
||||
GridMessage msg = (GridMessage)e.data;
|
||||
switch(msg.messageType()){
|
||||
// Receive data from Server
|
||||
case GridMessage.INIT_DATA:
|
||||
thread.setInitData(msg.getData());
|
||||
break;
|
||||
case GridMessage.COMP_DATA:
|
||||
jobQueue.add(new GridJob(msg.getJobQueueID(), (Queue)msg.getData()));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register whit the server that the job is done
|
||||
*
|
||||
* @param jobID is the job id
|
||||
* @param correct if the answer was right
|
||||
* @param result the result of the computation
|
||||
* @throws IOException
|
||||
*/
|
||||
public static void jobDone(int jobID, boolean correct, Object result) throws IOException{
|
||||
if(correct)
|
||||
network.send(new GridMessage(GridMessage.COMP_SUCCESSFUL, jobID, result));
|
||||
else
|
||||
network.send(new GridMessage(GridMessage.COMP_INCORRECT, jobID, result));
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers with the server that there was an
|
||||
* error when computing this job
|
||||
*
|
||||
* @param jobID is the job id
|
||||
*/
|
||||
public static void jobError(int jobID){
|
||||
try{
|
||||
network.send(new GridMessage(GridMessage.COMP_SUCCESSFUL, jobID));
|
||||
}catch(Exception e){e.printStackTrace();}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a new job to compute
|
||||
* @throws IOException
|
||||
*/
|
||||
public static synchronized GridJob getNextJob() throws IOException{
|
||||
if(jobQueue.isEmpty()){
|
||||
network.send(new GridMessage(GridMessage.NEW_DATA));
|
||||
while(jobQueue.isEmpty()){
|
||||
try{Thread.sleep(100);}catch(Exception e){}
|
||||
}
|
||||
}
|
||||
MultiPrintStream.out.println("Starting job");
|
||||
return jobQueue.poll();
|
||||
}
|
||||
|
||||
}
|
||||
22
src/zutil/net/nio/worker/grid/GridJob.java
Normal file
22
src/zutil/net/nio/worker/grid/GridJob.java
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
package zutil.net.nio.worker.grid;
|
||||
|
||||
/**
|
||||
* A internal class for handling the jobs
|
||||
*
|
||||
* @author Ziver
|
||||
*/
|
||||
public class GridJob{
|
||||
public int jobID;
|
||||
public Object job;
|
||||
public long timestamp;
|
||||
|
||||
public GridJob(int jobID, Object job){
|
||||
this.jobID = jobID;
|
||||
this.job = job;
|
||||
renewTimeStamp();
|
||||
}
|
||||
|
||||
public void renewTimeStamp(){
|
||||
timestamp = System.currentTimeMillis();
|
||||
}
|
||||
}
|
||||
18
src/zutil/net/nio/worker/grid/GridJobGenerator.java
Normal file
18
src/zutil/net/nio/worker/grid/GridJobGenerator.java
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
package zutil.net.nio.worker.grid;
|
||||
|
||||
|
||||
/**
|
||||
* Generates new jobs for the grid to compute
|
||||
*
|
||||
* @author Ziver
|
||||
*/
|
||||
public interface GridJobGenerator<T> {
|
||||
/**
|
||||
* @return static and final values that do not change for every job
|
||||
*/
|
||||
public Object initValues();
|
||||
/**
|
||||
* @return a new generated job
|
||||
*/
|
||||
public T generateJob();
|
||||
}
|
||||
11
src/zutil/net/nio/worker/grid/GridResultHandler.java
Normal file
11
src/zutil/net/nio/worker/grid/GridResultHandler.java
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
package zutil.net.nio.worker.grid;
|
||||
|
||||
/**
|
||||
* Handles the incoming results from the grid
|
||||
*
|
||||
* @author Ziver
|
||||
*/
|
||||
public interface GridResultHandler<T> {
|
||||
|
||||
public void resultEvent(int jobID, boolean correct, T result);
|
||||
}
|
||||
111
src/zutil/net/nio/worker/grid/GridServerWorker.java
Normal file
111
src/zutil/net/nio/worker/grid/GridServerWorker.java
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
package zutil.net.nio.worker.grid;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Queue;
|
||||
|
||||
import zutil.net.nio.message.GridMessage;
|
||||
import zutil.net.nio.worker.ThreadedEventWorker;
|
||||
import zutil.net.nio.worker.WorkerDataEvent;
|
||||
|
||||
/**
|
||||
* Implements a simple network computing server
|
||||
*
|
||||
* @author Ziver
|
||||
*/
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
public class GridServerWorker extends ThreadedEventWorker{
|
||||
// Job timeout after 30 min
|
||||
public static int JOB_TIMEOUT = 1000*60*30;
|
||||
|
||||
private HashMap<Integer, GridJob> jobs; // contains all the ongoing jobs
|
||||
private Queue<GridJob> reSendjobQueue; // Contains all the jobs that will be recalculated
|
||||
private GridJobGenerator jobGenerator; // The job generator
|
||||
private GridResultHandler resHandler;
|
||||
private int nextJobID;
|
||||
|
||||
public GridServerWorker(GridResultHandler resHandler, GridJobGenerator jobGenerator){
|
||||
this.resHandler = resHandler;
|
||||
this.jobGenerator = jobGenerator;
|
||||
nextJobID = 0;
|
||||
|
||||
jobs = new HashMap<Integer, GridJob>();
|
||||
reSendjobQueue = new LinkedList<GridJob>();
|
||||
GridMaintainer maintainer = new GridMaintainer();
|
||||
maintainer.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void messageEvent(WorkerDataEvent e) {
|
||||
try {
|
||||
// ignores other messages than GridMessage
|
||||
if(e.data instanceof GridMessage){
|
||||
GridMessage msg = (GridMessage)e.data;
|
||||
GridJob job = null;
|
||||
|
||||
switch(msg.messageType()){
|
||||
case GridMessage.REGISTER:
|
||||
e.network.send(e.socket, new GridMessage(GridMessage.INIT_DATA, 0, jobGenerator.initValues()));
|
||||
break;
|
||||
// Sending new data to compute to the client
|
||||
case GridMessage.NEW_DATA:
|
||||
if(!reSendjobQueue.isEmpty()){ // checks first if there is a job for recalculation
|
||||
job = reSendjobQueue.poll();
|
||||
job.renewTimeStamp();
|
||||
}
|
||||
else{ // generates new job
|
||||
job = new GridJob(nextJobID,
|
||||
jobGenerator.generateJob());
|
||||
jobs.put(job.jobID, job);
|
||||
nextJobID++;
|
||||
}
|
||||
GridMessage newMsg = new GridMessage(GridMessage.COMP_DATA, job.jobID, job.job);
|
||||
e.network.send(e.socket, newMsg);
|
||||
break;
|
||||
|
||||
// Received computation results
|
||||
case GridMessage.COMP_SUCCESSFUL:
|
||||
resHandler.resultEvent(msg.getJobQueueID(), true, msg.getData());
|
||||
break;
|
||||
case GridMessage.COMP_INCORRECT:
|
||||
resHandler.resultEvent(msg.getJobQueueID(), false, msg.getData());
|
||||
break;
|
||||
case GridMessage.COMP_ERROR: // marks the job for recalculation
|
||||
job = jobs.get(msg.getJobQueueID());
|
||||
reSendjobQueue.add(job);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (IOException e1) {
|
||||
e1.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the job timeout value
|
||||
* @param min is the timeout in minutes
|
||||
*/
|
||||
public static void setJobTimeOut(int min){
|
||||
JOB_TIMEOUT = 1000*60*min;
|
||||
}
|
||||
|
||||
class GridMaintainer extends Thread{
|
||||
/**
|
||||
* Runs some behind the scenes stuff
|
||||
* like job timeout.
|
||||
*/
|
||||
public void run(){
|
||||
while(true){
|
||||
long time = System.currentTimeMillis();
|
||||
for(int jobID : jobs.keySet()){
|
||||
if(time-jobs.get(jobID).timestamp > JOB_TIMEOUT){
|
||||
reSendjobQueue.add(jobs.get(jobID));
|
||||
}
|
||||
}
|
||||
try{Thread.sleep(1000*60*1);}catch(Exception e){};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
38
src/zutil/net/nio/worker/grid/GridThread.java
Normal file
38
src/zutil/net/nio/worker/grid/GridThread.java
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
package zutil.net.nio.worker.grid;
|
||||
|
||||
/**
|
||||
* This interface is the thread that will do
|
||||
* all the computation in the grid
|
||||
*
|
||||
* @author Ziver
|
||||
*/
|
||||
public abstract class GridThread implements Runnable{
|
||||
/**
|
||||
* The initial static and final data will be sent to this
|
||||
* method.
|
||||
*
|
||||
* @param data is the static and or final data
|
||||
*/
|
||||
public abstract void setInitData(Object data);
|
||||
|
||||
public void run(){
|
||||
while(true){
|
||||
GridJob tmp = null;
|
||||
try {
|
||||
tmp = GridClient.getNextJob();
|
||||
compute(tmp);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
if(tmp != null){
|
||||
GridClient.jobError(tmp.jobID);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the given data and return
|
||||
* @param data
|
||||
*/
|
||||
public abstract void compute(GridJob data) throws Exception;
|
||||
}
|
||||
193
src/zutil/net/ssdp/SSDPClient.java
Normal file
193
src/zutil/net/ssdp/SSDPClient.java
Normal file
|
|
@ -0,0 +1,193 @@
|
|||
package zutil.net.ssdp;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.DatagramPacket;
|
||||
import java.net.InetAddress;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import zutil.io.StringOutputStream;
|
||||
import zutil.log.LogUtil;
|
||||
import zutil.net.http.HTTPHeaderParser;
|
||||
import zutil.net.http.HttpPrintStream;
|
||||
import zutil.net.threaded.ThreadedUDPNetwork;
|
||||
import zutil.net.threaded.ThreadedUDPNetworkThread;
|
||||
|
||||
/**
|
||||
* An SSDP client class that will request
|
||||
* service information.
|
||||
*
|
||||
* @author Ziver
|
||||
*/
|
||||
public class SSDPClient extends ThreadedUDPNetwork implements ThreadedUDPNetworkThread{
|
||||
public static final Logger logger = LogUtil.getLogger();
|
||||
// Contains all the received services
|
||||
private HashMap<String, LinkedList<SSDPServiceInfo>> services_st;
|
||||
private HashMap<String, SSDPServiceInfo> services_usn;
|
||||
|
||||
|
||||
public static void main(String[] args) throws IOException{
|
||||
System.out.println(LogUtil.getCalingClass());
|
||||
LogUtil.setGlobalLevel(Level.FINEST);
|
||||
SSDPClient ssdp = new SSDPClient();
|
||||
ssdp.requestService("upnp:rootdevice");
|
||||
ssdp.start();
|
||||
|
||||
for(int i=0; true ;++i){
|
||||
while( i==ssdp.getServicesCount("upnp:rootdevice") ){ try{Thread.sleep(100);}catch(Exception e){} }
|
||||
logger.log(Level.FINEST, "************************" );
|
||||
logger.log(Level.FINEST, ""+ssdp.getServices("upnp:rootdevice").get(i) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new instance of this class. An UDP
|
||||
* listening socket at the SSDP port.
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
public SSDPClient() throws IOException{
|
||||
super( null );
|
||||
super.setThread( this );
|
||||
|
||||
services_st = new HashMap<String, LinkedList<SSDPServiceInfo>>();
|
||||
services_usn = new HashMap<String, SSDPServiceInfo>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends an request for an service
|
||||
*
|
||||
* @param st is the SearchTarget of the service
|
||||
*
|
||||
* ***** REQUEST:
|
||||
* M-SEARCH * HTTP/1.1
|
||||
* Host: 239.255.255.250:reservedSSDPport
|
||||
* Man: "ssdp:discover"
|
||||
* ST: ge:fridge
|
||||
* MX: 3
|
||||
*
|
||||
*/
|
||||
public void requestService(String st){
|
||||
try {
|
||||
services_st.put( st, new LinkedList<SSDPServiceInfo>() );
|
||||
|
||||
// Generate an SSDP discover message
|
||||
StringOutputStream msg = new StringOutputStream();
|
||||
HttpPrintStream http = new HttpPrintStream( msg, HttpPrintStream.HTTPMessageType.REQUEST );
|
||||
http.setRequestType("M-SEARCH");
|
||||
http.setRequestURL("*");
|
||||
http.setHeader("Host", SSDPServer.SSDP_MULTICAST_ADDR+":"+SSDPServer.SSDP_PORT );
|
||||
http.setHeader("ST", st );
|
||||
http.setHeader("Man", "\"ssdp:discover\"" );
|
||||
http.setHeader("MX", "3" );
|
||||
|
||||
http.close();
|
||||
logger.log(Level.FINEST, "***** REQUEST: \n"+msg);
|
||||
byte[] data = msg.toString().getBytes();
|
||||
DatagramPacket packet = new DatagramPacket(
|
||||
data, data.length,
|
||||
InetAddress.getByName( SSDPServer.SSDP_MULTICAST_ADDR ),
|
||||
SSDPServer.SSDP_PORT );
|
||||
super.send( packet );
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of received services by
|
||||
* the given search target.
|
||||
*
|
||||
* @param st is the search target
|
||||
* @return a list of received services
|
||||
*/
|
||||
public LinkedList<SSDPServiceInfo> getServices(String st){
|
||||
return services_st.get( st );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the amount of services in the search target
|
||||
*
|
||||
* @param st is the search target
|
||||
* @return the amount of services
|
||||
*/
|
||||
public int getServicesCount(String st){
|
||||
if( services_st.containsKey( st ) ){
|
||||
return services_st.get( st ).size();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a service with the given USN.
|
||||
*
|
||||
* @param usn is the unique identifier for the service
|
||||
* @return an service, null if there is no such service
|
||||
*/
|
||||
public SSDPServiceInfo getService(String usn){
|
||||
return services_usn.get( usn );
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears all the received information of the services
|
||||
*/
|
||||
public void clearServices(){
|
||||
services_usn.clear();
|
||||
services_st.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits for responses
|
||||
*
|
||||
* ***** RESPONSE;
|
||||
* HTTP/1.1 200 OK
|
||||
* Ext:
|
||||
* Cache-Control: no-cache="Ext", max-age = 5000
|
||||
* ST: ge:fridge
|
||||
* USN: uuid:abcdefgh-7dec-11d0-a765-00a0c91e6bf6
|
||||
* Location: http://localhost:80
|
||||
*/
|
||||
public void receivedPacket(DatagramPacket packet, ThreadedUDPNetwork network) {
|
||||
HTTPHeaderParser header = new HTTPHeaderParser( new String( packet.getData() ) );
|
||||
logger.log(Level.FINEST, "*********** Recived\n"+header);
|
||||
|
||||
String usn = header.getHeader("USN");
|
||||
String st = header.getHeader("ST");
|
||||
StandardSSDPInfo service;
|
||||
// Get existing service
|
||||
if( services_usn.containsKey( usn )){
|
||||
service = (StandardSSDPInfo)services_usn.get( usn );
|
||||
}
|
||||
// Add new service
|
||||
else{
|
||||
service = new StandardSSDPInfo();
|
||||
services_usn.put( usn, service);
|
||||
if( !services_st.containsKey(st) )
|
||||
services_st.put( st, new LinkedList<SSDPServiceInfo>() );
|
||||
services_st.get( header.getHeader("ST") ).add( service );
|
||||
}
|
||||
|
||||
service.setLocation( header.getHeader("LOCATION") );
|
||||
service.setST( st );
|
||||
service.setUSN( usn );
|
||||
service.setExpirationTime(
|
||||
System.currentTimeMillis() +
|
||||
1000 * getCacheTime(header.getHeader("Cache-Control")) );
|
||||
logger.log(Level.FINEST, "*********** Recived\n"+service);
|
||||
}
|
||||
|
||||
private long getCacheTime(String cache_control){
|
||||
long ret = 0;
|
||||
String[] tmp = cache_control.split(",");
|
||||
for( String element : tmp ){
|
||||
element = element.replaceAll("\\s", "").toLowerCase();
|
||||
if( element.startsWith("max-age=") ){
|
||||
ret = Long.parseLong( element.substring( "max-age=".length() ) );
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
}
|
||||
309
src/zutil/net/ssdp/SSDPServer.java
Normal file
309
src/zutil/net/ssdp/SSDPServer.java
Normal file
|
|
@ -0,0 +1,309 @@
|
|||
package zutil.net.ssdp;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.DatagramPacket;
|
||||
import java.net.InetAddress;
|
||||
import java.util.HashMap;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import zutil.io.MultiPrintStream;
|
||||
import zutil.io.StringOutputStream;
|
||||
import zutil.log.LogUtil;
|
||||
import zutil.net.http.HTTPHeaderParser;
|
||||
import zutil.net.http.HttpPrintStream;
|
||||
import zutil.net.threaded.ThreadedUDPNetworkThread;
|
||||
import zutil.net.threaded.ThreadedUDPNetwork;
|
||||
|
||||
/**
|
||||
* A Server class that announces an service by the SSDP
|
||||
* protocol specified at:
|
||||
* http://coherence.beebits.net/chrome/site/draft-cai-ssdp-v1-03.txt
|
||||
* ftp://ftp.pwg.org/pub/pwg/www/hypermail/ps/att-0188/01-psi_SSDP.pdf
|
||||
*
|
||||
* @author Ziver
|
||||
*
|
||||
* ********* Message clarification:
|
||||
* ****** Incoming:
|
||||
* ST: Search Target, this is object of the discovery request, (e.g., ssdp:all, etc.)
|
||||
* HOST: This is the SSDP multicast address
|
||||
* MAN: Description of packet type, (e.g., "ssdp:discover", )
|
||||
* MX: Wait these few seconds and then send response
|
||||
*
|
||||
* ****** Outgoing:
|
||||
* EXT: required by HTTP - not used with SSDP
|
||||
* SERVER: informational
|
||||
* LOCATION: This is the URL to request the QueryEndpointsInterface endpoint
|
||||
* USN: advertisement UUID
|
||||
* CACHE-CONTROL: max-age = seconds until advertisement expires
|
||||
* NT: Notify target same as ST
|
||||
* NTS: same as Man but for Notify messages
|
||||
*/
|
||||
public class SSDPServer extends ThreadedUDPNetwork implements ThreadedUDPNetworkThread{
|
||||
public static final Logger logger = LogUtil.getLogger();
|
||||
public static final String SERVER_INFO = "SSDP Java Server by Ziver Koc";
|
||||
public static final int DEFAULT_CACHE_TIME = 60*30; // 30 min
|
||||
public static final int BUFFER_SIZE = 512;
|
||||
public static final String SSDP_MULTICAST_ADDR = "239.255.255.250";
|
||||
public static final int SSDP_PORT = 1900;
|
||||
|
||||
// instance specific values
|
||||
private int cache_time;
|
||||
private NotifyTimer notifyTimer = null;
|
||||
/** HashMap that contains services as < SearchTargetName, SSDPServiceInfo > */
|
||||
private HashMap<String, SSDPServiceInfo> services;
|
||||
|
||||
|
||||
public static void main(String[] args) throws IOException{
|
||||
SSDPServer ssdp = new SSDPServer();
|
||||
StandardSSDPInfo service = new StandardSSDPInfo();
|
||||
service.setLocation("nowhere");
|
||||
service.setST("upnp:rootdevice");
|
||||
ssdp.addService(service);
|
||||
ssdp.start();
|
||||
MultiPrintStream.out.println("SSDP Server running");
|
||||
}
|
||||
|
||||
public SSDPServer() throws IOException{
|
||||
super( null, SSDP_MULTICAST_ADDR, SSDP_PORT );
|
||||
super.setThread( this );
|
||||
|
||||
services = new HashMap<String, SSDPServiceInfo>();
|
||||
|
||||
setChacheTime( DEFAULT_CACHE_TIME );
|
||||
enableNotify( true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an service that will be announced.
|
||||
*
|
||||
* @param searchTarget is the ST value in SSDP
|
||||
* @param location is the location of the service
|
||||
*/
|
||||
public void addService(SSDPServiceInfo service){
|
||||
services.put( service.getSearchTarget(), service );
|
||||
}
|
||||
/**
|
||||
* Remove a service from being announced. This function will
|
||||
* send out an byebye message to the clients that the service is down.
|
||||
*
|
||||
* @param searchTarget is the ST value in SSDP
|
||||
*/
|
||||
public void removeService(String searchTarget){
|
||||
sendByeBye( searchTarget );
|
||||
services.remove( searchTarget );
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the cache time that will be sent to
|
||||
* the clients. If notification is enabled then an
|
||||
* notification message will be sent every cache_time/2 seconds
|
||||
*
|
||||
* @param time is the time in seconds
|
||||
*/
|
||||
public void setChacheTime(int time){
|
||||
cache_time = time;
|
||||
if( isNotifyEnabled() ){
|
||||
enableNotify(false);
|
||||
enableNotify(true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable or disable notification messages to clients
|
||||
* every cache_time/2 seconds
|
||||
*/
|
||||
public void enableNotify(boolean enable){
|
||||
if( enable && notifyTimer==null ){
|
||||
notifyTimer = new NotifyTimer();
|
||||
Timer timer = new Timer();
|
||||
timer.schedule(new NotifyTimer(), 0, cache_time*1000/2);
|
||||
}else if( !enable && notifyTimer!=null ){
|
||||
notifyTimer.cancel();
|
||||
notifyTimer = null;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @return if notification messages is enabled
|
||||
*/
|
||||
public boolean isNotifyEnabled(){
|
||||
return notifyTimer != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the incoming packets like this:
|
||||
*
|
||||
* ***** REQUEST:
|
||||
* M-SEARCH * HTTP/1.1
|
||||
* Host: 239.255.255.250:reservedSSDPport
|
||||
* Man: "ssdp:discover"
|
||||
* ST: ge:fridge
|
||||
* MX: 3
|
||||
*
|
||||
* ***** RESPONSE;
|
||||
* HTTP/1.1 200 OK
|
||||
* Ext:
|
||||
* Cache-Control: no-cache="Ext", max-age = 5000
|
||||
* ST: ge:fridge
|
||||
* USN: uuid:abcdefgh-7dec-11d0-a765-00a0c91e6bf6
|
||||
* Location: http://localhost:80
|
||||
*
|
||||
*/
|
||||
public void receivedPacket(DatagramPacket packet, ThreadedUDPNetwork network) {
|
||||
try {
|
||||
String msg = new String( packet.getData() );
|
||||
|
||||
HTTPHeaderParser header = new HTTPHeaderParser( msg );
|
||||
logger.log(Level.FINEST, "**** Received:\n"+header);
|
||||
|
||||
// ******* Respond
|
||||
// Check that the message is an ssdp discovery message
|
||||
if( header.getRequestType().equalsIgnoreCase("M-SEARCH") ){
|
||||
String man = header.getHeader("Man").replace("\"", "");
|
||||
String st = header.getHeader("ST");
|
||||
// Check that its the correct URL and that its an ssdp:discover message
|
||||
if( header.getRequestURL().equals("*") && man.equalsIgnoreCase("ssdp:discover") ){
|
||||
// Check if the requested service exists
|
||||
if( services.containsKey( st ) ){
|
||||
// Generate the SSDP response
|
||||
StringOutputStream response = new StringOutputStream();
|
||||
HttpPrintStream http = new HttpPrintStream( response );
|
||||
http.setStatusCode(200);
|
||||
http.setHeader("Server", SERVER_INFO );
|
||||
http.setHeader("ST", st );
|
||||
http.setHeader("Location", services.get(st).getLocation() );
|
||||
http.setHeader("EXT", "" );
|
||||
http.setHeader("Cache-Control", "max-age = "+cache_time );
|
||||
http.setHeader("USN", services.get(st).getUSN() );
|
||||
|
||||
http.close();
|
||||
logger.log(Level.FINEST, "********** Response:\n"+response);
|
||||
byte[] data = response.toString().getBytes();
|
||||
packet = new DatagramPacket(
|
||||
data, data.length,
|
||||
packet.getAddress(),
|
||||
packet.getPort());
|
||||
network.send( packet );
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This thread is a timer task that sends an
|
||||
* notification message to the network every
|
||||
* cache_time/2 seconds.
|
||||
*
|
||||
* @author Ziver
|
||||
*/
|
||||
private class NotifyTimer extends TimerTask {
|
||||
public void run(){
|
||||
sendNotify();
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Sends keepalive messages to update the cache of the clients
|
||||
*/
|
||||
public void sendNotify(){
|
||||
for(String st : services.keySet()){
|
||||
sendNotify( st );
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Sends an keepalive message to update the cache of the clients
|
||||
*
|
||||
* @param searchTarget is the ST value of the service
|
||||
*
|
||||
* ********** Message ex:
|
||||
* NOTIFY * HTTP/1.1
|
||||
* Host: 239.255.255.250:reservedSSDPport
|
||||
* NT: blenderassociation:blender
|
||||
* NTS: ssdp:alive
|
||||
* USN: someunique:idscheme3
|
||||
* Location: http://localhost:80
|
||||
* Cache-Control: max-age = 7393
|
||||
*/
|
||||
public void sendNotify(String searchTarget){
|
||||
try {
|
||||
// Generate the SSDP response
|
||||
StringOutputStream msg = new StringOutputStream();
|
||||
HttpPrintStream http = new HttpPrintStream( msg, HttpPrintStream.HTTPMessageType.REQUEST );
|
||||
http.setRequestType("NOTIFY");
|
||||
http.setRequestURL("*");
|
||||
http.setHeader("Server", SERVER_INFO );
|
||||
http.setHeader("Host", SSDP_MULTICAST_ADDR+":"+SSDP_PORT );
|
||||
http.setHeader("NT", searchTarget );
|
||||
http.setHeader("NTS", "ssdp:alive" );
|
||||
http.setHeader("Location", services.get(searchTarget).getLocation() );
|
||||
http.setHeader("Cache-Control", "max-age = "+cache_time );
|
||||
http.setHeader("USN", services.get(searchTarget).getUSN() );
|
||||
|
||||
http.close();
|
||||
logger.log(Level.FINEST, "******** Notification:\n"+msg);
|
||||
byte[] data = msg.toString().getBytes();
|
||||
DatagramPacket packet = new DatagramPacket(
|
||||
data, data.length,
|
||||
InetAddress.getByName( SSDP_MULTICAST_ADDR ),
|
||||
SSDP_PORT );
|
||||
super.send( packet );
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Shutdown message is sent to the clients that all
|
||||
* the service is shutting down.
|
||||
*/
|
||||
public void sendByeBye(){
|
||||
for(String st : services.keySet()){
|
||||
sendByeBye( st );
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Shutdown message is sent to the clients that the service is shutting down
|
||||
*
|
||||
* @param searchTarget is the ST value of the service
|
||||
*
|
||||
* ********** Message ex:
|
||||
* NOTIFY * HTTP/1.1
|
||||
* Host: 239.255.255.250:reservedSSDPport
|
||||
* NT: someunique:idscheme3
|
||||
* NTS: ssdp:byebye
|
||||
* USN: someunique:idscheme3
|
||||
*/
|
||||
public void sendByeBye(String searchTarget){
|
||||
try {
|
||||
// Generate the SSDP response
|
||||
StringOutputStream msg = new StringOutputStream();
|
||||
HttpPrintStream http = new HttpPrintStream( msg, HttpPrintStream.HTTPMessageType.REQUEST );
|
||||
http.setRequestType("NOTIFY");
|
||||
http.setRequestURL("*");
|
||||
http.setHeader("Server", SERVER_INFO );
|
||||
http.setHeader("Host", SSDP_MULTICAST_ADDR+":"+SSDP_PORT );
|
||||
http.setHeader("NT", searchTarget );
|
||||
http.setHeader("NTS", "ssdp:byebye" );
|
||||
http.setHeader("USN", services.get(searchTarget).getUSN() );
|
||||
|
||||
http.close();
|
||||
logger.log(Level.FINEST, "******** ByeBye:\n"+msg);
|
||||
byte[] data = msg.toString().getBytes();
|
||||
DatagramPacket packet = new DatagramPacket(
|
||||
data, data.length,
|
||||
InetAddress.getByName( SSDP_MULTICAST_ADDR ),
|
||||
SSDP_PORT );
|
||||
super.send( packet );
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
35
src/zutil/net/ssdp/SSDPServiceInfo.java
Normal file
35
src/zutil/net/ssdp/SSDPServiceInfo.java
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
package zutil.net.ssdp;
|
||||
|
||||
/**
|
||||
* This class contains information about a service from
|
||||
* or through the SSDP protocol
|
||||
*
|
||||
* @author Ziver
|
||||
*/
|
||||
public interface SSDPServiceInfo {
|
||||
|
||||
/**
|
||||
* @return The URL to the Service, e.g. "http://192.168.0.1:80/index.html"
|
||||
*/
|
||||
public String getLocation();
|
||||
|
||||
/**
|
||||
* @return the Search Target, e.g. "upnp:rootdevice"
|
||||
*/
|
||||
public String getSearchTarget();
|
||||
|
||||
/**
|
||||
* @return the expiration time for the values in this object
|
||||
*/
|
||||
public long getExpirationTime();
|
||||
|
||||
/**
|
||||
* @return the USN value, e.g. "uuid:abcdefgh-7dec-11d0-a765-00a0c91e6bf6 "
|
||||
*/
|
||||
public String getUSN();
|
||||
|
||||
/**
|
||||
* @return only the USN UUID String
|
||||
*/
|
||||
public String getUUID();
|
||||
}
|
||||
98
src/zutil/net/ssdp/StandardSSDPInfo.java
Normal file
98
src/zutil/net/ssdp/StandardSSDPInfo.java
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
package zutil.net.ssdp;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* This class contains information about a service from
|
||||
* or through the SSDP protocol
|
||||
*
|
||||
* @author Ziver
|
||||
*/
|
||||
public class StandardSSDPInfo implements SSDPServiceInfo{
|
||||
private String location;
|
||||
private String st;
|
||||
private String usn;
|
||||
private long expiration_time;
|
||||
|
||||
/**
|
||||
* @param l is the value to set the Location variable
|
||||
*/
|
||||
public void setLocation(String l) {
|
||||
location = l;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param st is the value to set the SearchTarget variable
|
||||
*/
|
||||
public void setST(String st) {
|
||||
this.st = st;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param usn is the value to set the USN variable
|
||||
*/
|
||||
protected void setUSN(String usn) {
|
||||
this.usn = usn;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param time sets the expiration time of values in this object
|
||||
*/
|
||||
protected void setExpirationTime(long time) {
|
||||
expiration_time = time;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The URL to the Service, e.g. "http://192.168.0.1:80/index.html"
|
||||
*/
|
||||
public String getLocation(){
|
||||
return location;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the Search Target, e.g. "upnp:rootdevice"
|
||||
*/
|
||||
public String getSearchTarget(){
|
||||
return st;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the expiration time for the values in this object
|
||||
*/
|
||||
public long getExpirationTime(){
|
||||
return expiration_time;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the USN value, e.g. "uuid:abcdefgh-7dec-11d0-a765-00a0c91e6bf6 "
|
||||
*/
|
||||
public String getUSN(){
|
||||
if( usn==null )
|
||||
usn = genUSN();
|
||||
return usn+"::"+st;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return only the USN UUID String
|
||||
*/
|
||||
public String getUUID(){
|
||||
if( usn==null )
|
||||
usn = genUSN();
|
||||
return usn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates an unique USN for the service
|
||||
*
|
||||
* @param searchTarget is the service ST name
|
||||
* @return an unique string that corresponds to the service
|
||||
*/
|
||||
private String genUSN(){
|
||||
return "uuid:" + UUID.nameUUIDFromBytes( (st+location+Math.random()).getBytes() );
|
||||
}
|
||||
|
||||
public String toString(){
|
||||
return "USN: "+usn+"\nLocation: "+location+"\nST: "+st+"\nExpiration-Time: "+new Date(expiration_time);
|
||||
}
|
||||
}
|
||||
115
src/zutil/net/threaded/ThreadedTCPNetworkServer.java
Normal file
115
src/zutil/net/threaded/ThreadedTCPNetworkServer.java
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
package zutil.net.threaded;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
import java.security.KeyStoreException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.NoSuchProviderException;
|
||||
import java.security.cert.CertificateException;
|
||||
|
||||
import javax.net.ssl.SSLServerSocketFactory;
|
||||
|
||||
import zutil.io.MultiPrintStream;
|
||||
|
||||
|
||||
/**
|
||||
* A simple web server that handles both cookies and
|
||||
* sessions for all the clients
|
||||
*
|
||||
* @author Ziver
|
||||
*/
|
||||
public abstract class ThreadedTCPNetworkServer extends Thread{
|
||||
public final int port;
|
||||
private File keyStore;
|
||||
private String keyStorePass;
|
||||
|
||||
/**
|
||||
* Creates a new instance of the sever
|
||||
*
|
||||
* @param port The port that the server should listen to
|
||||
*/
|
||||
public ThreadedTCPNetworkServer(int port){
|
||||
this(port, null, null);
|
||||
}
|
||||
/**
|
||||
* Creates a new instance of the sever
|
||||
*
|
||||
* @param port The port that the server should listen to
|
||||
* @param sslCert If this is not null then the server will use SSL connection with this keyStore file path
|
||||
* @param sslCert If this is not null then the server will use a SSL connection with the given certificate
|
||||
*/
|
||||
public ThreadedTCPNetworkServer(int port, File keyStore, String keyStorePass){
|
||||
this.port = port;
|
||||
this.keyStorePass = keyStorePass;
|
||||
this.keyStore = keyStore;
|
||||
}
|
||||
|
||||
|
||||
public void run(){
|
||||
ServerSocket ss = null;
|
||||
try{
|
||||
if(keyStorePass != null && keyStore != null){
|
||||
registerCertificate(keyStore, keyStorePass);
|
||||
ss = initSSL( port );
|
||||
}
|
||||
else{
|
||||
ss = new ServerSocket( port );
|
||||
}
|
||||
|
||||
while(true){
|
||||
Socket s = ss.accept();
|
||||
ThreadedTCPNetworkServerThread t = getThreadInstance( s );
|
||||
if( t!=null )
|
||||
new Thread( t ).start();
|
||||
else{
|
||||
MultiPrintStream.out.println("Unable to instantiate ThreadedTCPNetworkServerThread, closing connection!");
|
||||
s.close();
|
||||
}
|
||||
}
|
||||
} catch(Exception e) {
|
||||
e.printStackTrace( MultiPrintStream.out );
|
||||
}
|
||||
|
||||
if( ss!=null ){
|
||||
try{
|
||||
ss.close();
|
||||
}catch(IOException e){ e.printStackTrace( MultiPrintStream.out ); }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns an new instance of the ThreadedTCPNetworkServerThread
|
||||
* that will handle the newly made connection, if an null value is returned
|
||||
* then the ThreadedTCPNetworkServer will close the new connection.
|
||||
*
|
||||
* @param s is an new connection to an host
|
||||
* @return a new instance of an thread or null
|
||||
*/
|
||||
protected abstract ThreadedTCPNetworkServerThread getThreadInstance( Socket s );
|
||||
|
||||
/**
|
||||
* Initiates a SSLServerSocket
|
||||
*
|
||||
* @param port The port to listen to
|
||||
* @return The SSLServerSocket
|
||||
* @throws IOException
|
||||
*/
|
||||
private ServerSocket initSSL(int port) throws IOException{
|
||||
SSLServerSocketFactory sslserversocketfactory =
|
||||
(SSLServerSocketFactory) SSLServerSocketFactory.getDefault();
|
||||
return sslserversocketfactory.createServerSocket(port);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the given cert file to the KeyStore
|
||||
*
|
||||
* @param certFile The cert file
|
||||
*/
|
||||
protected void registerCertificate(File keyStore, String keyStorePass) throws CertificateException, IOException, KeyStoreException, NoSuchProviderException, NoSuchAlgorithmException{
|
||||
System.setProperty("javax.net.ssl.keyStore", keyStore.getAbsolutePath());
|
||||
System.setProperty("javax.net.ssl.keyStorePassword", keyStorePass);
|
||||
}
|
||||
}
|
||||
12
src/zutil/net/threaded/ThreadedTCPNetworkServerThread.java
Normal file
12
src/zutil/net/threaded/ThreadedTCPNetworkServerThread.java
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
package zutil.net.threaded;
|
||||
|
||||
/**
|
||||
* The class that will handle the TCP connection will incclude
|
||||
* this interface
|
||||
*
|
||||
* @author Ziver
|
||||
*
|
||||
*/
|
||||
public interface ThreadedTCPNetworkServerThread extends Runnable{
|
||||
|
||||
}
|
||||
116
src/zutil/net/threaded/ThreadedUDPNetwork.java
Normal file
116
src/zutil/net/threaded/ThreadedUDPNetwork.java
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
package zutil.net.threaded;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.DatagramPacket;
|
||||
import java.net.DatagramSocket;
|
||||
import java.net.InetAddress;
|
||||
import java.net.MulticastSocket;
|
||||
import java.net.SocketException;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* A simple web server that handles both cookies and
|
||||
* sessions for all the clients
|
||||
*
|
||||
* @author Ziver
|
||||
*/
|
||||
public class ThreadedUDPNetwork extends Thread{
|
||||
public static final int BUFFER_SIZE = 512;
|
||||
|
||||
// Type of UDP socket
|
||||
enum UDPType{
|
||||
MULTICAST,
|
||||
UNICAST
|
||||
}
|
||||
protected final UDPType type;
|
||||
protected final int port;
|
||||
protected DatagramSocket socket;
|
||||
protected ThreadedUDPNetworkThread thread = null;
|
||||
|
||||
/**
|
||||
* Creates a new unicast Clien instance of the class
|
||||
*
|
||||
* @param thread is the class that will handle incoming packets
|
||||
* @throws SocketException
|
||||
*/
|
||||
public ThreadedUDPNetwork(ThreadedUDPNetworkThread thread) throws SocketException{
|
||||
this.type = UDPType.UNICAST;
|
||||
this.port = -1;
|
||||
setThread( thread );
|
||||
|
||||
socket = new DatagramSocket();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new unicast Server instance of the class
|
||||
*
|
||||
* @param thread is the class that will handle incoming packets
|
||||
* @param port is the port that the server should listen to
|
||||
* @throws SocketException
|
||||
*/
|
||||
public ThreadedUDPNetwork(ThreadedUDPNetworkThread thread, int port) throws SocketException{
|
||||
this.type = UDPType.UNICAST;
|
||||
this.port = port;
|
||||
setThread( thread );
|
||||
|
||||
socket = new DatagramSocket( port );
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new multicast Server instance of the class
|
||||
*
|
||||
* @param thread is the class that will handle incoming packets
|
||||
* @param port is the port that the server should listen to
|
||||
* @param multicast_addr is the multicast address that the server will listen on
|
||||
* @throws IOException
|
||||
*/
|
||||
public ThreadedUDPNetwork(ThreadedUDPNetworkThread thread, String multicast_addr, int port ) throws IOException{
|
||||
this.type = UDPType.MULTICAST;
|
||||
this.port = port;
|
||||
setThread( thread );
|
||||
|
||||
// init udp socket
|
||||
MulticastSocket msocket = new MulticastSocket( port );
|
||||
InetAddress group = InetAddress.getByName( multicast_addr );
|
||||
msocket.joinGroup( group );
|
||||
|
||||
socket = msocket;
|
||||
}
|
||||
|
||||
|
||||
public void run(){
|
||||
try{
|
||||
while(true){
|
||||
byte[] buf = new byte[BUFFER_SIZE];
|
||||
DatagramPacket packet = new DatagramPacket(buf, buf.length);
|
||||
socket.receive( packet );
|
||||
if( thread!=null )
|
||||
thread.receivedPacket( packet, this );
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the given packet
|
||||
*
|
||||
* @param packet is the packet to send
|
||||
* @throws IOException
|
||||
*/
|
||||
public synchronized void send( DatagramPacket packet ) throws IOException{
|
||||
socket.send(packet);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the thread that will handle the incoming packets
|
||||
*
|
||||
* @param thread is the thread
|
||||
*/
|
||||
public void setThread(ThreadedUDPNetworkThread thread){
|
||||
this.thread = thread;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
21
src/zutil/net/threaded/ThreadedUDPNetworkThread.java
Normal file
21
src/zutil/net/threaded/ThreadedUDPNetworkThread.java
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
package zutil.net.threaded;
|
||||
|
||||
import java.net.DatagramPacket;
|
||||
|
||||
/**
|
||||
* This interface is for processing received packets
|
||||
* from the TNetworkUDPServer
|
||||
*
|
||||
* @author Ziver
|
||||
*
|
||||
*/
|
||||
public interface ThreadedUDPNetworkThread extends Runnable{
|
||||
|
||||
/**
|
||||
* Packet will be processed in this method
|
||||
*
|
||||
* @param packet is the received packet
|
||||
* @param network is the network class that received the packet
|
||||
*/
|
||||
public void receivedPacket(DatagramPacket packet, ThreadedUDPNetwork network);
|
||||
}
|
||||
102
src/zutil/net/torrent/Torrent.java
Normal file
102
src/zutil/net/torrent/Torrent.java
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
package zutil.net.torrent;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
|
||||
import zutil.io.file.FileUtil;
|
||||
|
||||
public class Torrent {
|
||||
// Name of the torrent
|
||||
private String name;
|
||||
// Comment
|
||||
private String comment;
|
||||
// Creation date as unix timestamp
|
||||
private long date;
|
||||
// Files in the torrent
|
||||
private ArrayList<String> file_list;
|
||||
// Size of of the full torrent (after download)
|
||||
private long size;
|
||||
// Signature of the software which created the torrent
|
||||
private String created_by;
|
||||
// tracker (the tracker the torrent has been received from)
|
||||
private String main_tracker;
|
||||
// List of known trackers for the torrent
|
||||
private ArrayList<String> tracker_list;
|
||||
private HashMap<String,Object> info_hash;
|
||||
// Torrent is marked as 'private'.
|
||||
private boolean is_private;
|
||||
|
||||
public Torrent(File torrent) throws IOException{
|
||||
this(FileUtil.getFileContent( torrent ));
|
||||
}
|
||||
|
||||
public Torrent(String data){
|
||||
reset();
|
||||
decode(data);
|
||||
}
|
||||
|
||||
private void reset(){
|
||||
// Reset
|
||||
name = "";
|
||||
comment = "";
|
||||
date = 0;
|
||||
file_list = new ArrayList<String>();
|
||||
size = 0;
|
||||
created_by = "";
|
||||
main_tracker = "";
|
||||
tracker_list = new ArrayList<String>();
|
||||
info_hash = new HashMap<String,Object>();
|
||||
is_private = false;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void decode(String data){
|
||||
HashMap<?,?> dataMap = (HashMap<?,?>)TorrentParser.decode(data);
|
||||
|
||||
name = (String)dataMap.get("name");
|
||||
comment = (String)dataMap.get("comment");
|
||||
date = (Long)dataMap.get("creation date");
|
||||
file_list = new ArrayList<String>();
|
||||
size = (Long)dataMap.get("length");
|
||||
created_by = (String)dataMap.get("created by");
|
||||
main_tracker = (String)dataMap.get("announce");
|
||||
tracker_list = (ArrayList<String>)dataMap.get("announce-list");
|
||||
info_hash = (HashMap<String, Object>)dataMap.get("info");
|
||||
is_private = (((Integer)dataMap.get("private")) != 0);
|
||||
}
|
||||
|
||||
// ************** GETTER **************
|
||||
public String getName(){
|
||||
return name;
|
||||
}
|
||||
public String getComments(){
|
||||
return comment;
|
||||
}
|
||||
public long getDate(){
|
||||
return date;
|
||||
}
|
||||
public ArrayList<String> getFileList(){
|
||||
return file_list;
|
||||
}
|
||||
public long getSize(){
|
||||
return size;
|
||||
}
|
||||
public String getAuthor(){
|
||||
return created_by;
|
||||
}
|
||||
public String getMainTracker(){
|
||||
return main_tracker;
|
||||
}
|
||||
public ArrayList<String> getTrackerList(){
|
||||
return tracker_list;
|
||||
}
|
||||
public HashMap<String,Object> getInfoHash(){
|
||||
return info_hash;
|
||||
}
|
||||
public boolean isPrivate(){
|
||||
return is_private;
|
||||
}
|
||||
// ************************************
|
||||
}
|
||||
122
src/zutil/net/torrent/TorrentParser.java
Normal file
122
src/zutil/net/torrent/TorrentParser.java
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
package zutil.net.torrent;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
|
||||
import zutil.io.MultiPrintStream;
|
||||
import zutil.io.file.FileUtil;
|
||||
|
||||
/**
|
||||
* http://wiki.theory.org/BitTorrentSpecification
|
||||
* @author Ziver
|
||||
*
|
||||
*/
|
||||
public class TorrentParser {
|
||||
/**
|
||||
* Example use
|
||||
*/
|
||||
public static void main(String[] args){
|
||||
try {
|
||||
String tmp = FileUtil.getFileContent(FileUtil.find("C:\\Users\\Ziver\\Desktop\\test.torrent"));
|
||||
MultiPrintStream.out.dump(TorrentParser.decode(tmp));
|
||||
|
||||
} catch (FileNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the representation of the data in the BEncoded string
|
||||
*
|
||||
* @param data The data to be decoded
|
||||
* @return
|
||||
*/
|
||||
public static Object decode(String data){
|
||||
return decode_BEncoded(new StringBuffer(data));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the representation of the data in the BEncoded string
|
||||
*
|
||||
* @param data The data to be decoded
|
||||
* @param index The index in data to start from
|
||||
* @return
|
||||
*/
|
||||
private static Object decode_BEncoded(StringBuffer data){
|
||||
String tmp;
|
||||
char c = ' ';
|
||||
|
||||
switch (data.charAt(0)) {
|
||||
/**
|
||||
* Integers are prefixed with an i and terminated by an e. For
|
||||
* example, 123 would bEcode to i123e, -3272002 would bEncode to
|
||||
* i-3272002e.
|
||||
*/
|
||||
case 'i':
|
||||
//System.out.println("Found Integer at "+index);
|
||||
data.deleteCharAt(0);
|
||||
tmp = data.substring(0, data.indexOf("e"));
|
||||
data.delete(0, tmp.length() + 1);
|
||||
//System.out.println(tmp);
|
||||
return new Long(tmp);
|
||||
/**
|
||||
* Lists are prefixed with a l and terminated by an e. The list
|
||||
* should contain a series of bEncoded elements. For example, the
|
||||
* list of strings ["Monduna", "Bit", "Torrents"] would bEncode to
|
||||
* l7:Monduna3:Bit8:Torrentse. The list [1, "Monduna", 3, ["Sub", "List"]]
|
||||
* would bEncode to li1e7:Mondunai3el3:Sub4:Listee
|
||||
*/
|
||||
case 'l':
|
||||
//System.out.println("Found List at "+index);
|
||||
data.deleteCharAt(0);
|
||||
LinkedList<Object> list = new LinkedList<Object>();
|
||||
c = data.charAt(0);
|
||||
while(c != 'e'){
|
||||
list.add(decode_BEncoded(data));
|
||||
c = data.charAt(0);
|
||||
}
|
||||
data.deleteCharAt(0);
|
||||
//MultiPrintStream.out.dump(list);
|
||||
if(list.size() == 1) return list.poll();
|
||||
else return list;
|
||||
/**
|
||||
* Dictionaries are prefixed with a d and terminated by an e. They
|
||||
* are similar to list, except that items are in key value pairs. The
|
||||
* dictionary {"key":"value", "Monduna":"com", "bit":"Torrents", "number":7}
|
||||
* would bEncode to d3:key5:value7:Monduna3:com3:bit:8:Torrents6:numberi7ee
|
||||
*/
|
||||
case 'd':
|
||||
//System.out.println("Found Dictionary at "+index);
|
||||
data.deleteCharAt(0);
|
||||
HashMap<Object,Object> map = new HashMap<Object,Object>();
|
||||
c = data.charAt(0);
|
||||
while(c != 'e'){
|
||||
Object tmp2 = decode_BEncoded(data);
|
||||
map.put(tmp2, decode_BEncoded(data));
|
||||
c = data.charAt(0);
|
||||
}
|
||||
data.deleteCharAt(0);
|
||||
//MultiPrintStream.out.dump(map);
|
||||
return map;
|
||||
/**
|
||||
* Strings are prefixed with their length followed by a colon.
|
||||
* For example, "Monduna" would bEncode to 7:Monduna and "BitTorrents"
|
||||
* would bEncode to 11:BitTorrents.
|
||||
*/
|
||||
default:
|
||||
//System.out.println("Found String at "+index);
|
||||
tmp = data.substring(0, data.indexOf(":"));
|
||||
int length = Integer.parseInt(tmp);
|
||||
data.delete(0, tmp.length()+1);
|
||||
String ret = data.substring(0, length);
|
||||
data.delete(0, length);
|
||||
//System.out.println(data.substring(i, i+length));
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
89
src/zutil/net/upnp/UPnPMediaServer.java
Normal file
89
src/zutil/net/upnp/UPnPMediaServer.java
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
package zutil.net.upnp;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
import zutil.net.http.HttpPrintStream;
|
||||
|
||||
/**
|
||||
* This class is a UPnP AV Media Server that handles all the
|
||||
* other UPnP services
|
||||
*
|
||||
* @author Ziver
|
||||
*/
|
||||
public class UPnPMediaServer extends UPnPRootDevice{
|
||||
public static final String RELATIVE_URL = "upnp/rootdev";
|
||||
|
||||
private String url;
|
||||
private String uuid;
|
||||
|
||||
public UPnPMediaServer(String location){
|
||||
url = location;
|
||||
}
|
||||
|
||||
public void respond(HttpPrintStream out, Map<String, String> clientInfo,
|
||||
Map<String, Object> session, Map<String, String> cookie,
|
||||
Map<String, String> request) {
|
||||
|
||||
out.enableBuffering(true);
|
||||
out.setHeader("Content-Type", "text/xml");
|
||||
|
||||
out.println("<?xml version=\"1.0\" encoding=\"utf-8\"?>");
|
||||
out.println("<root xmlns=\"urn:schemas-upnp-org:service:ContentDirectory:1\">");
|
||||
out.println(" <specVersion>");
|
||||
out.println(" <major>1</major>");
|
||||
out.println(" <minor>0</minor>");
|
||||
out.println(" </specVersion>");
|
||||
out.println(" <URLBase>"+url+"</URLBase>");//"+ssdp.getLocation()+"
|
||||
out.println(" <device>");
|
||||
out.println(" <deviceType>urn:schemas-upnp-org:device:MediaServer:1</deviceType>");
|
||||
out.println(" <friendlyName>ZupNP AV Media Server</friendlyName>");
|
||||
out.println(" <manufacturer>Ziver Koc</manufacturer>");
|
||||
out.println(" <manufacturerURL>http://ziver.koc.se</manufacturerURL>");
|
||||
out.println("");
|
||||
out.println(" <modelName>ZupNP Server</modelName>");
|
||||
out.println(" <modelDescription>UPnP AV Media Server</modelDescription>");
|
||||
out.println(" <modelNumber>0.1</modelNumber>");
|
||||
out.println(" <UDN>"+getUUID()+"</UDN>");
|
||||
out.println(" <serviceList>");
|
||||
out.println(" <service>");
|
||||
out.println(" <serviceType>urn:schemas-upnp-org:service:ConnectionManager:1</serviceType>");
|
||||
out.println(" <serviceId>urn:upnp-org:serviceId:CMGR_1-0</serviceId>");
|
||||
out.println(" <SCPDURL>CMGR_Control/GetServDesc</SCPDURL>");
|
||||
out.println(" <controlURL>CMGR_Control</controlURL>");
|
||||
out.println(" <eventSubURL>CMGR_Event</eventSubURL>");
|
||||
out.println(" </service>");
|
||||
out.println(" <service>");
|
||||
out.println(" <serviceType>urn:schemas-upnp-org:service:ContentDirectory:1</serviceType>");
|
||||
out.println(" <serviceId>urn:upnp-org:serviceId:CDS_1-0</serviceId>");
|
||||
out.println(" <SCPDURL>SCP/ContentDir</SCPDURL>");
|
||||
out.println(" <controlURL>Action/ContentDir</controlURL>");
|
||||
out.println(" <eventSubURL>Event/ContentDir</eventSubURL>");
|
||||
out.println(" </service>");
|
||||
out.println(" </serviceList>");
|
||||
out.println(" </device>");
|
||||
out.println("</root>");
|
||||
out.flush();
|
||||
}
|
||||
|
||||
|
||||
public long getExpirationTime() {
|
||||
return 60*30; // 30min
|
||||
}
|
||||
public String getLocation() {
|
||||
return url+"RootDesc";
|
||||
}
|
||||
public String getSearchTarget() {
|
||||
return "upnp:rootdevice";
|
||||
}
|
||||
public String getUSN() {
|
||||
return getUUID()+"::upnp:rootdevice";
|
||||
}
|
||||
public String getUUID() {
|
||||
if(uuid==null){
|
||||
uuid = "uuid:"+UUID.nameUUIDFromBytes( this.getClass().toString().getBytes() ); //(url+Math.random()).getBytes()
|
||||
}
|
||||
return uuid;
|
||||
}
|
||||
|
||||
}
|
||||
13
src/zutil/net/upnp/UPnPRootDevice.java
Normal file
13
src/zutil/net/upnp/UPnPRootDevice.java
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
package zutil.net.upnp;
|
||||
|
||||
import zutil.net.http.HttpPage;
|
||||
import zutil.net.ssdp.SSDPServiceInfo;
|
||||
/**
|
||||
* This class is a UPnP Server class that will be extended
|
||||
* by all root devices handles all the other UPnP services
|
||||
*
|
||||
* @author Ziver
|
||||
*/
|
||||
public abstract class UPnPRootDevice implements HttpPage, SSDPServiceInfo{
|
||||
|
||||
}
|
||||
10
src/zutil/net/upnp/UPnPService.java
Normal file
10
src/zutil/net/upnp/UPnPService.java
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
package zutil.net.upnp;
|
||||
|
||||
/**
|
||||
* Information about a UPNP Service
|
||||
*
|
||||
* @author Ziver
|
||||
*/
|
||||
public interface UPnPService {
|
||||
|
||||
}
|
||||
14
src/zutil/net/upnp/services/BrowseRetObj.java
Normal file
14
src/zutil/net/upnp/services/BrowseRetObj.java
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
package zutil.net.upnp.services;
|
||||
|
||||
import zutil.net.ws.WSReturnValueList;
|
||||
|
||||
public class BrowseRetObj extends WSReturnValueList{
|
||||
@WSValueName("Result")
|
||||
public String Result;
|
||||
@WSValueName("NumberReturned")
|
||||
public int NumberReturned;
|
||||
@WSValueName("TotalMatches")
|
||||
public int TotalMatches;
|
||||
@WSValueName("UpdateID")
|
||||
public int UpdateID;
|
||||
}
|
||||
590
src/zutil/net/upnp/services/UPnPContentDirectory.java
Normal file
590
src/zutil/net/upnp/services/UPnPContentDirectory.java
Normal file
|
|
@ -0,0 +1,590 @@
|
|||
package zutil.net.upnp.services;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.dom4j.DocumentException;
|
||||
|
||||
import zutil.io.file.FileUtil;
|
||||
import zutil.net.http.HttpPage;
|
||||
import zutil.net.http.HttpPrintStream;
|
||||
import zutil.net.upnp.UPnPService;
|
||||
import zutil.net.ws.WSInterface;
|
||||
import zutil.net.ws.WSReturnValueList;
|
||||
|
||||
/**
|
||||
* Information about a UPNP Service
|
||||
*
|
||||
* @author Ziver
|
||||
*/
|
||||
public class UPnPContentDirectory implements UPnPService, HttpPage, WSInterface {
|
||||
public static final int VERSION = 1;
|
||||
|
||||
private static List<File> file_list;
|
||||
|
||||
public UPnPContentDirectory(){}
|
||||
|
||||
public UPnPContentDirectory(File dir){
|
||||
file_list = FileUtil.search(dir, new LinkedList<File>(), true, Integer.MAX_VALUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* This action returns the searching capabilities
|
||||
* that are supported by the device.
|
||||
*
|
||||
*/
|
||||
@WSReturnName("SortCaps")
|
||||
public String GetSearchCapabilities(){
|
||||
// "dc:title,res@size"
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the CSV list of meta-data tags that can
|
||||
* be used in sortCriteria
|
||||
*
|
||||
*/
|
||||
@WSReturnName("SortCaps")
|
||||
public String GetSortCapabilities(){
|
||||
return "dc:title";
|
||||
}
|
||||
|
||||
/**
|
||||
* This action returns the current value of state variable
|
||||
* SystemUpdateID. It can be used by clients that want to
|
||||
* 'poll' for any changes in the Content Directory
|
||||
* (as opposed to subscribing to events).
|
||||
*
|
||||
*/
|
||||
@WSReturnName("Id")
|
||||
public int GetSystemUpdateID(){
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* This action allows the caller to incrementally browse
|
||||
* the native hierarchy of the Content Directory objects
|
||||
* exposed by the Content Directory Service, including
|
||||
* information listing the classes of objects available
|
||||
* in any particular object container.
|
||||
* @throws DocumentException
|
||||
*
|
||||
*/
|
||||
//@WSNameSpace("urn:schemas-upnp-org:service:ContentDirectory:1")
|
||||
public BrowseRetObj Browse(
|
||||
@WSParamName("ObjectID") String ObjectID,
|
||||
@WSParamName("BrowseFlag") String BrowseFlag,
|
||||
@WSParamName("Filter") String Filter,
|
||||
@WSParamName("StartingIndex") int StartingIndex,
|
||||
@WSParamName("RequestedCount") int RequestedCount,
|
||||
@WSParamName("SortCriteria") String SortCriteria) throws DocumentException{
|
||||
|
||||
BrowseRetObj ret = new BrowseRetObj();
|
||||
if( BrowseFlag.equals("BrowseMetadata") ){
|
||||
|
||||
}
|
||||
else if( BrowseFlag.equals("BrowseDirectChildren") ){
|
||||
StringBuffer xml = new StringBuffer();
|
||||
xml.append( "<DIDL-Lite xmlns:dc=\"http://purl.org/dc/elements/1.1/\" " +
|
||||
"xmlns:upnp=\"urn:schemas-upnp-org:metadata-1-0/upnp/\" " +
|
||||
"xmlns=\"urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/\">" );
|
||||
List<File> tmp = FileUtil.search( file_list.get(Integer.parseInt(ObjectID)), new LinkedList<File>(), false );
|
||||
for(File file : tmp){
|
||||
xml.append(" <container id=\""+file_list.indexOf(file)+"\" ");
|
||||
if(tmp.get(0) != file) xml.append("parentID=\""+file_list.indexOf(file.getParent())+"\" ");
|
||||
if(file.isDirectory()) xml.append("childCount=\""+file.list().length+"\" ");
|
||||
xml.append("restricted=\"1\" searchable=\"0\" >");
|
||||
|
||||
xml.append(" <dc:title>"+file.getName()+"</dc:title> ");
|
||||
if( file.isDirectory() )
|
||||
xml.append(" <upnp:class>object.container.storageFolder</upnp:class> ");
|
||||
else
|
||||
xml.append(" <upnp:class>object.container</upnp:class> ");
|
||||
xml.append(" <upnp:storageUsed>"+(int)(file.length()/1000)+"</upnp:storageUsed> ");
|
||||
xml.append(" </container> ");
|
||||
|
||||
ret.NumberReturned++;
|
||||
ret.TotalMatches++;
|
||||
}
|
||||
xml.append( "</DIDL-Lite>" );
|
||||
|
||||
ret.Result = xml.toString();
|
||||
//Document document = DocumentHelper.parseText( xml.toString() );
|
||||
//ret.Result = document.getRootElement();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
public class BrowseRetObj extends WSReturnValueList{
|
||||
@WSValueName("Result")
|
||||
public String Result;
|
||||
@WSValueName("NumberReturned")
|
||||
public int NumberReturned;
|
||||
@WSValueName("TotalMatches")
|
||||
public int TotalMatches;
|
||||
@WSValueName("UpdateID")
|
||||
public int UpdateID;
|
||||
}
|
||||
|
||||
|
||||
@WSDisabled
|
||||
public void respond(HttpPrintStream out, Map<String, String> clientInfo,
|
||||
Map<String, Object> session, Map<String, String> cookie,
|
||||
Map<String, String> request) {
|
||||
|
||||
out.enableBuffering(true);
|
||||
out.setHeader("Content-Type", "text/xml");
|
||||
|
||||
out.println("<?xml version=\"1.0\" encoding=\"utf-8\"?>");
|
||||
out.println("<scpd xmlns=\"urn:schemas-upnp-org:service-1-0\">");
|
||||
out.println(" <serviceStateTable>");
|
||||
out.println(" <stateVariable>");
|
||||
out.println(" <Optional/>");
|
||||
out.println(" <name>TransferIDs</name>");
|
||||
out.println(" <sendEventsAttribute>yes</sendEventsAttribute>");
|
||||
out.println(" <dataType>string</dataType>");
|
||||
out.println(" </stateVariable>");
|
||||
out.println(" <stateVariable>");
|
||||
out.println(" <name>A_ARG_TYPE_ObjectID</name>");
|
||||
out.println(" <sendEventsAttribute>no</sendEventsAttribute>");
|
||||
out.println(" <dataType>string</dataType>");
|
||||
out.println(" </stateVariable>");
|
||||
out.println(" <stateVariable>");
|
||||
out.println(" <name>A_ARG_TYPE_Result</name>");
|
||||
out.println(" <sendEventsAttribute>no</sendEventsAttribute>");
|
||||
out.println(" <dataType>string</dataType>");
|
||||
out.println(" </stateVariable>");
|
||||
out.println(" <stateVariable>");
|
||||
out.println(" <Optional/>");
|
||||
out.println(" <name>A_ARG_TYPE_SearchCriteria</name>");
|
||||
out.println(" <sendEventsAttribute>no</sendEventsAttribute>");
|
||||
out.println(" <dataType>string</dataType>");
|
||||
out.println(" </stateVariable>");
|
||||
out.println(" <stateVariable>");
|
||||
out.println(" <name>A_ARG_TYPE_BrowseFlag</name>");
|
||||
out.println(" <sendEventsAttribute>no</sendEventsAttribute>");
|
||||
out.println(" <dataType>string</dataType>");
|
||||
out.println(" <allowedValueList>");
|
||||
out.println(" <allowedValue>BrowseMetadata</allowedValue>");
|
||||
out.println(" <allowedValue>BrowseDirectChildren</allowedValue>");
|
||||
out.println(" </allowedValueList>");
|
||||
out.println(" </stateVariable>");
|
||||
out.println(" <stateVariable>");
|
||||
out.println(" <name>A_ARG_TYPE_Filter</name>");
|
||||
out.println(" <sendEventsAttribute>no</sendEventsAttribute>");
|
||||
out.println(" <dataType>string</dataType>");
|
||||
out.println(" </stateVariable>");
|
||||
out.println(" <stateVariable>");
|
||||
out.println(" <name>A_ARG_TYPE_SortCriteria</name>");
|
||||
out.println(" <sendEventsAttribute>no</sendEventsAttribute>");
|
||||
out.println(" <dataType>string</dataType>");
|
||||
out.println(" </stateVariable>");
|
||||
out.println(" <stateVariable>");
|
||||
out.println(" <name>A_ARG_TYPE_Index</name>");
|
||||
out.println(" <sendEventsAttribute>no</sendEventsAttribute>");
|
||||
out.println(" <dataType>ui4</dataType>");
|
||||
out.println(" </stateVariable>");
|
||||
out.println(" <stateVariable>");
|
||||
out.println(" <name>A_ARG_TYPE_Count</name>");
|
||||
out.println(" <sendEventsAttribute>no</sendEventsAttribute>");
|
||||
out.println(" <dataType>ui4</dataType>");
|
||||
out.println(" </stateVariable>");
|
||||
out.println(" <stateVariable>");
|
||||
out.println(" <name>A_ARG_TYPE_UpdateID</name>");
|
||||
out.println(" <sendEventsAttribute>no</sendEventsAttribute>");
|
||||
out.println(" <dataType>ui4</dataType>");
|
||||
out.println(" </stateVariable>");
|
||||
out.println(" <stateVariable>");
|
||||
out.println(" <Optional/>");
|
||||
out.println(" <name>A_ARG_TYPE_TransferID</name>");
|
||||
out.println(" <sendEventsAttribute>no</sendEventsAttribute>");
|
||||
out.println(" <dataType>ui4</dataType>");
|
||||
out.println(" </stateVariable>");
|
||||
out.println(" <stateVariable>");
|
||||
out.println(" <Optional/>");
|
||||
out.println(" <name>A_ARG_TYPE_TransferStatus</name>");
|
||||
out.println(" <sendEventsAttribute>no</sendEventsAttribute>");
|
||||
out.println(" <dataType>string</dataType>");
|
||||
out.println(" <allowedValueList>");
|
||||
out.println(" <allowedValue>COMPLETED</allowedValue>");
|
||||
out.println(" <allowedValue>ERROR</allowedValue>");
|
||||
out.println(" <allowedValue>IN_PROGRESS</allowedValue>");
|
||||
out.println(" <allowedValue>STOPPED</allowedValue>");
|
||||
out.println(" </allowedValueList>");
|
||||
out.println(" </stateVariable>");
|
||||
out.println(" <stateVariable>");
|
||||
out.println(" <Optional/>");
|
||||
out.println(" <name>A_ARG_TYPE_TransferLength</name>");
|
||||
out.println(" <sendEventsAttribute>no</sendEventsAttribute>");
|
||||
out.println(" <dataType>string</dataType>");
|
||||
out.println(" </stateVariable>");
|
||||
out.println(" <stateVariable>");
|
||||
out.println(" <Optional/>");
|
||||
out.println(" <name>A_ARG_TYPE_TransferTotal</name>");
|
||||
out.println(" <sendEventsAttribute>no</sendEventsAttribute>");
|
||||
out.println(" <dataType>string</dataType>");
|
||||
out.println(" </stateVariable>");
|
||||
out.println(" <stateVariable>");
|
||||
out.println(" <Optional/>");
|
||||
out.println(" <name>A_ARG_TYPE_TagValueList</name>");
|
||||
out.println(" <sendEventsAttribute>no</sendEventsAttribute>");
|
||||
out.println(" <dataType>string</dataType>");
|
||||
out.println(" </stateVariable>");
|
||||
out.println(" <stateVariable>");
|
||||
out.println(" <Optional/>");
|
||||
out.println(" <name>A_ARG_TYPE_URI</name>");
|
||||
out.println(" <sendEventsAttribute>no</sendEventsAttribute>");
|
||||
out.println(" <dataType>uri</dataType>");
|
||||
out.println(" </stateVariable>");
|
||||
out.println(" <stateVariable>");
|
||||
out.println(" <name>SearchCapabilities</name>");
|
||||
out.println(" <sendEventsAttribute>no</sendEventsAttribute>");
|
||||
out.println(" <dataType>string</dataType>");
|
||||
out.println(" </stateVariable>");
|
||||
out.println(" <stateVariable>");
|
||||
out.println(" <name>SortCapabilities</name>");
|
||||
out.println(" <sendEventsAttribute>no</sendEventsAttribute>");
|
||||
out.println(" <dataType>string</dataType>");
|
||||
out.println(" </stateVariable>");
|
||||
out.println(" <stateVariable>");
|
||||
out.println(" <name>SystemUpdateID</name>");
|
||||
out.println(" <sendEventsAttribute>yes</sendEventsAttribute>");
|
||||
out.println(" <dataType>ui4</dataType>");
|
||||
out.println(" </stateVariable>");
|
||||
out.println(" <stateVariable>");
|
||||
out.println(" <Optional/>");
|
||||
out.println(" <name>ContainerUpdateIDs</name>");
|
||||
out.println(" <sendEventsAttribute>yes</sendEventsAttribute>");
|
||||
out.println(" <dataType>string</dataType>");
|
||||
out.println(" </stateVariable>");
|
||||
out.println(" </serviceStateTable>");
|
||||
|
||||
|
||||
out.println(" <actionList>");
|
||||
out.println(" <action>");
|
||||
out.println(" <name>GetSearchCapabilities</name>");
|
||||
out.println(" <argumentList>");
|
||||
out.println(" <argument>");
|
||||
out.println(" <name>SearchCaps</name>");
|
||||
out.println(" <direction>out</direction>");
|
||||
out.println(" <relatedStateVariable>SearchCapabilities</relatedStateVariable>");
|
||||
out.println(" </argument>");
|
||||
out.println(" </argumentList>");
|
||||
out.println(" </action>");
|
||||
out.println(" <action>");
|
||||
out.println(" <name>GetSortCapabilities</name>");
|
||||
out.println(" <argumentList>");
|
||||
out.println(" <argument>");
|
||||
out.println(" <name>SortCaps</name>");
|
||||
out.println(" <direction>out</direction>");
|
||||
out.println(" <relatedStateVariable>SortCapabilities</relatedStateVariable>");
|
||||
out.println(" </argument>");
|
||||
out.println(" </argumentList>");
|
||||
out.println(" </action>");
|
||||
out.println(" <action>");
|
||||
out.println(" <name>GetSystemUpdateID</name>");
|
||||
out.println(" <argumentList>");
|
||||
out.println(" <argument>");
|
||||
out.println(" <name>Id</name>");
|
||||
out.println(" <direction>out</direction>");
|
||||
out.println(" <relatedStateVariable>SystemUpdateID</relatedStateVariable>");
|
||||
out.println(" </argument>");
|
||||
out.println(" </argumentList>");
|
||||
out.println(" </action>");
|
||||
|
||||
out.println(" <action>");
|
||||
out.println(" <name>Browse</name>");
|
||||
out.println(" <argumentList>");
|
||||
out.println(" <argument>");
|
||||
out.println(" <name>ObjectID</name>");
|
||||
out.println(" <direction>in</direction>");
|
||||
out.println(" <relatedStateVariable>A_ARG_TYPE_ObjectID</relatedStateVariable>");
|
||||
out.println(" </argument>");
|
||||
out.println(" <argument>");
|
||||
out.println(" <name>BrowseFlag</name>");
|
||||
out.println(" <direction>in</direction>");
|
||||
out.println(" <relatedStateVariable>A_ARG_TYPE_BrowseFlag</relatedStateVariable>");
|
||||
out.println(" </argument>");
|
||||
out.println(" <argument>");
|
||||
out.println(" <name>Filter</name>");
|
||||
out.println(" <direction>in</direction>");
|
||||
out.println(" <relatedStateVariable>A_ARG_TYPE_Filter</relatedStateVariable>");
|
||||
out.println(" </argument>");
|
||||
out.println(" <argument>");
|
||||
out.println(" <name>StartingIndex</name>");
|
||||
out.println(" <direction>in</direction>");
|
||||
out.println(" <relatedStateVariable>A_ARG_TYPE_Index</relatedStateVariable>");
|
||||
out.println(" </argument>");
|
||||
out.println(" <argument>");
|
||||
out.println(" <name>RequestedCount</name>");
|
||||
out.println(" <direction>in</direction>");
|
||||
out.println(" <relatedStateVariable>A_ARG_TYPE_Count</relatedStateVariable>");
|
||||
out.println(" </argument>");
|
||||
out.println(" <argument>");
|
||||
out.println(" <name>SortCriteria</name>");
|
||||
out.println(" <direction>in</direction>");
|
||||
out.println(" <relatedStateVariable>A_ARG_TYPE_SortCriteria</relatedStateVariable>");
|
||||
out.println(" </argument>");
|
||||
out.println(" <argument>");
|
||||
out.println(" <name>Result</name>");
|
||||
out.println(" <direction>out</direction>");
|
||||
out.println(" <relatedStateVariable>A_ARG_TYPE_Result</relatedStateVariable>");
|
||||
out.println(" </argument>");
|
||||
out.println(" <argument>");
|
||||
out.println(" <name>NumberReturned</name>");
|
||||
out.println(" <direction>out</direction>");
|
||||
out.println(" <relatedStateVariable>A_ARG_TYPE_Count</relatedStateVariable>");
|
||||
out.println(" </argument>");
|
||||
out.println(" <argument>");
|
||||
out.println(" <name>TotalMatches</name>");
|
||||
out.println(" <direction>out</direction>");
|
||||
out.println(" <relatedStateVariable>A_ARG_TYPE_Count</relatedStateVariable>");
|
||||
out.println(" </argument>");
|
||||
out.println(" <argument>");
|
||||
out.println(" <name>UpdateID</name>");
|
||||
out.println(" <direction>out</direction>");
|
||||
out.println(" <relatedStateVariable>A_ARG_TYPE_UpdateID</relatedStateVariable>");
|
||||
out.println(" </argument>");
|
||||
out.println(" </argumentList>");
|
||||
out.println(" </action>");
|
||||
|
||||
/*out.println(" <action>");
|
||||
out.println(" <Optional/>");
|
||||
out.println(" <name>Search</name>");
|
||||
out.println(" <argumentList>");
|
||||
out.println(" <argument>");
|
||||
out.println(" <name>ContainerID</name>");
|
||||
out.println(" <direction>in</direction>");
|
||||
out.println(" <relatedStateVariable>A_ARG_TYPE_ObjectID</relatedStateVariable>");
|
||||
out.println(" </argument>");
|
||||
out.println(" <argument>");
|
||||
out.println(" <name>SearchCriteria</name>");
|
||||
out.println(" <direction>in</direction>");
|
||||
out.println(" <relatedStateVariable>A_ARG_TYPE_SearchCriteria </relatedStateVariable>");
|
||||
out.println(" </argument>");
|
||||
out.println(" <argument>");
|
||||
out.println(" <name>Filter</name>");
|
||||
out.println(" <direction>in</direction>");
|
||||
out.println(" <relatedStateVariable>A_ARG_TYPE_Filter</relatedStateVariable>");
|
||||
out.println(" </argument>");
|
||||
out.println(" <argument>");
|
||||
out.println(" <name>StartingIndex</name>");
|
||||
out.println(" <direction>in</direction>");
|
||||
out.println(" <relatedStateVariable>A_ARG_TYPE_Index</relatedStateVariable>");
|
||||
out.println(" </argument>");
|
||||
out.println(" <argument>");
|
||||
out.println(" <name>RequestedCount</name>");
|
||||
out.println(" <direction>in</direction>");
|
||||
out.println(" <relatedStateVariable>A_ARG_TYPE_Count</relatedStateVariable>");
|
||||
out.println(" </argument>");
|
||||
out.println(" <argument>");
|
||||
out.println(" <name>SortCriteria</name>");
|
||||
out.println(" <direction>in</direction>");
|
||||
out.println(" <relatedStateVariable>A_ARG_TYPE_SortCriteria</relatedStateVariable>");
|
||||
out.println(" </argument>");
|
||||
out.println(" <argument>");
|
||||
out.println(" <name>Result</name>");
|
||||
out.println(" <direction>out</direction>");
|
||||
out.println(" <relatedStateVariable>A_ARG_TYPE_Result</relatedStateVariable>");
|
||||
out.println(" </argument>");
|
||||
out.println(" <argument>");
|
||||
out.println(" <name>NumberReturned</name>");
|
||||
out.println(" <direction>out</direction>");
|
||||
out.println(" <relatedStateVariable>A_ARG_TYPE_Count</relatedStateVariable>");
|
||||
out.println(" </argument>");
|
||||
out.println(" <argument>");
|
||||
out.println(" <name>TotalMatches</name>");
|
||||
out.println(" <direction>out</direction>");
|
||||
out.println(" <relatedStateVariable>A_ARG_TYPE_Count</relatedStateVariable>");
|
||||
out.println(" </argument>");
|
||||
out.println(" <argument>");
|
||||
out.println(" <name>UpdateID</name>");
|
||||
out.println(" <direction>out</direction>");
|
||||
out.println(" <relatedStateVariable>A_ARG_TYPE_UpdateID</relatedStateVariable>");
|
||||
out.println(" </argument>");
|
||||
out.println(" </argumentList>");
|
||||
out.println(" </action>");*/
|
||||
|
||||
/*out.println(" <action>");
|
||||
out.println(" <Optional/>");
|
||||
out.println(" <name>CreateObject</name>");
|
||||
out.println(" <argumentList>");
|
||||
out.println(" <argument>");
|
||||
out.println(" <name>ContainerID</name>");
|
||||
out.println(" <direction>in</direction>");
|
||||
out.println(" <relatedStateVariable>A_ARG_TYPE_ObjectID</relatedStateVariable>");
|
||||
out.println(" </argument>");
|
||||
out.println(" <argument>");
|
||||
out.println(" <name>Elements</name>");
|
||||
out.println(" <direction>in</direction>");
|
||||
out.println(" <relatedStateVariable>A_ARG_TYPE_Result</relatedStateVariable>");
|
||||
out.println(" </argument>");
|
||||
out.println(" <argument>");
|
||||
out.println(" <name>ObjectID</name>");
|
||||
out.println(" <direction>out</direction>");
|
||||
out.println(" <relatedStateVariable>A_ARG_TYPE_ObjectID</relatedStateVariable>");
|
||||
out.println(" </argument>");
|
||||
out.println(" <argument>");
|
||||
out.println(" <name>Result</name>");
|
||||
out.println(" <direction>out</direction>");
|
||||
out.println(" <relatedStateVariable>A_ARG_TYPE_Result</relatedStateVariable>");
|
||||
out.println(" </argument>");
|
||||
out.println(" </argumentList>");
|
||||
out.println(" </action>");*/
|
||||
|
||||
/*out.println(" <action>");
|
||||
out.println(" <Optional/>");
|
||||
out.println(" <name>DestroyObject</name>");
|
||||
out.println(" <argumentList>");
|
||||
out.println(" <argument>");
|
||||
out.println(" <name>ObjectID</name>");
|
||||
out.println(" <direction>in</direction>");
|
||||
out.println(" <relatedStateVariable>A_ARG_TYPE_ObjectID</relatedStateVariable>");
|
||||
out.println(" </argument>");
|
||||
out.println(" </argumentList>");
|
||||
out.println(" </action>");*/
|
||||
|
||||
/*out.println(" <action>");
|
||||
out.println(" <Optional/>");
|
||||
out.println(" <name>UpdateObject</name>");
|
||||
out.println(" <argumentList>");
|
||||
out.println(" <argument>");
|
||||
out.println(" <name>ObjectID</name>");
|
||||
out.println(" <direction>in</direction>");
|
||||
out.println(" <relatedStateVariable>A_ARG_TYPE_ObjectID</relatedStateVariable>");
|
||||
out.println(" </argument>");
|
||||
out.println(" <argument>");
|
||||
out.println(" <name>CurrentTagValue</name>");
|
||||
out.println(" <direction>in</direction>");
|
||||
out.println(" <relatedStateVariable>A_ARG_TYPE_TagValueList </relatedStateVariable>");
|
||||
out.println(" </argument>");
|
||||
out.println(" <argument>");
|
||||
out.println(" <name>NewTagValue</name>");
|
||||
out.println(" <direction>in</direction>");
|
||||
out.println(" <relatedStateVariable>A_ARG_TYPE_TagValueList </relatedStateVariable>");
|
||||
out.println(" </argument>");
|
||||
out.println(" </argumentList>");
|
||||
out.println(" </action>");*/
|
||||
|
||||
/*out.println(" <action>");
|
||||
out.println(" <Optional/>");
|
||||
out.println(" <name>ImportResource</name>");
|
||||
out.println(" <argumentList>");
|
||||
out.println(" <argument>");
|
||||
out.println(" <name>SourceURI</name>");
|
||||
out.println(" <direction>in</direction>");
|
||||
out.println(" <relatedStateVariable>A_ARG_TYPE_URI</relatedStateVariable>");
|
||||
out.println(" </argument>");
|
||||
out.println(" <argument>");
|
||||
out.println(" <name>DestinationURI</name>");
|
||||
out.println(" <direction>in</direction>");
|
||||
out.println(" <relatedStateVariable>A_ARG_TYPE_URI</relatedStateVariable>");
|
||||
out.println(" </argument>");
|
||||
out.println(" <argument>");
|
||||
out.println(" <name>TransferID</name>");
|
||||
out.println(" <direction>out</direction>");
|
||||
out.println(" <relatedStateVariable>A_ARG_TYPE_TransferID </relatedStateVariable>");
|
||||
out.println(" </argument>");
|
||||
out.println(" </argumentList>");
|
||||
out.println(" </action>");*/
|
||||
|
||||
/*out.println(" <action>");
|
||||
out.println(" <Optional/>");
|
||||
out.println(" <name>ExportResource</name>");
|
||||
out.println(" <argumentList>");
|
||||
out.println(" <argument>");
|
||||
out.println(" <name>SourceURI</name>");
|
||||
out.println(" <direction>in</direction>");
|
||||
out.println(" <relatedStateVariable>A_ARG_TYPE_URI</relatedStateVariable>");
|
||||
out.println(" </argument>");
|
||||
out.println(" <argument>");
|
||||
out.println(" <name>DestinationURI</name>");
|
||||
out.println(" <direction>in</direction>");
|
||||
out.println(" <relatedStateVariable>A_ARG_TYPE_URI</relatedStateVariable>");
|
||||
out.println(" </argument>");
|
||||
out.println(" <argument>");
|
||||
out.println(" <name>TransferID</name>");
|
||||
out.println(" <direction>out</direction>");
|
||||
out.println(" <relatedStateVariable>A_ARG_TYPE_TransferID </relatedStateVariable>");
|
||||
out.println(" </argument>");
|
||||
out.println(" </argumentList>");
|
||||
out.println(" </action>");*/
|
||||
|
||||
/*out.println(" <action>");
|
||||
out.println(" <Optional/>");
|
||||
out.println(" <name>StopTransferResource</name>");
|
||||
out.println(" <argumentList>");
|
||||
out.println(" <argument>");
|
||||
out.println(" <name>TransferID</name>");
|
||||
out.println(" <direction>in</direction>");
|
||||
out.println(" <relatedStateVariable>A_ARG_TYPE_TransferID </relatedStateVariable>");
|
||||
out.println(" </argument>");
|
||||
out.println(" </argumentList>");
|
||||
out.println(" </action>");*/
|
||||
|
||||
/*out.println(" <action>");
|
||||
out.println(" <Optional/>");
|
||||
out.println(" <name>GetTransferProgress</name>");
|
||||
out.println(" <argumentList>");
|
||||
out.println(" <argument>");
|
||||
out.println(" <name>TransferID</name>");
|
||||
out.println(" <direction>in</direction>");
|
||||
out.println(" <relatedStateVariable>A_ARG_TYPE_TransferID </relatedStateVariable>");
|
||||
out.println(" </argument>");
|
||||
out.println(" <argument>");
|
||||
out.println(" <name>TransferStatus</name>");
|
||||
out.println(" <direction>out</direction>");
|
||||
out.println(" <relatedStateVariable>A_ARG_TYPE_TransferStatus </relatedStateVariable>");
|
||||
out.println(" </argument>");
|
||||
out.println(" <argument>");
|
||||
out.println(" <name>TransferLength</name>");
|
||||
out.println(" <direction>out</direction>");
|
||||
out.println(" <relatedStateVariable>A_ARG_TYPE_TransferLength </relatedStateVariable>");
|
||||
out.println(" </argument>");
|
||||
out.println(" <argument>");
|
||||
out.println(" <name>TransferTotal</name>");
|
||||
out.println(" <direction>out</direction>");
|
||||
out.println(" <relatedStateVariable>A_ARG_TYPE_TransferTotal</relatedStateVariable>");
|
||||
out.println(" </argument>");
|
||||
out.println(" </argumentList>");
|
||||
out.println(" </action>");*/
|
||||
|
||||
/*out.println(" <action>");
|
||||
out.println(" <Optional/>");
|
||||
out.println(" <name>DeleteResource</name>");
|
||||
out.println(" <argumentList>");
|
||||
out.println(" <argument>");
|
||||
out.println(" <name>ResourceURI</name>");
|
||||
out.println(" <direction>in</direction>");
|
||||
out.println(" <relatedStateVariable>A_ARG_TYPE_URI</relatedStateVariable>");
|
||||
out.println(" </argument>");
|
||||
out.println(" </argumentList>");
|
||||
out.println(" </action>");*/
|
||||
|
||||
/*out.println(" <action>");
|
||||
out.println(" <Optional/>");
|
||||
out.println(" <name>CreateReference</name>");
|
||||
out.println(" <argumentList>");
|
||||
out.println(" <argument>");
|
||||
out.println(" <name>ContainerID</name>");
|
||||
out.println(" <direction>in</direction>");
|
||||
out.println(" <relatedStateVariable>A_ARG_TYPE_ObjectID</relatedStateVariable>");
|
||||
out.println(" </argument>");
|
||||
out.println(" <argument>");
|
||||
out.println(" <name>ObjectID</name>");
|
||||
out.println(" <direction>in</direction>");
|
||||
out.println(" <relatedStateVariable>A_ARG_TYPE_ObjectID</relatedStateVariable>");
|
||||
out.println(" </argument>");
|
||||
out.println(" <argument>");
|
||||
out.println(" <name>NewID</name>");
|
||||
out.println(" <direction>out</direction>");
|
||||
out.println(" <relatedStateVariable>A_ARG_TYPE_ObjectID</relatedStateVariable>");
|
||||
out.println(" </argument>");
|
||||
out.println(" </argumentList>");
|
||||
out.println(" </action>");*/
|
||||
|
||||
out.println(" </actionList>");
|
||||
out.println("</scpd>");
|
||||
}
|
||||
}
|
||||
112
src/zutil/net/ws/WSInterface.java
Normal file
112
src/zutil/net/ws/WSInterface.java
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
package zutil.net.ws;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
/**
|
||||
*
|
||||
* Specifies web service parameter names and other things.
|
||||
* Example:
|
||||
* <pre>
|
||||
* private static class Test implements WSInterface{
|
||||
* public Test(){}
|
||||
*
|
||||
* @WSDocumentation("blabla")
|
||||
* @WSDLParamDocumentation("olle = an variable?")
|
||||
* public void pubZ(
|
||||
* @WSParamName("olle") int lol)
|
||||
* throws Exception{
|
||||
* ....
|
||||
* }
|
||||
*
|
||||
* @WSReturnName("param")
|
||||
* public String pubA(
|
||||
* @WSParamName(value="lol", optional=true) String lol)
|
||||
* throws Exception{
|
||||
* ....
|
||||
* }
|
||||
*
|
||||
* @WSDisabled()
|
||||
* public void privaZ(....){
|
||||
* ...
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* </pre>
|
||||
* @author Ziver
|
||||
*/
|
||||
public interface WSInterface {
|
||||
/**
|
||||
* Annotation that assigns a name to an parameters
|
||||
* in an method.
|
||||
*
|
||||
* @author Ziver
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.PARAMETER)
|
||||
public @interface WSParamName {
|
||||
String value();
|
||||
boolean optional() default false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Annotation that assigns a name to the return value
|
||||
* in an method.
|
||||
*
|
||||
* @author Ziver
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.METHOD)
|
||||
public @interface WSReturnName {
|
||||
String value();
|
||||
}
|
||||
|
||||
/**
|
||||
* Disables publication of the given method
|
||||
*
|
||||
* @author Ziver
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.METHOD)
|
||||
public @interface WSDisabled { }
|
||||
|
||||
/**
|
||||
* Method or Parameter comments for the WSDL.
|
||||
* These comments are put in the message part of the WSDL
|
||||
*
|
||||
* @author Ziver
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface WSDocumentation{
|
||||
String value();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parameter comments for the WSDL.
|
||||
* These comments are put in the message part of the WSDL
|
||||
*
|
||||
* @author Ziver
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface WSParamDocumentation{
|
||||
String value();
|
||||
}
|
||||
|
||||
/**
|
||||
* This method will be used in the header.
|
||||
*
|
||||
* @author Ziver
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.METHOD)
|
||||
public @interface WSHeader { }
|
||||
|
||||
/**
|
||||
* Specifies the name space for the method.
|
||||
*
|
||||
* @author Ziver
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.METHOD)
|
||||
public @interface WSNamespace {
|
||||
String value();
|
||||
}
|
||||
}
|
||||
175
src/zutil/net/ws/WSMethodDef.java
Normal file
175
src/zutil/net/ws/WSMethodDef.java
Normal file
|
|
@ -0,0 +1,175 @@
|
|||
package zutil.net.ws;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import zutil.net.ws.WSInterface.WSDocumentation;
|
||||
|
||||
// TODO: Header parameters
|
||||
public class WSMethodDef {
|
||||
/** A list of input parameters **/
|
||||
private ArrayList<WSParameterDef> inputs;
|
||||
/** A List of return parameters of the method **/
|
||||
private ArrayList<WSParameterDef> outputs;
|
||||
/** A List of exceptions that this method throws **/
|
||||
private ArrayList<Class<?>> exceptions;
|
||||
/** The real method that this class represent, can be null if its a remote method **/
|
||||
private Method method;
|
||||
/** Documentation of the method **/
|
||||
private String doc;
|
||||
/** The published name of the method **/
|
||||
private String name;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @param me is a method in a class that implements WSInterface
|
||||
*/
|
||||
public WSMethodDef(Method me) {
|
||||
if(!WSInterface.class.isAssignableFrom(me.getDeclaringClass()) )
|
||||
throw new ClassCastException("Declaring class does not implement WSInterface!");
|
||||
method = me;
|
||||
inputs = new ArrayList<WSParameterDef>();
|
||||
outputs = new ArrayList<WSParameterDef>();
|
||||
exceptions = new ArrayList<Class<?>>();
|
||||
name = method.getName();
|
||||
|
||||
//***** Documentation
|
||||
WSDocumentation tmpDoc = method.getAnnotation(WSInterface.WSDocumentation.class);
|
||||
if(tmpDoc != null){
|
||||
doc = tmpDoc.value();
|
||||
}
|
||||
//***** Exceptions
|
||||
for( Class<?> exc : method.getExceptionTypes() ){
|
||||
exceptions.add( exc );
|
||||
}
|
||||
//********* Get the input parameter names **********
|
||||
Annotation[][] paramAnnotation = method.getParameterAnnotations();
|
||||
|
||||
for(int i=0; i<paramAnnotation.length ;i++){
|
||||
WSParameterDef param = new WSParameterDef();
|
||||
for(Annotation annotation : paramAnnotation[i]){
|
||||
if(annotation instanceof WSInterface.WSParamName){
|
||||
WSInterface.WSParamName paramName = (WSInterface.WSParamName) annotation;
|
||||
param.name = paramName.value();
|
||||
param.optional = paramName.optional();
|
||||
}
|
||||
}
|
||||
// if no name was found then use default
|
||||
if(param.name == null)
|
||||
param.name = "args"+i;
|
||||
|
||||
inputs.add( param );
|
||||
}
|
||||
|
||||
//******** The return parameter name ************
|
||||
WSInterface.WSReturnName returnName = method.getAnnotation(WSInterface.WSReturnName.class);
|
||||
if( WSReturnValueList.class.isAssignableFrom( method.getReturnType() ) ){
|
||||
Class<?> retClass = method.getReturnType();
|
||||
Field[] fields = retClass.getFields();
|
||||
for(int i=0; i<fields.length ;i++){
|
||||
WSParameterDef ret_param = new WSParameterDef();
|
||||
WSReturnValueList.WSValueName retValName = fields[i]
|
||||
.getAnnotation( WSReturnValueList.WSValueName.class );
|
||||
if(retValName != null) ret_param.name = retValName.value();
|
||||
else ret_param.name = fields[i].getName();
|
||||
ret_param.paramClass = fields[i].getType();
|
||||
outputs.add( ret_param );
|
||||
}
|
||||
}
|
||||
else{
|
||||
WSParameterDef ret_param = new WSParameterDef();
|
||||
if(returnName != null) ret_param.name = returnName.value();
|
||||
else ret_param.name = "return";
|
||||
ret_param.paramClass = method.getReturnType();
|
||||
outputs.add( ret_param );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the published name of the method
|
||||
*/
|
||||
public String getName(){
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the number of exceptions this method throws
|
||||
*/
|
||||
public int exceptionCount(){
|
||||
return exceptions.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a list of exceptions this method throws
|
||||
*/
|
||||
public List<Class<?>> getExceptions(){
|
||||
return exceptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the number of parameters for this method
|
||||
*/
|
||||
public int inputCount(){
|
||||
return inputs.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a list of input parameters
|
||||
*/
|
||||
public List<WSParameterDef> getInputs(){
|
||||
return inputs;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the number of parameters for this method
|
||||
*/
|
||||
public int outputCount(){
|
||||
return outputs.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a list of input parameters
|
||||
*/
|
||||
public List<WSParameterDef> getOutputs(){
|
||||
return outputs;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Documentation of the method if one exists or else null
|
||||
*/
|
||||
public String getDocumentation(){
|
||||
return doc;
|
||||
}
|
||||
|
||||
|
||||
public String toString(){
|
||||
StringBuilder tmp = new StringBuilder();
|
||||
boolean first = true;
|
||||
tmp.append(name).append("(");
|
||||
for(WSParameterDef param : inputs){
|
||||
if(first)
|
||||
first = false;
|
||||
else
|
||||
tmp.append(" ,");
|
||||
tmp.append(param.paramClass.getSimpleName());
|
||||
tmp.append(" ");
|
||||
tmp.append(param.name);
|
||||
}
|
||||
tmp.append(") => ");
|
||||
first = true;
|
||||
for(WSParameterDef param : outputs){
|
||||
if(first)
|
||||
first = false;
|
||||
else
|
||||
tmp.append(" ,");
|
||||
tmp.append(param.paramClass.getSimpleName());
|
||||
tmp.append(" ");
|
||||
tmp.append(param.name);
|
||||
}
|
||||
return tmp.toString();
|
||||
}
|
||||
}
|
||||
56
src/zutil/net/ws/WSObject.java
Normal file
56
src/zutil/net/ws/WSObject.java
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
package zutil.net.ws;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* This class is used as an return Object for a web service.
|
||||
* If an class implements this interface then it can return
|
||||
* multiple values through the SOAPInterface. Example:
|
||||
* <pre>
|
||||
* private static class TestObject implements WSObject{
|
||||
* @SOAPFieldName("name")
|
||||
* public String name;
|
||||
* @SOAPFieldName("lastname")
|
||||
* public String lastname;
|
||||
*
|
||||
* public TestObject(String n, String l){
|
||||
* name = n;
|
||||
* lastname = l;
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* @author Ziver
|
||||
*
|
||||
*/
|
||||
public interface WSObject{
|
||||
/**
|
||||
* Specifies the name of a field.
|
||||
* The fields that are available in the service should
|
||||
* be declared public.
|
||||
*
|
||||
* @author Ziver
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.FIELD)
|
||||
public @interface WSFieldName {
|
||||
String value();
|
||||
boolean optional() default false;
|
||||
}
|
||||
|
||||
/**
|
||||
* This generates an documentation tag in the
|
||||
* WSDL for the object type
|
||||
*
|
||||
* @author Ziver
|
||||
*/
|
||||
/*
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.TYPE)
|
||||
public @interface WSDLDocumentation {
|
||||
String value();
|
||||
}*/
|
||||
}
|
||||
43
src/zutil/net/ws/WSParameterDef.java
Normal file
43
src/zutil/net/ws/WSParameterDef.java
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
package zutil.net.ws;
|
||||
|
||||
public class WSParameterDef{
|
||||
/** The class type of the parameter **/
|
||||
protected Class<?> paramClass;
|
||||
/** The web service name of the parameter **/
|
||||
protected String name;
|
||||
/** Developer documentation **/
|
||||
protected String doc;
|
||||
/** If this parameter is optional **/
|
||||
protected boolean optional;
|
||||
/** Is it an header parameter **/
|
||||
//boolean header;
|
||||
|
||||
|
||||
public Class<?> getParamClass() {
|
||||
return paramClass;
|
||||
}
|
||||
protected void setParamClass(Class<?> paramClass) {
|
||||
this.paramClass = paramClass;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
protected void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getDoc() {
|
||||
return doc;
|
||||
}
|
||||
protected void setDoc(String doc) {
|
||||
this.doc = doc;
|
||||
}
|
||||
|
||||
public boolean isOptional() {
|
||||
return optional;
|
||||
}
|
||||
protected void setOptional(boolean optional) {
|
||||
this.optional = optional;
|
||||
}
|
||||
}
|
||||
57
src/zutil/net/ws/WSReturnValueList.java
Normal file
57
src/zutil/net/ws/WSReturnValueList.java
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
package zutil.net.ws;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
/**
|
||||
* This interface is for returning multiple object.
|
||||
* All the public fields in the class that implements
|
||||
* this class will be set as return values. And the
|
||||
* implementing class will be transparent.
|
||||
*
|
||||
* @author Ziver
|
||||
*
|
||||
*/
|
||||
public class WSReturnValueList {
|
||||
|
||||
/**
|
||||
* Method comments for the WSDL.
|
||||
* These comments are put in the operation part of the WSDL
|
||||
*
|
||||
* @author Ziver
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface WSDLDocumentation{
|
||||
String value();
|
||||
}
|
||||
|
||||
/**
|
||||
* Disables publication of the given field.
|
||||
*
|
||||
* @author Ziver
|
||||
*/
|
||||
/*@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.METHOD)
|
||||
public @interface SOAPDisabledValue { }*/
|
||||
|
||||
/**
|
||||
* Annotation that assigns a name to the return value
|
||||
* to the field.
|
||||
*
|
||||
* @author Ziver
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.FIELD)
|
||||
public @interface WSValueName {
|
||||
String value();
|
||||
}
|
||||
|
||||
|
||||
public Object getValue(Field field) throws IllegalArgumentException, IllegalAccessException{
|
||||
return field.get(this);
|
||||
}
|
||||
}
|
||||
|
||||
66
src/zutil/net/ws/WebServiceDef.java
Normal file
66
src/zutil/net/ws/WebServiceDef.java
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
package zutil.net.ws;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.wsdl.WSDLException;
|
||||
|
||||
public class WebServiceDef {
|
||||
/** A map of methods in this Service **/
|
||||
private HashMap<String,WSMethodDef> methods;
|
||||
/** URL of the Service **/
|
||||
//private String url;
|
||||
/** Namespace of the service **/
|
||||
//private String namespace;
|
||||
/** Name of the web service **/
|
||||
private String name;
|
||||
/** This is the WSInterface class **/
|
||||
private Class<? extends WSInterface> intf;
|
||||
|
||||
|
||||
public WebServiceDef(Class<? extends WSInterface> intf) throws WSDLException{
|
||||
this.intf = intf;
|
||||
methods = new HashMap<String,WSMethodDef>();
|
||||
name = intf.getSimpleName();
|
||||
|
||||
for(Method m : intf.getDeclaredMethods()){
|
||||
// check for public methods
|
||||
if((m.getModifiers() & Modifier.PUBLIC) > 0 &&
|
||||
!m.isAnnotationPresent(WSInterface.WSDisabled.class)){
|
||||
WSMethodDef method = new WSMethodDef(m);
|
||||
methods.put(method.getName(), method);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the class that defines this web service
|
||||
*/
|
||||
public Class<? extends WSInterface> getWSClass(){
|
||||
return intf;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the name of the Service (usually the class name of the WSInterface)
|
||||
*/
|
||||
public String getName(){
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a Set of all the method names
|
||||
*/
|
||||
public Set<String> getMethodNames(){
|
||||
return methods.keySet();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return all the methods
|
||||
*/
|
||||
public Collection<WSMethodDef> getMethods(){
|
||||
return methods.values();
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue