Main Page | Packages | Class Hierarchy | Alphabetical List | Class List | File List | Class Members | Related Pages

ControllerServerThread.java

00001 /**
00002  * C-JDBC: Clustered JDBC.
00003  * Copyright (C) 2002-2005 French National Institute For Research In Computer
00004  * Science And Control (INRIA).
00005  * Contact: c-jdbc@objectweb.org
00006  * 
00007  * This library is free software; you can redistribute it and/or modify it
00008  * under the terms of the GNU Lesser General Public License as published by the
00009  * Free Software Foundation; either version 2.1 of the License, or any later
00010  * version.
00011  * 
00012  * This library is distributed in the hope that it will be useful, but WITHOUT
00013  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
00014  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
00015  * for more details.
00016  * 
00017  * You should have received a copy of the GNU Lesser General Public License
00018  * along with this library; if not, write to the Free Software Foundation,
00019  * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
00020  *
00021  * Initial developer(s): Emmanuel Cecchet. 
00022  * Contributor(s): Duncan Smith.
00023  */
00024 
00025 package org.objectweb.cjdbc.controller.core;
00026 
00027 import java.io.IOException;
00028 import java.net.InetAddress;
00029 import java.net.ServerSocket;
00030 import java.net.Socket;
00031 import java.util.ArrayList;
00032 
00033 import javax.net.ServerSocketFactory;
00034 
00035 import org.objectweb.cjdbc.common.i18n.Translate;
00036 import org.objectweb.cjdbc.common.log.Trace;
00037 import org.objectweb.cjdbc.common.net.SSLException;
00038 import org.objectweb.cjdbc.common.net.SocketFactoryFactory;
00039 import org.objectweb.cjdbc.common.stream.CJDBCOutputStream;
00040 
00041 /**
00042  * A <code>ControllerServerThread</code> listens for C-JDBC driver
00043  * connections. It accepts the connection and give them to
00044  * <code>ControllerWorkerThreads</code>.
00045  * 
00046  * @see org.objectweb.cjdbc.controller.core.ControllerWorkerThread
00047  * @author <a href="mailto:Emmanuel.Cecchet@inria.fr">Emmanuel Cecchet </a>
00048  * @author <a href="mailto:duncan@mightybot.com">Duncan Smith </a>
00049  * @version 1.0
00050  */
00051 public class ControllerServerThread extends Thread
00052 {
00053   private ServerSocket                   serverSocket;
00054   private static final SecurityException BLOCKED                            = new SecurityException(
00055                                                                                 Translate
00056                                                                                     .get("controller.server.thread.security.connection.refused"));
00057   private boolean                        isShuttingDown                     = false;
00058   protected Controller                   controller;
00059   /** Pending queue of client (driver) socket connections */
00060   protected ArrayList                    controllerServerThreadPendingQueue = new ArrayList();
00061   /**
00062    * Number of idle <code>ControllerWorkerThread</code>. Access to this
00063    * variable must be synchronized using pendingQueue.
00064    */
00065   protected int                          idleWorkerThreads                  = 0;
00066 
00067   /** Logger instance. */
00068   static Trace                           logger                             = Trace
00069                                                                                 .getLogger("org.objectweb.cjdbc.controller.core.Controller");
00070 
00071   /**
00072    * Creates a new ControllerServerThread that listens on the given port.
00073    * 
00074    * @param controller The controller which created this thread.
00075    */
00076   public ControllerServerThread(Controller controller)
00077   {
00078     super("ControllerServerThread");
00079     this.controller = controller;
00080 
00081     try
00082     {
00083       InetAddress bindAddress = InetAddress
00084           .getByName(controller.getIPAddress());
00085 
00086       // Determine if a specific IP address has been requested.
00087       if (!controller.getIPAddress().equals(ControllerConstants.DEFAULT_IP))
00088       { // Yes, an IP has been specified that is not localhost...
00089 
00090         // Build an InetAddress by passing the requested IP address to the
00091         // InetAddress class constructor. This will validate the sanity of the
00092         // IP by either accepting the requested value or throwing a
00093         // BindException.
00094         if (controller.isSecurityEnabled()
00095             && controller.getSecurity().isSSLEnabled())
00096         {
00097           ServerSocketFactory sslFact = SocketFactoryFactory
00098               .createServerFactory(controller.getSecurity().getSslConfig());
00099           serverSocket = sslFact.createServerSocket(controller.getPortNumber(),
00100               controller.getBacklogSize(), bindAddress);
00101         }
00102         else
00103         {
00104           serverSocket = new ServerSocket(controller.getPortNumber(),
00105               controller.getBacklogSize(), bindAddress);
00106         }
00107       }
00108       else
00109       { // No, an IP was not requested or was left as the default. Create a
00110         // basic local socket.
00111 
00112         if (controller.isSecurityEnabled()
00113             && controller.getSecurity().isSSLEnabled())
00114         {
00115           ServerSocketFactory sslFact = SocketFactoryFactory
00116               .createServerFactory(controller.getSecurity().getSslConfig());
00117           serverSocket = sslFact.createServerSocket(controller.getPortNumber(),
00118               controller.getBacklogSize());
00119         }
00120         else
00121         {
00122           serverSocket = new ServerSocket(controller.getPortNumber(),
00123               controller.getBacklogSize());
00124         }
00125       }
00126 
00127     }
00128     catch (java.net.BindException e)
00129     { // Thrown if an illegal IP address was specified.
00130       logger.fatal(Translate.get("controller.server.thread.illegal.ip",
00131           new String[]{controller.getIPAddress(), e.getMessage()}));
00132       controller.endOfController(e);
00133     }
00134     catch (IOException e)
00135     {
00136       logger.fatal(Translate.get("controller.server.thread.socket.failed",
00137           controller.getPortNumber() + ""));
00138       controller.endOfController(e);
00139     }
00140     catch (SSLException e)
00141     {
00142       logger.fatal(Translate.get("controller.server.thread.socket.failed",
00143           controller.getPortNumber() + ""));
00144       controller.endOfController(e);
00145     }
00146     if (logger.isInfoEnabled())
00147     {
00148       logger.info(Translate.get("controller.server.thread.waiting.connections",
00149           new String[]{serverSocket.getInetAddress().getHostAddress(),
00150               String.valueOf(serverSocket.getLocalPort())}));
00151       logger.debug(Translate.get("controller.server.thread.backlog.size", ""
00152           + controller.getBacklogSize()));
00153     }
00154   }
00155 
00156   /**
00157    * Accepts connections from drivers, read the virtual database name and
00158    * returns the connection point.
00159    */
00160   public void run()
00161   {
00162     if (controller == null)
00163     {
00164       logger.error(Translate.get("controller.server.thread.controller.null"));
00165       isShuttingDown = true;
00166     }
00167 
00168     Socket clientSocket = null;
00169     while (!isShuttingDown)
00170     {
00171       try
00172       { // Accept a connection
00173         clientSocket = serverSocket.accept();
00174         if (isShuttingDown)
00175           break;
00176         if (controller.isSecurityEnabled()
00177             && !controller.getSecurity().allowConnection(clientSocket))
00178         {
00179           logger.warn(Translate.get(
00180               "controller.server.thread.connection.refused", clientSocket
00181                   .getInetAddress().getHostName()));
00182           CJDBCOutputStream out = new CJDBCOutputStream(clientSocket);
00183           out.writeObject(BLOCKED);
00184           out.flush();
00185           clientSocket = null;
00186           continue;
00187         }
00188         else
00189         {
00190           if (logger.isDebugEnabled())
00191             logger.debug(Translate.get(
00192                 "controller.server.thread.connection.accept", clientSocket
00193                     .getInetAddress().getHostName()));
00194         }
00195         boolean createThread = false;
00196         if (isShuttingDown)
00197           break;
00198         synchronized (controllerServerThreadPendingQueue)
00199         {
00200           // Add the connection to the queue
00201           controllerServerThreadPendingQueue.add(clientSocket);
00202           // Check if we need to create a new thread or just wake up an
00203           // existing one
00204           if (idleWorkerThreads == 0)
00205             createThread = true;
00206           else
00207             // Here we notify all threads else if one thread doesn't wake up
00208             // after the first notify() we will send a second notify() and
00209             // one signal will be lost. So the safe way is to wake up everybody
00210             // and that worker threads go back to sleep if there is no job.
00211             controllerServerThreadPendingQueue.notifyAll();
00212         }
00213         if (createThread)
00214         { // Start a new worker thread if needed
00215           ControllerWorkerThread thread = new ControllerWorkerThread(this);
00216           thread.start();
00217           if (logger.isDebugEnabled())
00218             logger.debug(Translate.get("controller.server.thread.starting"));
00219         }
00220       }
00221       catch (IOException e)
00222       {
00223         if (!isShuttingDown)
00224         {
00225           logger.warn(Translate.get(
00226               "controller.server.thread.new.connection.error", e), e);
00227         }
00228       }
00229     }
00230     if (logger.isInfoEnabled())
00231       logger.info(Translate.get("controller.server.thread.terminating"));
00232   }
00233 
00234   /**
00235    * Refuse new connection to clients and finish transaction
00236    */
00237   public void shutdown()
00238   {
00239     isShuttingDown = true;
00240     // Shutting down server thread
00241     try
00242     {
00243       serverSocket.close();
00244     }
00245     catch (Exception e)
00246     {
00247       logger.warn(Translate.get("controller.shutdown.server.socket.exception"),
00248           e);
00249     }
00250     int nbSockets = controllerServerThreadPendingQueue.size();
00251     Socket socket = null;
00252     for (int i = 0; i < nbSockets; i++)
00253     {
00254       socket = (Socket) controllerServerThreadPendingQueue.get(i);
00255       logger.info(Translate.get("controller.shutdown.client.socket", socket
00256           .getInetAddress().toString()));
00257     }
00258     this.controllerServerThreadPendingQueue = null;
00259   }
00260 
00261   /**
00262    * @return Returns the controllerServerThreadPendingQueue size.
00263    */
00264   public int getControllerServerThreadPendingQueueSize()
00265   {
00266     synchronized (controllerServerThreadPendingQueue)
00267     {
00268       return controllerServerThreadPendingQueue.size();
00269     }
00270   }
00271 
00272   /**
00273    * @return Returns the idleWorkerThreads.
00274    */
00275   public int getIdleWorkerThreads()
00276   {
00277     synchronized (controllerServerThreadPendingQueue)
00278     {
00279       return idleWorkerThreads;
00280     }
00281   }
00282 
00283   /**
00284    * Returns the isShuttingDown value.
00285    * 
00286    * @return Returns the isShuttingDown.
00287    */
00288   public boolean isShuttingDown()
00289   {
00290     return isShuttingDown;
00291   }
00292 }

Generated on Mon Apr 11 22:01:30 2005 for C-JDBC by  doxygen 1.3.9.1