You are viewing a plain text version of this content. The canonical link for it is here.
Posted to derby-commits@db.apache.org by km...@apache.org on 2005/05/11 21:46:30 UTC
svn commit: r169688 [2/2] - in
/incubator/derby/code/trunk/java/drda/org/apache/derby/impl/drda:
DB2jServerImpl.java NetworkServerControlImpl.java
Copied: incubator/derby/code/trunk/java/drda/org/apache/derby/impl/drda/NetworkServerControlImpl.java (from r169684, incubator/derby/code/trunk/java/drda/org/apache/derby/impl/drda/DB2jServerImpl.java)
URL: http://svn.apache.org/viewcvs/incubator/derby/code/trunk/java/drda/org/apache/derby/impl/drda/NetworkServerControlImpl.java?p2=incubator/derby/code/trunk/java/drda/org/apache/derby/impl/drda/NetworkServerControlImpl.java&p1=incubator/derby/code/trunk/java/drda/org/apache/derby/impl/drda/DB2jServerImpl.java&r1=169684&r2=169688&rev=169688&view=diff
==============================================================================
--- incubator/derby/code/trunk/java/drda/org/apache/derby/impl/drda/DB2jServerImpl.java (original)
+++ incubator/derby/code/trunk/java/drda/org/apache/derby/impl/drda/NetworkServerControlImpl.java Wed May 11 12:46:30 2005
@@ -0,0 +1,3447 @@
+/*
+
+ Derby - Class org.apache.derby.impl.drda.NetworkServerControlImpl
+
+ Copyright 2002, 2004 The Apache Software Foundation or its licensors, as applicable.
+
+ 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.apache.derby.impl.drda;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.DataOutputStream;
+import java.io.PrintWriter;
+import java.io.UnsupportedEncodingException;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.sql.Connection;
+import java.sql.Driver;
+import java.sql.DriverManager;
+import java.sql.ResultSet;
+import java.sql.Statement;
+import java.sql.SQLException;
+import java.sql.SQLWarning;
+import java.util.Enumeration;
+import java.util.ArrayList;
+import java.util.Hashtable;
+import java.util.Locale;
+import java.util.Properties;
+import java.util.StringTokenizer;
+import java.util.Vector;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+
+import org.apache.derby.iapi.reference.Attribute;
+import org.apache.derby.iapi.reference.Property;
+import org.apache.derby.iapi.services.info.ProductGenusNames;
+import org.apache.derby.iapi.services.info.ProductVersionHolder;
+import org.apache.derby.iapi.services.info.JVMInfo;
+import org.apache.derby.iapi.reference.SQLState;
+import org.apache.derby.impl.jdbc.EmbedSQLException;
+import org.apache.derby.iapi.jdbc.DRDAServerStarter;
+import org.apache.derby.iapi.tools.i18n.LocalizedResource;
+import org.apache.derby.iapi.tools.i18n.LocalizedOutput;
+import org.apache.derby.iapi.services.property.PropertyUtil;
+import org.apache.derby.iapi.services.sanity.SanityManager;
+import org.apache.derby.iapi.services.i18n.MessageService;
+import org.apache.derby.iapi.services.monitor.Monitor;
+import org.apache.derby.iapi.util.StringUtil;
+import org.apache.derby.drda.NetworkServerControl;
+
+/**
+
+ NetworkServerControlImpl does all the work for NetworkServerControl
+ @see NetworkServerControl for description
+
+*/
+public class NetworkServerControlImpl {
+ private final static int NO_USAGE_MSGS= 12;
+ private final static String [] COMMANDS =
+ {"start","shutdown","trace","tracedirectory","ping",
+ "logconnections", "sysinfo", "runtimeinfo", "maxthreads", "timeslice"};
+ // number of required arguments for each command
+ private final static int [] COMMAND_ARGS =
+ {0, 0, 1, 1, 0, 1, 0, 0, 1, 1};
+ private final static int COMMAND_START = 0;
+ private final static int COMMAND_SHUTDOWN = 1;
+ private final static int COMMAND_TRACE = 2;
+ private final static int COMMAND_TRACEDIRECTORY = 3;
+ private final static int COMMAND_TESTCONNECTION = 4;
+ private final static int COMMAND_LOGCONNECTIONS = 5;
+ private final static int COMMAND_SYSINFO = 6;
+ private final static int COMMAND_RUNTIME_INFO = 7;
+ private final static int COMMAND_MAXTHREADS = 8;
+ private final static int COMMAND_TIMESLICE = 9;
+ private final static int COMMAND_PROPERTIES = 10;
+ private final static int COMMAND_UNKNOWN = -1;
+ private final static String [] DASHARGS =
+ {"p","d","u","ld","ea","ep", "b", "h", "s"};
+ private final static int DASHARG_PORT = 0;
+ private final static int DASHARG_DATABASE = 1;
+ private final static int DASHARG_USER = 2;
+ private final static int DASHARG_LOADSYSIBM = 3;
+ private final static int DASHARG_ENCALG = 4;
+ private final static int DASHARG_ENCPRV = 5;
+ private final static int DASHARG_BOOTPASSWORD = 6;
+ private final static int DASHARG_HOST = 7;
+ private final static int DASHARG_SESSION = 8;
+
+ // command protocol version - you need to increase this number each time
+ // the command protocol changes
+ private final static int PROTOCOL_VERSION = 1;
+ private final static String COMMAND_HEADER = "CMD:";
+ private final static String REPLY_HEADER = "RPY:";
+ private final static int REPLY_HEADER_LENGTH = REPLY_HEADER.length();
+ private final static int OK = 0;
+ private final static int WARNING = 1;
+ private final static int ERROR = 2;
+ private final static int SQLERROR = 3;
+ private final static int SQLWARNING = 4;
+
+ private final static String
+ NETWORKSERVER_PROP_STREAM_ERROR_FIELD="derby.stream.error.field";
+
+ private final static String
+ NETWORKSERVER_PROP_STREAM_ERROR_METHOD="derby.stream.error.method";
+ private final static String
+ NETWORKSERVER_PROP_STREAM_ERROR_FILE="derby.stream.error.file";
+
+ private final static String DRDA_PROP_MESSAGES = "org.apache.derby.loc.drda.messages";
+ private final static String DRDA_PROP_DEBUG = "derby.drda.debug";
+ private final static String CLOUDSCAPE_DRIVER = "org.apache.derby.jdbc.EmbeddedDriver";
+
+ protected final static String UNEXPECTED_ERR = "Unexpected exception";
+
+ private final static int MIN_MAXTHREADS = -1;
+ private final static int MIN_TIMESLICE = -1;
+ private final static int USE_DEFAULT = -1;
+ private final static int DEFAULT_MAXTHREADS = 0; //for now create whenever needed
+ private final static int DEFAULT_TIMESLICE = 0; //for now never yield
+
+ private final static String DEFAULT_HOST = "localhost";
+ private final static String DRDA_MSG_PREFIX = "DRDA_";
+ private final static String DEFAULT_LOCALE= "en";
+ private final static String DEFAULT_LOCALE_COUNTRY="US";
+
+ // Check up to 10 seconds to see if shutdown occurred
+ private final static int SHUTDOWN_CHECK_ATTEMPTS = 20;
+ private final static int SHUTDOWN_CHECK_INTERVAL= 500;
+
+ // maximum reply size
+ private final static int MAXREPLY = 32767;
+
+ // Application Server Attributes.
+ protected static String att_srvclsnm;
+ protected final static String ATT_SRVNAM = "NetworkServerControl";
+
+ protected static String att_extnam;
+ protected static String att_srvrlslv;
+ protected static String prdId;
+ private static String buildNumber;
+ // we will use single or mixed, not double byte to reduce traffic on the
+ // wire, this is in keeping with JCC
+ // Note we specify UTF8 for the single byte encoding even though it can
+ // be multi-byte.
+ protected final static int CCSIDSBC = 1208; //use UTF8
+ protected final static int CCSIDMBC = 1208; //use UTF8
+ protected final static String DEFAULT_ENCODING = "UTF8"; // use UTF8 for writing
+ protected final static int DEFAULT_CCSID = 1208;
+ protected final static byte SPACE_CHAR = 32;
+
+
+ // Application Server manager levels - this needs to be in sync
+ // with CodePoint.MGR_CODEPOINTS
+ protected final static int [] MGR_LEVELS = { 7, // AGENT
+ 4, // CCSID Manager
+ 0, // CNMAPPC not implemented
+ 0, // CMNSYNCPT not implemented
+ 5, // CMNTCPIP
+ 0, // DICTIONARY
+ 7, // RDB
+ 0, // RSYNCMGR
+ 7, // SECMGR
+ 7, // SQLAM
+ 0, // SUPERVISOR
+ 0, // SYNCPTMGR
+ 7 // XAMGR
+ };
+
+
+ protected PrintWriter logWriter; // console
+ protected PrintWriter cloudscapeLogWriter; // derby.log
+ private static Driver cloudscapeDriver;
+
+ // error types
+ private final static int ERRTYPE_SEVERE = 1;
+ private final static int ERRTYPE_USER = 2;
+ private final static int ERRTYPE_INFO = 3;
+ private final static int ERRTYPE_UNKNOWN = -1;
+
+ // command argument information
+ private Vector commandArgs = new Vector();
+ private String databaseArg;
+ private String userArg;
+ private String passwordArg;
+ private String bootPasswordArg;
+ private String encAlgArg;
+ private String encPrvArg;
+ private String hostArg = DEFAULT_HOST;
+ private InetAddress hostAddress;
+ private int sessionArg;
+
+ // Used to debug memory in SanityManager.DEBUG mode
+ private memCheck mc;
+
+ // reply buffer
+ private byte [] replyBuffer;
+ private int replyBufferCount; //length of reply
+ private int replyBufferPos; //current position in reply
+
+ //
+ // server configuration
+ //
+ // static values - set at start can't be changed once server has started
+ private int portNumber = NetworkServerControl.DEFAULT_PORTNUMBER; // port server listens to
+
+ // configurable values
+ private String traceDirectory; // directory to place trace files in
+ private Object traceDirectorySync = new Object();// object to use for syncing
+ private boolean traceAll; // trace all sessions
+ private Object traceAllSync = new Object(); // object to use for syncing reading
+ // and changing trace all
+ private Object serverStartSync = new Object(); // for syncing start of server.
+ private boolean logConnections; // log connect and disconnects
+ private Object logConnectionsSync = new Object(); // object to use for syncing
+ // logConnections value
+ private int minThreads; // default minimum number of connection threads
+ private int maxThreads; // default maximum number of connection threads
+ private Object threadsSync = new Object(); // object to use for syncing reading
+ // and changing default min and max threads
+ private int timeSlice; // default time slice of a session to a thread
+ private Object timeSliceSync = new Object();// object to use for syncing reading
+ // and changing timeSlice
+
+ private boolean keepAlive = true; // keepAlive value for client socket
+ private int minPoolSize; //minimum pool size for pooled connections
+ private int maxPoolSize; //maximum pool size for pooled connections
+ private Object poolSync = new Object(); // object to use for syning reading
+
+ protected boolean debugOutput = false;
+ private boolean cleanupOnStart = false; // Should we clean up when starting the server?
+ private boolean restartFlag = false;
+
+ private String errorLogLocation = null;
+
+ //
+ // variables for a client command session
+ //
+ private Socket clientSocket = null;
+ private InputStream clientIs = null;
+ private OutputStream clientOs = null;
+ private ByteArrayOutputStream byteArrayOs = new ByteArrayOutputStream();
+ private DataOutputStream commandOs = new DataOutputStream(byteArrayOs);
+
+ private Object shutdownSync = new Object();
+ private boolean shutdown;
+ private int connNum; // number of connections since server started
+ private ServerSocket serverSocket;
+ private NetworkServerControlImpl serverInstance;
+ private LocalizedResource langUtil;
+ public String clientLocale;
+ ArrayList localAddresses; // list of local addresses for checking admin
+ // commands.
+
+ // open sessions
+ private Hashtable sessionTable = new Hashtable();
+
+ // current session
+ private Session currentSession;
+ // DRDAConnThreads
+ private Vector threadList = new Vector();
+
+ // queue of sessions waiting for a free thread - the queue is managed
+ // in a simple first come, first serve manner - no priorities
+ private Vector runQueue = new Vector();
+
+ // number of DRDAConnThreads waiting for something to do
+ private int freeThreads;
+
+ // known application requesters
+ private Hashtable appRequesterTable = new Hashtable();
+
+ // accessed by inner classes for privileged action
+ private String propertyFileName;
+ private Runnable acceptClients;
+
+
+
+
+ // constructor
+ public NetworkServerControlImpl() throws Exception
+ {
+ getPropertyInfo();
+ init();
+ }
+
+
+ /**
+ * Internal constructor for NetworkServerControl API.
+ * @param address - InetAddress to listen on, May not be null. Throws NPE if null
+ * @param portNumber - portNumber to listen on, -1 use propert or default.
+ * @exception throw Exception on error
+ * @see NetworkServerControl
+ */
+ public NetworkServerControlImpl(InetAddress address, int portNumber) throws Exception
+ {
+ getPropertyInfo();
+ this.hostAddress = address;
+ this.portNumber = (portNumber <= 0) ?
+ this.portNumber: portNumber;
+ this.hostArg = address.getHostAddress();
+ init();
+ }
+
+ private void init() throws Exception
+ {
+
+ // adjust the application in accordance with derby.ui.locale and derby.ui.codeset
+ langUtil = new LocalizedResource(null,null,DRDA_PROP_MESSAGES);
+
+ serverInstance = this;
+
+ //set Server attributes to be used in EXCSAT
+ ProductVersionHolder myPVH = getNetProductVersionHolder();
+ att_extnam = ATT_SRVNAM + " " + java.lang.Thread.currentThread().getName();
+
+ att_srvclsnm = myPVH.getProductName();
+
+ String majorStr = String.valueOf(myPVH.getMajorVersion());
+ String minorStr = String.valueOf(myPVH.getMinorVersion());
+ // Maintenance version. Server protocol version.
+ // Only changed if client needs to recognize a new server version.
+ String drdaMaintStr = String.valueOf(myPVH.getDrdaMaintVersion());
+
+ // PRDID format as JCC expects it: CSSMMmx
+ // MM = major version
+ // mm = minor version
+ // x = drda MaintenanceVersion
+
+ prdId = "CSS";
+ if (majorStr.length() == 1)
+ prdId += "0";
+ prdId += majorStr;
+
+ if (minorStr.length() == 1)
+ prdId += "0";
+
+ prdId += minorStr;
+
+ prdId += drdaMaintStr;
+ att_srvrlslv = prdId + "/" + myPVH.getVersionBuildString(false);
+
+ if (SanityManager.DEBUG)
+ {
+ if (majorStr.length() > 2 ||
+ minorStr.length() > 2 ||
+ drdaMaintStr.length() > 1)
+ SanityManager.THROWASSERT("version values out of expected range for PRDID");
+ }
+
+ buildNumber = myPVH.getBuildNumber();
+ }
+
+ private PrintWriter makePrintWriter( OutputStream out)
+ {
+ if (out != null)
+ return new PrintWriter(out, true /* flush the buffer at the end of each line */);
+ else
+ return null;
+ }
+
+ protected static Driver getDriver()
+ {
+ return cloudscapeDriver;
+ }
+
+
+ /********************************************************************************
+ * Implementation of NetworkServerControl API
+ * The server commands throw exceptions for errors, so that users can handle
+ * them themselves in addition to having the errors written to the console
+ * and possibly derby.log. To turn off logging the errors to the console,
+ * set the output writer to null.
+ ********************************************************************************/
+
+
+ /**
+ * Set the output stream for console messages
+ * If this is set to null, no messages will be written to the console
+ *
+ * @param outWriter output stream for console messages
+ */
+ public void setLogWriter(PrintWriter outWriter)
+ {
+ logWriter = outWriter;
+ }
+
+
+
+ /**
+ * Write an error message to console output stream
+ * and throw an exception for this error
+ *
+ * @param msg error message
+ * @exception Exception
+ */
+ public void consoleError(String msg)
+ throws Exception
+ {
+ consoleMessage(msg);
+ throw new Exception(msg);
+ }
+
+ /**
+ * Write an exception to console output stream,
+ * but only if debugOutput is true.
+ *
+ * @param e exception
+ */
+ public void consoleExceptionPrint(Exception e)
+ {
+ if (debugOutput == true)
+ consoleExceptionPrintTrace(e);
+
+ return;
+ }
+
+ /**
+ * Write an exception (with trace) to console
+ * output stream.
+ *
+ * @param e exception
+ */
+ public void consoleExceptionPrintTrace(Throwable e)
+ {
+ consoleMessage(e.getMessage());
+ if (logWriter != null)
+ {
+ synchronized (logWriter) {
+ e.printStackTrace(logWriter);
+ }
+ }
+ else
+ {
+ e.printStackTrace();
+ }
+
+ if (cloudscapeLogWriter != null)
+ {
+ synchronized(cloudscapeLogWriter) {
+ e.printStackTrace(cloudscapeLogWriter);
+ }
+ }
+ }
+
+
+
+
+ /**
+ * Write a message to console output stream
+ *
+ * @param msg message
+ */
+ public void consoleMessage(String msg)
+ {
+ // print to console if we have one
+ if (logWriter != null)
+ {
+ synchronized(logWriter) {
+ logWriter.println(msg);
+ }
+ }
+ // always print to derby.log
+ if (cloudscapeLogWriter != null)
+ synchronized(cloudscapeLogWriter)
+ {
+ Monitor.logMessage(msg);
+ }
+ }
+
+
+
+ /**
+ * Start a network server. Launches a separate thread with
+ * DRDAServerStarter. Want to use Monitor.startModule,
+ * so it can all get shutdown when cloudscape shuts down, but
+ * can't get it working right now.
+ *
+ * @param consoleWriter PrintWriter to which server console will be
+ * output. Null will disable console output.
+ *
+ *
+ * @exception Exception throws an exception if an error occurs
+ */
+ public void start(PrintWriter consoleWriter)
+ throws Exception
+ {
+ DRDAServerStarter starter = new DRDAServerStarter();
+ starter.setStartInfo(hostAddress,portNumber,consoleWriter);
+ startNetworkServer();
+ starter.boot(false,null);
+ }
+
+
+ /**
+ * Start a network server
+ *
+ * @param consoleWriter PrintWriter to which server console will be
+ * output. Null will disable console output.
+ *
+ *
+ * @exception Exception throws an exception if an error occurs
+ */
+ public void blockingStart(PrintWriter consoleWriter)
+ throws Exception
+ {
+ startNetworkServer();
+ setLogWriter(consoleWriter);
+ cloudscapeLogWriter = Monitor.getStream().getPrintWriter();
+ if (SanityManager.DEBUG && debugOutput)
+ {
+ memCheck.showmem();
+ mc = new memCheck(200000);
+ mc.start();
+ }
+ // Open a server socket listener
+ try{
+ serverSocket = (ServerSocket) AccessController.doPrivileged(
+ new PrivilegedExceptionAction() {
+ public Object run() throws IOException,UnknownHostException
+ {
+ if (hostAddress == null)
+ hostAddress = InetAddress.getByName(hostArg);
+ // Make a list of valid
+ // InetAddresses for NetworkServerControl
+ // admin commands.
+ buildLocalAddressList(hostAddress);
+ return new ServerSocket(portNumber
+ ,0,
+ hostAddress);
+ }
+ }
+ );
+ } catch (PrivilegedActionException e) {
+ Exception e1 = e.getException();
+ if (e1 instanceof IOException)
+ consolePropertyMessage("DRDA_ListenPort.S",
+ new String [] {
+ Integer.toString(portNumber),
+ hostArg});
+ if (e1 instanceof UnknownHostException) {
+ consolePropertyMessage("DRDA_UnknownHost.S", hostArg);
+ }
+ else
+ throw e1;
+ } catch (Exception e) {
+ // If we find other (unexpected) errors, we ultimately exit--so make
+ // sure we print the error message before doing so (Beetle 5033).
+ throwUnexpectedException(e);
+ }
+
+ consolePropertyMessage("DRDA_Ready.I", Integer.toString(portNumber));
+
+ // We accept clients on a separate thread so we don't run into a problem
+ // blocking on the accept when trying to process a shutdown
+ acceptClients = (Runnable)new ClientThread(this, serverSocket);
+ Thread clientThread = (Thread) AccessController.doPrivileged(
+ new PrivilegedExceptionAction() {
+ public Object run() throws Exception
+ {
+ return new Thread(acceptClients);
+ }
+ }
+ );
+ clientThread.start();
+
+ // wait until we are told to shutdown or someone sends an InterruptedException
+ synchronized(shutdownSync) {
+ try {
+ shutdownSync.wait();
+ }
+ catch (InterruptedException e)
+ {
+ shutdown = true;
+ }
+ }
+
+ // Need to interrupt the memcheck thread if it is sleeping.
+ if (mc != null)
+ mc.interrupt();
+
+ //interrupt client thread
+ clientThread.interrupt();
+
+ // Close out the sessions
+ synchronized(sessionTable) {
+ for (Enumeration e = sessionTable.elements(); e.hasMoreElements(); )
+ {
+ Session session = (Session) e.nextElement();
+ session.close();
+ }
+ }
+
+ synchronized (threadList)
+ {
+ //interupt any connection threads still active
+ for (int i = 0; i < threadList.size(); i++)
+ {
+ ((DRDAConnThread)threadList.get(i)).close();
+ ((DRDAConnThread)threadList.get(i)).interrupt();
+ }
+ threadList.clear();
+ }
+
+
+
+
+ // close the listener socket
+ try{
+ serverSocket.close();
+ }catch(IOException e){
+ consolePropertyMessage("DRDA_ListenerClose.S");
+ }
+
+
+ // Wake up those waiting on sessions, so
+ // they can close down
+ synchronized (runQueue) {
+ runQueue.notifyAll();
+ }
+
+ /*
+ // Shutdown Cloudscape
+ try {
+ if (cloudscapeDriver != null)
+ cloudscapeDriver.connect("jdbc:derby:;shutdown=true",
+ (Properties) null);
+ } catch (SQLException sqle) {
+ // If we can't shutdown cloudscape. Perhaps authentication is
+ // set to true or some other reason. We will just print a
+ // message to the console and proceed.
+ if (((EmbedSQLException)sqle).getMessageId() !=
+ SQLState.CLOUDSCAPE_SYSTEM_SHUTDOWN)
+ consolePropertyMessage("DRDA_ShutdownWarning.I",
+ sqle.getMessage());
+ }
+ */
+
+
+ consolePropertyMessage("DRDA_ShutdownSuccess.I");
+
+
+ }
+
+ /**
+ * Load Cloudscape and save driver for future use.
+ * We can't call Driver Manager when the client connects,
+ * because they might be holding the DriverManager lock.
+ *
+ *
+ */
+
+
+
+
+ protected void startNetworkServer() throws Exception
+ {
+
+ // we start the cloudscape server here.
+ boolean restartCheck = this.restartFlag;
+ synchronized (serverStartSync) {
+
+ if (restartCheck == this.restartFlag) {
+ // then we can go ahead and restart the server (odds
+ // that some else has just done so are very slim (but not
+ // impossible--however, even if it does happen, things
+ // should still work correctly, just not as efficiently...))
+
+ try {
+
+ if (cleanupOnStart) {
+ // we're restarting the server (probably after a shutdown
+ // exception), so we need to clean up first.
+
+ // Close and remove sessions on runQueue.
+ for (int i = 0; i < runQueue.size(); i++)
+ ((Session)runQueue.get(i)).close();
+ runQueue.clear();
+
+ // Close and remove DRDAConnThreads on threadList.
+ for (int i = 0; i < threadList.size(); i++)
+ ((DRDAConnThread)threadList.get(i)).close();
+ threadList.clear();
+ freeThreads = 0;
+
+ // Unload driver, then restart the server.
+ cloudscapeDriver = null; // so it gets collected.
+ System.gc();
+ }
+
+ // start the server.
+ Class.forName(CLOUDSCAPE_DRIVER).newInstance();
+ cloudscapeDriver = DriverManager.getDriver(Attribute.PROTOCOL);
+
+ }
+ catch (Exception e) {
+ consolePropertyMessage("DRDA_LoadException.S", e.getMessage());
+ }
+ cleanupOnStart = true;
+ this.restartFlag = !this.restartFlag;
+ }
+ // else, multiple threads hit this synchronize block at the same
+ // time, but one of them already executed it--so all others just
+ // return and do nothing (no need to restart the server multiple
+ // times in a row).
+ }
+ }
+
+ /**
+ * Shutdown a network server
+ *
+ * @exception Exception throws an exception if an error occurs
+ */
+ public void shutdown()
+ throws Exception
+ {
+ setUpSocket();
+ writeCommandHeader(COMMAND_SHUTDOWN);
+ send();
+ readResult();
+ // Wait up to 10 seconds for things to really shut down
+ // need a quiet ping so temporarily disable the logwriter
+ PrintWriter savWriter = logWriter;
+ setLogWriter(null);
+ int ntry;
+ for (ntry = 0; ntry < SHUTDOWN_CHECK_ATTEMPTS; ntry++)
+ {
+ Thread.sleep(SHUTDOWN_CHECK_INTERVAL);
+ try {
+ ping();
+ } catch (Exception e)
+ {
+ // as soon as we can't ping return
+ if (ntry == SHUTDOWN_CHECK_ATTEMPTS)
+ consolePropertyMessage("DRDA_ShutdownError.S", new String [] {
+ Integer.toString(portNumber),
+ hostArg});
+ break;
+ }
+ }
+ logWriter= savWriter;
+ return;
+ }
+
+ /*
+ Shutdown the server directly (If you have the original object)
+ No Network communication needed.
+ */
+ public void directShutdown() {
+ shutdown = true;
+ synchronized(shutdownSync) {
+ // wake up the server thread
+ shutdownSync.notifyAll();
+ }
+
+ }
+
+
+ /**
+ */
+ public boolean isServerStarted() throws Exception
+ {
+ try {
+ ping();
+ }
+ catch (Exception e) {
+ return false;
+ }
+ return true;
+ }
+
+ public void ping() throws Exception
+ {
+ // database no longer used, but don't change the protocol
+ // in case we add
+ // authorization later.
+ String database = null; // no longer used but don't change the protocol
+ String user = null;
+ String password = null;
+
+ setUpSocket();
+ writeCommandHeader(COMMAND_TESTCONNECTION);
+ writeLDString(database);
+ writeLDString(user);
+ writeLDString(password);
+ send();
+ readResult();
+
+ }
+
+
+ /**
+ * Turn tracing on or off for all sessions
+ *
+ * @param on true to turn tracing on, false to turn tracing off
+ *
+ * @exception Exception throws an exception if an error occurs
+ */
+ public void trace(boolean on)
+ throws Exception
+ {
+ trace(0, on);
+ }
+
+ /**
+ * Turn tracing on or off for one session or all sessions
+ *
+ * @param connNum the connNum of the session, 0 if all sessions
+ * @param on true to turn tracing on, false to turn tracing off
+ *
+ * @exception Exception throws an exception if an error occurs
+ */
+ public void trace(int connNum, boolean on)
+ throws Exception
+ {
+ setUpSocket();
+ writeCommandHeader(COMMAND_TRACE);
+ commandOs.writeInt(connNum);
+ writeByte(on ? 1 : 0);
+ send();
+ readResult();
+ consoleTraceMessage(connNum, on);
+ }
+
+ /**
+ * Print trace change message to console
+ *
+ * @param on true to print tracing on, false to print tracing off
+ *
+ * @exception Exception throws an exception if an error occurs
+ */
+ private void consoleTraceMessage(int connNum, boolean on)
+ throws Exception
+ {
+ if (connNum == 0)
+ consolePropertyMessage("DRDA_TraceChangeAll.I", on ? "DRDA_ON.I" : "DRDA_OFF.I");
+ else
+ {
+ String[] args = new String[2];
+ args[0] = on ? "DRDA_ON.I" : "DRDA_OFF.I";
+ args[1] = new Integer(connNum).toString();
+ consolePropertyMessage("DRDA_TraceChangeOne.I", args);
+ }
+ }
+
+ /**
+ * Turn logging connections on or off. When logging is turned on a message is
+ * written to derby.log each time a connection connects or disconnects.
+ *
+ * @param on true to turn on, false to turn off
+ *
+ * @exception Exception throws an exception if an error occurs
+ */
+ public void logConnections(boolean on)
+ throws Exception
+ {
+ setUpSocket();
+ writeCommandHeader(COMMAND_LOGCONNECTIONS);
+ writeByte(on ? 1 : 0);
+ send();
+ readResult();
+ }
+
+ /**
+ *@see NetworkServerControl#setTraceDirectory
+ */
+ public void sendSetTraceDirectory(String traceDirectory)
+ throws Exception
+ {
+ setUpSocket();
+ writeCommandHeader(COMMAND_TRACEDIRECTORY);
+ writeLDString(traceDirectory);
+ send();
+ readResult();
+ }
+
+ /**
+ *@see NetworkServerControl#getSysinfo
+ */
+ public String sysinfo()
+ throws Exception
+ {
+ setUpSocket();
+ writeCommandHeader(COMMAND_SYSINFO);
+ send();
+ return readStringReply("DRDA_SysInfoError.S");
+ }
+
+ /**
+ *@see NetworkServerControl#runtimeinfo
+ */
+ public String runtimeInfo()
+ throws Exception
+ {
+ setUpSocket();
+ writeCommandHeader(COMMAND_RUNTIME_INFO);
+ send();
+ return readStringReply("DRDA_RuntimeInfoError.S");
+ }
+
+ /**
+ * Display usage information
+ *
+ */
+ public void usage()
+ {
+ try {
+ for (int i = 1; i <= NO_USAGE_MSGS; i++)
+ consolePropertyMessage("DRDA_Usage"+i+".I");
+ } catch (Exception e) {} // ignore exceptions - there shouldn't be any
+ }
+
+ /**
+ * Set connection pool parameters for a database
+ *
+ * @param database database parameters applied to
+ * @param min minimum number of connections, if 0, pooled connections not used
+ * if -1, use default
+ * @param max maximum number of connections, if 0, pooled connections
+ * created when no free connection available, if -1,
+ * use default
+ * @param host machine network server is running on, if null, localhost is used
+ * @param portNumber port number server is to use, if <= 0, default port number
+ * is used
+ *
+ * @exception Exception throws an exception if an error occurs
+ */
+ protected void setConnectionPoolParameters(String database, int min, int max,
+ String host, int portNumber)
+ throws Exception
+ {
+ consolePropertyMessage("DRDA_NotImplemented.S", "conpool");
+ }
+
+ /**
+ * Set default connection pool parameters
+ *
+ * @param min minimum number of connections, if 0, pooled connections not used
+ * if -1, use default
+ * @param max maximum number of connections, if 0, pooled connections
+ * created when no free connection available
+ * if -1, use default
+ * @param host machine network server is running on, if null, localhost is used
+ * @param portNumber port number server is to use, if <= 0, default port number
+ * is used
+ *
+ * @exception Exception throws an exception if an error occurs
+ */
+ protected void setConnectionPoolParameters(int min, int max, String host,
+ int portNumber) throws Exception
+ {
+ consolePropertyMessage("DRDA_NotImplemented.S", "conpool");
+ }
+
+ /**
+ * Connect to network server and set connection maxthread parameter
+ *
+ * @param max maximum number of connections, if 0, connections
+ * created when no free connection available
+ * if -1, use default
+ *
+ * @exception Exception throws an exception if an error occurs
+ */
+ public void netSetMaxThreads(int max) throws Exception
+ {
+ setUpSocket();
+ writeCommandHeader(COMMAND_MAXTHREADS);
+ commandOs.writeInt(max);
+ send();
+ readResult();
+ int newval = readInt();
+ consolePropertyMessage("DRDA_MaxThreadsChange.I",
+ new Integer(newval).toString());
+ }
+
+ /**
+ * Set network server connection timeslice parameter
+ *
+ * @param timeslice amount of time given to each session before yielding to
+ * another session, if 0, never yield. if -1, use default.
+ *
+ * @exception Exception throws an exception if an error occurs
+ */
+ public void netSetTimeSlice(int timeslice)
+ throws Exception
+ {
+ setUpSocket();
+ writeCommandHeader(COMMAND_TIMESLICE);
+ commandOs.writeInt(timeslice);
+ send();
+ readResult();
+ int newval = readInt();
+ consolePropertyMessage("DRDA_TimeSliceChange.I",
+ new Integer(newval).toString());
+ }
+
+ /**
+ * Get current properties
+ *
+ * @return Properties object containing properties
+ * @exception Exception throws an exception if an error occurs
+ */
+ public Properties getCurrentProperties()
+ throws Exception
+ {
+ setUpSocket();
+ writeCommandHeader(COMMAND_PROPERTIES);
+ send();
+ byte [] val = readBytesReply("DRDA_PropertyError.S");
+ Properties p = new Properties();
+ try {
+ ByteArrayInputStream bs = new ByteArrayInputStream(val);
+ p.load(bs);
+ } catch (IOException io) {
+ consolePropertyMessage("DRDA_IOException.S",
+ io.getMessage());
+ }
+ return p;
+ }
+
+ /**
+ * Set a thread name to be something that is both meaningful and unique (primarily
+ * for debugging purposes).
+ *
+ * @param thrd An instance of a Thread object that still has its default
+ * thread name (as generated by the jvm Thread constructor). This should
+ * always be of the form "Thread-N", where N is a unique thread id
+ * generated by the jvm. Ex. "Thread-0", "Thread-1", etc.
+ *
+ * @return The received thread's name has been set to a new string of the form
+ * [newName + "_n"], where 'n' is a unique thread id originally generated
+ * by the jvm Thread constructor. If the default name of the thread has
+ * been changed before getting here, then nothing is done.
+ *
+ **/
+ public static void setUniqueThreadName(Thread thrd, String newName) {
+
+ // First, pull off the unique thread id already found in thrd's default name;
+ // we do so by searching for the '-' character, and then counting everything
+ // after it as a N.
+ if (thrd.getName().indexOf("Thread-") == -1) {
+ // default name has been changed; don't do anything.
+ return;
+ }
+ else {
+ String oldName = thrd.getName();
+ thrd.setName(newName + "_" +
+ oldName.substring(oldName.indexOf("-")+1, oldName.length()));
+ } // end else.
+
+ return;
+
+ }
+
+ /*******************************************************************************/
+ /* Protected methods */
+ /*******************************************************************************/
+ /**
+ * Remove session from session table
+ *
+ * @param sessionid id of session to be removed
+ */
+ protected void removeFromSessionTable(int sessionid)
+ {
+ sessionTable.remove(new Integer(sessionid));
+ }
+
+ /**
+ * processCommands reads and processes NetworkServerControlImpl commands sent
+ * to the network server over the socket. The protocol used is
+ * 4 bytes - String CMD:
+ * 2 bytes - Protocol version
+ * 1 byte - length of locale (0 for default)
+ * n bytes - locale
+ * 1 byte - length of codeset (0 for default)
+ * n bytes - codeset
+ * 1 byte - command
+ * n bytes - parameters for the command
+ * The server returns
+ * 4 bytes - String RPY:
+ * for most commands
+ * 1 byte - command result, 0 - OK, 1 - warning, 2 - error
+ * if warning or error
+ * 1 bytes - length of message key
+ * n bytes - message key
+ * 1 byte - number of parameters to message
+ * {2 bytes - length of parameter
+ * n bytes - parameter} for each parameter
+ * for sysinfo
+ * 1 byte - command result, 0 - OK, 1 - warning, 2 - error
+ * if OK
+ * 2 bytes - length of sysinfo
+ * n bytes - sysinfo
+ *
+ *
+ * Note, the 3rd byte of the command must not be 'D0' to distinquish it
+ * from DSS structures.
+ * The protocol for the parameters for each command follows:
+ *
+ * Command: trace <connection id> {on | off}
+ * Protocol:
+ * 4 bytes - connection id - connection id of 0 means all sessions
+ * 1 byte - 0 off, 1 on
+ *
+ * Command: logConnections {on | off}
+ * Protocol:
+ * 1 byte - 0 off, 1 on
+ *
+ * Command: shutdown
+ * No parameters
+ *
+ * Command: sysinfo
+ * No parameters
+ *
+ * Command: dbstart
+ * Protocol:
+ * 2 bytes - length of database name
+ * n bytes - database name
+ * 2 bytes - length of boot password
+ * n bytes - boot password
+ * 2 bytes - length of encryption algorithm
+ * n bytes - encryption algorithm
+ * 2 bytes - length of encryption provider
+ * n bytes - encryption provider
+ * 2 bytes - length of user name
+ * n bytes - user name
+ * 2 bytes - length of password
+ * n bytes - password
+ *
+ * Command: dbshutdown
+ * Protocol:
+ * 2 bytes - length of database name
+ * n bytes - database name
+ * 2 bytes - length of user name
+ * n bytes - user name
+ * 2 bytes - length of password
+ * n bytes - password
+ *
+ * Command: connpool
+ * Protocol:
+ * 2 bytes - length of database name, if 0, default for all databases
+ * is set
+ * n bytes - database name
+ * 2 bytes - minimum number of connections, if 0, connection pool not used
+ * if value is -1 use default
+ * 2 bytes - maximum number of connections, if 0, connections are created
+ * as needed, if value is -1 use default
+ *
+ * Command: maxthreads
+ * Protocol:
+ * 2 bytes - maximum number of threads
+ *
+ * Command: timeslice
+ * Protocol:
+ * 4 bytes - timeslice value
+ *
+ * Command: tracedirectory
+ * Protocol:
+ * 2 bytes - length of directory name
+ * n bytes - directory name
+ *
+ * Command: test connection
+ * Protocol:
+ * 2 bytes - length of database name if 0, just the connection
+ * to the network server is tested and user name and
+ * password aren't sent
+ * n bytes - database name
+ * 2 bytes - length of user name (optional)
+ * n bytes - user name
+ * 2 bytes - length of password (optional)
+ * n bytes - password
+ *
+ * The calling routine is synchronized so that multiple threads don't clobber each
+ * other. This means that configuration commands will be serialized.
+ * This shouldn't be a problem since they should be fairly rare.
+ *
+ * @param reader input reader for command
+ * @param writer output writer for command
+ * @param session session information
+ *
+ * @exception Throwable throws an exception if an error occurs
+ */
+ protected synchronized void processCommands(DDMReader reader, DDMWriter writer,
+ Session session) throws Throwable
+ {
+ try {
+ String protocolStr = reader.readCmdString(4);
+ String locale = DEFAULT_LOCALE;
+ String codeset = null;
+ // get the version
+ int version = reader.readNetworkShort();
+ if (version <= 0 || version > PROTOCOL_VERSION)
+ throw new Throwable(langUtil.getTextMessage("DRDA_UnknownProtocol.S", new Integer(version).toString()));
+ int localeLen = reader.readByte();
+ if (localeLen > 0)
+ {
+ currentSession = session;
+ locale = reader.readCmdString(localeLen);
+ session.langUtil = new LocalizedResource(codeset,locale,DRDA_PROP_MESSAGES);
+ }
+ String notLocalMessage = null;
+ // for now codesetLen is always 0
+ int codesetLen = reader.readByte();
+ int command = reader.readByte();
+ if (command != COMMAND_TESTCONNECTION)
+ {
+ try {
+ checkAddressIsLocal(session.clientSocket.getInetAddress());
+ }catch (Exception e)
+ {
+ notLocalMessage = e.getMessage();
+ }
+ }
+ if (notLocalMessage != null)
+ {
+ sendMessage(writer, ERROR,notLocalMessage);
+ session.langUtil = null;
+ currentSession = null;
+ return;
+ }
+
+ switch(command)
+ {
+ case COMMAND_SHUTDOWN:
+ sendOK(writer);
+ directShutdown();
+ break;
+ case COMMAND_TRACE:
+ sessionArg = reader.readNetworkInt();
+ boolean on = (reader.readByte() == 1);
+ if (setTrace(on))
+ {
+ sendOK(writer);
+ }
+ else
+ {
+ sendMessage(writer, ERROR,
+ localizeMessage("DRDA_SessionNotFound.U",
+ (session.langUtil == null) ? langUtil : session.langUtil,
+ new String [] {new Integer(sessionArg).toString()}));
+ }
+ break;
+ case COMMAND_TRACEDIRECTORY:
+ setTraceDirectory(reader.readCmdString());
+ sendOK(writer);
+ consolePropertyMessage("DRDA_TraceDirectoryChange.I", traceDirectory);
+ break;
+ case COMMAND_TESTCONNECTION:
+ databaseArg = reader.readCmdString();
+ userArg = reader.readCmdString();
+ passwordArg = reader.readCmdString();
+ if (databaseArg != null)
+ connectToDatabase(writer, databaseArg, userArg, passwordArg);
+ else
+ sendOK(writer);
+ break;
+ case COMMAND_LOGCONNECTIONS:
+ boolean log = (reader.readByte() == 1);
+ setLogConnections(log);
+ sendOK(writer);
+ consolePropertyMessage("DRDA_LogConnectionsChange.I",
+ (log ? "DRDA_ON.I" : "DRDA_OFF.I"));
+ break;
+ case COMMAND_SYSINFO:
+ sendSysInfo(writer);
+ break;
+ case COMMAND_PROPERTIES:
+ sendPropInfo(writer);
+ break;
+ case COMMAND_RUNTIME_INFO:
+ sendRuntimeInfo(writer);
+ break;
+ case COMMAND_MAXTHREADS:
+ int max = reader.readNetworkInt();
+ try {
+ setMaxThreads(max);
+ }catch (Exception e) {
+ sendMessage(writer, ERROR, e.getMessage());
+ return;
+ }
+ int newval = getMaxThreads();
+ sendOKInt(writer, newval);
+ consolePropertyMessage("DRDA_MaxThreadsChange.I",
+ new Integer(newval).toString());
+ break;
+ case COMMAND_TIMESLICE:
+ int timeslice = reader.readNetworkInt();
+ try {
+ setTimeSlice(timeslice);
+ }catch (Exception e) {
+ sendMessage(writer, ERROR, e.getMessage());
+ return;
+ }
+ newval = getTimeSlice();
+ sendOKInt(writer, newval);
+ consolePropertyMessage("DRDA_TimeSliceChange.I",
+ new Integer(newval).toString());
+ break;
+ }
+ } catch (DRDAProtocolException e) {
+ //we need to handle this since we aren't in DRDA land here
+ consoleExceptionPrintTrace(e);
+
+ } catch (Exception e) {
+ consoleExceptionPrintTrace(e);
+ }
+ finally {
+ session.langUtil = null;
+ currentSession = null;
+ }
+ }
+ /**
+ * Get the next session for the thread to work on
+ * Called from DRDAConnThread after session completes or timeslice
+ * exceeded.
+ *
+ * If there is a waiting session, pick it up and put currentSession
+ * at the back of the queue if there is one.
+ * @param currentSession session thread is currently working on
+ *
+ * @return next session to work on, could be same as current session
+ */
+ protected Session getNextSession(Session currentSession)
+ {
+ Session retval = null;
+ if (shutdown == true)
+ return retval;
+ synchronized (runQueue)
+ {
+ try {
+ // nobody waiting - go on with current session
+ if (runQueue.size() == 0)
+ {
+ // no current session - wait for some work
+ if (currentSession == null)
+ {
+ while (runQueue.size() == 0)
+ {
+ // This thread has nothing to do now so
+ // we will add it to freeThreads
+ freeThreads++;
+ runQueue.wait();
+ if (shutdown == true)
+ return null;
+ freeThreads--;
+ }
+ }
+ else
+ return currentSession;
+ }
+ retval = (Session) runQueue.elementAt(0);
+ runQueue.removeElementAt(0);
+ if (currentSession != null)
+ runQueueAdd(currentSession);
+ } catch (InterruptedException e) {
+ // If for whatever reason (ex. database shutdown) a waiting thread is
+ // interrupted while in this method, that thread is going to be
+ // closed down, so we need to decrement the number of threads
+ // that will be available for use.
+ freeThreads--;
+ }
+ }
+ return retval;
+ }
+ /**
+ * Get the stored application requester or store if we haven't seen it yet
+ *
+ * @param appRequester Application Requester to look for
+ *
+ * @return stored application requester
+ */
+ protected AppRequester getAppRequester(AppRequester appRequester)
+ {
+ AppRequester s = null;
+
+ if (SanityManager.DEBUG) {
+ if (appRequester == null)
+ SanityManager.THROWASSERT("null appRequester in getAppRequester");
+ }
+
+ if (!appRequesterTable.isEmpty())
+ s = (AppRequester)appRequesterTable.get(appRequester.prdid);
+
+ if (s == null)
+ {
+ appRequesterTable.put(appRequester.prdid, appRequester);
+ return appRequester;
+ }
+ else
+ {
+ //compare just in case there are some differences
+ //if they are different use the one we just read in
+ if (s.equals(appRequester))
+ return s;
+ else
+ return appRequester;
+ }
+ }
+ /**
+ * Get the server manager level for a given manager
+ *
+ * @param manager codepoint for manager
+ * @return manager level
+ */
+ protected int getManagerLevel(int manager)
+ {
+ int mindex = CodePoint.getManagerIndex(manager);
+ if (SanityManager.DEBUG) {
+ if (mindex == CodePoint.UNKNOWN_MANAGER)
+ SanityManager.THROWASSERT("manager out of bounds");
+ }
+ return MGR_LEVELS[mindex];
+ }
+ /**
+ * Check whether a CCSID code page is supported
+ *
+ * @param ccsid CCSID to check
+ * @return true if supported; false otherwise
+ */
+ protected boolean supportsCCSID(int ccsid)
+ {
+ try {
+ CharacterEncodings.getJavaEncoding(ccsid);
+ }
+ catch (Exception e) {
+ return false;
+ }
+ return true;
+ }
+ /**
+ * Put property message on console
+ *
+ * @param msgProp message property key
+ *
+ * @exception throws an Exception if an error occurs
+ */
+ protected void consolePropertyMessage(String msgProp)
+ throws Exception
+ {
+ consolePropertyMessageWork(msgProp, null);
+ }
+ /**
+ * Put property message on console
+ *
+ * @param msgProp message property key
+ * @param arg argument for message
+ *
+ * @exception throws an Exception if an error occurs
+ */
+ protected void consolePropertyMessage(String msgProp, String arg)
+ throws Exception
+ {
+ consolePropertyMessageWork(msgProp, new String [] {arg});
+ }
+ /**
+ * Put property message on console
+ *
+ * @param msgProp message property key
+ * @param args argument array for message
+ *
+ * @exception throws an Exception if an error occurs
+ */
+ protected void consolePropertyMessage(String msgProp, String [] args)
+ throws Exception
+ {
+ consolePropertyMessageWork(msgProp, args);
+ }
+ /**
+ * Is this the command protocol
+ *
+ * @param val
+ */
+ protected static boolean isCmd(String val)
+ {
+ if (val.equals(COMMAND_HEADER))
+ return true;
+ else
+ return false;
+ }
+
+ /*******************************************************************************/
+ /* Private methods */
+ /*******************************************************************************/
+ /**
+ * Write Command reply
+ *
+ * @param writer writer to use
+ *
+ * @exception throws Exception if a problem occurs sending OK
+ */
+ private void writeCommandReplyHeader(DDMWriter writer) throws Exception
+ {
+ writer.setCMDProtocol();
+ writer.writeString(REPLY_HEADER);
+ }
+
+ /**
+ * Send OK from server to client after processing a command
+ *
+ * @param writer writer to use for sending OK
+ *
+ * @exception throws Exception if a problem occurs sending OK
+ */
+ private void sendOK(DDMWriter writer) throws Exception
+ {
+ writeCommandReplyHeader(writer);
+ writer.writeByte(OK);
+ writer.flush();
+ }
+ /**
+ * Send OK and int value
+ *
+ * @param writer writer to use for sending
+ * @param val int val to send
+ *
+ * @exception throws Exception if a problem occurs
+ */
+ private void sendOKInt(DDMWriter writer, int val) throws Exception
+ {
+ writeCommandReplyHeader(writer);
+ writer.writeByte(OK);
+ writer.writeNetworkInt(val);
+ writer.flush();
+ }
+ /**
+ * Send Error or Warning from server to client after processing a command
+ *
+ * @param writer writer to use for sending message
+ * @param messageType 1 for Warning, 2 for Error 3 for SQLError
+ * @param message message
+ *
+ * @exception throws Exception if a problem occurs sending message
+ */
+ private void sendMessage(DDMWriter writer, int messageType, String message)
+ throws Exception
+ {
+ writeCommandReplyHeader(writer);
+ writer.writeByte(messageType);
+ writer.writeLDString(message);
+ writer.flush();
+ }
+ /**
+ * Send SQL Exception from server to client after processing a command
+ *
+ * @param writer writer to use for sending message
+ * @param se Cloudscape exception
+ * @param type type of exception, SQLERROR or SQLWARNING
+ *
+ * @exception throws Exception if a problem occurs sending message
+ */
+ private void sendSQLMessage(DDMWriter writer, SQLException se, int type)
+ throws Exception
+ {
+ StringBuffer locMsg = new StringBuffer();
+ //localize message if necessary
+ while (se != null)
+ {
+ if (currentSession != null && currentSession.langUtil != null)
+ {
+ locMsg.append(se.getSQLState()+":"+
+ MessageService.getLocalizedMessage(
+ currentSession.langUtil.getLocale(), ((EmbedSQLException)se).getMessageId(),
+ ((EmbedSQLException)se).getArguments()));
+ }
+ else
+ locMsg.append(se.getSQLState()+":"+se.getMessage());
+ se = se.getNextException();
+ if (se != null)
+ locMsg.append("\n");
+ }
+ sendMessage(writer, type, locMsg.toString());
+ }
+ /**
+ * Send SysInfo information from server to client
+ *
+ * @param writer writer to use for sending sysinfo
+ *
+ * @exception throws Exception if a problem occurs sending value
+ */
+ private void sendSysInfo(DDMWriter writer) throws Exception
+ {
+ StringBuffer sysinfo = new StringBuffer();
+ sysinfo.append(getNetSysInfo());
+ sysinfo.append(getCLSSysInfo());
+ try {
+ writeCommandReplyHeader(writer);
+ writer.writeByte(0); //O.K.
+ writer.writeLDString(sysinfo.toString());
+ } catch (DRDAProtocolException e) {
+ consolePropertyMessage("DRDA_SysInfoWriteError.S", e.getMessage());
+ }
+ writer.flush();
+ }
+
+ /**
+ * Send RuntimeInfo information from server to client
+ *
+ * @param writer writer to use for sending sysinfo
+ *
+ * @exception throws Exception if a problem occurs sending value
+ */
+ private void sendRuntimeInfo(DDMWriter writer) throws Exception
+ {
+ try {
+ writeCommandReplyHeader(writer);
+ writer.writeByte(0); //O.K.
+ writer.writeLDString(getRuntimeInfo());
+ } catch (DRDAProtocolException e) {
+ consolePropertyMessage("DRDA_SysInfoWriteError.S", e.getMessage());
+ }
+ writer.flush();
+ }
+
+
+
+ /**
+ * Send property information from server to client
+ *
+ * @param writer writer to use for sending sysinfo
+ *
+ * @exception throws Exception if a problem occurs sending value
+ */
+ private void sendPropInfo(DDMWriter writer) throws Exception
+ {
+ try {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ Properties p = getPropertyValues();
+ p.store(out, "NetworkServerControl properties");
+ try {
+ writeCommandReplyHeader(writer);
+ writer.writeByte(0); //O.K.
+ writer.writeLDBytes(out.toByteArray());
+ } catch (DRDAProtocolException e) {
+ consolePropertyMessage("DRDA_PropInfoWriteError.S", e.getMessage());
+ }
+ writer.flush();
+ }
+ catch (Exception e) {
+ consoleExceptionPrintTrace(e);
+ }
+ }
+
+ /**
+ * Get Net Server information
+ *
+ * @return system information for the Network Server
+ */
+ private String getNetSysInfo()
+ {
+ StringBuffer sysinfo = new StringBuffer();
+ LocalizedResource localLangUtil = langUtil;
+ if (currentSession != null && currentSession.langUtil != null)
+ localLangUtil = currentSession.langUtil;
+ sysinfo.append(localLangUtil.getTextMessage("DRDA_SysInfoBanner.I")+ "\n");
+ sysinfo.append(localLangUtil.getTextMessage("DRDA_SysInfoVersion.I")+ " " + att_srvrlslv);
+ sysinfo.append(" ");
+ sysinfo.append(localLangUtil.getTextMessage("DRDA_SysInfoBuild.I")+ " " + buildNumber);
+ sysinfo.append(" ");
+ sysinfo.append(localLangUtil.getTextMessage("DRDA_SysInfoDrdaPRDID.I")+ " " + prdId);
+ if (SanityManager.DEBUG)
+ {
+ sysinfo.append(" ** SANE BUILD **");
+ }
+ sysinfo.append("\n");
+ // add property information
+ Properties p = getPropertyValues();
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ PrintStream ps = new PrintStream(bos);
+ p.list(ps);
+ sysinfo.append(bos.toString());
+ return sysinfo.toString();
+ }
+
+ /**
+ * @see NetworkServerControl#getRuntimeInfo
+ */
+ private String getRuntimeInfo()
+ {
+ return buildRuntimeInfo(langUtil);
+ }
+
+ /**
+ * Get Cloudscape information
+ *
+ * @return system information for Cloudscape
+ *
+ * @exception throws IOException if a problem occurs encoding string
+ */
+ private String getCLSSysInfo() throws IOException
+ {
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ LocalizedResource localLangUtil = langUtil;
+ if (currentSession != null && currentSession.langUtil != null)
+ localLangUtil = currentSession.langUtil;
+ LocalizedOutput aw = localLangUtil.getNewOutput(bos);
+ org.apache.derby.impl.tools.sysinfo.Main.getMainInfo(aw, false);
+ return bos.toString();
+ }
+
+ /**
+ * Execute the command given on the command line
+ *
+ * @param args array of arguments indicating command to be executed
+ *
+ * @exception Exception throws an exception if an error occurs
+ * see class comments for more information
+ */
+ public static void execute(String args[])
+ {
+ NetworkServerControlImpl server = null;
+ try {
+ server = new NetworkServerControlImpl();
+ server.executeWork(args);
+ } catch (Exception e){
+ //if there was an error, exit(1)
+ if ((e.getMessage() == null) ||
+ !e.getMessage().equals(NetworkServerControlImpl.UNEXPECTED_ERR))
+ {
+ if (server != null)
+ server.consoleExceptionPrint(e);
+ else
+ e.printStackTrace(); // default output stream is System.out
+ }
+ // else, we've already printed a trace, so just exit.
+ System.exit(1);
+ }
+ System.exit(0);
+ }
+
+
+ /**
+ * Execute the command given on the command line
+ *
+ * @param args array of arguments indicating command to be executed
+ *
+ * @exception Exception throws an exception if an error occurs
+ * see class comments for more information
+ */
+ protected void executeWork(String args[]) throws Exception
+ {
+ // For convenience just use NetworkServerControlImpls log writer for user messages
+ logWriter = makePrintWriter(System.out);
+
+ int command = 0;
+ if (args.length > 0)
+ command = findCommand(args);
+ else
+ {
+ consolePropertyMessage("DRDA_NoArgs.U");
+ }
+
+ // if we didn't have a valid command just return - error already generated
+ if (command == COMMAND_UNKNOWN)
+ return;
+
+ // check that we have the right number of required arguments
+ if (commandArgs.size() != COMMAND_ARGS[command])
+ consolePropertyMessage("DRDA_InvalidNoArgs.U", COMMANDS[command]);
+ int min;
+ int max;
+
+
+ switch (command)
+ {
+ case COMMAND_START:
+ blockingStart(makePrintWriter(System.out));
+ break;
+ case COMMAND_SHUTDOWN:
+ shutdown();
+ consolePropertyMessage("DRDA_ShutdownSuccess.I");
+ break;
+ case COMMAND_TRACE:
+ {
+ boolean on = isOn((String)commandArgs.elementAt(0));
+ trace(sessionArg, on);
+ consoleTraceMessage(sessionArg, on);
+ break;
+ }
+ case COMMAND_TRACEDIRECTORY:
+ setTraceDirectory((String) commandArgs.elementAt(0));
+ consolePropertyMessage("DRDA_TraceDirectoryChange.I", traceDirectory);
+ break;
+ case COMMAND_TESTCONNECTION:
+ ping();
+ consolePropertyMessage("DRDA_ConnectionTested.I", new String []
+ {hostArg, (new Integer(portNumber)).toString()});
+ break;
+ case COMMAND_LOGCONNECTIONS:
+ {
+ boolean on = isOn((String)commandArgs.elementAt(0));
+ logConnections(on);
+ consolePropertyMessage("DRDA_LogConnectionsChange.I", on ? "DRDA_ON.I" : "DRDA_OFF.I");
+ break;
+ }
+ case COMMAND_SYSINFO:
+ {
+ String info = sysinfo();
+ consoleMessage(info);
+ break;
+ }
+ case COMMAND_MAXTHREADS:
+ max = 0;
+ try{
+ max = Integer.parseInt((String)commandArgs.elementAt(0));
+ }catch(NumberFormatException e){
+ consolePropertyMessage("DRDA_InvalidValue.U", new String []
+ {(String)commandArgs.elementAt(0), "maxthreads"});
+ }
+ if (max < MIN_MAXTHREADS)
+ consolePropertyMessage("DRDA_InvalidValue.U", new String []
+ {new Integer(max).toString(), "maxthreads"});
+ netSetMaxThreads(max);
+
+ break;
+ case COMMAND_RUNTIME_INFO:
+ String reply = runtimeInfo();
+ consoleMessage(reply);
+ break;
+ case COMMAND_TIMESLICE:
+ int timeslice = 0;
+ String timeSliceArg = (String)commandArgs.elementAt(0);
+ try{
+ timeslice = Integer.parseInt(timeSliceArg);
+ }catch(NumberFormatException e){
+ consolePropertyMessage("DRDA_InvalidValue.U", new String []
+ {(String)commandArgs.elementAt(0), "timeslice"});
+ }
+ if (timeslice < MIN_TIMESLICE)
+ consolePropertyMessage("DRDA_InvalidValue.U", new String []
+ {new Integer(timeslice).toString(), "timeslice"});
+ netSetTimeSlice(timeslice);
+
+ break;
+ default:
+ //shouldn't get here
+ if (SanityManager.DEBUG)
+ SanityManager.THROWASSERT("Invalid command in switch:"+ command);
+ }
+ }
+
+
+ /**
+ * Add session to the run queue
+ *
+ * @param clientSession session needing work
+ */
+ protected void runQueueAdd(Session clientSession)
+ {
+ synchronized(runQueue)
+ {
+ runQueue.addElement(clientSession);
+ runQueue.notify();
+ }
+ }
+ /**
+ * Go through the arguments and find the command and save the dash arguments
+ * and arguments to the command. Only one command is allowed in the argument
+ * list.
+ *
+ * @param args arguments to search
+ *
+ * @return command
+ */
+ private int findCommand(String [] args) throws Exception
+ {
+ try {
+ // process the dashArgs and pull out the command args
+ int i = 0;
+ int newpos = 0;
+ while (i < args.length)
+ {
+ if (args[i].startsWith("-"))
+ {
+ newpos = processDashArg(i, args);
+ if (newpos == i)
+ commandArgs.addElement(args[i++]);
+ else
+ i = newpos;
+ }
+ else
+ commandArgs.addElement(args[i++]);
+ }
+
+ // look up command
+ if (commandArgs.size() > 0)
+ {
+ for (i = 0; i < COMMANDS.length; i++)
+ {
+ if (StringUtil.SQLEqualsIgnoreCase(COMMANDS[i],
+ (String)commandArgs.firstElement()))
+ {
+ commandArgs.removeElementAt(0);
+ return i;
+ }
+ }
+ }
+ // didn't find command
+ consolePropertyMessage("DRDA_UnknownCommand.U",
+ (String) commandArgs.firstElement());
+ } catch (Exception e) {
+ if (e.getMessage().equals(NetworkServerControlImpl.UNEXPECTED_ERR))
+ throw e;
+ //Ignore expected exceptions, they will have been
+ //handled by the consolePropertyMessage routine
+ }
+ return COMMAND_UNKNOWN;
+ }
+ /**
+ * Get the dash argument. Optional arguments are formated as -x value.
+ *
+ * @param pos starting point
+ * @param args arguments to search
+ *
+ * @return command
+ *
+ * @exception Exception thrown if an error occurs
+ */
+ private int processDashArg(int pos, String[] args)
+ throws Exception
+ {
+ //check for a negative number
+ char c = args[pos].charAt(1);
+ if (c >= '0' && c <= '9')
+ return pos;
+ int dashArg = -1;
+ for (int i = 0; i < DASHARGS.length; i++)
+ {
+ if (DASHARGS[i].equals(args[pos].substring(1)))
+ {
+ dashArg = i;
+ pos++;
+ break;
+ }
+ }
+ if (dashArg == -1)
+ consolePropertyMessage("DRDA_UnknownArgument.U", args[pos]);
+ switch (dashArg)
+ {
+ case DASHARG_PORT:
+ if (pos < args.length)
+ {
+ try{
+ portNumber = Integer.parseInt(args[pos]);
+ }catch(NumberFormatException e){
+ consolePropertyMessage("DRDA_InvalidValue.U",
+ new String [] {args[pos], "DRDA_PortNumber.I"});
+ }
+ }
+ else
+ consolePropertyMessage("DRDA_MissingValue.U", "DRDA_PortNumber.I");
+ break;
+ case DASHARG_HOST:
+ if (pos < args.length)
+ {
+ hostArg = args[pos];
+ }
+ else
+ consolePropertyMessage("DRDA_MissingValue.U", "DRDA_Host.I");
+ break;
+ case DASHARG_DATABASE:
+ if (pos < args.length)
+ databaseArg = args[pos];
+ else
+ consolePropertyMessage("DRDA_MissingValue.U",
+ "DRDA_DatabaseDirectory.I");
+ break;
+ case DASHARG_USER:
+ if (pos < args.length)
+ {
+ userArg = args[pos++];
+ if (pos < args.length)
+ passwordArg = args[pos];
+ else
+ consolePropertyMessage("DRDA_MissingValue.U",
+ "DRDA_Password.I");
+ }
+ else
+ consolePropertyMessage("DRDA_MissingValue.U", "DRDA_User.I");
+ break;
+ case DASHARG_ENCALG:
+ if (pos < args.length)
+ encAlgArg = args[pos];
+ else
+ consolePropertyMessage("DRDA_MissingValue.U",
+ "DRDA_EncryptionAlgorithm.I");
+ break;
+ case DASHARG_ENCPRV:
+ if (pos < args.length)
+ encPrvArg = args[pos];
+ else
+ consolePropertyMessage("DRDA_MissingValue.U",
+ "DRDA_EncryptionProvider.I");
+ break;
+ case DASHARG_LOADSYSIBM:
+ break;
+ case DASHARG_SESSION:
+ if (pos < args.length)
+ try{
+ sessionArg = Integer.parseInt(args[pos]);
+ }catch(NumberFormatException e){
+ consolePropertyMessage("DRDA_InvalidValue.U",
+ new String [] {args[pos], "DRDA_Session.I"});
+ }
+ else
+ consolePropertyMessage("DRDA_MissingValue.U", "DRDA_Session.I");
+ break;
+ default:
+ //shouldn't get here
+ }
+ return pos+1;
+ }
+
+ /**
+ * Is string "on" or "off"
+ *
+ * @param arg string to check
+ *
+ * @return true if string is "on", false if string is "off"
+ *
+ * @exception Exception thrown if string is not one of "on" or "off"
+ */
+ private boolean isOn(String arg)
+ throws Exception
+ {
+ if (StringUtil.SQLEqualsIgnoreCase(arg, "on"))
+ return true;
+ else if (!StringUtil.SQLEqualsIgnoreCase(arg, "off"))
+ consolePropertyMessage("DRDA_OnOffValue.U", arg);
+ return false;
+ }
+
+ /**
+ * Set up client socket to send a command to the network server
+ *
+ * @exception Exception thrown if exception encountered
+ */
+ private void setUpSocket() throws Exception
+ {
+
+ try {
+ clientSocket = (Socket) AccessController.doPrivileged(
+ new PrivilegedExceptionAction() {
+
+ public Object run() throws UnknownHostException,IOException
+ {
+ if (hostAddress == null)
+ hostAddress = InetAddress.getByName(hostArg);
+
+ // JDK131 can't connect with a client
+ // socket with 0.0.0.0 (all addresses) so we will
+ // getLocalHost() which will suffice.
+ InetAddress connectAddress;
+ if (JVMInfo.JDK_ID <= JVMInfo.J2SE_13 &&
+ hostAddress.getHostAddress().equals("0.0.0.0"))
+ connectAddress = InetAddress.getLocalHost();
+ else
+ connectAddress = hostAddress;
+
+ return new Socket(connectAddress, portNumber);
+ }
+ }
+ );
+ } catch (PrivilegedActionException pae) {
+ Exception e1 = pae.getException();
+ if (e1 instanceof UnknownHostException) {
+ consolePropertyMessage("DRDA_UnknownHost.S", hostArg);
+ }
+ else if (e1 instanceof IOException) {
+ consolePropertyMessage("DRDA_NoIO.S",
+ new String [] {hostArg, (new Integer(portNumber)).toString()});
+ }
+ } catch (Exception e) {
+ // If we find other (unexpected) errors, we ultimately exit--so make
+ // sure we print the error message before doing so (Beetle 5033).
+ throwUnexpectedException(e);
+ }
+
+ try
+ {
+ clientIs = clientSocket.getInputStream();
+ clientOs = clientSocket.getOutputStream();
+ } catch (IOException e) {
+ consolePropertyMessage("DRDA_NoInputStream.I");
+ throw e;
+ }
+ }
+
+
+ private void checkAddressIsLocal(InetAddress inetAddr) throws UnknownHostException,Exception
+ {
+ for(int i = 0; i < localAddresses.size(); i++)
+ {
+ if (inetAddr.equals((InetAddress)localAddresses.get(i)))
+ {
+ return;
+ }
+ }
+ consolePropertyMessage("DRDA_NeedLocalHost.S", new String[] {inetAddr.getHostName(),serverSocket.getInetAddress().getHostName()});
+
+ }
+
+
+ /**
+ * Build local address list to allow admin commands.
+ *
+ * @param bindAddr Address on which server was started
+ *
+ * Note: Some systems may not support localhost.
+ * In that case a console message will print for the localhost entries,
+ * but the server will continue to start.
+ **/
+ private void buildLocalAddressList(InetAddress bindAddr)
+ {
+ localAddresses = new ArrayList(3);
+ localAddresses.add(bindAddr);
+ try {
+ localAddresses.add(InetAddress.getLocalHost());
+ localAddresses.add(InetAddress.getByName("localhost"));
+ }catch(UnknownHostException uhe)
+ {
+ try {
+ consolePropertyMessage("DRDA_UnknownHostWarning.I",uhe.getMessage());
+ } catch (Exception e)
+ { // just a warning shouldn't actually throw an exception
+ }
+ }
+ }
+
+ /**
+ * Routines for writing commands for NetworkServerControlImpl being used as a client
+ * to a server
+ */
+
+ /**
+ * Write command header consisting of command header string and protocol
+ * version and command
+ *
+ * @param command command to be written
+ *
+ * @exception Exception throws an exception if an error occurs
+ */
+ private void writeCommandHeader(int command) throws Exception
+ {
+ try {
+ writeString(COMMAND_HEADER);
+ commandOs.writeByte((byte)((PROTOCOL_VERSION & 0xf0) >> 8 ));
+ commandOs.writeByte((byte)(PROTOCOL_VERSION & 0x0f));
+
+ if (clientLocale != null && clientLocale != DEFAULT_LOCALE)
+ {
+ commandOs.writeByte(clientLocale.length());
+ commandOs.writeBytes(clientLocale);
+ }
+ else
+ commandOs.writeByte((byte) 0);
+ commandOs.writeByte((byte) 0);
+ commandOs.writeByte((byte) command);
+ }
+ catch (IOException e)
+ {
+ clientSocketError(e);
+ }
+ }
+ /**
+ * Write length delimited string string
+ *
+ * @param msg string to be written
+ *
+ * @exception Exception throws an exception if an error occurs
+ */
+ private void writeLDString(String msg) throws Exception
+ {
+ try {
+ if (msg == null)
+ {
+ commandOs.writeShort(0);
+ }
+ else
+ {
+ commandOs.writeShort(msg.length());
+ writeString(msg);
+ }
+ }
+ catch (IOException e)
+ {
+ clientSocketError(e);
+ }
+ }
+
+ /** Write string
+ *
+ * @param msg String to write
+ */
+
+ protected void writeString(String msg) throws Exception
+ {
+ byte[] msgBytes = msg.getBytes(DEFAULT_ENCODING);
+ commandOs.write(msgBytes,0,msgBytes.length);
+ }
+
+ /**
+ * Write short
+ *
+ * @param value value to be written
+ *
+ * @exception Exception throws an exception if an error occurs
+ */
+ private void writeShort(int value) throws Exception
+ {
+ try {
+ commandOs.writeByte((byte)((value & 0xf0) >> 8 ));
+ commandOs.writeByte((byte)(value & 0x0f));
+ }
+ catch (IOException e)
+ {
+ clientSocketError(e);
+ }
+ }
+ /**
+ * Write byte
+ *
+ * @param value value to be written
+ *
+ * @exception Exception throws an exception if an error occurs
+ */
+ private void writeByte(int value) throws Exception
+ {
+ try {
+ commandOs.writeByte((byte)(value & 0x0f));
+ }
+ catch (IOException e)
+ {
+ clientSocketError(e);
+ }
+ }
+ /**
+ * Send client message to server
+ *
+ *
+ * @exception Exception throws an exception if an error occurs
+ */
+ private void send() throws Exception
+ {
+ try {
+ byteArrayOs.writeTo(clientOs);
+ commandOs.flush();
+ byteArrayOs.reset(); //discard anything currently in the byte array
+ }
+ catch (IOException e)
+ {
+ clientSocketError(e);
+ }
+ }
+ /**
+ * Stream error writing to client socket
+ */
+ private void clientSocketError(IOException e) throws IOException
+ {
+ try {
+ consolePropertyMessage("DRDA_ClientSocketError.S", e.getMessage());
+ } catch (Exception ce) {} // catch the exception consolePropertyMessage will
+ // throw since we also want to print a stack trace
+ consoleExceptionPrintTrace(e);
+ throw e;
+ }
+ /**
+ * Read result from sending client message to server
+ *
+ * @exception Exception throws an exception if an error occurs
+ */
+ private void readResult() throws Exception
+ {
+ fillReplyBuffer();
+ readCommandReplyHeader();
+ if (replyBufferPos >= replyBufferCount)
+ consolePropertyMessage("DRDA_InvalidReplyTooShort.S");
+ int messageType = replyBuffer[replyBufferPos++] & 0xFF;
+ if (messageType == OK) // O.K.
+ return;
+ // get error and display and throw exception
+ String message = readLDString();
+ if (messageType == SQLERROR)
+ wrapSQLError(message);
+ else if (messageType == SQLWARNING)
+ wrapSQLWarning(message);
+ else
+ consolePropertyMessage(message);
+ }
+
+
+
+ /**
+ * Ensure the reply buffer is at large enought to hold all the data;
+ * don't just rely on OS level defaults
+ *
+ *
+ * @param minimumBytesNeeded size of buffer required
+ * @exception Exception throws an exception if a problem reading the reply
+ */
+ private void ensureDataInBuffer(int minimumBytesNeeded) throws Exception
+ {
+ // make sure the buffer is large enough
+ while ((replyBufferCount - replyBufferPos) < minimumBytesNeeded)
+ {
+ try {
+ int bytesRead = clientIs.read(replyBuffer, replyBufferCount, replyBuffer.length - replyBufferCount);
+ replyBufferCount += bytesRead;
+
+ } catch (IOException e)
+ {
+ clientSocketError(e);
+ }
+ }
+ }
+
+
+ /**
+ * Fill the reply buffer with the reply allocates a reply buffer if one doesn't
+ * exist
+ *
+ *
+ * @exception Exception throws an exception if a problem reading the reply
+ */
+ private void fillReplyBuffer() throws Exception
+ {
+ if (replyBuffer == null)
+ replyBuffer = new byte[MAXREPLY];
+ try {
+ replyBufferCount = clientIs.read(replyBuffer);
+ }
+ catch (IOException e)
+ {
+ clientSocketError(e);
+ }
+ if (replyBufferCount == -1)
+ consolePropertyMessage("DRDA_InvalidReplyTooShort.S");
+ replyBufferPos = 0;
+ }
+ /**
+ * Read the command reply header from the server
+ *
+ * @exception Exception throws an exception if an error occurs
+ */
+ private void readCommandReplyHeader() throws Exception
+ {
+ ensureDataInBuffer(REPLY_HEADER_LENGTH);
+ if (replyBufferCount < REPLY_HEADER_LENGTH)
+ {
+ consolePropertyMessage("DRDA_InvalidReplyHeader1.S", Integer.toString(replyBufferCount));
+ }
+ String header = new String(replyBuffer, 0, REPLY_HEADER_LENGTH, DEFAULT_ENCODING);
+ if (!header.equals(REPLY_HEADER))
+ {
+ consolePropertyMessage("DRDA_InvalidReplyHeader2.S", header);
+ }
+ replyBufferPos += REPLY_HEADER_LENGTH;
+ }
+ /**
+ * Read short from buffer
+ * @exception Exception throws an exception if an error occurs
+ */
+ private int readShort() throws Exception
+ {
+ ensureDataInBuffer(2);
+ if (replyBufferPos + 2 > replyBufferCount)
+ consolePropertyMessage("DRDA_InvalidReplyTooShort.S");
+ return ((replyBuffer[replyBufferPos++] & 0xff) << 8) +
+ (replyBuffer[replyBufferPos++] & 0xff);
+ }
+ /**
+ * Read int from buffer
+ * @exception Exception throws an exception if an error occurs
+ */
+ private int readInt() throws Exception
+ {
+ ensureDataInBuffer(4);
+ if (replyBufferPos + 4 > replyBufferCount)
+ consolePropertyMessage("DRDA_InvalidReplyTooShort.S");
+ return ((replyBuffer[replyBufferPos++] & 0xff) << 24) +
+ ((replyBuffer[replyBufferPos++] & 0xff) << 16) +
+ ((replyBuffer[replyBufferPos++] & 0xff) << 8) +
+ (replyBuffer[replyBufferPos++] & 0xff);
+ }
+ /**
+ * Read String reply
+ *
+ * @param msgKey error message key
+ * @return string value or null
+ * @exception Exception throws an error if problems reading reply
+ */
+ private String readStringReply(String msgKey) throws Exception
+ {
+ fillReplyBuffer();
+ readCommandReplyHeader();
+ if (replyBuffer[replyBufferPos++] == 0) // O.K.
+ return readLDString();
+ else
+ consolePropertyMessage(msgKey);
+ return null;
+
+ }
+
+
+
+
+ /**
+ * Read length delimited string from a buffer
+ *
+ * @return string value from buffer
+ * @exception Exception throws an error if problems reading reply
+ */
+ private String readLDString() throws Exception
+ {
+ int strlen = readShort();
+ ensureDataInBuffer(strlen);
+ if (replyBufferPos + strlen > replyBufferCount)
+ consolePropertyMessage("DRDA_InvalidReplyTooShort.S");
+ String retval= new String(replyBuffer, replyBufferPos, strlen, DEFAULT_ENCODING);
+ replyBufferPos += strlen;
+ return retval;
+ }
+ /**
+ * Read Bytes reply
+ *
+ * @param msgKey error message key
+ * @return string value or null
+ * @exception Exception throws an error if problems reading reply
+ */
+ private byte [] readBytesReply(String msgKey) throws Exception
+ {
+ fillReplyBuffer();
+ readCommandReplyHeader();
+ if (replyBuffer[replyBufferPos++] == 0) // O.K.
+ return readLDBytes();
+ else
+ consolePropertyMessage(msgKey);
+ return null;
+
+ }
+ /**
+ * Read length delimited bytes from a buffer
+ *
+ * @return byte array from buffer
+ * @exception Exception throws an error if problems reading reply
+ */
+ private byte[] readLDBytes() throws Exception
+ {
+ int len = readShort();
+ ensureDataInBuffer(len);
+ if (replyBufferPos + len > replyBufferCount)
+ consolePropertyMessage("DRDA_InvalidReplyTooShort.S");
+ byte [] retval = new byte[len];
+ for (int i = 0; i < len; i++)
+ retval[i] = replyBuffer[replyBufferPos++];
+ return retval;
+ }
+
+ /**
+ * Get property info
+ *
+ * @return system properties
+ */
+ private void getPropertyInfo() throws Exception
+ {
+ //set values according to properties
+
+ String directory = PropertyUtil.getSystemProperty(Property.SYSTEM_HOME_PROPERTY);
+ String propval = PropertyUtil.getSystemProperty(
+ Property.DRDA_PROP_LOGCONNECTIONS);
+ if (propval != null && StringUtil.SQLEqualsIgnoreCase(propval,"true"))
+ setLogConnections(true);
+ propval = PropertyUtil.getSystemProperty(Property.DRDA_PROP_TRACEALL);
+ if (propval != null && StringUtil.SQLEqualsIgnoreCase(propval,
+ "true"))
+ setTraceAll(true);
+
+ setTraceDirectory(PropertyUtil.getSystemProperty(
+ Property.DRDA_PROP_TRACEDIRECTORY));
+
+ propval = PropertyUtil.getSystemProperty(
+ Property.DRDA_PROP_MINTHREADS);
+ if (propval != null)
+ setMinThreads(getIntPropVal(Property.DRDA_PROP_MINTHREADS, propval));
+
+ propval = PropertyUtil.getSystemProperty(
+ Property.DRDA_PROP_MAXTHREADS);
+ if (propval != null)
+ setMaxThreads(getIntPropVal(Property.DRDA_PROP_MAXTHREADS, propval));
+
+
+ propval = PropertyUtil.getSystemProperty(
+ Property.DRDA_PROP_TIMESLICE);
+ if (propval != null)
+ setTimeSlice(getIntPropVal(Property.DRDA_PROP_TIMESLICE, propval));
+
+ propval = PropertyUtil.getSystemProperty(
+ Property.DRDA_PROP_PORTNUMBER);
+ if (propval != null)
+ portNumber = getIntPropVal(Property.DRDA_PROP_PORTNUMBER, propval);
+
+ propval = PropertyUtil.getSystemProperty(
+ Property.DRDA_PROP_KEEPALIVE);
+ if (propval != null &&
+ StringUtil.SQLEqualsIgnoreCase(propval,"false"))
+ keepAlive = false;
+
+ propval = PropertyUtil.getSystemProperty(
+ Property.DRDA_PROP_HOSTNAME);
+ if (propval != null)
+ hostArg = propval;
+
+ propval = PropertyUtil.getSystemProperty(
+ NetworkServerControlImpl.DRDA_PROP_DEBUG);
+ if (propval != null && StringUtil.SQLEqualsIgnoreCase(propval, "true"))
+ debugOutput = true;
+
+ //RESOLVE: Need to clean this up. There should be just a
+ // server API call to get the log location
+ // Determine errror log location
+ propval = PropertyUtil.getSystemProperty(
+ NetworkServerControlImpl.NETWORKSERVER_PROP_STREAM_ERROR_FIELD);
+ if (propval == null)
+ propval = PropertyUtil.getSystemProperty(
+ NetworkServerControlImpl.NETWORKSERVER_PROP_STREAM_ERROR_METHOD);
+ if (propval == null)
+ {
+ propval = PropertyUtil.getSystemProperty(
+ NetworkServerControlImpl.NETWORKSERVER_PROP_STREAM_ERROR_FILE);
+ if (propval == null)
+ propval = "derby.log";
+ }
+ File errorFile = new File(propval);
+ if (errorFile.isAbsolute())
+ errorLogLocation = errorFile.getPath();
+ else
+ errorLogLocation = (new File
+ (directory,propval)).getPath();
+
+ }
+
+ /**
+ * Get integer property values
+ *
+ * @param propName property name
+ * @param propVal string property value
+ * @return integer value
+ *
+ * @exception Exception if not a valid integer
+ */
+ private int getIntPropVal(String propName, String propVal)
+ throws Exception
+ {
+ int val = 0;
+ try {
+ val = (new Integer(propVal)).intValue();
+ } catch (Exception e)
+ {
+ consolePropertyMessage("DRDA_InvalidPropVal.S", new String []
+ {propName, propVal});
+ }
+ return val;
+ }
+
+ /**
+ * Handle console error message
+ * - display on console and if it is a user error, display usage
+ * - if user error or severe error, throw exception with message key and message
+ *
+ * @param messageKey message key
+ * @param args arguments to message
+ *
+ * @exception throws an Exception if an error occurs
+ */
+ private void consolePropertyMessageWork(String messageKey, String [] args)
+ throws Exception
+ {
+ String locMsg = null;
+
+ int type = getMessageType(messageKey);
+
+ if (type == ERRTYPE_UNKNOWN)
+ locMsg = messageKey;
+ else
+ locMsg = localizeMessage(messageKey, langUtil, args);
+
+ //display on the console
+ consoleMessage(locMsg);
+
+ //if it is a user error display usage
+ if (type == ERRTYPE_USER)
+ usage();
+
+ //we may want to use a different locale for throwing the exception
+ //since this can be sent to a browser with a different locale
+ if (currentSession != null &&
+ currentSession.langUtil != null &&
+ type != ERRTYPE_UNKNOWN)
+ locMsg = localizeMessage(messageKey, currentSession.langUtil, args);
+
+ // throw an exception for severe and user errors
+ if (type == ERRTYPE_SEVERE || type == ERRTYPE_USER)
+ {
+ if (messageKey.equals("DRDA_SQLException.S"))
+ throwSQLException(args[0]);
+ else if (messageKey.equals("DRDA_SQLWarning.I"))
+ throwSQLWarning(args[0]);
+ else
+ throw new Exception(messageKey+":"+locMsg);
+ }
+
+ // throw an exception with just the message if the error type is
+ // unknown
+ if (type == ERRTYPE_UNKNOWN)
+ throw new Exception(locMsg);
+
+ return;
+
+ }
+ /**
+ * Throw a SQL Exception which was sent over by a server
+ * Format of the msg is SQLSTATE:localized message\nSQLSTATE:next localized message
+ *
+ * @param msg msg containing SQL Exception
+ *
+ * @exception throws a SQLException
+ */
+ private void throwSQLException(String msg) throws SQLException
+ {
+ SQLException se = null;
+ SQLException ne;
+ SQLException ce = null;
+ StringBuffer strbuf = new StringBuffer();
+ StringTokenizer tokenizer = new StringTokenizer(msg, "\n");
+ String sqlstate = null;
+ String str;
+ while (tokenizer.hasMoreTokens())
+ {
+ str = tokenizer.nextToken();
+ //start of the next message
+ if (str.charAt(5) == ':')
+ {
+ if (strbuf.length() > 0)
+ {
+ if (se == null)
+ {
+ se = new SQLException(strbuf.toString(), sqlstate);
+ ce = se;
+ }
+ else
+ {
+ ne = new SQLException(strbuf.toString(), sqlstate);
+ ce.setNextException(ne);
+ ce = ne;
+ }
+ strbuf = new StringBuffer();
+ }
+ strbuf.append(str.substring(6));
+ sqlstate = str.substring(0,5);
+ }
+ else
+ strbuf.append(str);
+ }
+ if (strbuf.length() > 0)
+ {
+ if (se == null)
+ {
+ se = new SQLException(strbuf.toString(), sqlstate);
+ ce = se;
+ }
+ else
+ {
+ ne = new SQLException(strbuf.toString(), sqlstate);
+ ce.setNextException(ne);
+ ce = ne;
+ }
+ }
+ throw se;
+ }
+ /**
+ * Throw a SQL Warning which was sent over by a server
+ * Format of the msg is SQLSTATE:localized message\nSQLSTATE:next localized message
+ *
+ * @param msg msg containing SQL Warning
+ *
+ * @exception throws a SQLWarning
+ */
+ private void throwSQLWarning(String msg) throws SQLWarning
+ {
+ SQLWarning se = null;
+ SQLWarning ne;
+ SQLWarning ce = null;
+ StringBuffer strbuf = new StringBuffer();
+ StringTokenizer tokenizer = new StringTokenizer(msg, "\n");
+ String sqlstate = null;
+ String str;
+ while (tokenizer.hasMoreTokens())
+ {
+ str = tokenizer.nextToken();
+ //start of the next message
+ if (str.charAt(5) == ':')
+ {
+ if (strbuf.length() > 0)
+ {
+ if (se == null)
+ {
+ se = new SQLWarning(strbuf.toString(), sqlstate);
+ ce = se;
+ }
+ else
+ {
+ ne = new SQLWarning(strbuf.toString(), sqlstate);
+ ce.setNextException(ne);
+ ce = ne;
+ }
+ strbuf = new StringBuffer();
+ }
+ strbuf.append(str.substring(6));
+ sqlstate = str.substring(0,5);
+ }
+ else
+ strbuf.append(str);
+ }
+ if (strbuf.length() > 0)
+ {
+ if (se == null)
+ {
+ se = new SQLWarning(strbuf.toString(), sqlstate);
+ ce = se;
+ }
+ else
+ {
+ ne = new SQLWarning(strbuf.toString(), sqlstate);
+ ce.setNextException(ne);
+ ce = ne;
+ }
+ }
+ throw se;
+ }
+
+ /**
+ * Print a trace for the (unexpected) exception received, then
+ * throw a generic exception indicating that 1) an unexpected
+ * exception was thrown, and 2) we've already printed the trace
+ * (so don't do it again).
+ *
+ * @param e An unexpected exception.
+ * @exception throws an Exception with message UNEXPECTED_ERR.
+ */
+ private void throwUnexpectedException(Exception e)
+ throws Exception {
+
+ consoleExceptionPrintTrace(e);
+ throw new Exception(UNEXPECTED_ERR);
+
+ }
+
+ /**
+ * Localize a message given a particular AppUI
+ *
+ * @param msgProp message key
+ * @param localLangUtil LocalizedResource to use to localize message
+ * @param args arguments to message
+ *
+ */
+ private String localizeMessage(String msgProp, LocalizedResource localLangUtil, String [] args)
+ {
+ String locMsg = null;
+ //check if the argument is a property
+ if (args != null)
+ {
+ String [] argMsg = new String[args.length];
+ for (int i = 0; i < args.length; i++)
+ {
+ if (isMsgProperty(args[i]))
+ argMsg[i] = localLangUtil.getTextMessage(args[i]);
+ else
+ argMsg[i] = args[i];
+ }
+ switch (args.length)
+ {
+ case 1:
+ locMsg = localLangUtil.getTextMessage(msgProp, argMsg[0]);
+ break;
+ case 2:
+ locMsg = localLangUtil.getTextMessage(msgProp, argMsg[0], argMsg[1]);
+ break;
+ case 3:
+ locMsg = localLangUtil.getTextMessage(msgProp, argMsg[0], argMsg[1], argMsg[2]);
+ break;
+ case 4:
+ locMsg = localLangUtil.getTextMessage(msgProp, argMsg[0], argMsg[1], argMsg[2], argMsg[3]);
+ break;
+ default:
+ //shouldn't get here
+ }
+ }
+ else
+ locMsg = localLangUtil.getTextMessage(msgProp);
+ return locMsg;
+ }
+ /**
+ * Determine type of message
+ *
+ * @param msg message
+ *
+ * @return message type
+ */
+ private int getMessageType(String msg)
+ {
+ //all property messages should start with DRDA_
+ if (!msg.startsWith(DRDA_MSG_PREFIX))
+ return ERRTYPE_UNKNOWN;
+ int startpos = msg.indexOf('.')+1;
+ if (startpos >= msg.length())
+ return ERRTYPE_UNKNOWN;
+ if (msg.length() > (startpos + 1))
+ return ERRTYPE_UNKNOWN;
+ char type = msg.charAt(startpos);
+ if (type == 'S')
+ return ERRTYPE_SEVERE;
+ if (type == 'U')
+ return ERRTYPE_USER;
+ if (type == 'I')
+ return ERRTYPE_INFO;
+ return ERRTYPE_UNKNOWN;
+ }
+ /**
+ * Determine whether string is a property key or not
+ * property keys start with DRDA_MSG_PREFIX
+ *
+ * @param msg message
+ *
+ * @return true if it is a property key; false otherwise
+ */
+ private boolean isMsgProperty(String msg)
+ {
+ if (msg.startsWith(DRDA_MSG_PREFIX))
+ return true;
+ else
+ return false;
+ }
+ /**
+ * Get the current value of logging connections
+ *
+ * @return true if logging connections is on; false otherwise
+ */
+ public boolean getLogConnections()
+ {
+ synchronized(logConnectionsSync) {
+ return logConnections;
+ }
+ }
+ /**
+ * Set the current value of logging connections
+ *
+ * @param value true to turn logging connections on; false to turn it off
+ */
+ private void setLogConnections(boolean value)
+ {
+ synchronized(logConnectionsSync) {
+ logConnections = value;
+ }
+ // update the value in all the threads
+ synchronized(threadList) {
+ for (Enumeration e = threadList.elements(); e.hasMoreElements(); )
+ {
+ DRDAConnThread thread = (DRDAConnThread)e.nextElement();
+ thread.setLogConnections(value);
+ }
+ }
+ }
+
+ /**
+ * Set the trace on/off for all sessions, or one session, depending on
+ * whether we got -s argument.
+ *
+ * @param on true to turn trace on; false to turn it off
+ * @return true if set false if an error occurred
+ */
+ private boolean setTrace(boolean on)
+ {
+ if (sessionArg == 0)
+ {
+ setTraceAll(on);
+ synchronized(sessionTable) {
+ for (Enumeration e = sessionTable.elements(); e.hasMoreElements(); )
+ {
+ Session session = (Session) e.nextElement();
+ if (on)
+ session.setTraceOn(traceDirectory);
+ else
+ session.setTraceOff();
+ }
+ }
+ }
+ else
+ {
+ Session session = (Session) sessionTable.get(new Integer(sessionArg));
+ if (session != null)
+ {
+ if (on)
+ session.setTraceOn(traceDirectory);
+ else
+ session.setTraceOff();
+ }
+ else
+ return false;
+ }
+ return true;
+ }
+
+
+ /**
+ * Get the current value of the time slice
+ *
+ * @return time slice value
+ */
+ protected int getTimeSlice()
+ {
+ return timeSlice;
+ }
+ /**
+ * Set the current value of time slice
+ *
+ * @param value time slice value
+ * @exception Exception if value is < 0
+ */
+ private void setTimeSlice(int value)
+ throws Exception
+ {
+ if (value < MIN_TIMESLICE)
+ consolePropertyMessage("DRDA_InvalidValue.U", new String []
+ {new Integer(value).toString(), "timeslice"});
+ if (value == USE_DEFAULT)
+ value = DEFAULT_TIMESLICE;
+ synchronized(timeSliceSync) {
+ timeSlice = value;
+ }
+ }
+
+ /**
+ * Get the current value of keepAlive to configure how long the server
+ * should keep the socket alive for a disconnected client
+ */
+ protected boolean getKeepAlive()
+ {
+ return keepAlive;
+ }
+
+ /**
+ * Get the current value of minimum number of threads to create at start
+ *
+ * @return value of minimum number of threads
+ */
+ private int getMinThreads()
+ {
+ synchronized(threadsSync) {
+ return minThreads;
+ }
+ }
+ /**
+ * Set the current value of minimum number of threads to create at start
+ *
+ * @param value value of minimum number of threads
+ */
+ private void setMinThreads(int value)
+ {
+ synchronized(threadsSync) {
+ minThreads = value;
+ }
+ }
+ /**
+ * Get the current value of maximum number of threads to create
+ *
+ * @return value of maximum number of threads
+ */
+ protected int getMaxThreads()
+ {
+ synchronized(threadsSync) {
+ return maxThreads;
+ }
+ }
+ /**
+ * Set the current value of maximum number of threads to create
+ *
+ * @param value value of maximum number of threads
+ * @exception Exception if value is less than 0
+ */
+ private void setMaxThreads(int value) throws Exception
+ {
+ if (value < MIN_MAXTHREADS)
+ consolePropertyMessage("DRDA_InvalidValue.U", new String []
+ {new Integer(value).toString(), "maxthreads"});
+ if (value == USE_DEFAULT)
+ value = DEFAULT_MAXTHREADS;
+ synchronized(threadsSync) {
+ maxThreads = value;
+ }
+ }
+ /**
+ * Get the current value of minimum number of pooled connections to create at start
+ *
+ * @return value of minimum number of pooled connections
+ */
+ private int getMinPoolSize()
+ {
+ synchronized(threadsSync) {
+ return minPoolSize;
+ }
+ }
+
+ /**
+ * Set the current value of minimum number of pooled connections to create at start
+ *
+ * @param value value of minimum number of pooled connections
+ */
+ private void setMinPoolSize(int value)
+ {
+ synchronized(poolSync) {
+ minPoolSize = value;
+ }
+ }
+
+ /**
+ * Get the current value of maximum number of pooled connections to create
+ *
+ * @return value of maximum number of pooled connections
+ */
+ private int getMaxPoolSize()
+ {
+ synchronized(poolSync) {
+ return maxPoolSize;
+ }
+ }
+ /**
+ * Set the current value of maximum number of pooled connections to create
+ *
+ * @param value value of maximum number of pooled connections
+ */
+ private void setMaxPoolSize(int value)
+ {
+ synchronized(poolSync) {
+ maxPoolSize = value;
+ }
+ }
+ /**
+ * Get the current value of whether to trace all the sessions
+ *
+ * @return true if tracing is on for all sessions; false otherwise
+ */
+ protected boolean getTraceAll()
+ {
+ synchronized(traceAllSync) {
+ return traceAll;
+ }
+ }
+ /**
+ * Set the current value of whether to trace all the sessions
+ *
+ * @param value true if tracing is on for all sessions; false otherwise
+ */
+ private void setTraceAll(boolean value)
+ {
+ synchronized(traceAllSync) {
+ traceAll = value;
+ }
+ }
+ /**
+ * Get the current value of trace directory
+ *
+ * @return trace directory
+ */
+ protected String getTraceDirectory()
+ {
+ synchronized(traceDirectorySync) {
+ return traceDirectory;
+ }
+ }
+ /**
+ * Set the current value of trace directory
+ *
+ * @param value trace directory
[... 344 lines stripped ...]