rmi java 防火墙_基于Servlet实现RMI突破防火墙

package com.mypack.web.rmi;

import java.io.DataInputStream;

import java.io.DataOutputStream;

import java.io.EOFException;

import java.io.IOException;

import java.io.OutputStream;

import java.io.PrintWriter;

import java.net.InetAddress;

import java.net.Socket;

import java.net.URL;

import java.net.UnknownHostException;

import java.rmi.Naming;

import java.rmi.RMISecurityManager;

import java.rmi.RemoteException;

import java.rmi.registry.LocateRegistry;

import java.rmi.server.RMIClassLoader;

import java.rmi.server.UnicastRemoteObject;

import java.util.Hashtable;

import javax.servlet.ServletConfig;

import javax.servlet.ServletException;

import javax.servlet.http.HttpServlet;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

/**

* The default RMI socket factory contains several "fallback" mechanisms which

* enable an RMI client to communicate with a remote server. When an RMI client

* initiates contact with a remote server, it attempts to establish a connection

* using each of the following protocols in turn, until one succeeds:

*

* 1. Direct TCP connection. 2. Direct HTTP connection. 3. Proxy connection

* (SOCKS or HTTP). 4. Connection on port 80 over HTTP to a CGI script. 5. Proxy

* HTTP connection to CGI script on port 80.

*

* The RMI ServletHandler can be used as replacement for the java-rmi.cgi script

* that comes with the Java Development Kit (and is invoked in protocols 4 and 5

* above). The java-rmi.cgi script and the ServletHandler both function as proxy

* applications that forward remote calls embedded in HTTP to local RMI servers

* which service these calls. The RMI ServletHandler enables RMI to tunnel

* remote method calls over HTTP more efficiently than the existing java-rmi.cgi

* script. The ServletHandler is only loaded once from the servlet

* administration utility. The script, java-rmi.cgi, is executed once every

* remote call.

*

* The ServletHandler class contains methods for executing as a Java servlet

* extension. Because the RMI protocol only makes use of the HTTP post command,

* the ServletHandler only supports the doPost

* HttpServlet method. The doPost method of this class

* interprets a servlet request's query string as a command of the form

* "=". These commands are represented by the abstract

* interface, RMICommandHandler. Once the doPost

* method has parsed the requested command, it calls the execute method on one

* of several command handlers in the commands array.

*

* The command that actually proxies remote calls is the

* ServletForwardCommand. When the execute method is invoked on the

* ServletForwardCommand, the command will open a connection on a local port

* specified by its param parameter and will proceed to write the

* body of the relevant post request into this connection. It is assumed that an

* RMI server (e.g. SampleRMIServer) is listening on the local port, "param."

* The "forward" command will then read the RMI server's response and send this

* information back to the RMI client as the body of the response to the HTTP

* post method.

*

* Because the ServletHandler uses a local socket to proxy remote calls, the

* servlet has the ability to forward remote calls to local RMI objects that

* reside in the ServletVM or outside of it.

*

* Servlet documentation may be found at the following location:

*

* http://jserv.javasoft.com/products/java-server/documentation/

* webserver1.0.2/apidoc/Package-javax.servlet.http.html

*/

public class ServletHandler extends HttpServlet implements Runnable {

/* Variables to hold optional configuration properties. */

/**

* serialVersionUID

*/

private static final long serialVersionUID = 1L;

/** codebase from which this servlet will load remote objects. */

protected static String initialServerCodebase = null;

/** name of RMI server class to be created in init method */

protected static String initialServerClass = null;

/** name of RMI server class to be created in init method */

protected static String initialServerBindName = null;

/**

* RMICommandHandler is the abstraction for an object that handles a

* particular supported command (for example the "forward" command

* "forwards" call information to a remote server on the local machine).

*

* The command handler is only used by the ServletHandler so the interface

* is protected.

*/

protected interface RMICommandHandler {

/**

* Return the string form of the command to be recognized in the query

* string.

*/

public String getName();

/**

* Execute the command with the given string as parameter.

*/

public void execute(HttpServletRequest req, HttpServletResponse res,

String param) throws ServletClientException,

ServletServerException, IOException;

}

/**

* List of handlers for supported commands. A new command will be created

* for every service request

*/

private static RMICommandHandler commands[] = new RMICommandHandler[] {

new ServletForwardCommand(), new ServletGethostnameCommand(),

new ServletPingCommand(), new ServletTryHostnameCommand() };

/* construct table mapping command strings to handlers */

private static Hashtable commandLookup;

static {

commandLookup = new Hashtable();

for (int i = 0; i < commands.length; ++i)

commandLookup.put(commands[i].getName(), commands[i]);

}

/**

* Once loaded, Java Servlets continue to run until they are unloaded or the

* webserver is stopped. This example takes advantage of the extended

* Servlet life-cycle and runs a remote object in the Servlet VM.

*

* To initialize this remote object the Servlet Administrator should specify

* a set of parameters which will be used to download and install an initial

* remote server (see readme.txt).

*

* If configuration parameters are valid (not blank), the servlet will

* attempt to load and start a remote object and a registry in which the

* object may be bound.

*

* @param config

* Standard configuration object for an http servlet.

*

* @exception ServletException

* Calling super.init(config) may cause a

* servlet exception to be thrown.

*/

public void init(ServletConfig config) throws ServletException {

super.init(config);

try {

setConfigParameters(config);

if (!verifyConfigParameters()) {

// dont export any objects.

System.err.println("Some optional parameters not set, "

+ "remote object not exported; "

+ "ServletHandler is runnning.");

return;

}

/*

* RMI requires that a local security manager be responsible for the

* method invocations from remote clients - we need to make sure a

* security manager is installed.

*/

if (System.getSecurityManager() == null) {

System.setSecurityManager(new RMISecurityManager());

}

// create a registry if one is not running already.

try {

LocateRegistry.createRegistry(1099);

} catch (java.rmi.server.ExportException ee) {

// registry already exists, we'll just use it.

} catch (RemoteException re) {

System.err.println(re.getMessage());

re.printStackTrace();

}

/**

* Download and create a server object in a thread so we do not

* interfere with other servlets. Allow init method to return more

* quickly.

*/

(new Thread(this)).start();

System.out.println("RMI Servlet Handler loaded sucessfully.");

} catch (Exception e) {

System.err.println("Exception thrown in RMI ServletHandler: "

+ e.getMessage());

e.printStackTrace();

}

}

/**

* Create the sample RMI server.

*/

public void run() {

try {

UnicastRemoteObject server = createRemoteObjectUsingDownloadedClass();

if (server != null) {

Naming.rebind(initialServerBindName, server);

System.err.println("Remote object created successfully.");

}

} catch (Exception e) {

System.err.println("Exception received while intalling object:");

System.err.println(e.getMessage());

e.printStackTrace();

}

}

/**

* Load and export an initial remote object. The implementation class for

* this remote object should not be loaded from the servlet's class path;

* instead it should be loaded from a URL location that will be accessible

* from a remote client. In the case of this example, that location will be

* initialServerCodebase

*/

UnicastRemoteObject createRemoteObjectUsingDownloadedClass()

throws Exception {

UnicastRemoteObject server = null;

Class serverClass = null;

int MAX_RETRY = 5;

int retry = 0;

int sleep = 2000;

while ((retry < MAX_RETRY) && (serverClass == null)) {

try {

System.err.println("Attempting to load remote class...");

serverClass = RMIClassLoader.loadClass(new URL(

initialServerCodebase), initialServerClass);

// Before we instantiate the obj. make sure it

// is a UnicastRemoteObject.

if (!Class.forName("java.rmi.server.UnicastRemoteObject")

.isAssignableFrom(serverClass)) {

System.err.println("This example requires an "

+ " instance of UnicastRemoteObject,"

+ " remote object not exported.");

} else {

System.out.println("Server class loaded successfully...");

server = ((UnicastRemoteObject) serverClass.newInstance());

}

} catch (java.lang.ClassNotFoundException cnfe) {

retry++;

/**

* The class for the remote object could not be loaded, perhaps

* the webserver has not finished initializing itself yet. Try

* to load the class a few more times...

*/

if (retry >= MAX_RETRY) {

System.err.println("Failed to load remote server "

+ " class. Remote object not " + " exported... ");

} else {

System.err.println("Could not load remote class, "

+ "trying again...");

try {

Thread.sleep(sleep);

} catch (InterruptedException ie) {

}

continue;

}

}

}

return server;

}

/*

* NOTE: If you are using JDK1.2Beta4 or later, it is recommended that you

* provide your servlet with a destroy method that will unexport any remote

* objects that your servlet ever exports. As mentioned in the readme file

* for this example, it is not possible to unexport remote objects in

* JDK1.1.x; there is no method in the RMI 1.1 public API that will perform

* this task. To restart remote objects in the servlet VM, you will have to

* restart your webserver. In JDK1.2x, the methods to unexport a remote

* object are as follows:

*

* java.rmi.activation.Activatable. unexportObject(Remote obj, boolean

* force) java.rmi.server.UnicastRemoteObject. unexportObject(Remote obj,

* boolean force)

*/

/**

* Execute the command given in the servlet request query string. The string

* before the first '=' in the queryString is interpreted as the command

* name, and the string after the first '=' is the parameters to the

* command.

*

* @param req

* HTTP servlet request, contains incoming command and arguments

* @param res

* HTTP servlet response

* @exception ServletException

* and IOException when invoking methods of

* req or res.

*/

public void doPost(HttpServletRequest req, HttpServletResponse res)

throws ServletException, IOException {

try {

// Command and parameter for this POST request.

String queryString = req.getQueryString();

String command, param;

int delim = queryString.indexOf("=");

if (delim == -1) {

command = queryString;

param = "";

} else {

command = queryString.substring(0, delim);

param = queryString.substring(delim + 1);

}

System.out.println("command: " + command);

System.out.println("param: " + param);

// lookup command to execute on the client's behalf

RMICommandHandler handler = (RMICommandHandler) commandLookup

.get(command);

// execute the command

if (handler != null)

try {

handler.execute(req, res, param);

} catch (ServletClientException e) {

returnClientError(res, "client error: " + e.getMessage());

e.printStackTrace();

} catch (ServletServerException e) {

returnServerError(res, "internal server error: "

+ e.getMessage());

e.printStackTrace();

}

else

returnClientError(res, "invalid command: " + command);

} catch (Exception e) {

returnServerError(res, "internal error: " + e.getMessage());

e.printStackTrace();

}

}

/**

* Provide more intelligible errors for methods that are likely to be

* called. Let unsupported HTTP "do*" methods result in an error generated

* by the super class.

*

* @param req

* http Servlet request, contains incoming command and arguments

*

* @param res

* http Servlet response

*

* @exception ServletException

* and IOException when invoking methods of

* req or res.

*/

public void doGet(HttpServletRequest req, HttpServletResponse res)

throws ServletException, IOException {

returnClientError(res, "GET Operation not supported: "

+ "Can only forward POST requests.");

}

public void doPut(HttpServletRequest req, HttpServletResponse res)

throws ServletException, IOException {

returnClientError(res, "PUT Operation not supported: "

+ "Can only forward POST requests.");

}

public String getServletInfo() {

return "RMI Call Forwarding Servlet Servlet.
\n";

}

/**

* Return an HTML error message indicating there was error in the client's

* request.

*

* @param res

* Servlet response object through which message

* will be written to the client which invoked one of this

* servlet's methods.

* @param message

* Error message to be written to client.

*/

private static void returnClientError(HttpServletResponse res,

String message) throws IOException {

res

.sendError(HttpServletResponse.SC_BAD_REQUEST, "

"

+ "

Java RMI Client Error" + ""

+ "

" + "

Java RMI Client Error

" + message

+ "");

System.err.println(HttpServletResponse.SC_BAD_REQUEST

+ "Java RMI Client Error" + message);

}

/**

* Return an HTML error message indicating an internal error occurred here

* on the server.

*

* @param res

* Servlet response object through which message

* will be written to the servlet client.

* @param message

* Error message to be written to servlet client.

*/

private static void returnServerError(HttpServletResponse res,

String message) throws IOException {

res.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,

"

" + "Java RMI Server Error"

+ "" + "

"

+ "

Java RMI Server Error

" + message

+ "");

System.err.println(HttpServletResponse.SC_INTERNAL_SERVER_ERROR

+ "Java RMI Server Error: " + message);

}

/**

* Retrieve parameters from servlet configuration object.

*

* @param config

* Standard configuration object for an HTTP servlet.

*/

protected synchronized void setConfigParameters(ServletConfig config) {

try {

initialServerCodebase = config

.getInitParameter("rmiservlethandler.initialServerCodebase");

initialServerClass = config

.getInitParameter("rmiservlethandler.initialServerClass");

initialServerBindName = config

.getInitParameter("rmiservlethandler.initialServerBindName");

} catch (Exception e) {

System.err.println("");

System.err.println("Could not access init parameter:");

System.err.println(e.getMessage());

e.printStackTrace();

}

}

/**

* Ensure that servlet configuration parameters are valid.

*

* @return true if all relevant configuration parameters are

* valid (i.e. not "") false otherwise.

*/

protected synchronized boolean verifyConfigParameters() {

return ((verifyParameter("rmiservlethandler.initialServerClass ",

initialServerClass))

&& (verifyParameter("rmiservlethandler.initialServerBindName ",

initialServerBindName)) && (verifyParameter(

"rmiservlethandler.initialServerCodebase",

initialServerCodebase)));

}

/**

* Verify that a single parameter is valid.

*

* @return true if the parameter is valid.

*/

protected boolean verifyParameter(String parameterName, String parameter) {

if ((parameter == null) || (parameter.equals(""))) {

System.err.println("optional parameter is invalid and "

+ "will not be used: \n " + parameterName + " = "

+ parameter);

return false;

} else {

System.err.println(parameterName + " " + "valid: " + parameter);

}

return true;

}

/*

* The ServletHandler class is the only object that needs to access the

* CommandHandler subclasses, so we write the commands internal to the

* servlet handler.

*/

/**

* Class that has an execute command to forward request body to local port

* on the server and send server reponse back to client.

*/

protected static class ServletForwardCommand implements RMICommandHandler {

public String getName() {

return "forward";

}

/**

* Execute the forward command. Forwards data from incoming servlet

* request to a port on the local machine. Presumably, an RMI server

* will be reading the data that this method sends.

*

* @param req

* The servlet request.

* @param res

* The servlet response.

* @param param

* Port to which data will be sent.

*/

public void execute(HttpServletRequest req, HttpServletResponse res,

String param) throws ServletClientException,

ServletServerException, IOException {

int port;

try {

port = Integer.parseInt(param);

} catch (NumberFormatException e) {

throw new ServletClientException("invalid port number: "

+ param);

}

if (port <= 0 || port > 0xFFFF)

throw new ServletClientException("invalid port: " + port);

if (port < 1024)

throw new ServletClientException("permission denied for port: "

+ port);

byte buffer[];

Socket socket;

try {

socket = new Socket(InetAddress.getLocalHost(), port);

} catch (IOException e) {

throw new ServletServerException("could not connect to "

+ "local port");

}

// read client's request body

DataInputStream clientIn = new DataInputStream(req.getInputStream());

buffer = new byte[req.getContentLength()];

try {

clientIn.readFully(buffer);

} catch (EOFException e) {

throw new ServletClientException("unexpected EOF "

+ "reading request body");

} catch (IOException e) {

throw new ServletClientException("error reading request"

+ " body");

}

DataOutputStream socketOut = null;

// send to local server in HTTP

try {

socketOut = new DataOutputStream(socket.getOutputStream());

socketOut.writeBytes("POST / HTTP/1.0\r\n");

socketOut.writeBytes("Content-length: "

+ req.getContentLength() + "\r\n\r\n");

socketOut.write(buffer);

socketOut.flush();

} catch (IOException e) {

throw new ServletServerException("error writing to server");

}

// read response from local server

DataInputStream socketIn;

try {

socketIn = new DataInputStream(socket.getInputStream());

} catch (IOException e) {

throw new ServletServerException("error reading from "

+ "server");

}

String key = "Content-length:".toLowerCase();

boolean contentLengthFound = false;

String line;

int responseContentLength = -1;

do {

try {

line = socketIn.readLine();

} catch (IOException e) {

throw new ServletServerException(

"error reading from server");

}

if (line == null)

throw new ServletServerException(

"unexpected EOF reading server response");

if (line.toLowerCase().startsWith(key)) {

if (contentLengthFound)

; // what would we want to do in this case??

responseContentLength = Integer.parseInt(line.substring(

key.length()).trim());

contentLengthFound = true;

}

} while ((line.length() != 0) && (line.charAt(0) != '\r')

&& (line.charAt(0) != '\n'));

if (!contentLengthFound || responseContentLength < 0)

throw new ServletServerException(

"missing or invalid content length in server response");

buffer = new byte[responseContentLength];

try {

socketIn.readFully(buffer);

} catch (EOFException e) {

throw new ServletServerException(

"unexpected EOF reading server response");

} catch (IOException e) {

throw new ServletServerException("error reading from server");

}

// send local server response back to servlet client

res.setStatus(HttpServletResponse.SC_OK);

res.setContentType("application/octet-stream");

res.setContentLength(buffer.length);

try {

OutputStream out = res.getOutputStream();

out.write(buffer);

out.flush();

} catch (IOException e) {

throw new ServletServerException("error writing response");

} finally {

socketOut.close();

socketIn.close();

}

}

}

/**

* Class that has an execute method to return the host name of the server as

* the response body.

*/

protected static class ServletGethostnameCommand implements

RMICommandHandler {

public String getName() {

return "gethostname";

}

public void execute(HttpServletRequest req, HttpServletResponse res,

String param) throws ServletClientException,

ServletServerException, IOException {

byte[] getHostStringBytes = req.getServerName().getBytes();

res.setStatus(HttpServletResponse.SC_OK);

res.setContentType("application/octet-stream");

res.setContentLength(getHostStringBytes.length);

OutputStream out = res.getOutputStream();

out.write(getHostStringBytes);

out.flush();

}

}

/**

* Class that has an execute method to return an OK status to indicate that

* connection was successful.

*/

protected static class ServletPingCommand implements RMICommandHandler {

public String getName() {

return "ping";

}

public void execute(HttpServletRequest req, HttpServletResponse res,

String param) throws ServletClientException,

ServletServerException, IOException {

res.setStatus(HttpServletResponse.SC_OK);

res.setContentType("application/octet-stream");

res.setContentLength(0);

}

}

/**

* Class that has an execute method to return a human readable message

* describing which host name is available to local Java VMs.

*/

protected static class ServletTryHostnameCommand implements

RMICommandHandler {

public String getName() {

return "hostname";

}

public void execute(HttpServletRequest req, HttpServletResponse res,

String param) throws ServletClientException,

ServletServerException, IOException {

PrintWriter pw = res.getWriter();

pw.println("");

pw.println("" + "

Java RMI Server Hostname Info"

+ "

" + "");

pw.println("

Java RMI Server Hostname Info

");

pw.println("

Local host name available to Java VM:

");

pw.print("

InetAddress.getLocalHost().getHostName()");

try {

String localHostName = InetAddress.getLocalHost().getHostName();

pw.println(" = " + localHostName);

} catch (UnknownHostException e) {

pw.println(" threw java.net.UnknownHostException");

}

pw.println("

Server host information obtained through Servlet "

+ "interface from HTTP server:");

pw.println("

SERVER_NAME = " + req.getServerName());

pw.println("

SERVER_PORT = " + req.getServerPort());

pw.println("");

}

}

/**

* ServletClientException is thrown when an error is detected in a client's

* request.

*/

protected static class ServletClientException extends Exception {

public ServletClientException(String s) {

super(s);

}

}

/**

* ServletServerException is thrown when an error occurs here on the server.

*/

protected static class ServletServerException extends Exception {

public ServletServerException(String s) {

super(s);

}

}

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值