Java tutorial
/* * Copyright 1999-2010 University of Chicago * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software distributed under the License is * distributed on an "AS IS" BASIS,WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either * express or implied. * * See the License for the specific language governing permissions and limitations under the License. */ package org.globus.net; import java.net.ServerSocket; import java.net.Socket; import java.net.URL; import java.net.MalformedURLException; import java.net.InetAddress; import java.io.IOException; import org.globus.util.deactivator.DeactivationHandler; import org.globus.util.deactivator.Deactivator; import org.globus.util.Util; import org.globus.gsi.gssapi.auth.Authorization; import org.globus.gsi.gssapi.auth.SelfAuthorization; import org.globus.gsi.GSIConstants; import org.globus.gsi.gssapi.GSSConstants; import org.globus.gsi.gssapi.net.GssSocket; import org.globus.gsi.gssapi.net.GssSocketFactory; import org.ietf.jgss.GSSCredential; import org.ietf.jgss.GSSException; import org.ietf.jgss.GSSManager; import org.gridforum.jgss.ExtendedGSSManager; import org.gridforum.jgss.ExtendedGSSContext; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** * This class provides the basics for writing various servers. * <b>Note:</b> Sockets created by this server have a 5 minute default timeout. * The timeout can be changed using the {@link #setTimeout(int) setTimeout()} * function. */ public abstract class BaseServer implements Runnable { private static Log logger = LogFactory.getLog(BaseServer.class.getName()); /** Socket timeout in milliseconds. */ public static final int SO_TIMEOUT = 5 * 60 * 1000; protected boolean accept; protected ServerSocket _server = null; private boolean secure = true; protected String url = null; private Thread serverThread = null; protected GSSCredential credentials = null; protected Authorization authorization = null; protected Integer gssMode = GSIConstants.MODE_SSL; protected int timeout = SO_TIMEOUT; public BaseServer() throws IOException { this(null, 0); } public BaseServer(int port) throws IOException { this(null, port); } public BaseServer(GSSCredential cred, int port) throws IOException { this.credentials = cred; this._server = ServerSocketFactory.getDefault().createServerSocket(port); this.secure = true; initialize(); } public BaseServer(boolean secure, int port) throws IOException { this.credentials = null; this._server = ServerSocketFactory.getDefault().createServerSocket(port); this.secure = secure; initialize(); } /** * This method should be called by all subclasses. * */ protected void initialize() { setAuthorization(SelfAuthorization.getInstance()); start(); } /** * Starts the server. */ protected void start() { if (serverThread == null) { accept = true; serverThread = new Thread(this); serverThread.start(); } } /** * Sets timeout for the created sockets. * By default if not set, 5 minute timeout is used. */ public void setTimeout(int timeout) { this.timeout = timeout; } public int getTimeout() { return this.timeout; } /** * Stops the server but does * not stop all the client threads */ public void shutdown() { accept = false; try { _server.close(); } catch (Exception e) { } // this is a hack to ensue the server socket is // unblocked from accpet() // but this is not guaranteed to work still SocketFactory factory = SocketFactory.getDefault(); Socket s = null; try { s = factory.createSocket(InetAddress.getLocalHost(), getPort()); s.getInputStream(); } catch (Exception e) { // can be ignored } finally { if (s != null) { try { s.close(); } catch (Exception e) { } } } // reset everything serverThread = null; _server = null; } public GSSCredential getCredentials() { return this.credentials; } public String getProtocol() { return (secure) ? "https" : "http"; } /** * Returns url of this server * * @return url of this server */ public String getURL() { if (url == null) { StringBuffer buf = new StringBuffer(); buf.append(getProtocol()).append("://").append(getHost()).append(":").append(String.valueOf(getPort())); url = buf.toString(); } return url; } /** * Returns port of this server * * @return port number */ public int getPort() { return _server.getLocalPort(); } /** * Returns hostname of this server * * @return hostname */ public String getHostname() { return Util.getLocalHostAddress(); } /** * Returns hostname of this server. The format of the host conforms * to RFC 2732, i.e. for a literal IPv6 address, this method will * return the IPv6 address enclosed in square brackets ('[' and ']'). * * @return hostname */ public String getHost() { String host = Util.getLocalHostAddress(); try { URL u = new URL("http", host, 80, "/"); return u.getHost(); } catch (MalformedURLException e) { return host; } } public void run() { Socket socket = null; while (accept) { try { socket = _server.accept(); if (!accept) { break; } socket.setSoTimeout(getTimeout()); } catch (IOException e) { if (accept) { // display error message logger.error("Server died: " + e.getMessage(), e); } break; } if (this.secure) { try { socket = wrapSocket(socket); } catch (GSSException e) { logger.error("Failed to secure the socket", e); break; } } handleConnection(socket); } logger.debug("server thread stopped"); } protected Socket wrapSocket(Socket socket) throws GSSException { GSSManager manager = ExtendedGSSManager.getInstance(); ExtendedGSSContext context = (ExtendedGSSContext) manager.createContext(credentials); context.setOption(GSSConstants.GSS_MODE, gssMode); GssSocketFactory factory = GssSocketFactory.getDefault(); GssSocket gsiSocket = (GssSocket) factory.createSocket(socket, null, 0, context); // server socket gsiSocket.setUseClientMode(false); gsiSocket.setAuthorization(this.authorization); return gsiSocket; } public void setGssMode(Integer mode) { this.gssMode = mode; } public void setAuthorization(Authorization auth) { authorization = auth; } /** * This method needs to be implemented by subclasses. * Optimmaly, it should be a non-blocking call starting * a separate thread to handle the client. Note that to * start an SSL handshake, you need to call socket.getInput(Output) * stream(). */ protected abstract void handleConnection(Socket socket); /** * Registers a default deactivation handler. It is used * to shutdown the server without having a reference to * the server. Call Deactivate.deactivateAll() to shutdown * all registered servers. */ public void registerDefaultDeactivator() { if (deactivator == null) { deactivator = new AbstractServerDeactivator(this); } Deactivator.registerDeactivation(deactivator); } /** * Unregisters a default deactivation handler. */ public void unregisterDefaultDeactivator() { if (deactivator == null) return; Deactivator.unregisterDeactivation(deactivator); } /** * A handler for the deactivation framework. */ protected AbstractServerDeactivator deactivator = null; } class AbstractServerDeactivator implements DeactivationHandler { private BaseServer server = null; public AbstractServerDeactivator(BaseServer server) { this.server = server; } public void deactivate() { if (server != null) server.shutdown(); } }