0001: /*
0002:
0003: Derby - Class org.apache.derby.impl.drda.NetworkServerControlImpl
0004:
0005: Licensed to the Apache Software Foundation (ASF) under one or more
0006: contributor license agreements. See the NOTICE file distributed with
0007: this work for additional information regarding copyright ownership.
0008: The ASF licenses this file to You under the Apache License, Version 2.0
0009: (the "License"); you may not use this file except in compliance with
0010: the License. You may obtain a copy of the License at
0011:
0012: http://www.apache.org/licenses/LICENSE-2.0
0013:
0014: Unless required by applicable law or agreed to in writing, software
0015: distributed under the License is distributed on an "AS IS" BASIS,
0016: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0017: See the License for the specific language governing permissions and
0018: limitations under the License.
0019:
0020: */
0021:
0022: package org.apache.derby.impl.drda;
0023:
0024: import java.io.ByteArrayInputStream;
0025: import java.io.ByteArrayOutputStream;
0026: import java.io.DataOutputStream;
0027: import java.io.FilterOutputStream;
0028: import java.io.IOException;
0029: import java.io.InputStream;
0030: import java.io.OutputStream;
0031: import java.io.PrintStream;
0032: import java.io.PrintWriter;
0033: import java.net.InetAddress;
0034: import java.net.ServerSocket;
0035: import java.net.Socket;
0036: import java.net.UnknownHostException;
0037: import java.security.AccessController;
0038: import java.security.PrivilegedActionException;
0039: import java.security.PrivilegedExceptionAction;
0040: import java.sql.Connection;
0041: import java.sql.Driver;
0042: import java.sql.DriverManager;
0043: import java.sql.SQLException;
0044: import java.sql.SQLWarning;
0045: import java.util.ArrayList;
0046: import java.util.Enumeration;
0047: import java.util.Hashtable;
0048: import java.util.Properties;
0049: import java.util.StringTokenizer;
0050: import java.util.Vector;
0051:
0052: import org.apache.derby.drda.NetworkServerControl;
0053: import org.apache.derby.iapi.error.StandardException;
0054: import org.apache.derby.iapi.jdbc.DRDAServerStarter;
0055: import org.apache.derby.iapi.reference.Attribute;
0056: import org.apache.derby.iapi.reference.DRDAConstants;
0057: import org.apache.derby.iapi.reference.Property;
0058: import org.apache.derby.iapi.reference.SQLState;
0059: import org.apache.derby.iapi.services.i18n.MessageService;
0060: import org.apache.derby.iapi.services.info.JVMInfo;
0061: import org.apache.derby.iapi.services.info.ProductGenusNames;
0062: import org.apache.derby.iapi.services.info.ProductVersionHolder;
0063: import org.apache.derby.iapi.services.monitor.Monitor;
0064: import org.apache.derby.iapi.services.property.PropertyUtil;
0065: import org.apache.derby.iapi.services.sanity.SanityManager;
0066: import org.apache.derby.iapi.tools.i18n.LocalizedOutput;
0067: import org.apache.derby.iapi.tools.i18n.LocalizedResource;
0068: import org.apache.derby.iapi.util.CheapDateFormatter;
0069: import org.apache.derby.iapi.util.StringUtil;
0070: import org.apache.derby.impl.jdbc.EmbedSQLException;
0071:
0072: /**
0073:
0074: NetworkServerControlImpl does all the work for NetworkServerControl
0075: @see NetworkServerControl for description
0076:
0077: */
0078: public final class NetworkServerControlImpl {
0079: private final static int NO_USAGE_MSGS = 12;
0080: private final static String[] COMMANDS = { "start", "shutdown",
0081: "trace", "tracedirectory", "ping", "logconnections",
0082: "sysinfo", "runtimeinfo", "maxthreads", "timeslice" };
0083: // number of required arguments for each command
0084: private final static int[] COMMAND_ARGS = { 0, 0, 1, 1, 0, 1, 0, 0,
0085: 1, 1 };
0086: private final static int COMMAND_START = 0;
0087: private final static int COMMAND_SHUTDOWN = 1;
0088: private final static int COMMAND_TRACE = 2;
0089: private final static int COMMAND_TRACEDIRECTORY = 3;
0090: private final static int COMMAND_TESTCONNECTION = 4;
0091: private final static int COMMAND_LOGCONNECTIONS = 5;
0092: private final static int COMMAND_SYSINFO = 6;
0093: private final static int COMMAND_RUNTIME_INFO = 7;
0094: private final static int COMMAND_MAXTHREADS = 8;
0095: private final static int COMMAND_TIMESLICE = 9;
0096: private final static int COMMAND_PROPERTIES = 10;
0097: private final static int COMMAND_UNKNOWN = -1;
0098: private final static String[] DASHARGS = { "p", "d", "u", "ld",
0099: "ea", "ep", "b", "h", "s" };
0100: private final static int DASHARG_PORT = 0;
0101: private final static int DASHARG_DATABASE = 1;
0102: private final static int DASHARG_USER = 2;
0103: private final static int DASHARG_LOADSYSIBM = 3;
0104: private final static int DASHARG_ENCALG = 4;
0105: private final static int DASHARG_ENCPRV = 5;
0106: private final static int DASHARG_BOOTPASSWORD = 6;
0107: private final static int DASHARG_HOST = 7;
0108: private final static int DASHARG_SESSION = 8;
0109:
0110: // command protocol version - you need to increase this number each time
0111: // the command protocol changes
0112: private final static int PROTOCOL_VERSION = 1;
0113: private final static String COMMAND_HEADER = "CMD:";
0114: private final static String REPLY_HEADER = "RPY:";
0115: private final static int REPLY_HEADER_LENGTH = REPLY_HEADER
0116: .length();
0117: private final static int OK = 0;
0118: private final static int WARNING = 1;
0119: private final static int ERROR = 2;
0120: private final static int SQLERROR = 3;
0121: private final static int SQLWARNING = 4;
0122:
0123: private final static String DRDA_PROP_MESSAGES = "org.apache.derby.loc.drda.messages";
0124: private final static String DRDA_PROP_DEBUG = "derby.drda.debug";
0125: private final static String CLOUDSCAPE_DRIVER = "org.apache.derby.jdbc.EmbeddedDriver";
0126:
0127: public final static String UNEXPECTED_ERR = "Unexpected exception";
0128:
0129: private final static int MIN_MAXTHREADS = -1;
0130: private final static int MIN_TIMESLICE = -1;
0131: private final static int USE_DEFAULT = -1;
0132: private final static int DEFAULT_MAXTHREADS = 0; //for now create whenever needed
0133: private final static int DEFAULT_TIMESLICE = 0; //for now never yield
0134:
0135: private final static String DEFAULT_HOST = "localhost";
0136: private final static String DRDA_MSG_PREFIX = "DRDA_";
0137: private final static String DEFAULT_LOCALE = "en";
0138: private final static String DEFAULT_LOCALE_COUNTRY = "US";
0139:
0140: // Check up to 10 seconds to see if shutdown occurred
0141: private final static int SHUTDOWN_CHECK_ATTEMPTS = 20;
0142: private final static int SHUTDOWN_CHECK_INTERVAL = 500;
0143:
0144: // maximum reply size
0145: private final static int MAXREPLY = 32767;
0146:
0147: // Application Server Attributes.
0148: protected static String att_srvclsnm;
0149: protected final static String ATT_SRVNAM = "NetworkServerControl";
0150:
0151: protected static String att_extnam;
0152: protected static String att_srvrlslv;
0153: protected static String prdId;
0154: protected static byte[] prdIdBytes_;
0155:
0156: private static String buildNumber;
0157: private static String versionString;
0158: // we will use single or mixed, not double byte to reduce traffic on the
0159: // wire, this is in keeping with JCC
0160: // Note we specify UTF8 for the single byte encoding even though it can
0161: // be multi-byte.
0162: protected final static int CCSIDSBC = 1208; //use UTF8
0163: protected final static int CCSIDMBC = 1208; //use UTF8
0164: protected final static String DEFAULT_ENCODING = "UTF8"; // use UTF8 for writing
0165: protected final static int DEFAULT_CCSID = 1208;
0166: protected final static byte SPACE_CHAR = 32;
0167:
0168: // Application Server manager levels - this needs to be in sync
0169: // with CodePoint.MGR_CODEPOINTS
0170: protected final static int[] MGR_LEVELS = { 7, // AGENT
0171: 4, // CCSID Manager
0172: 0, // CNMAPPC not implemented
0173: 0, // CMNSYNCPT not implemented
0174: 5, // CMNTCPIP
0175: 0, // DICTIONARY
0176: 7, // RDB
0177: 0, // RSYNCMGR
0178: 7, // SECMGR
0179: 7, // SQLAM
0180: 0, // SUPERVISOR
0181: 0, // SYNCPTMGR
0182: 7 // XAMGR
0183: };
0184:
0185: protected PrintWriter logWriter; // console
0186: protected PrintWriter cloudscapeLogWriter; // derby.log
0187: private static Driver cloudscapeDriver;
0188:
0189: // error types
0190: private final static int ERRTYPE_SEVERE = 1;
0191: private final static int ERRTYPE_USER = 2;
0192: private final static int ERRTYPE_INFO = 3;
0193: private final static int ERRTYPE_UNKNOWN = -1;
0194:
0195: // command argument information
0196: private Vector commandArgs = new Vector();
0197: private String databaseArg;
0198: private String userArg;
0199: private String passwordArg;
0200: private String bootPasswordArg;
0201: private String encAlgArg;
0202: private String encPrvArg;
0203: private String hostArg = DEFAULT_HOST;
0204: private InetAddress hostAddress;
0205: private int sessionArg;
0206:
0207: // Used to debug memory in SanityManager.DEBUG mode
0208: private memCheck mc;
0209:
0210: // reply buffer
0211: private byte[] replyBuffer;
0212: private int replyBufferCount; //length of reply
0213: private int replyBufferPos; //current position in reply
0214:
0215: //
0216: // server configuration
0217: //
0218: // static values - set at start can't be changed once server has started
0219: private int portNumber = NetworkServerControl.DEFAULT_PORTNUMBER; // port server listens to
0220:
0221: // configurable values
0222: private String traceDirectory; // directory to place trace files in
0223: private Object traceDirectorySync = new Object();// object to use for syncing
0224: private boolean traceAll; // trace all sessions
0225: private Object traceAllSync = new Object(); // object to use for syncing reading
0226: // and changing trace all
0227: private Object serverStartSync = new Object(); // for syncing start of server.
0228: private boolean logConnections; // log connects
0229: private Object logConnectionsSync = new Object(); // object to use for syncing
0230: // logConnections value
0231: private int minThreads; // default minimum number of connection threads
0232: private int maxThreads; // default maximum number of connection threads
0233: private Object threadsSync = new Object(); // object to use for syncing reading
0234: // and changing default min and max threads
0235: private int timeSlice; // default time slice of a session to a thread
0236: private Object timeSliceSync = new Object();// object to use for syncing reading
0237: // and changing timeSlice
0238:
0239: private boolean keepAlive = true; // keepAlive value for client socket
0240: private int minPoolSize; //minimum pool size for pooled connections
0241: private int maxPoolSize; //maximum pool size for pooled connections
0242: private Object poolSync = new Object(); // object to use for syning reading
0243:
0244: protected boolean debugOutput = false;
0245: private boolean cleanupOnStart = false; // Should we clean up when starting the server?
0246: private boolean restartFlag = false;
0247:
0248: protected final static int INVALID_OR_NOTSET_SECURITYMECHANISM = -1;
0249: // variable to store value set to derby.drda.securityMechanism
0250: // default value is -1 which indicates that this property isnt set or
0251: // the value is invalid
0252: private int allowOnlySecurityMechanism = INVALID_OR_NOTSET_SECURITYMECHANISM;
0253: //
0254: // variables for a client command session
0255: //
0256: private Socket clientSocket = null;
0257: private InputStream clientIs = null;
0258: private OutputStream clientOs = null;
0259: private ByteArrayOutputStream byteArrayOs = new ByteArrayOutputStream();
0260: private DataOutputStream commandOs = new DataOutputStream(
0261: byteArrayOs);
0262:
0263: private Object shutdownSync = new Object();
0264: private boolean shutdown;
0265: private int connNum; // number of connections since server started
0266: private ServerSocket serverSocket;
0267: private NetworkServerControlImpl serverInstance;
0268: private LocalizedResource langUtil;
0269: public String clientLocale;
0270: ArrayList localAddresses; // list of local addresses for checking admin
0271: // commands.
0272:
0273: // open sessions
0274: private Hashtable sessionTable = new Hashtable();
0275:
0276: // current session
0277: private Session currentSession;
0278: // DRDAConnThreads
0279: private Vector threadList = new Vector();
0280:
0281: // queue of sessions waiting for a free thread - the queue is managed
0282: // in a simple first come, first serve manner - no priorities
0283: private Vector runQueue = new Vector();
0284:
0285: // number of DRDAConnThreads waiting for something to do
0286: private int freeThreads;
0287:
0288: // known application requesters
0289: private Hashtable appRequesterTable = new Hashtable();
0290:
0291: // accessed by inner classes for privileged action
0292: private String propertyFileName;
0293: private Runnable acceptClients;
0294:
0295: // if the server is started from the command line, it should shutdown the
0296: // databases it has booted.
0297: private boolean shutdownDatabasesOnShutdown = false;
0298:
0299: /**
0300: * Can EUSRIDPWD security mechanism be used with
0301: * the current JVM
0302: */
0303: private static boolean SUPPORTS_EUSRIDPWD = false;
0304:
0305: /*
0306: * DRDA Specification for the EUSRIDPWD security mechanism
0307: * requires DH algorithm support with a 32-byte prime to be
0308: * used. Not all JCE implementations have support for this.
0309: * Hence here we need to find out if EUSRIDPWD can be used
0310: * with the current JVM.
0311: */
0312: static {
0313: try {
0314: // The DecryptionManager class will instantiate objects of the required
0315: // security algorithms that are needed for EUSRIDPWD
0316: // An exception will be thrown if support is not available
0317: // in the JCE implementation in the JVM in which the server
0318: // is started.
0319: new DecryptionManager();
0320: SUPPORTS_EUSRIDPWD = true;
0321: } catch (Exception e) {
0322: // if an exception is thrown, ignore exception.
0323: // set SUPPORTS_EUSRIDPWD to false indicating that the server
0324: // does not have support for EUSRIDPWD security mechanism
0325: SUPPORTS_EUSRIDPWD = false;
0326: }
0327: }
0328:
0329: // constructor
0330: public NetworkServerControlImpl() throws Exception {
0331: init();
0332: getPropertyInfo();
0333: }
0334:
0335: /**
0336: * Internal constructor for NetworkServerControl API.
0337: * @param address - InetAddress to listen on, May not be null. Throws NPE if null
0338: * @param portNumber - portNumber to listen on, -1 use propert or default.
0339: * @throws Exception on error
0340: * @see NetworkServerControl
0341: */
0342: public NetworkServerControlImpl(InetAddress address, int portNumber)
0343: throws Exception {
0344: init();
0345: getPropertyInfo();
0346: this .hostAddress = address;
0347: this .portNumber = (portNumber <= 0) ? this .portNumber
0348: : portNumber;
0349: this .hostArg = address.getHostAddress();
0350: }
0351:
0352: private void init() throws Exception {
0353:
0354: // adjust the application in accordance with derby.ui.locale and derby.ui.codeset
0355: langUtil = new LocalizedResource(null, null, DRDA_PROP_MESSAGES);
0356:
0357: serverInstance = this ;
0358:
0359: //set Server attributes to be used in EXCSAT
0360: ProductVersionHolder myPVH = getNetProductVersionHolder();
0361: att_extnam = ATT_SRVNAM + " "
0362: + java.lang.Thread.currentThread().getName();
0363:
0364: att_srvclsnm = myPVH.getProductName();
0365: versionString = myPVH.getVersionBuildString(true);
0366:
0367: String majorStr = String.valueOf(myPVH.getMajorVersion());
0368: String minorStr = String.valueOf(myPVH.getMinorVersion());
0369: // Maintenance version. Server protocol version.
0370: // Only changed if client needs to recognize a new server version.
0371: String drdaMaintStr = String.valueOf(myPVH
0372: .getDrdaMaintVersion());
0373:
0374: // PRDID format as JCC expects it: CSSMMmx
0375: // MM = major version
0376: // mm = minor version
0377: // x = drda MaintenanceVersion
0378:
0379: prdId = DRDAConstants.DERBY_DRDA_SERVER_ID;
0380: if (majorStr.length() == 1)
0381: prdId += "0";
0382: prdId += majorStr;
0383:
0384: if (minorStr.length() == 1)
0385: prdId += "0";
0386:
0387: prdId += minorStr;
0388:
0389: prdId += drdaMaintStr;
0390: att_srvrlslv = prdId + "/" + myPVH.getVersionBuildString(true);
0391: // Precompute this to save some cycles
0392: prdIdBytes_ = prdId.getBytes(DEFAULT_ENCODING);
0393:
0394: if (SanityManager.DEBUG) {
0395: if (majorStr.length() > 2 || minorStr.length() > 2
0396: || drdaMaintStr.length() > 1)
0397: SanityManager
0398: .THROWASSERT("version values out of expected range for PRDID");
0399: }
0400:
0401: buildNumber = myPVH.getBuildNumber();
0402: }
0403:
0404: private PrintWriter makePrintWriter(OutputStream out) {
0405: if (out != null)
0406: return new PrintWriter(out, true /* flush the buffer at the end of each line */);
0407: else
0408: return null;
0409: }
0410:
0411: protected static Driver getDriver() {
0412: return cloudscapeDriver;
0413: }
0414:
0415: /********************************************************************************
0416: * Implementation of NetworkServerControl API
0417: * The server commands throw exceptions for errors, so that users can handle
0418: * them themselves in addition to having the errors written to the console
0419: * and possibly derby.log. To turn off logging the errors to the console,
0420: * set the output writer to null.
0421: ********************************************************************************/
0422:
0423: /**
0424: * Set the output stream for console messages
0425: * If this is set to null, no messages will be written to the console
0426: *
0427: * @param outWriter output stream for console messages
0428: */
0429: public void setLogWriter(PrintWriter outWriter) {
0430: // wrap the user-set outWriter with, autoflush to true.
0431: // this will ensure that messages to console will be
0432: // written out to the outWriter on a println.
0433: // DERBY-1466
0434: if (outWriter != null)
0435: logWriter = new PrintWriter(outWriter, true);
0436: else
0437: logWriter = outWriter;
0438: }
0439:
0440: /**
0441: * Write an error message to console output stream
0442: * and throw an exception for this error
0443: *
0444: * @param msg error message
0445: * @exception Exception
0446: */
0447: public void consoleError(String msg) throws Exception {
0448: consoleMessage(msg);
0449: throw new Exception(msg);
0450: }
0451:
0452: /**
0453: * Write an exception to console output stream,
0454: * but only if debugOutput is true.
0455: *
0456: * @param e exception
0457: */
0458: public void consoleExceptionPrint(Exception e) {
0459: if (debugOutput == true)
0460: consoleExceptionPrintTrace(e);
0461:
0462: return;
0463: }
0464:
0465: /**
0466: * Write an exception (with trace) to console
0467: * output stream.
0468: *
0469: * @param e exception
0470: */
0471: public void consoleExceptionPrintTrace(Throwable e) {
0472: consoleMessage(e.getMessage());
0473: if (logWriter != null) {
0474: synchronized (logWriter) {
0475: e.printStackTrace(logWriter);
0476: }
0477: } else {
0478: e.printStackTrace();
0479: }
0480:
0481: if (cloudscapeLogWriter != null) {
0482: synchronized (cloudscapeLogWriter) {
0483: e.printStackTrace(cloudscapeLogWriter);
0484: }
0485: }
0486: }
0487:
0488: /**
0489: * Write a message to console output stream
0490: *
0491: * @param msg message
0492: */
0493: public void consoleMessage(String msg) {
0494: // print to console if we have one
0495: if (logWriter != null) {
0496: synchronized (logWriter) {
0497: logWriter.println(msg);
0498: }
0499: }
0500: // always print to derby.log
0501: if (cloudscapeLogWriter != null)
0502: synchronized (cloudscapeLogWriter) {
0503: Monitor.logMessage(msg);
0504: }
0505: }
0506:
0507: /**
0508: * Start a network server. Launches a separate thread with
0509: * DRDAServerStarter. Want to use Monitor.startModule,
0510: * so it can all get shutdown when cloudscape shuts down, but
0511: * can't get it working right now.
0512: *
0513: * @param consoleWriter PrintWriter to which server console will be
0514: * output. Null will disable console output.
0515: *
0516: *
0517: * @exception Exception throws an exception if an error occurs
0518: */
0519: public void start(PrintWriter consoleWriter) throws Exception {
0520: DRDAServerStarter starter = new DRDAServerStarter();
0521: starter.setStartInfo(hostAddress, portNumber, consoleWriter);
0522: startNetworkServer();
0523: starter.boot(false, null);
0524: }
0525:
0526: /**
0527: * Start a network server
0528: *
0529: * @param consoleWriter PrintWriter to which server console will be
0530: * output. Null will disable console output.
0531: *
0532: *
0533: * @exception Exception throws an exception if an error occurs
0534: */
0535: public void blockingStart(PrintWriter consoleWriter)
0536: throws Exception {
0537: startNetworkServer();
0538: setLogWriter(consoleWriter);
0539: cloudscapeLogWriter = Monitor.getStream().getPrintWriter();
0540: if (SanityManager.DEBUG && debugOutput) {
0541: memCheck.showmem();
0542: mc = new memCheck(200000);
0543: mc.start();
0544: }
0545: // Open a server socket listener
0546: try {
0547: serverSocket = (ServerSocket) AccessController
0548: .doPrivileged(new PrivilegedExceptionAction() {
0549: public Object run() throws IOException,
0550: UnknownHostException {
0551: if (hostAddress == null)
0552: hostAddress = InetAddress
0553: .getByName(hostArg);
0554: // Make a list of valid
0555: // InetAddresses for NetworkServerControl
0556: // admin commands.
0557: buildLocalAddressList(hostAddress);
0558: return new ServerSocket(portNumber, 0,
0559: hostAddress);
0560: }
0561: });
0562: } catch (PrivilegedActionException e) {
0563: Exception e1 = e.getException();
0564: if (e1 instanceof IOException)
0565: consolePropertyMessage("DRDA_ListenPort.S",
0566: new String[] { Integer.toString(portNumber),
0567: hostArg });
0568: if (e1 instanceof UnknownHostException) {
0569: consolePropertyMessage("DRDA_UnknownHost.S", hostArg);
0570: } else
0571: throw e1;
0572: } catch (Exception e) {
0573: // If we find other (unexpected) errors, we ultimately exit--so make
0574: // sure we print the error message before doing so (Beetle 5033).
0575: throwUnexpectedException(e);
0576: }
0577:
0578: consolePropertyMessage("DRDA_Ready.I", new String[] {
0579: Integer.toString(portNumber), att_srvclsnm,
0580: versionString, getFormattedTimestamp() });
0581:
0582: // We accept clients on a separate thread so we don't run into a problem
0583: // blocking on the accept when trying to process a shutdown
0584: acceptClients = (Runnable) new ClientThread(this , serverSocket);
0585: Thread clientThread = (Thread) AccessController
0586: .doPrivileged(new PrivilegedExceptionAction() {
0587: public Object run() throws Exception {
0588: return new Thread(acceptClients);
0589: }
0590: });
0591: clientThread.start();
0592:
0593: // wait until we are told to shutdown or someone sends an InterruptedException
0594: synchronized (shutdownSync) {
0595: try {
0596: shutdownSync.wait();
0597: } catch (InterruptedException e) {
0598: shutdown = true;
0599: }
0600: }
0601:
0602: // Need to interrupt the memcheck thread if it is sleeping.
0603: if (mc != null)
0604: mc.interrupt();
0605:
0606: //interrupt client thread
0607: clientThread.interrupt();
0608:
0609: // Close out the sessions
0610: synchronized (sessionTable) {
0611: for (Enumeration e = sessionTable.elements(); e
0612: .hasMoreElements();) {
0613: Session session = (Session) e.nextElement();
0614: session.close();
0615: }
0616: }
0617:
0618: synchronized (threadList) {
0619: //interupt any connection threads still active
0620: for (int i = 0; i < threadList.size(); i++) {
0621: ((DRDAConnThread) threadList.get(i)).close();
0622: ((DRDAConnThread) threadList.get(i)).interrupt();
0623: }
0624: threadList.clear();
0625: }
0626:
0627: // close the listener socket
0628: try {
0629: serverSocket.close();
0630: } catch (IOException e) {
0631: consolePropertyMessage("DRDA_ListenerClose.S");
0632: }
0633:
0634: // Wake up those waiting on sessions, so
0635: // they can close down
0636: synchronized (runQueue) {
0637: runQueue.notifyAll();
0638: }
0639:
0640: if (shutdownDatabasesOnShutdown) {
0641:
0642: // Shutdown Cloudscape
0643: try {
0644: if (cloudscapeDriver != null)
0645: cloudscapeDriver.connect(
0646: "jdbc:derby:;shutdown=true",
0647: (Properties) null);
0648: } catch (SQLException sqle) {
0649: // If we can't shutdown cloudscape. Perhaps authentication is
0650: // set to true or some other reason. We will just print a
0651: // message to the console and proceed.
0652: String expectedState = StandardException
0653: .getSQLStateFromIdentifier(SQLState.CLOUDSCAPE_SYSTEM_SHUTDOWN);
0654: if (!expectedState.equals(sqle.getSQLState())) {
0655: consolePropertyMessage("DRDA_ShutdownWarning.I",
0656: sqle.getMessage());
0657: }
0658: }
0659: }
0660:
0661: consolePropertyMessage("DRDA_ShutdownSuccess.I", new String[] {
0662: att_srvclsnm, versionString, getFormattedTimestamp() });
0663:
0664: }
0665:
0666: /**
0667: * Load Cloudscape and save driver for future use.
0668: * We can't call Driver Manager when the client connects,
0669: * because they might be holding the DriverManager lock.
0670: *
0671: *
0672: */
0673:
0674: protected void startNetworkServer() throws Exception {
0675:
0676: // we start the cloudscape server here.
0677: boolean restartCheck = this .restartFlag;
0678: synchronized (serverStartSync) {
0679:
0680: if (restartCheck == this .restartFlag) {
0681: // then we can go ahead and restart the server (odds
0682: // that some else has just done so are very slim (but not
0683: // impossible--however, even if it does happen, things
0684: // should still work correctly, just not as efficiently...))
0685:
0686: try {
0687:
0688: if (cleanupOnStart) {
0689: // we're restarting the server (probably after a shutdown
0690: // exception), so we need to clean up first.
0691:
0692: // Close and remove sessions on runQueue.
0693: synchronized (runQueue) {
0694: for (int i = 0; i < runQueue.size(); i++) {
0695: Session s = (Session) runQueue.get(i);
0696: s.close();
0697: removeFromSessionTable(s.getConnNum());
0698: }
0699: runQueue.clear();
0700: }
0701:
0702: // DERBY-1326: There could be active threads that
0703: // contain old/invalid sessions. These sessions won't
0704: // be cleaned up until there is some activity on
0705: // them. We could optimize this by going through
0706: // sessionTable and closing the sessions' socket
0707: // streams.
0708:
0709: // Unload driver, then restart the server.
0710: cloudscapeDriver = null; // so it gets collected.
0711: System.gc();
0712: }
0713:
0714: // start the server.
0715: Class.forName(CLOUDSCAPE_DRIVER).newInstance();
0716: cloudscapeDriver = DriverManager
0717: .getDriver(Attribute.PROTOCOL);
0718:
0719: } catch (Exception e) {
0720: consolePropertyMessage("DRDA_LoadException.S", e
0721: .getMessage());
0722: }
0723: cleanupOnStart = true;
0724: this .restartFlag = !this .restartFlag;
0725: }
0726: // else, multiple threads hit this synchronize block at the same
0727: // time, but one of them already executed it--so all others just
0728: // return and do nothing (no need to restart the server multiple
0729: // times in a row).
0730: }
0731: }
0732:
0733: /**
0734: * Shutdown a network server
0735: *
0736: * @exception Exception throws an exception if an error occurs
0737: */
0738: public void shutdown() throws Exception {
0739: setUpSocket();
0740: writeCommandHeader(COMMAND_SHUTDOWN);
0741: send();
0742: readResult();
0743: // Wait up to 10 seconds for things to really shut down
0744: // need a quiet ping so temporarily disable the logwriter
0745: PrintWriter savWriter = logWriter;
0746: // DERBY-1571: If logWriter is null, stack traces are printed to
0747: // System.err. Set logWriter to a silent stream to suppress stack
0748: // traces too.
0749: FilterOutputStream silentStream = new FilterOutputStream(null) {
0750: public void write(int b) {
0751: }
0752:
0753: public void flush() {
0754: }
0755:
0756: public void close() {
0757: }
0758: };
0759: setLogWriter(new PrintWriter(silentStream));
0760: int ntry;
0761: for (ntry = 0; ntry < SHUTDOWN_CHECK_ATTEMPTS; ntry++) {
0762: Thread.sleep(SHUTDOWN_CHECK_INTERVAL);
0763: try {
0764: ping();
0765: } catch (Exception e) {
0766: // as soon as we can't ping return
0767: if (ntry == SHUTDOWN_CHECK_ATTEMPTS)
0768: consolePropertyMessage("DRDA_ShutdownError.S",
0769: new String[] {
0770: Integer.toString(portNumber),
0771: hostArg });
0772: break;
0773: }
0774: }
0775: logWriter = savWriter;
0776: return;
0777: }
0778:
0779: /*
0780: Shutdown the server directly (If you have the original object)
0781: No Network communication needed.
0782: */
0783: public void directShutdown() {
0784: shutdown = true;
0785: synchronized (shutdownSync) {
0786: // wake up the server thread
0787: shutdownSync.notifyAll();
0788: }
0789:
0790: }
0791:
0792: /**
0793: */
0794: public boolean isServerStarted() throws Exception {
0795: try {
0796: ping();
0797: } catch (Exception e) {
0798: return false;
0799: }
0800: return true;
0801: }
0802:
0803: public void ping() throws Exception {
0804: // database no longer used, but don't change the protocol
0805: // in case we add
0806: // authorization later.
0807: String database = null; // no longer used but don't change the protocol
0808: String user = null;
0809: String password = null;
0810:
0811: setUpSocket();
0812: writeCommandHeader(COMMAND_TESTCONNECTION);
0813: writeLDString(database);
0814: writeLDString(user);
0815: writeLDString(password);
0816: send();
0817: readResult();
0818:
0819: }
0820:
0821: /**
0822: * Turn tracing on or off for all sessions
0823: *
0824: * @param on true to turn tracing on, false to turn tracing off
0825: *
0826: * @exception Exception throws an exception if an error occurs
0827: */
0828: public void trace(boolean on) throws Exception {
0829: trace(0, on);
0830: }
0831:
0832: /**
0833: * Turn tracing on or off for one session or all sessions
0834: *
0835: * @param connNum the connNum of the session, 0 if all sessions
0836: * @param on true to turn tracing on, false to turn tracing off
0837: *
0838: * @exception Exception throws an exception if an error occurs
0839: */
0840: public void trace(int connNum, boolean on) throws Exception {
0841: setUpSocket();
0842: writeCommandHeader(COMMAND_TRACE);
0843: commandOs.writeInt(connNum);
0844: writeByte(on ? 1 : 0);
0845: send();
0846: readResult();
0847: consoleTraceMessage(connNum, on);
0848: }
0849:
0850: /**
0851: * Print trace change message to console
0852: *
0853: * @param on true to print tracing on, false to print tracing off
0854: *
0855: * @exception Exception throws an exception if an error occurs
0856: */
0857: private void consoleTraceMessage(int connNum, boolean on)
0858: throws Exception {
0859: if (connNum == 0)
0860: consolePropertyMessage("DRDA_TraceChangeAll.I",
0861: on ? "DRDA_ON.I" : "DRDA_OFF.I");
0862: else {
0863: String[] args = new String[2];
0864: args[0] = on ? "DRDA_ON.I" : "DRDA_OFF.I";
0865: args[1] = new Integer(connNum).toString();
0866: consolePropertyMessage("DRDA_TraceChangeOne.I", args);
0867: }
0868: }
0869:
0870: /**
0871: * Turn logging connections on or off. When logging is turned on a message is
0872: * written to derby.log each time a connection is made.
0873: *
0874: * @param on true to turn on, false to turn off
0875: *
0876: * @exception Exception throws an exception if an error occurs
0877: */
0878: public void logConnections(boolean on) throws Exception {
0879: setUpSocket();
0880: writeCommandHeader(COMMAND_LOGCONNECTIONS);
0881: writeByte(on ? 1 : 0);
0882: send();
0883: readResult();
0884: }
0885:
0886: /**
0887: *@see NetworkServerControl#setTraceDirectory
0888: */
0889: public void sendSetTraceDirectory(String traceDirectory)
0890: throws Exception {
0891: setUpSocket();
0892: writeCommandHeader(COMMAND_TRACEDIRECTORY);
0893: writeLDString(traceDirectory);
0894: send();
0895: readResult();
0896: }
0897:
0898: /**
0899: *@see NetworkServerControl#getSysinfo
0900: */
0901: public String sysinfo() throws Exception {
0902: setUpSocket();
0903: writeCommandHeader(COMMAND_SYSINFO);
0904: send();
0905: return readStringReply("DRDA_SysInfoError.S");
0906: }
0907:
0908: /**
0909: *@see NetworkServerControl#getRuntimeInfo
0910: */
0911: public String runtimeInfo() throws Exception {
0912: setUpSocket();
0913: writeCommandHeader(COMMAND_RUNTIME_INFO);
0914: send();
0915: return readStringReply("DRDA_RuntimeInfoError.S");
0916: }
0917:
0918: /**
0919: * Display usage information
0920: *
0921: */
0922: public void usage() {
0923: try {
0924: for (int i = 1; i <= NO_USAGE_MSGS; i++)
0925: consolePropertyMessage("DRDA_Usage" + i + ".I");
0926: } catch (Exception e) {
0927: } // ignore exceptions - there shouldn't be any
0928: }
0929:
0930: /**
0931: * Connect to network server and set connection maxthread parameter
0932: *
0933: * @param max maximum number of connections, if 0, connections
0934: * created when no free connection available
0935: * if -1, use default
0936: *
0937: * @exception Exception throws an exception if an error occurs
0938: */
0939: public void netSetMaxThreads(int max) throws Exception {
0940: setUpSocket();
0941: writeCommandHeader(COMMAND_MAXTHREADS);
0942: commandOs.writeInt(max);
0943: send();
0944: readResult();
0945: int newval = readInt();
0946: consolePropertyMessage("DRDA_MaxThreadsChange.I", new Integer(
0947: newval).toString());
0948: }
0949:
0950: /**
0951: * Set network server connection timeslice parameter
0952: *
0953: * @param timeslice amount of time given to each session before yielding to
0954: * another session, if 0, never yield. if -1, use default.
0955: *
0956: * @exception Exception throws an exception if an error occurs
0957: */
0958: public void netSetTimeSlice(int timeslice) throws Exception {
0959: setUpSocket();
0960: writeCommandHeader(COMMAND_TIMESLICE);
0961: commandOs.writeInt(timeslice);
0962: send();
0963: readResult();
0964: int newval = readInt();
0965: consolePropertyMessage("DRDA_TimeSliceChange.I", new Integer(
0966: newval).toString());
0967: }
0968:
0969: /**
0970: * Get current properties
0971: *
0972: * @return Properties object containing properties
0973: * @exception Exception throws an exception if an error occurs
0974: */
0975: public Properties getCurrentProperties() throws Exception {
0976: setUpSocket();
0977: writeCommandHeader(COMMAND_PROPERTIES);
0978: send();
0979: byte[] val = readBytesReply("DRDA_PropertyError.S");
0980: Properties p = new Properties();
0981: try {
0982: ByteArrayInputStream bs = new ByteArrayInputStream(val);
0983: p.load(bs);
0984: } catch (IOException io) {
0985: consolePropertyMessage("DRDA_IOException.S", io
0986: .getMessage());
0987: }
0988: return p;
0989: }
0990:
0991: /**
0992: * Set a thread name to be something that is both meaningful and unique (primarily
0993: * for debugging purposes).
0994: *
0995: * The received thread's name is set to a new string of the form
0996: * [newName + "_n"], where 'n' is a unique thread id originally generated
0997: * by the jvm Thread constructor. If the default name of the thread has
0998: * been changed before getting here, then nothing is done.
0999: *
1000: * @param thrd An instance of a Thread object that still has its default
1001: * thread name (as generated by the jvm Thread constructor). This should
1002: * always be of the form "Thread-N", where N is a unique thread id
1003: * generated by the jvm. Ex. "Thread-0", "Thread-1", etc.
1004: *
1005: **/
1006: public static void setUniqueThreadName(Thread thrd, String newName) {
1007:
1008: // First, pull off the unique thread id already found in thrd's default name;
1009: // we do so by searching for the '-' character, and then counting everything
1010: // after it as a N.
1011: if (thrd.getName().indexOf("Thread-") == -1) {
1012: // default name has been changed; don't do anything.
1013: return;
1014: } else {
1015: String oldName = thrd.getName();
1016: thrd.setName(newName
1017: + "_"
1018: + oldName.substring(oldName.indexOf("-") + 1,
1019: oldName.length()));
1020: } // end else.
1021:
1022: return;
1023:
1024: }
1025:
1026: /*******************************************************************************/
1027: /* Protected methods */
1028: /*******************************************************************************/
1029: /**
1030: * Remove session from session table
1031: *
1032: * @param sessionid id of session to be removed
1033: */
1034: protected void removeFromSessionTable(int sessionid) {
1035: sessionTable.remove(new Integer(sessionid));
1036: }
1037:
1038: /**
1039: * processCommands reads and processes NetworkServerControlImpl commands sent
1040: * to the network server over the socket. The protocol used is
1041: * 4 bytes - String CMD:
1042: * 2 bytes - Protocol version
1043: * 1 byte - length of locale (0 for default)
1044: * n bytes - locale
1045: * 1 byte - length of codeset (0 for default)
1046: * n bytes - codeset
1047: * 1 byte - command
1048: * n bytes - parameters for the command
1049: * The server returns
1050: * 4 bytes - String RPY:
1051: * for most commands
1052: * 1 byte - command result, 0 - OK, 1 - warning, 2 - error
1053: * if warning or error
1054: * 1 bytes - length of message key
1055: * n bytes - message key
1056: * 1 byte - number of parameters to message
1057: * {2 bytes - length of parameter
1058: * n bytes - parameter} for each parameter
1059: * for sysinfo
1060: * 1 byte - command result, 0 - OK, 1 - warning, 2 - error
1061: * if OK
1062: * 2 bytes - length of sysinfo
1063: * n bytes - sysinfo
1064: *
1065: *
1066: * Note, the 3rd byte of the command must not be 'D0' to distinquish it
1067: * from DSS structures.
1068: * The protocol for the parameters for each command follows:
1069: *
1070: * Command: trace <connection id> {on | off}
1071: * Protocol:
1072: * 4 bytes - connection id - connection id of 0 means all sessions
1073: * 1 byte - 0 off, 1 on
1074: *
1075: * Command: logConnections {on | off}
1076: * Protocol:
1077: * 1 byte - 0 off, 1 on
1078: *
1079: * Command: shutdown
1080: * No parameters
1081: *
1082: * Command: sysinfo
1083: * No parameters
1084: *
1085: * Command: dbstart
1086: * Protocol:
1087: * 2 bytes - length of database name
1088: * n bytes - database name
1089: * 2 bytes - length of boot password
1090: * n bytes - boot password
1091: * 2 bytes - length of encryption algorithm
1092: * n bytes - encryption algorithm
1093: * 2 bytes - length of encryption provider
1094: * n bytes - encryption provider
1095: * 2 bytes - length of user name
1096: * n bytes - user name
1097: * 2 bytes - length of password
1098: * n bytes - password
1099: *
1100: * Command: dbshutdown
1101: * Protocol:
1102: * 2 bytes - length of database name
1103: * n bytes - database name
1104: * 2 bytes - length of user name
1105: * n bytes - user name
1106: * 2 bytes - length of password
1107: * n bytes - password
1108: *
1109: * Command: connpool
1110: * Protocol:
1111: * 2 bytes - length of database name, if 0, default for all databases
1112: * is set
1113: * n bytes - database name
1114: * 2 bytes - minimum number of connections, if 0, connection pool not used
1115: * if value is -1 use default
1116: * 2 bytes - maximum number of connections, if 0, connections are created
1117: * as needed, if value is -1 use default
1118: *
1119: * Command: maxthreads
1120: * Protocol:
1121: * 2 bytes - maximum number of threads
1122: *
1123: * Command: timeslice
1124: * Protocol:
1125: * 4 bytes - timeslice value
1126: *
1127: * Command: tracedirectory
1128: * Protocol:
1129: * 2 bytes - length of directory name
1130: * n bytes - directory name
1131: *
1132: * Command: test connection
1133: * Protocol:
1134: * 2 bytes - length of database name if 0, just the connection
1135: * to the network server is tested and user name and
1136: * password aren't sent
1137: * n bytes - database name
1138: * 2 bytes - length of user name (optional)
1139: * n bytes - user name
1140: * 2 bytes - length of password (optional)
1141: * n bytes - password
1142: *
1143: * The calling routine is synchronized so that multiple threads don't clobber each
1144: * other. This means that configuration commands will be serialized.
1145: * This shouldn't be a problem since they should be fairly rare.
1146: *
1147: * @param reader input reader for command
1148: * @param writer output writer for command
1149: * @param session session information
1150: *
1151: * @exception Throwable throws an exception if an error occurs
1152: */
1153: protected synchronized void processCommands(DDMReader reader,
1154: DDMWriter writer, Session session) throws Throwable {
1155: try {
1156: String protocolStr = reader.readCmdString(4);
1157: String locale = DEFAULT_LOCALE;
1158: String codeset = null;
1159: // get the version
1160: int version = reader.readNetworkShort();
1161: if (version <= 0 || version > PROTOCOL_VERSION)
1162: throw new Throwable(langUtil.getTextMessage(
1163: "DRDA_UnknownProtocol.S", new Integer(version)
1164: .toString()));
1165: int localeLen = reader.readByte();
1166: if (localeLen > 0) {
1167: currentSession = session;
1168: locale = reader.readCmdString(localeLen);
1169: session.langUtil = new LocalizedResource(codeset,
1170: locale, DRDA_PROP_MESSAGES);
1171: }
1172: String notLocalMessage = null;
1173: // for now codesetLen is always 0
1174: int codesetLen = reader.readByte();
1175: int command = reader.readByte();
1176: if (command != COMMAND_TESTCONNECTION) {
1177: try {
1178: checkAddressIsLocal(session.clientSocket
1179: .getInetAddress());
1180: } catch (Exception e) {
1181: notLocalMessage = e.getMessage();
1182: }
1183: }
1184: if (notLocalMessage != null) {
1185: sendMessage(writer, ERROR, notLocalMessage);
1186: session.langUtil = null;
1187: currentSession = null;
1188: return;
1189: }
1190:
1191: switch (command) {
1192: case COMMAND_SHUTDOWN:
1193: sendOK(writer);
1194: directShutdown();
1195: break;
1196: case COMMAND_TRACE:
1197: sessionArg = reader.readNetworkInt();
1198: boolean on = (reader.readByte() == 1);
1199: if (setTrace(on)) {
1200: sendOK(writer);
1201: } else {
1202: sendMessage(writer, ERROR, localizeMessage(
1203: "DRDA_SessionNotFound.U",
1204: (session.langUtil == null) ? langUtil
1205: : session.langUtil,
1206: new String[] { new Integer(sessionArg)
1207: .toString() }));
1208: }
1209: break;
1210: case COMMAND_TRACEDIRECTORY:
1211: setTraceDirectory(reader.readCmdString());
1212: sendOK(writer);
1213: consolePropertyMessage("DRDA_TraceDirectoryChange.I",
1214: traceDirectory);
1215: break;
1216: case COMMAND_TESTCONNECTION:
1217: databaseArg = reader.readCmdString();
1218: userArg = reader.readCmdString();
1219: passwordArg = reader.readCmdString();
1220: if (databaseArg != null)
1221: connectToDatabase(writer, databaseArg, userArg,
1222: passwordArg);
1223: else
1224: sendOK(writer);
1225: break;
1226: case COMMAND_LOGCONNECTIONS:
1227: boolean log = (reader.readByte() == 1);
1228: setLogConnections(log);
1229: sendOK(writer);
1230: consolePropertyMessage("DRDA_LogConnectionsChange.I",
1231: (log ? "DRDA_ON.I" : "DRDA_OFF.I"));
1232: break;
1233: case COMMAND_SYSINFO:
1234: sendSysInfo(writer);
1235: break;
1236: case COMMAND_PROPERTIES:
1237: sendPropInfo(writer);
1238: break;
1239: case COMMAND_RUNTIME_INFO:
1240: sendRuntimeInfo(writer);
1241: break;
1242: case COMMAND_MAXTHREADS:
1243: int max = reader.readNetworkInt();
1244: try {
1245: setMaxThreads(max);
1246: } catch (Exception e) {
1247: sendMessage(writer, ERROR, e.getMessage());
1248: return;
1249: }
1250: int newval = getMaxThreads();
1251: sendOKInt(writer, newval);
1252: consolePropertyMessage("DRDA_MaxThreadsChange.I",
1253: new Integer(newval).toString());
1254: break;
1255: case COMMAND_TIMESLICE:
1256: int timeslice = reader.readNetworkInt();
1257: try {
1258: setTimeSlice(timeslice);
1259: } catch (Exception e) {
1260: sendMessage(writer, ERROR, e.getMessage());
1261: return;
1262: }
1263: newval = getTimeSlice();
1264: sendOKInt(writer, newval);
1265: consolePropertyMessage("DRDA_TimeSliceChange.I",
1266: new Integer(newval).toString());
1267: break;
1268: }
1269: } catch (DRDAProtocolException e) {
1270: //we need to handle this since we aren't in DRDA land here
1271: consoleExceptionPrintTrace(e);
1272:
1273: } catch (Exception e) {
1274: consoleExceptionPrintTrace(e);
1275: } finally {
1276: session.langUtil = null;
1277: currentSession = null;
1278: }
1279: }
1280:
1281: /**
1282: * Get the next session for the thread to work on
1283: * Called from DRDAConnThread after session completes or timeslice
1284: * exceeded.
1285: *
1286: * If there is a waiting session, pick it up and put currentSession
1287: * at the back of the queue if there is one.
1288: * @param currentSession session thread is currently working on
1289: *
1290: * @return next session to work on, could be same as current session
1291: */
1292: protected Session getNextSession(Session currentSession) {
1293: Session retval = null;
1294: if (shutdown == true)
1295: return retval;
1296: synchronized (runQueue) {
1297: try {
1298: // nobody waiting - go on with current session
1299: if (runQueue.size() == 0) {
1300: // no current session - wait for some work
1301: if (currentSession == null) {
1302: while (runQueue.size() == 0) {
1303: // This thread has nothing to do now so
1304: // we will add it to freeThreads
1305: freeThreads++;
1306: runQueue.wait();
1307: if (shutdown == true)
1308: return null;
1309: freeThreads--;
1310: }
1311: } else
1312: return currentSession;
1313: }
1314: retval = (Session) runQueue.elementAt(0);
1315: runQueue.removeElementAt(0);
1316: if (currentSession != null)
1317: runQueueAdd(currentSession);
1318: } catch (InterruptedException e) {
1319: // If for whatever reason (ex. database shutdown) a waiting thread is
1320: // interrupted while in this method, that thread is going to be
1321: // closed down, so we need to decrement the number of threads
1322: // that will be available for use.
1323: freeThreads--;
1324: }
1325: }
1326: return retval;
1327: }
1328:
1329: /**
1330: * Get the stored application requester or store if we haven't seen it yet
1331: *
1332: * @param appRequester Application Requester to look for
1333: *
1334: * @return stored application requester
1335: */
1336: protected AppRequester getAppRequester(AppRequester appRequester) {
1337: AppRequester s = null;
1338:
1339: if (SanityManager.DEBUG) {
1340: if (appRequester == null)
1341: SanityManager
1342: .THROWASSERT("null appRequester in getAppRequester");
1343: }
1344:
1345: if (!appRequesterTable.isEmpty())
1346: s = (AppRequester) appRequesterTable
1347: .get(appRequester.prdid);
1348:
1349: if (s == null) {
1350: appRequesterTable.put(appRequester.prdid, appRequester);
1351: return appRequester;
1352: } else {
1353: //compare just in case there are some differences
1354: //if they are different use the one we just read in
1355: if (s.equals(appRequester))
1356: return s;
1357: else
1358: return appRequester;
1359: }
1360: }
1361:
1362: /**
1363: * Get the server manager level for a given manager
1364: *
1365: * @param manager codepoint for manager
1366: * @return manager level
1367: */
1368: protected int getManagerLevel(int manager) {
1369: int mindex = CodePoint.getManagerIndex(manager);
1370: if (SanityManager.DEBUG) {
1371: if (mindex == CodePoint.UNKNOWN_MANAGER)
1372: SanityManager.THROWASSERT("manager out of bounds");
1373: }
1374: return MGR_LEVELS[mindex];
1375: }
1376:
1377: /**
1378: * Check whether a CCSID code page is supported
1379: *
1380: * @param ccsid CCSID to check
1381: * @return true if supported; false otherwise
1382: */
1383: protected boolean supportsCCSID(int ccsid) {
1384: try {
1385: CharacterEncodings.getJavaEncoding(ccsid);
1386: } catch (Exception e) {
1387: return false;
1388: }
1389: return true;
1390: }
1391:
1392: /**
1393: * Put property message on console
1394: *
1395: * @param msgProp message property key
1396: *
1397: * @throws Exception if an error occurs
1398: */
1399: protected void consolePropertyMessage(String msgProp)
1400: throws Exception {
1401: consolePropertyMessageWork(msgProp, null);
1402: }
1403:
1404: /**
1405: * Put property message on console
1406: *
1407: * @param msgProp message property key
1408: * @param arg argument for message
1409: *
1410: * @throws Exception if an error occurs
1411: */
1412: protected void consolePropertyMessage(String msgProp, String arg)
1413: throws Exception {
1414: consolePropertyMessageWork(msgProp, new String[] { arg });
1415: }
1416:
1417: /**
1418: * Put property message on console
1419: *
1420: * @param msgProp message property key
1421: * @param args argument array for message
1422: *
1423: * @throws Exception if an error occurs
1424: */
1425: protected void consolePropertyMessage(String msgProp, String[] args)
1426: throws Exception {
1427: consolePropertyMessageWork(msgProp, args);
1428: }
1429:
1430: /**
1431: * Is this the command protocol
1432: *
1433: * @param val
1434: */
1435: protected static boolean isCmd(String val) {
1436: if (val.equals(COMMAND_HEADER))
1437: return true;
1438: else
1439: return false;
1440: }
1441:
1442: /*******************************************************************************/
1443: /* Private methods */
1444: /*******************************************************************************/
1445: /**
1446: * Write Command reply
1447: *
1448: * @param writer writer to use
1449: *
1450: * @throws Exception if a problem occurs sending OK
1451: */
1452: private void writeCommandReplyHeader(DDMWriter writer)
1453: throws Exception {
1454: writer.setCMDProtocol();
1455: writer.writeString(REPLY_HEADER);
1456: }
1457:
1458: /**
1459: * Send OK from server to client after processing a command
1460: *
1461: * @param writer writer to use for sending OK
1462: *
1463: * @throws Exception if a problem occurs sending OK
1464: */
1465: private void sendOK(DDMWriter writer) throws Exception {
1466: writeCommandReplyHeader(writer);
1467: writer.writeByte(OK);
1468: writer.flush();
1469: }
1470:
1471: /**
1472: * Send OK and int value
1473: *
1474: * @param writer writer to use for sending
1475: * @param val int val to send
1476: *
1477: * @throws Exception if a problem occurs
1478: */
1479: private void sendOKInt(DDMWriter writer, int val) throws Exception {
1480: writeCommandReplyHeader(writer);
1481: writer.writeByte(OK);
1482: writer.writeNetworkInt(val);
1483: writer.flush();
1484: }
1485:
1486: /**
1487: * Send Error or Warning from server to client after processing a command
1488: *
1489: * @param writer writer to use for sending message
1490: * @param messageType 1 for Warning, 2 for Error 3 for SQLError
1491: * @param message message
1492: *
1493: * @throws Exception if a problem occurs sending message
1494: */
1495: private void sendMessage(DDMWriter writer, int messageType,
1496: String message) throws Exception {
1497: writeCommandReplyHeader(writer);
1498: writer.writeByte(messageType);
1499: writer.writeLDString(message);
1500: writer.flush();
1501: }
1502:
1503: /**
1504: * Send SQL Exception from server to client after processing a command
1505: *
1506: * @param writer writer to use for sending message
1507: * @param se Cloudscape exception
1508: * @param type type of exception, SQLERROR or SQLWARNING
1509: *
1510: * @throws Exception if a problem occurs sending message
1511: */
1512: private void sendSQLMessage(DDMWriter writer, SQLException se,
1513: int type) throws Exception {
1514: StringBuffer locMsg = new StringBuffer();
1515: //localize message if necessary
1516: while (se != null) {
1517: if (currentSession != null
1518: && currentSession.langUtil != null
1519: && se instanceof EmbedSQLException) {
1520: locMsg.append(se.getSQLState()
1521: + ":"
1522: + MessageService
1523: .getLocalizedMessage(
1524: currentSession.langUtil
1525: .getLocale(),
1526: ((EmbedSQLException) se)
1527: .getMessageId(),
1528: ((EmbedSQLException) se)
1529: .getArguments()));
1530: } else
1531: locMsg.append(se.getSQLState() + ":" + se.getMessage());
1532: se = se.getNextException();
1533: if (se != null)
1534: locMsg.append("\n");
1535: }
1536: sendMessage(writer, type, locMsg.toString());
1537: }
1538:
1539: /**
1540: * Send SysInfo information from server to client
1541: *
1542: * @param writer writer to use for sending sysinfo
1543: *
1544: * @throws Exception if a problem occurs sending value
1545: */
1546: private void sendSysInfo(DDMWriter writer) throws Exception {
1547: StringBuffer sysinfo = new StringBuffer();
1548: sysinfo.append(getNetSysInfo());
1549: sysinfo.append(getCLSSysInfo());
1550: try {
1551: writeCommandReplyHeader(writer);
1552: writer.writeByte(0); //O.K.
1553: writer.writeLDString(sysinfo.toString());
1554: } catch (DRDAProtocolException e) {
1555: consolePropertyMessage("DRDA_SysInfoWriteError.S", e
1556: .getMessage());
1557: }
1558: writer.flush();
1559: }
1560:
1561: /**
1562: * Send RuntimeInfo information from server to client
1563: *
1564: * @param writer writer to use for sending sysinfo
1565: *
1566: * @throws Exception if a problem occurs sending value
1567: */
1568: private void sendRuntimeInfo(DDMWriter writer) throws Exception {
1569: try {
1570: writeCommandReplyHeader(writer);
1571: writer.writeByte(0); //O.K.
1572: writer.writeLDString(getRuntimeInfo());
1573: } catch (DRDAProtocolException e) {
1574: consolePropertyMessage("DRDA_SysInfoWriteError.S", e
1575: .getMessage());
1576: }
1577: writer.flush();
1578: }
1579:
1580: /**
1581: * Send property information from server to client
1582: *
1583: * @param writer writer to use for sending sysinfo
1584: *
1585: * @throws Exception if a problem occurs sending value
1586: */
1587: private void sendPropInfo(DDMWriter writer) throws Exception {
1588: try {
1589: ByteArrayOutputStream out = new ByteArrayOutputStream();
1590: Properties p = getPropertyValues();
1591: p.store(out, "NetworkServerControl properties");
1592: try {
1593: writeCommandReplyHeader(writer);
1594: writer.writeByte(0); //O.K.
1595: writer.writeLDBytes(out.toByteArray());
1596: } catch (DRDAProtocolException e) {
1597: consolePropertyMessage("DRDA_PropInfoWriteError.S", e
1598: .getMessage());
1599: }
1600: writer.flush();
1601: } catch (Exception e) {
1602: consoleExceptionPrintTrace(e);
1603: }
1604: }
1605:
1606: /**
1607: * Get Net Server information
1608: *
1609: * @return system information for the Network Server
1610: */
1611: private String getNetSysInfo() {
1612: StringBuffer sysinfo = new StringBuffer();
1613: LocalizedResource localLangUtil = langUtil;
1614: if (currentSession != null && currentSession.langUtil != null)
1615: localLangUtil = currentSession.langUtil;
1616: sysinfo.append(localLangUtil
1617: .getTextMessage("DRDA_SysInfoBanner.I")
1618: + "\n");
1619: sysinfo.append(localLangUtil
1620: .getTextMessage("DRDA_SysInfoVersion.I")
1621: + " " + att_srvrlslv);
1622: sysinfo.append(" ");
1623: sysinfo.append(localLangUtil
1624: .getTextMessage("DRDA_SysInfoBuild.I")
1625: + " " + buildNumber);
1626: sysinfo.append(" ");
1627: sysinfo.append(localLangUtil
1628: .getTextMessage("DRDA_SysInfoDrdaPRDID.I")
1629: + " " + prdId);
1630: if (SanityManager.DEBUG) {
1631: sysinfo.append(" ** SANE BUILD **");
1632: }
1633: sysinfo.append("\n");
1634: // add property information
1635: Properties p = getPropertyValues();
1636: ByteArrayOutputStream bos = new ByteArrayOutputStream();
1637: PrintStream ps = new PrintStream(bos);
1638: p.list(ps);
1639: sysinfo.append(bos.toString());
1640: return sysinfo.toString();
1641: }
1642:
1643: /**
1644: * @see NetworkServerControl#getRuntimeInfo
1645: */
1646: private String getRuntimeInfo() {
1647: return buildRuntimeInfo(langUtil);
1648: }
1649:
1650: /**
1651: * Get Cloudscape information
1652: *
1653: * @return system information for Cloudscape
1654: *
1655: * @throws IOException if a problem occurs encoding string
1656: */
1657: private String getCLSSysInfo() throws IOException {
1658: ByteArrayOutputStream bos = new ByteArrayOutputStream();
1659: LocalizedResource localLangUtil = langUtil;
1660: if (currentSession != null && currentSession.langUtil != null)
1661: localLangUtil = currentSession.langUtil;
1662: LocalizedOutput aw = localLangUtil.getNewOutput(bos);
1663: org.apache.derby.impl.tools.sysinfo.Main.getMainInfo(aw, false);
1664: return bos.toString();
1665: }
1666:
1667: /**
1668: * Execute the command given on the command line
1669: *
1670: * @param args array of arguments indicating command to be executed
1671: *
1672: * @exception Exception throws an exception if an error occurs
1673: * see class comments for more information
1674: */
1675: public void executeWork(String args[]) throws Exception {
1676: // For convenience just use NetworkServerControlImpls log writer for user messages
1677: logWriter = makePrintWriter(System.out);
1678:
1679: int command = 0;
1680: if (args.length > 0)
1681: command = findCommand(args);
1682: else {
1683: consolePropertyMessage("DRDA_NoArgs.U");
1684: }
1685:
1686: // if we didn't have a valid command just return - error already generated
1687: if (command == COMMAND_UNKNOWN)
1688: return;
1689:
1690: // check that we have the right number of required arguments
1691: if (commandArgs.size() != COMMAND_ARGS[command])
1692: consolePropertyMessage("DRDA_InvalidNoArgs.U",
1693: COMMANDS[command]);
1694: int min;
1695: int max;
1696:
1697: switch (command) {
1698: case COMMAND_START:
1699: // the server was started from the command line, shutdown the
1700: // databases when the server is shutdown
1701: shutdownDatabasesOnShutdown = true;
1702: blockingStart(makePrintWriter(System.out));
1703: break;
1704: case COMMAND_SHUTDOWN:
1705: shutdown();
1706: consolePropertyMessage("DRDA_ShutdownSuccess.I",
1707: new String[] { att_srvclsnm, versionString,
1708: getFormattedTimestamp() });
1709: break;
1710: case COMMAND_TRACE: {
1711: boolean on = isOn((String) commandArgs.elementAt(0));
1712: trace(sessionArg, on);
1713: consoleTraceMessage(sessionArg, on);
1714: break;
1715: }
1716: case COMMAND_TRACEDIRECTORY:
1717: setTraceDirectory((String) commandArgs.elementAt(0));
1718: consolePropertyMessage("DRDA_TraceDirectoryChange.I",
1719: traceDirectory);
1720: break;
1721: case COMMAND_TESTCONNECTION:
1722: ping();
1723: consolePropertyMessage("DRDA_ConnectionTested.I",
1724: new String[] { hostArg,
1725: (new Integer(portNumber)).toString() });
1726: break;
1727: case COMMAND_LOGCONNECTIONS: {
1728: boolean on = isOn((String) commandArgs.elementAt(0));
1729: logConnections(on);
1730: consolePropertyMessage("DRDA_LogConnectionsChange.I",
1731: on ? "DRDA_ON.I" : "DRDA_OFF.I");
1732: break;
1733: }
1734: case COMMAND_SYSINFO: {
1735: String info = sysinfo();
1736: consoleMessage(info);
1737: break;
1738: }
1739: case COMMAND_MAXTHREADS:
1740: max = 0;
1741: try {
1742: max = Integer.parseInt((String) commandArgs
1743: .elementAt(0));
1744: } catch (NumberFormatException e) {
1745: consolePropertyMessage("DRDA_InvalidValue.U",
1746: new String[] {
1747: (String) commandArgs.elementAt(0),
1748: "maxthreads" });
1749: }
1750: if (max < MIN_MAXTHREADS)
1751: consolePropertyMessage("DRDA_InvalidValue.U",
1752: new String[] { new Integer(max).toString(),
1753: "maxthreads" });
1754: netSetMaxThreads(max);
1755:
1756: break;
1757: case COMMAND_RUNTIME_INFO:
1758: String reply = runtimeInfo();
1759: consoleMessage(reply);
1760: break;
1761: case COMMAND_TIMESLICE:
1762: int timeslice = 0;
1763: String timeSliceArg = (String) commandArgs.elementAt(0);
1764: try {
1765: timeslice = Integer.parseInt(timeSliceArg);
1766: } catch (NumberFormatException e) {
1767: consolePropertyMessage("DRDA_InvalidValue.U",
1768: new String[] {
1769: (String) commandArgs.elementAt(0),
1770: "timeslice" });
1771: }
1772: if (timeslice < MIN_TIMESLICE)
1773: consolePropertyMessage("DRDA_InvalidValue.U",
1774: new String[] {
1775: new Integer(timeslice).toString(),
1776: "timeslice" });
1777: netSetTimeSlice(timeslice);
1778:
1779: break;
1780: default:
1781: //shouldn't get here
1782: if (SanityManager.DEBUG)
1783: SanityManager.THROWASSERT("Invalid command in switch:"
1784: + command);
1785: }
1786: }
1787:
1788: /**
1789: * Add session to the run queue
1790: *
1791: * @param clientSession session needing work
1792: */
1793: private void runQueueAdd(Session clientSession) {
1794: synchronized (runQueue) {
1795: runQueue.addElement(clientSession);
1796: runQueue.notify();
1797: }
1798: }
1799:
1800: /**
1801: * Go through the arguments and find the command and save the dash arguments
1802: * and arguments to the command. Only one command is allowed in the argument
1803: * list.
1804: *
1805: * @param args arguments to search
1806: *
1807: * @return command
1808: */
1809: private int findCommand(String[] args) throws Exception {
1810: try {
1811: // process the dashArgs and pull out the command args
1812: int i = 0;
1813: int newpos = 0;
1814: while (i < args.length) {
1815: if (args[i].startsWith("-")) {
1816: newpos = processDashArg(i, args);
1817: if (newpos == i)
1818: commandArgs.addElement(args[i++]);
1819: else
1820: i = newpos;
1821: } else
1822: commandArgs.addElement(args[i++]);
1823: }
1824:
1825: // look up command
1826: if (commandArgs.size() > 0) {
1827: for (i = 0; i < COMMANDS.length; i++) {
1828: if (StringUtil.SQLEqualsIgnoreCase(COMMANDS[i],
1829: (String) commandArgs.firstElement())) {
1830: commandArgs.removeElementAt(0);
1831: return i;
1832: }
1833: }
1834: }
1835: // didn't find command
1836: consolePropertyMessage("DRDA_UnknownCommand.U",
1837: (String) commandArgs.firstElement());
1838: } catch (Exception e) {
1839: if (e.getMessage().equals(
1840: NetworkServerControlImpl.UNEXPECTED_ERR))
1841: throw e;
1842: //Ignore expected exceptions, they will have been
1843: //handled by the consolePropertyMessage routine
1844: }
1845: return COMMAND_UNKNOWN;
1846: }
1847:
1848: /**
1849: * Get the dash argument. Optional arguments are formated as -x value.
1850: *
1851: * @param pos starting point
1852: * @param args arguments to search
1853: *
1854: * @return command
1855: *
1856: * @exception Exception thrown if an error occurs
1857: */
1858: private int processDashArg(int pos, String[] args) throws Exception {
1859: //check for a negative number
1860: char c = args[pos].charAt(1);
1861: if (c >= '0' && c <= '9')
1862: return pos;
1863: int dashArg = -1;
1864: for (int i = 0; i < DASHARGS.length; i++) {
1865: if (DASHARGS[i].equals(args[pos].substring(1))) {
1866: dashArg = i;
1867: pos++;
1868: break;
1869: }
1870: }
1871: if (dashArg == -1)
1872: consolePropertyMessage("DRDA_UnknownArgument.U", args[pos]);
1873: switch (dashArg) {
1874: case DASHARG_PORT:
1875: if (pos < args.length) {
1876: try {
1877: portNumber = Integer.parseInt(args[pos]);
1878: } catch (NumberFormatException e) {
1879: consolePropertyMessage("DRDA_InvalidValue.U",
1880: new String[] { args[pos],
1881: "DRDA_PortNumber.I" });
1882: }
1883: } else
1884: consolePropertyMessage("DRDA_MissingValue.U",
1885: "DRDA_PortNumber.I");
1886: break;
1887: case DASHARG_HOST:
1888: if (pos < args.length) {
1889: hostArg = args[pos];
1890: } else
1891: consolePropertyMessage("DRDA_MissingValue.U",
1892: "DRDA_Host.I");
1893: break;
1894: case DASHARG_DATABASE:
1895: if (pos < args.length)
1896: databaseArg = args[pos];
1897: else
1898: consolePropertyMessage("DRDA_MissingValue.U",
1899: "DRDA_DatabaseDirectory.I");
1900: break;
1901: case DASHARG_USER:
1902: if (pos < args.length) {
1903: userArg = args[pos++];
1904: if (pos < args.length)
1905: passwordArg = args[pos];
1906: else
1907: consolePropertyMessage("DRDA_MissingValue.U",
1908: "DRDA_Password.I");
1909: } else
1910: consolePropertyMessage("DRDA_MissingValue.U",
1911: "DRDA_User.I");
1912: break;
1913: case DASHARG_ENCALG:
1914: if (pos < args.length)
1915: encAlgArg = args[pos];
1916: else
1917: consolePropertyMessage("DRDA_MissingValue.U",
1918: "DRDA_EncryptionAlgorithm.I");
1919: break;
1920: case DASHARG_ENCPRV:
1921: if (pos < args.length)
1922: encPrvArg = args[pos];
1923: else
1924: consolePropertyMessage("DRDA_MissingValue.U",
1925: "DRDA_EncryptionProvider.I");
1926: break;
1927: case DASHARG_LOADSYSIBM:
1928: break;
1929: case DASHARG_SESSION:
1930: if (pos < args.length)
1931: try {
1932: sessionArg = Integer.parseInt(args[pos]);
1933: } catch (NumberFormatException e) {
1934: consolePropertyMessage(
1935: "DRDA_InvalidValue.U",
1936: new String[] { args[pos], "DRDA_Session.I" });
1937: }
1938: else
1939: consolePropertyMessage("DRDA_MissingValue.U",
1940: "DRDA_Session.I");
1941: break;
1942: default:
1943: //shouldn't get here
1944: }
1945: return pos + 1;
1946: }
1947:
1948: /**
1949: * Is string "on" or "off"
1950: *
1951: * @param arg string to check
1952: *
1953: * @return true if string is "on", false if string is "off"
1954: *
1955: * @exception Exception thrown if string is not one of "on" or "off"
1956: */
1957: private boolean isOn(String arg) throws Exception {
1958: if (StringUtil.SQLEqualsIgnoreCase(arg, "on"))
1959: return true;
1960: else if (!StringUtil.SQLEqualsIgnoreCase(arg, "off"))
1961: consolePropertyMessage("DRDA_OnOffValue.U", arg);
1962: return false;
1963: }
1964:
1965: /**
1966: * Set up client socket to send a command to the network server
1967: *
1968: * @exception Exception thrown if exception encountered
1969: */
1970: private void setUpSocket() throws Exception {
1971:
1972: try {
1973: clientSocket = (Socket) AccessController
1974: .doPrivileged(new PrivilegedExceptionAction() {
1975:
1976: public Object run()
1977: throws UnknownHostException,
1978: IOException {
1979: if (hostAddress == null)
1980: hostAddress = InetAddress
1981: .getByName(hostArg);
1982:
1983: // JDK131 can't connect with a client
1984: // socket with 0.0.0.0 (all addresses) so we will
1985: // getLocalHost() which will suffice.
1986: InetAddress connectAddress;
1987: if (JVMInfo.JDK_ID <= JVMInfo.J2SE_13
1988: && hostAddress.getHostAddress()
1989: .equals("0.0.0.0"))
1990: connectAddress = InetAddress
1991: .getLocalHost();
1992: else
1993: connectAddress = hostAddress;
1994:
1995: return new Socket(connectAddress,
1996: portNumber);
1997: }
1998: });
1999: } catch (PrivilegedActionException pae) {
2000: Exception e1 = pae.getException();
2001: if (e1 instanceof UnknownHostException) {
2002: consolePropertyMessage("DRDA_UnknownHost.S", hostArg);
2003: } else if (e1 instanceof IOException) {
2004: consolePropertyMessage("DRDA_NoIO.S", new String[] {
2005: hostArg, (new Integer(portNumber)).toString() });
2006: }
2007: } catch (Exception e) {
2008: // If we find other (unexpected) errors, we ultimately exit--so make
2009: // sure we print the error message before doing so (Beetle 5033).
2010: throwUnexpectedException(e);
2011: }
2012:
2013: try {
2014: clientIs = clientSocket.getInputStream();
2015: clientOs = clientSocket.getOutputStream();
2016: } catch (IOException e) {
2017: consolePropertyMessage("DRDA_NoInputStream.I");
2018: throw e;
2019: }
2020: }
2021:
2022: private void checkAddressIsLocal(InetAddress inetAddr)
2023: throws UnknownHostException, Exception {
2024: for (int i = 0; i < localAddresses.size(); i++) {
2025: if (inetAddr.equals((InetAddress) localAddresses.get(i))) {
2026: return;
2027: }
2028: }
2029: consolePropertyMessage("DRDA_NeedLocalHost.S", new String[] {
2030: inetAddr.getHostName(),
2031: serverSocket.getInetAddress().getHostName() });
2032:
2033: }
2034:
2035: /**
2036: * Build local address list to allow admin commands.
2037: *
2038: * @param bindAddr Address on which server was started
2039: *
2040: * Note: Some systems may not support localhost.
2041: * In that case a console message will print for the localhost entries,
2042: * but the server will continue to start.
2043: **/
2044: private void buildLocalAddressList(InetAddress bindAddr) {
2045: localAddresses = new ArrayList(3);
2046: localAddresses.add(bindAddr);
2047: try {
2048: localAddresses.add(InetAddress.getLocalHost());
2049: localAddresses.add(InetAddress.getByName("localhost"));
2050: } catch (UnknownHostException uhe) {
2051: try {
2052: consolePropertyMessage("DRDA_UnknownHostWarning.I", uhe
2053: .getMessage());
2054: } catch (Exception e) { // just a warning shouldn't actually throw an exception
2055: }
2056: }
2057: }
2058:
2059: /**
2060: * Routines for writing commands for NetworkServerControlImpl being used as a client
2061: * to a server
2062: */
2063:
2064: /**
2065: * Write command header consisting of command header string and protocol
2066: * version and command
2067: *
2068: * @param command command to be written
2069: *
2070: * @exception Exception throws an exception if an error occurs
2071: */
2072: private void writeCommandHeader(int command) throws Exception {
2073: try {
2074: writeString(COMMAND_HEADER);
2075: commandOs
2076: .writeByte((byte) ((PROTOCOL_VERSION & 0xf0) >> 8));
2077: commandOs.writeByte((byte) (PROTOCOL_VERSION & 0x0f));
2078:
2079: if (clientLocale != null && clientLocale != DEFAULT_LOCALE) {
2080: commandOs.writeByte(clientLocale.length());
2081: commandOs.writeBytes(clientLocale);
2082: } else
2083: commandOs.writeByte((byte) 0);
2084: commandOs.writeByte((byte) 0);
2085: commandOs.writeByte((byte) command);
2086: } catch (IOException e) {
2087: clientSocketError(e);
2088: }
2089: }
2090:
2091: /**
2092: * Write length delimited string string
2093: *
2094: * @param msg string to be written
2095: *
2096: * @exception Exception throws an exception if an error occurs
2097: */
2098: private void writeLDString(String msg) throws Exception {
2099: try {
2100: if (msg == null) {
2101: commandOs.writeShort(0);
2102: } else {
2103: commandOs.writeShort(msg.length());
2104: writeString(msg);
2105: }
2106: } catch (IOException e) {
2107: clientSocketError(e);
2108: }
2109: }
2110:
2111: /** Write string
2112: *
2113: * @param msg String to write
2114: */
2115:
2116: protected void writeString(String msg) throws Exception {
2117: byte[] msgBytes = msg.getBytes(DEFAULT_ENCODING);
2118: commandOs.write(msgBytes, 0, msgBytes.length);
2119: }
2120:
2121: /**
2122: * Write short
2123: *
2124: * @param value value to be written
2125: *
2126: * @exception Exception throws an exception if an error occurs
2127: */
2128: private void writeShort(int value) throws Exception {
2129: try {
2130: commandOs.writeByte((byte) ((value & 0xf0) >> 8));
2131: commandOs.writeByte((byte) (value & 0x0f));
2132: } catch (IOException e) {
2133: clientSocketError(e);
2134: }
2135: }
2136:
2137: /**
2138: * Write byte
2139: *
2140: * @param value value to be written
2141: *
2142: * @exception Exception throws an exception if an error occurs
2143: */
2144: private void writeByte(int value) throws Exception {
2145: try {
2146: commandOs.writeByte((byte) (value & 0x0f));
2147: } catch (IOException e) {
2148: clientSocketError(e);
2149: }
2150: }
2151:
2152: /**
2153: * Send client message to server
2154: *
2155: *
2156: * @exception Exception throws an exception if an error occurs
2157: */
2158: private void send() throws Exception {
2159: try {
2160: byteArrayOs.writeTo(clientOs);
2161: commandOs.flush();
2162: byteArrayOs.reset(); //discard anything currently in the byte array
2163: } catch (IOException e) {
2164: clientSocketError(e);
2165: }
2166: }
2167:
2168: /**
2169: * Stream error writing to client socket
2170: */
2171: private void clientSocketError(IOException e) throws IOException {
2172: try {
2173: consolePropertyMessage("DRDA_ClientSocketError.S", e
2174: .getMessage());
2175: } catch (Exception ce) {
2176: } // catch the exception consolePropertyMessage will
2177: // throw since we also want to print a stack trace
2178: consoleExceptionPrintTrace(e);
2179: throw e;
2180: }
2181:
2182: /**
2183: * Read result from sending client message to server
2184: *
2185: * @exception Exception throws an exception if an error occurs
2186: */
2187: private void readResult() throws Exception {
2188: fillReplyBuffer();
2189: readCommandReplyHeader();
2190: if (replyBufferPos >= replyBufferCount)
2191: consolePropertyMessage("DRDA_InvalidReplyTooShort.S");
2192: int messageType = replyBuffer[replyBufferPos++] & 0xFF;
2193: if (messageType == OK) // O.K.
2194: return;
2195: // get error and display and throw exception
2196: String message = readLDString();
2197: if (messageType == SQLERROR)
2198: wrapSQLError(message);
2199: else if (messageType == SQLWARNING)
2200: wrapSQLWarning(message);
2201: else
2202: consolePropertyMessage(message);
2203: }
2204:
2205: /**
2206: * Ensure the reply buffer is at large enought to hold all the data;
2207: * don't just rely on OS level defaults
2208: *
2209: *
2210: * @param minimumBytesNeeded size of buffer required
2211: * @exception Exception throws an exception if a problem reading the reply
2212: */
2213: private void ensureDataInBuffer(int minimumBytesNeeded)
2214: throws Exception {
2215: // make sure the buffer is large enough
2216: while ((replyBufferCount - replyBufferPos) < minimumBytesNeeded) {
2217: try {
2218: int bytesRead = clientIs.read(replyBuffer,
2219: replyBufferCount, replyBuffer.length
2220: - replyBufferCount);
2221: replyBufferCount += bytesRead;
2222:
2223: } catch (IOException e) {
2224: clientSocketError(e);
2225: }
2226: }
2227: }
2228:
2229: /**
2230: * Fill the reply buffer with the reply allocates a reply buffer if one doesn't
2231: * exist
2232: *
2233: *
2234: * @exception Exception throws an exception if a problem reading the reply
2235: */
2236: private void fillReplyBuffer() throws Exception {
2237: if (replyBuffer == null)
2238: replyBuffer = new byte[MAXREPLY];
2239: try {
2240: replyBufferCount = clientIs.read(replyBuffer);
2241: } catch (IOException e) {
2242: clientSocketError(e);
2243: }
2244: if (replyBufferCount == -1)
2245: consolePropertyMessage("DRDA_InvalidReplyTooShort.S");
2246: replyBufferPos = 0;
2247: }
2248:
2249: /**
2250: * Read the command reply header from the server
2251: *
2252: * @exception Exception throws an exception if an error occurs
2253: */
2254: private void readCommandReplyHeader() throws Exception {
2255: ensureDataInBuffer(REPLY_HEADER_LENGTH);
2256: if (replyBufferCount < REPLY_HEADER_LENGTH) {
2257: consolePropertyMessage("DRDA_InvalidReplyHeader1.S",
2258: Integer.toString(replyBufferCount));
2259: }
2260: String header = new String(replyBuffer, 0, REPLY_HEADER_LENGTH,
2261: DEFAULT_ENCODING);
2262: if (!header.equals(REPLY_HEADER)) {
2263: consolePropertyMessage("DRDA_InvalidReplyHeader2.S", header);
2264: }
2265: replyBufferPos += REPLY_HEADER_LENGTH;
2266: }
2267:
2268: /**
2269: * Read short from buffer
2270: * @exception Exception throws an exception if an error occurs
2271: */
2272: private int readShort() throws Exception {
2273: ensureDataInBuffer(2);
2274: if (replyBufferPos + 2 > replyBufferCount)
2275: consolePropertyMessage("DRDA_InvalidReplyTooShort.S");
2276: return ((replyBuffer[replyBufferPos++] & 0xff) << 8)
2277: + (replyBuffer[replyBufferPos++] & 0xff);
2278: }
2279:
2280: /**
2281: * Read int from buffer
2282: * @exception Exception throws an exception if an error occurs
2283: */
2284: private int readInt() throws Exception {
2285: ensureDataInBuffer(4);
2286: if (replyBufferPos + 4 > replyBufferCount)
2287: consolePropertyMessage("DRDA_InvalidReplyTooShort.S");
2288: return ((replyBuffer[replyBufferPos++] & 0xff) << 24)
2289: + ((replyBuffer[replyBufferPos++] & 0xff) << 16)
2290: + ((replyBuffer[replyBufferPos++] & 0xff) << 8)
2291: + (replyBuffer[replyBufferPos++] & 0xff);
2292: }
2293:
2294: /**
2295: * Read String reply
2296: *
2297: * @param msgKey error message key
2298: * @return string value or null
2299: * @exception Exception throws an error if problems reading reply
2300: */
2301: private String readStringReply(String msgKey) throws Exception {
2302: fillReplyBuffer();
2303: readCommandReplyHeader();
2304: if (replyBuffer[replyBufferPos++] == 0) // O.K.
2305: return readLDString();
2306: else
2307: consolePropertyMessage(msgKey);
2308: return null;
2309:
2310: }
2311:
2312: /**
2313: * Read length delimited string from a buffer
2314: *
2315: * @return string value from buffer
2316: * @exception Exception throws an error if problems reading reply
2317: */
2318: private String readLDString() throws Exception {
2319: int strlen = readShort();
2320: ensureDataInBuffer(strlen);
2321: if (replyBufferPos + strlen > replyBufferCount)
2322: consolePropertyMessage("DRDA_InvalidReplyTooShort.S");
2323: String retval = new String(replyBuffer, replyBufferPos, strlen,
2324: DEFAULT_ENCODING);
2325: replyBufferPos += strlen;
2326: return retval;
2327: }
2328:
2329: /**
2330: * Read Bytes reply
2331: *
2332: * @param msgKey error message key
2333: * @return string value or null
2334: * @exception Exception throws an error if problems reading reply
2335: */
2336: private byte[] readBytesReply(String msgKey) throws Exception {
2337: fillReplyBuffer();
2338: readCommandReplyHeader();
2339: if (replyBuffer[replyBufferPos++] == 0) // O.K.
2340: return readLDBytes();
2341: else
2342: consolePropertyMessage(msgKey);
2343: return null;
2344:
2345: }
2346:
2347: /**
2348: * Read length delimited bytes from a buffer
2349: *
2350: * @return byte array from buffer
2351: * @exception Exception throws an error if problems reading reply
2352: */
2353: private byte[] readLDBytes() throws Exception {
2354: int len = readShort();
2355: ensureDataInBuffer(len);
2356: if (replyBufferPos + len > replyBufferCount)
2357: consolePropertyMessage("DRDA_InvalidReplyTooShort.S");
2358: byte[] retval = new byte[len];
2359: for (int i = 0; i < len; i++)
2360: retval[i] = replyBuffer[replyBufferPos++];
2361: return retval;
2362: }
2363:
2364: /**
2365: * Initialize fields from system properties
2366: *
2367: */
2368: private void getPropertyInfo() throws Exception {
2369: //set values according to properties
2370:
2371: String directory = PropertyUtil
2372: .getSystemProperty(Property.SYSTEM_HOME_PROPERTY);
2373: String propval = PropertyUtil
2374: .getSystemProperty(Property.DRDA_PROP_LOGCONNECTIONS);
2375: if (propval != null
2376: && StringUtil.SQLEqualsIgnoreCase(propval, "true"))
2377: setLogConnections(true);
2378: propval = PropertyUtil
2379: .getSystemProperty(Property.DRDA_PROP_TRACEALL);
2380: if (propval != null
2381: && StringUtil.SQLEqualsIgnoreCase(propval, "true"))
2382: setTraceAll(true);
2383:
2384: //If the derby.system.home property has been set, it is the default.
2385: //Otherwise, the default is the current directory.
2386: //If derby.system.home is not set, directory will be null and trace files will get
2387: //created in current directory.
2388: propval = PropertyUtil.getSystemProperty(
2389: Property.DRDA_PROP_TRACEDIRECTORY, directory);
2390: if (propval != null) {
2391: if (propval.equals(""))
2392: propval = directory;
2393: setTraceDirectory(propval);
2394: }
2395:
2396: //DERBY-375 If a system property is specified without any value, getProperty returns
2397: //an empty string. Use default values in such cases.
2398: propval = PropertyUtil
2399: .getSystemProperty(Property.DRDA_PROP_MINTHREADS);
2400: if (propval != null) {
2401: if (propval.equals(""))
2402: propval = "0";
2403: setMinThreads(getIntPropVal(Property.DRDA_PROP_MINTHREADS,
2404: propval));
2405: }
2406:
2407: propval = PropertyUtil
2408: .getSystemProperty(Property.DRDA_PROP_MAXTHREADS);
2409: if (propval != null) {
2410: if (propval.equals(""))
2411: propval = "0";
2412: setMaxThreads(getIntPropVal(Property.DRDA_PROP_MAXTHREADS,
2413: propval));
2414: }
2415:
2416: propval = PropertyUtil
2417: .getSystemProperty(Property.DRDA_PROP_TIMESLICE);
2418: if (propval != null) {
2419: if (propval.equals(""))
2420: propval = "0";
2421: setTimeSlice(getIntPropVal(Property.DRDA_PROP_TIMESLICE,
2422: propval));
2423: }
2424:
2425: propval = PropertyUtil
2426: .getSystemProperty(Property.DRDA_PROP_PORTNUMBER);
2427: if (propval != null) {
2428: if (propval.equals(""))
2429: propval = String
2430: .valueOf(NetworkServerControl.DEFAULT_PORTNUMBER);
2431: portNumber = getIntPropVal(Property.DRDA_PROP_PORTNUMBER,
2432: propval);
2433: }
2434:
2435: propval = PropertyUtil
2436: .getSystemProperty(Property.DRDA_PROP_KEEPALIVE);
2437: if (propval != null
2438: && StringUtil.SQLEqualsIgnoreCase(propval, "false"))
2439: keepAlive = false;
2440:
2441: propval = PropertyUtil
2442: .getSystemProperty(Property.DRDA_PROP_HOSTNAME);
2443: if (propval != null) {
2444: if (propval.equals(""))
2445: hostArg = DEFAULT_HOST;
2446: else
2447: hostArg = propval;
2448: }
2449: propval = PropertyUtil
2450: .getSystemProperty(NetworkServerControlImpl.DRDA_PROP_DEBUG);
2451: if (propval != null
2452: && StringUtil.SQLEqualsIgnoreCase(propval, "true"))
2453: debugOutput = true;
2454:
2455: propval = PropertyUtil
2456: .getSystemProperty(Property.DRDA_PROP_SECURITYMECHANISM);
2457: if (propval != null) {
2458: setSecurityMechanism(propval);
2459: }
2460:
2461: }
2462:
2463: /**
2464: * Retrieve the SECMEC integer value from the
2465: * user friendly security mechanism name
2466: * @param s security mechanism name
2467: * @return integer value , return the SECMEC value for
2468: * the security mechanism as defined by DRDA spec
2469: * or INVALID_OR_NOTSET_SECURITYMECHANISM if 's'
2470: * passed is invalid or not supported security
2471: * mechanism
2472: */
2473: private int getSecMecValue(String s) {
2474: int secmec = INVALID_OR_NOTSET_SECURITYMECHANISM;
2475:
2476: if (StringUtil.SQLEqualsIgnoreCase(s, "USER_ONLY_SECURITY"))
2477: secmec = CodePoint.SECMEC_USRIDONL;
2478: else if (StringUtil.SQLEqualsIgnoreCase(s,
2479: "CLEAR_TEXT_PASSWORD_SECURITY"))
2480: secmec = CodePoint.SECMEC_USRIDPWD;
2481: else if (StringUtil.SQLEqualsIgnoreCase(s,
2482: "ENCRYPTED_USER_AND_PASSWORD_SECURITY"))
2483: secmec = CodePoint.SECMEC_EUSRIDPWD;
2484: else if (StringUtil.SQLEqualsIgnoreCase(s,
2485: "STRONG_PASSWORD_SUBSTITUTE_SECURITY"))
2486: secmec = CodePoint.SECMEC_USRSSBPWD;
2487:
2488: return secmec;
2489: }
2490:
2491: /**
2492: * Retrieve the string name for the integer
2493: * secmec value
2494: * @param secmecVal secmec value
2495: * @return String - return the string name corresponding
2496: * to the secmec value if recognized else returns null
2497: */
2498: private String getStringValueForSecMec(int secmecVal) {
2499: switch (secmecVal) {
2500: case CodePoint.SECMEC_USRIDONL:
2501: return "USER_ONLY_SECURITY";
2502:
2503: case CodePoint.SECMEC_USRIDPWD:
2504: return "CLEAR_TEXT_PASSWORD_SECURITY";
2505:
2506: case CodePoint.SECMEC_EUSRIDPWD:
2507: return "ENCRYPTED_USER_AND_PASSWORD_SECURITY";
2508:
2509: case CodePoint.SECMEC_USRSSBPWD:
2510: return "STRONG_PASSWORD_SUBSTITUTE_SECURITY";
2511: }
2512: return null;
2513: }
2514:
2515: /**
2516: * This method returns whether EUSRIDPWD security mechanism
2517: * is supported or not. See class static block for more
2518: * info.
2519: * @return true if EUSRIDPWD is supported, false otherwise
2520: */
2521: boolean supportsEUSRIDPWD() {
2522: return SUPPORTS_EUSRIDPWD;
2523: }
2524:
2525: /**
2526: * Get integer property values
2527: *
2528: * @param propName property name
2529: * @param propVal string property value
2530: * @return integer value
2531: *
2532: * @exception Exception if not a valid integer
2533: */
2534: private int getIntPropVal(String propName, String propVal)
2535: throws Exception {
2536: int val = 0;
2537: try {
2538: val = (new Integer(propVal)).intValue();
2539: } catch (Exception e) {
2540: consolePropertyMessage("DRDA_InvalidPropVal.S",
2541: new String[] { propName, propVal });
2542: }
2543: return val;
2544: }
2545:
2546: /**
2547: * Handle console error message
2548: * - display on console and if it is a user error, display usage
2549: * - if user error or severe error, throw exception with message key and message
2550: *
2551: * @param messageKey message key
2552: * @param args arguments to message
2553: *
2554: * @throws Exception if an error occurs
2555: */
2556: private void consolePropertyMessageWork(String messageKey,
2557: String[] args) throws Exception {
2558: String locMsg = null;
2559:
2560: int type = getMessageType(messageKey);
2561:
2562: if (type == ERRTYPE_UNKNOWN)
2563: locMsg = messageKey;
2564: else
2565: locMsg = localizeMessage(messageKey, langUtil, args);
2566:
2567: //display on the console
2568: consoleMessage(locMsg);
2569:
2570: //if it is a user error display usage
2571: if (type == ERRTYPE_USER)
2572: usage();
2573:
2574: //we may want to use a different locale for throwing the exception
2575: //since this can be sent to a browser with a different locale
2576: if (currentSession != null && currentSession.langUtil != null
2577: && type != ERRTYPE_UNKNOWN)
2578: locMsg = localizeMessage(messageKey,
2579: currentSession.langUtil, args);
2580:
2581: // throw an exception for severe and user errors
2582: if (type == ERRTYPE_SEVERE || type == ERRTYPE_USER) {
2583: if (messageKey.equals("DRDA_SQLException.S"))
2584: throwSQLException(args[0]);
2585: else if (messageKey.equals("DRDA_SQLWarning.I"))
2586: throwSQLWarning(args[0]);
2587: else
2588: throw new Exception(messageKey + ":" + locMsg);
2589: }
2590:
2591: // throw an exception with just the message if the error type is
2592: // unknown
2593: if (type == ERRTYPE_UNKNOWN)
2594: throw new Exception(locMsg);
2595:
2596: return;
2597:
2598: }
2599:
2600: /**
2601: * Throw a SQL Exception which was sent over by a server
2602: * Format of the msg is SQLSTATE:localized message\nSQLSTATE:next localized message
2603: *
2604: * @param msg msg containing SQL Exception
2605: *
2606: * @throws SQLException
2607: */
2608: private void throwSQLException(String msg) throws SQLException {
2609: SQLException se = null;
2610: SQLException ne;
2611: SQLException ce = null;
2612: StringBuffer strbuf = new StringBuffer();
2613: StringTokenizer tokenizer = new StringTokenizer(msg, "\n");
2614: String sqlstate = null;
2615: String str;
2616: while (tokenizer.hasMoreTokens()) {
2617: str = tokenizer.nextToken();
2618: //start of the next message
2619: if (str.charAt(5) == ':') {
2620: if (strbuf.length() > 0) {
2621: if (se == null) {
2622: se = new SQLException(strbuf.toString(),
2623: sqlstate);
2624: ce = se;
2625: } else {
2626: ne = new SQLException(strbuf.toString(),
2627: sqlstate);
2628: ce.setNextException(ne);
2629: ce = ne;
2630: }
2631: strbuf = new StringBuffer();
2632: }
2633: strbuf.append(str.substring(6));
2634: sqlstate = str.substring(0, 5);
2635: } else
2636: strbuf.append(str);
2637: }
2638: if (strbuf.length() > 0) {
2639: if (se == null) {
2640: se = new SQLException(strbuf.toString(), sqlstate);
2641: ce = se;
2642: } else {
2643: ne = new SQLException(strbuf.toString(), sqlstate);
2644: ce.setNextException(ne);
2645: ce = ne;
2646: }
2647: }
2648: throw se;
2649: }
2650:
2651: /**
2652: * Throw a SQL Warning which was sent over by a server
2653: * Format of the msg is SQLSTATE:localized message\nSQLSTATE:next localized message
2654: *
2655: * @param msg msg containing SQL Warning
2656: *
2657: * @throws SQLWarning
2658: */
2659: private void throwSQLWarning(String msg) throws SQLWarning {
2660: SQLWarning se = null;
2661: SQLWarning ne;
2662: SQLWarning ce = null;
2663: StringBuffer strbuf = new StringBuffer();
2664: StringTokenizer tokenizer = new StringTokenizer(msg, "\n");
2665: String sqlstate = null;
2666: String str;
2667: while (tokenizer.hasMoreTokens()) {
2668: str = tokenizer.nextToken();
2669: //start of the next message
2670: if (str.charAt(5) == ':') {
2671: if (strbuf.length() > 0) {
2672: if (se == null) {
2673: se = new SQLWarning(strbuf.toString(), sqlstate);
2674: ce = se;
2675: } else {
2676: ne = new SQLWarning(strbuf.toString(), sqlstate);
2677: ce.setNextException(ne);
2678: ce = ne;
2679: }
2680: strbuf = new StringBuffer();
2681: }
2682: strbuf.append(str.substring(6));
2683: sqlstate = str.substring(0, 5);
2684: } else
2685: strbuf.append(str);
2686: }
2687: if (strbuf.length() > 0) {
2688: if (se == null) {
2689: se = new SQLWarning(strbuf.toString(), sqlstate);
2690: ce = se;
2691: } else {
2692: ne = new SQLWarning(strbuf.toString(), sqlstate);
2693: ce.setNextException(ne);
2694: ce = ne;
2695: }
2696: }
2697: throw se;
2698: }
2699:
2700: /**
2701: * Print a trace for the (unexpected) exception received, then
2702: * throw a generic exception indicating that 1) an unexpected
2703: * exception was thrown, and 2) we've already printed the trace
2704: * (so don't do it again).
2705: *
2706: * @param e An unexpected exception.
2707: * @throws Exception with message UNEXPECTED_ERR.
2708: */
2709: private void throwUnexpectedException(Exception e) throws Exception {
2710:
2711: consoleExceptionPrintTrace(e);
2712: throw new Exception(UNEXPECTED_ERR);
2713:
2714: }
2715:
2716: /**
2717: * Localize a message given a particular AppUI
2718: *
2719: * @param msgProp message key
2720: * @param localLangUtil LocalizedResource to use to localize message
2721: * @param args arguments to message
2722: *
2723: */
2724: private String localizeMessage(String msgProp,
2725: LocalizedResource localLangUtil, String[] args) {
2726: String locMsg = null;
2727: //check if the argument is a property
2728: if (args != null) {
2729: String[] argMsg = new String[args.length];
2730: for (int i = 0; i < args.length; i++) {
2731: if (isMsgProperty(args[i]))
2732: argMsg[i] = localLangUtil.getTextMessage(args[i]);
2733: else
2734: argMsg[i] = args[i];
2735: }
2736: switch (args.length) {
2737: case 1:
2738: locMsg = localLangUtil.getTextMessage(msgProp,
2739: argMsg[0]);
2740: break;
2741: case 2:
2742: locMsg = localLangUtil.getTextMessage(msgProp,
2743: argMsg[0], argMsg[1]);
2744: break;
2745: case 3:
2746: locMsg = localLangUtil.getTextMessage(msgProp,
2747: argMsg[0], argMsg[1], argMsg[2]);
2748: break;
2749: case 4:
2750: locMsg = localLangUtil.getTextMessage(msgProp,
2751: argMsg[0], argMsg[1], argMsg[2], argMsg[3]);
2752: break;
2753: default:
2754: //shouldn't get here
2755: }
2756: } else
2757: locMsg = localLangUtil.getTextMessage(msgProp);
2758: return locMsg;
2759: }
2760:
2761: /**
2762: * Determine type of message
2763: *
2764: * @param msg message
2765: *
2766: * @return message type
2767: */
2768: private int getMessageType(String msg) {
2769: //all property messages should start with DRDA_
2770: if (!msg.startsWith(DRDA_MSG_PREFIX))
2771: return ERRTYPE_UNKNOWN;
2772: int startpos = msg.indexOf('.') + 1;
2773: if (startpos >= msg.length())
2774: return ERRTYPE_UNKNOWN;
2775: if (msg.length() > (startpos + 1))
2776: return ERRTYPE_UNKNOWN;
2777: char type = msg.charAt(startpos);
2778: if (type == 'S')
2779: return ERRTYPE_SEVERE;
2780: if (type == 'U')
2781: return ERRTYPE_USER;
2782: if (type == 'I')
2783: return ERRTYPE_INFO;
2784: return ERRTYPE_UNKNOWN;
2785: }
2786:
2787: /**
2788: * Determine whether string is a property key or not
2789: * property keys start with DRDA_MSG_PREFIX
2790: *
2791: * @param msg message
2792: *
2793: * @return true if it is a property key; false otherwise
2794: */
2795: private boolean isMsgProperty(String msg) {
2796: if (msg.startsWith(DRDA_MSG_PREFIX))
2797: return true;
2798: else
2799: return false;
2800: }
2801:
2802: /**
2803: * Get the current value of logging connections
2804: *
2805: * @return true if logging connections is on; false otherwise
2806: */
2807: public boolean getLogConnections() {
2808: synchronized (logConnectionsSync) {
2809: return logConnections;
2810: }
2811: }
2812:
2813: /**
2814: * Set the current value of logging connections
2815: *
2816: * @param value true to turn logging connections on; false to turn it off
2817: */
2818: private void setLogConnections(boolean value) {
2819: synchronized (logConnectionsSync) {
2820: logConnections = value;
2821: }
2822: // update the value in all the threads
2823: synchronized (threadList) {
2824: for (Enumeration e = threadList.elements(); e
2825: .hasMoreElements();) {
2826: DRDAConnThread thread = (DRDAConnThread) e
2827: .nextElement();
2828: thread.setLogConnections(value);
2829: }
2830: }
2831: }
2832:
2833: /**
2834: * Set the security mechanism for derby.drda.securityMechanism
2835: * If this property is set, server will only allow connections
2836: * from client with this security mechanism.
2837: * This method will map the user friendly string representing
2838: * the security mechanism to the corresponding drda secmec value
2839: * @param s security mechanism string value
2840: * @throws Exception if value to set is invalid
2841: * @see Property#DRDA_PROP_SECURITYMECHANISM
2842: */
2843: private void setSecurityMechanism(String s) throws Exception {
2844: allowOnlySecurityMechanism = getSecMecValue(s);
2845:
2846: // if server vm cannot support EUSRIDPWD, then do not allow
2847: // derby.drda.securityMechanism to be set to EUSRIDPWD security
2848: // mechanism
2849: if ((allowOnlySecurityMechanism == INVALID_OR_NOTSET_SECURITYMECHANISM)
2850: || (allowOnlySecurityMechanism == CodePoint.SECMEC_EUSRIDPWD && !SUPPORTS_EUSRIDPWD))
2851: consolePropertyMessage("DRDA_InvalidValue.U", new String[] {
2852: s, Property.DRDA_PROP_SECURITYMECHANISM });
2853: }
2854:
2855: /**
2856: * get the security mechanism (secmec value) that the server
2857: * will accept connections from.
2858: * @return the securitymechanism value. It is value that
2859: * the derby.drda.securityMechanism was set to, if it is not set, then
2860: * it is equal to INVALID_OR_NOTSET_SECURITYMECHANISM
2861: * @see Property#DRDA_PROP_SECURITYMECHANISM
2862: */
2863: protected int getSecurityMechanism() {
2864: return allowOnlySecurityMechanism;
2865: }
2866:
2867: /**
2868: * Set the trace on/off for all sessions, or one session, depending on
2869: * whether we got -s argument.
2870: *
2871: * @param on true to turn trace on; false to turn it off
2872: * @return true if set false if an error occurred
2873: */
2874: private boolean setTrace(boolean on) {
2875: if (sessionArg == 0) {
2876: setTraceAll(on);
2877: synchronized (sessionTable) {
2878: for (Enumeration e = sessionTable.elements(); e
2879: .hasMoreElements();) {
2880: Session session = (Session) e.nextElement();
2881: if (on)
2882: session.setTraceOn(traceDirectory);
2883: else
2884: session.setTraceOff();
2885: }
2886: }
2887: } else {
2888: Session session = (Session) sessionTable.get(new Integer(
2889: sessionArg));
2890: if (session != null) {
2891: if (on)
2892: session.setTraceOn(traceDirectory);
2893: else
2894: session.setTraceOff();
2895: } else
2896: return false;
2897: }
2898: return true;
2899: }
2900:
2901: /**
2902: * Get the current value of the time slice
2903: *
2904: * @return time slice value
2905: */
2906: protected int getTimeSlice() {
2907: return timeSlice;
2908: }
2909:
2910: /**
2911: * Set the current value of time slice
2912: *
2913: * @param value time slice value
2914: * @exception Exception if value is < 0
2915: */
2916: private void setTimeSlice(int value) throws Exception {
2917: if (value < MIN_TIMESLICE)
2918: consolePropertyMessage("DRDA_InvalidValue.U", new String[] {
2919: new Integer(value).toString(), "timeslice" });
2920: if (value == USE_DEFAULT)
2921: value = DEFAULT_TIMESLICE;
2922: synchronized (timeSliceSync) {
2923: timeSlice = value;
2924: }
2925: }
2926:
2927: /**
2928: * Get the current value of keepAlive to configure how long the server
2929: * should keep the socket alive for a disconnected client
2930: */
2931: protected boolean getKeepAlive() {
2932: return keepAlive;
2933: }
2934:
2935: /**
2936: * Get the current value of minimum number of threads to create at start
2937: *
2938: * @return value of minimum number of threads
2939: */
2940: private int getMinThreads() {
2941: synchronized (threadsSync) {
2942: return minThreads;
2943: }
2944: }
2945:
2946: /**
2947: * Set the current value of minimum number of threads to create at start
2948: *
2949: * @param value value of minimum number of threads
2950: */
2951: private void setMinThreads(int value) {
2952: synchronized (threadsSync) {
2953: minThreads = value;
2954: }
2955: }
2956:
2957: /**
2958: * Get the current value of maximum number of threads to create
2959: *
2960: * @return value of maximum number of threads
2961: */
2962: private int getMaxThreads() {
2963: synchronized (threadsSync) {
2964: return maxThreads;
2965: }
2966: }
2967:
2968: /**
2969: * Set the current value of maximum number of threads to create
2970: *
2971: * @param value value of maximum number of threads
2972: * @exception Exception if value is less than 0
2973: */
2974: private void setMaxThreads(int value) throws Exception {
2975: if (value < MIN_MAXTHREADS)
2976: consolePropertyMessage("DRDA_InvalidValue.U", new String[] {
2977: new Integer(value).toString(), "maxthreads" });
2978: if (value == USE_DEFAULT)
2979: value = DEFAULT_MAXTHREADS;
2980: synchronized (threadsSync) {
2981: maxThreads = value;
2982: }
2983: }
2984:
2985: /**
2986: * Get the current value of whether to trace all the sessions
2987: *
2988: * @return true if tracing is on for all sessions; false otherwise
2989: */
2990: protected boolean getTraceAll() {
2991: synchronized (traceAllSync) {
2992: return traceAll;
2993: }
2994: }
2995:
2996: /**
2997: * Set the current value of whether to trace all the sessions
2998: *
2999: * @param value true if tracing is on for all sessions; false otherwise
3000: */
3001: private void setTraceAll(boolean value) {
3002: synchronized (traceAllSync) {
3003: traceAll = value;
3004: }
3005: }
3006:
3007: /**
3008: * Get the current value of trace directory
3009: *
3010: * @return trace directory
3011: */
3012: protected String getTraceDirectory() {
3013: synchronized (traceDirectorySync) {
3014: return traceDirectory;
3015: }
3016: }
3017:
3018: /**
3019: * Set the current value of trace directory
3020: *
3021: * @param value trace directory
3022: */
3023: private void setTraceDirectory(String value) {
3024: synchronized (traceDirectorySync) {
3025: traceDirectory = value;
3026: }
3027: }
3028:
3029: /**
3030: * Connect to a database to test whether a connection can be made
3031: *
3032: * @param writer connection to send message to
3033: * @param database database directory to connect to
3034: * @param user user to use
3035: * @param password password to use
3036: */
3037: private void connectToDatabase(DDMWriter writer, String database,
3038: String user, String password) throws Exception {
3039: Properties p = new Properties();
3040: if (user != null)
3041: p.put("user", user);
3042: if (password != null)
3043: p.put("password", password);
3044: try {
3045: Class.forName(CLOUDSCAPE_DRIVER);
3046: } catch (Exception e) {
3047: sendMessage(writer, ERROR, e.getMessage());
3048: return;
3049: }
3050: try {
3051: //Note, we add database to the url so that we can allow additional
3052: //url attributes
3053: Connection conn = DriverManager.getConnection(
3054: Attribute.PROTOCOL + database, p);
3055: // send warnings
3056: SQLWarning warn = conn.getWarnings();
3057: if (warn != null)
3058: sendSQLMessage(writer, warn, SQLWARNING);
3059: else
3060: sendOK(writer);
3061: conn.close();
3062: return;
3063: } catch (SQLException se) {
3064: sendSQLMessage(writer, se, SQLERROR);
3065: }
3066: }
3067:
3068: /**
3069: * Boot database
3070: *
3071: * @param writer connection to send message to
3072: * @param database database directory to connect to
3073: * @param bootPassword boot password
3074: * @param encPrv encryption provider
3075: * @param encAlg encryption algorithm
3076: * @param user user to use
3077: * @param password password to use
3078: */
3079: private void startDatabase(DDMWriter writer, String database,
3080: String bootPassword, String encPrv, String encAlg,
3081: String user, String password) throws Exception {
3082: Properties p = new Properties();
3083: if (bootPassword != null)
3084: p.put(Attribute.BOOT_PASSWORD, bootPassword);
3085: if (encPrv != null)
3086: p.put(Attribute.CRYPTO_PROVIDER, encPrv);
3087: if (encAlg != null)
3088: p.put(Attribute.CRYPTO_ALGORITHM, encAlg);
3089: if (user != null)
3090: p.put(Attribute.USERNAME_ATTR, user);
3091: if (password != null)
3092: p.put(Attribute.PASSWORD_ATTR, password);
3093: try {
3094: Class.forName(CLOUDSCAPE_DRIVER);
3095: } catch (Exception e) {
3096: sendMessage(writer, ERROR, e.getMessage());
3097: return;
3098: }
3099: try {
3100: //Note, we add database to the url so that we can allow additional
3101: //url attributes
3102: Connection conn = DriverManager.getConnection(
3103: Attribute.PROTOCOL + database, p);
3104: SQLWarning warn = conn.getWarnings();
3105: if (warn != null)
3106: sendSQLMessage(writer, warn, SQLWARNING);
3107: else
3108: sendOK(writer);
3109: conn.close();
3110: } catch (SQLException se) {
3111: sendSQLMessage(writer, se, SQLERROR);
3112: } catch (Exception e) {
3113: sendMessage(writer, ERROR, e.getMessage());
3114: }
3115: }
3116:
3117: /**
3118: * Shutdown a database
3119: *
3120: * @param writer connection to send message to
3121: * @param database database directory to shutdown to
3122: * @param user user to use
3123: * @param password password to use
3124: */
3125: private void shutdownDatabase(DDMWriter writer, String database,
3126: String user, String password) throws Exception {
3127:
3128: StringBuffer url = new StringBuffer(Attribute.PROTOCOL
3129: + database);
3130: if (user != null)
3131: url.append(";user=" + user);
3132: if (password != null)
3133: url.append(";password=" + password);
3134: url.append(";shutdown=true");
3135: try {
3136: Class.forName(CLOUDSCAPE_DRIVER);
3137: } catch (Exception e) {
3138: sendMessage(writer, ERROR, e.getMessage());
3139: return;
3140: }
3141: try {
3142: Connection conn = DriverManager.getConnection(url
3143: .toString());
3144: SQLWarning warn = conn.getWarnings();
3145: if (warn != null)
3146: sendSQLMessage(writer, warn, SQLWARNING);
3147: else
3148: sendOK(writer);
3149: conn.close();
3150: } catch (SQLException se) {
3151: //ignore shutdown error
3152: String expectedState = StandardException
3153: .getSQLStateFromIdentifier(SQLState.SHUTDOWN_DATABASE);
3154: if (!expectedState.equals(se.getSQLState())) {
3155: sendSQLMessage(writer, se, SQLERROR);
3156: return;
3157: }
3158: sendOK(writer);
3159: }
3160: }
3161:
3162: /**
3163: * Wrap SQL Error - display to console and raise exception
3164: *
3165: * @param messageKey Cloudscape SQL Exception message id
3166: *
3167: * @exception Exception raises exception for message
3168: */
3169: private void wrapSQLError(String messageKey) throws Exception {
3170: consolePropertyMessage("DRDA_SQLException.S", messageKey);
3171: }
3172:
3173: /**
3174: * Wrap SQL Warning - display to console and raise exception
3175: *
3176: * @param messageKey Cloudscape SQL Exception message id
3177: *
3178: * @exception Exception raises exception for message
3179: */
3180: private void wrapSQLWarning(String messageKey) throws Exception {
3181: consolePropertyMessage("DRDA_SQLWarning.I", messageKey);
3182: }
3183:
3184: private Properties getPropertyValues() {
3185: Properties retval = new Properties();
3186: retval.put(Property.DRDA_PROP_PORTNUMBER, new Integer(
3187: portNumber).toString());
3188: retval.put(Property.DRDA_PROP_HOSTNAME, hostArg);
3189: retval.put(Property.DRDA_PROP_KEEPALIVE, new Boolean(keepAlive)
3190: .toString());
3191:
3192: String tracedir = getTraceDirectory();
3193: if (tracedir != null)
3194: retval.put(Property.DRDA_PROP_TRACEDIRECTORY, tracedir);
3195: retval.put(Property.DRDA_PROP_TRACEALL, new Boolean(
3196: getTraceAll()).toString());
3197: retval.put(Property.DRDA_PROP_MINTHREADS, new Integer(
3198: getMinThreads()).toString());
3199: retval.put(Property.DRDA_PROP_MAXTHREADS, new Integer(
3200: getMaxThreads()).toString());
3201: retval.put(Property.DRDA_PROP_TIMESLICE, new Integer(
3202: getTimeSlice()).toString());
3203:
3204: retval.put(Property.DRDA_PROP_TIMESLICE, new Integer(
3205: getTimeSlice()).toString());
3206: retval.put(Property.DRDA_PROP_LOGCONNECTIONS, new Boolean(
3207: getLogConnections()).toString());
3208: String startDRDA = PropertyUtil
3209: .getSystemProperty(Property.START_DRDA);
3210: //DERBY-375 If a system property is specified without any value, getProperty returns
3211: //an empty string. Use default values in such cases.
3212: if (startDRDA != null && startDRDA.equals(""))
3213: startDRDA = "false";
3214:
3215: retval.put(Property.START_DRDA, (startDRDA == null) ? "false"
3216: : startDRDA);
3217:
3218: // if Property.DRDA_PROP_SECURITYMECHANISM has been set on server
3219: // then put it in retval else the default behavior is as though
3220: // it is not set
3221: if (getSecurityMechanism() != INVALID_OR_NOTSET_SECURITYMECHANISM)
3222: retval.put(Property.DRDA_PROP_SECURITYMECHANISM,
3223: getStringValueForSecMec(getSecurityMechanism()));
3224:
3225: //get the trace value for each session if tracing for all is not set
3226: if (!getTraceAll()) {
3227: synchronized (sessionTable) {
3228: for (Enumeration e = sessionTable.elements(); e
3229: .hasMoreElements();) {
3230: Session session = (Session) e.nextElement();
3231: if (session.isTraceOn())
3232: retval.put(Property.DRDA_PROP_TRACE + "."
3233: + session.getConnNum(), "true");
3234: }
3235: }
3236: }
3237: return retval;
3238: }
3239:
3240: /**
3241: * Add a session - for use by <code>ClientThread</code>. Put the session
3242: * into the session table and the run queue. Start a new
3243: * <code>DRDAConnThread</code> if there are more sessions waiting than
3244: * there are free threads, and the maximum number of threads is not
3245: * exceeded.
3246: *
3247: * <p><code>addSession()</code> should only be called from one thread at a
3248: * time.
3249: *
3250: * @param clientSocket the socket to read from and write to
3251: */
3252: void addSession(Socket clientSocket) throws Exception {
3253:
3254: int connectionNumber = ++connNum;
3255:
3256: if (getLogConnections()) {
3257: consolePropertyMessage("DRDA_ConnNumber.I", Integer
3258: .toString(connectionNumber));
3259: }
3260:
3261: // Note that we always re-fetch the tracing configuration because it
3262: // may have changed (there are administrative commands which allow
3263: // dynamic tracing reconfiguration).
3264: Session session = new Session(connectionNumber, clientSocket,
3265: getTraceDirectory(), getTraceAll());
3266:
3267: sessionTable.put(new Integer(connectionNumber), session);
3268:
3269: // Check whether there are enough free threads to service all the
3270: // threads in the run queue in addition to the newly added session.
3271: boolean enoughThreads;
3272: synchronized (runQueue) {
3273: enoughThreads = (runQueue.size() < freeThreads);
3274: }
3275: // No need to hold the synchronization on runQueue any longer than
3276: // this. Since no other threads can make runQueue grow, and no other
3277: // threads will reduce the number of free threads without removing
3278: // sessions from runQueue, (runQueue.size() < freeThreads) cannot go
3279: // from true to false until addSession() returns.
3280:
3281: DRDAConnThread thread = null;
3282:
3283: // try to start a new thread if we don't have enough free threads
3284: if (!enoughThreads) {
3285: // Synchronize on threadsSync to ensure that the value of
3286: // maxThreads doesn't change until the new thread is added to
3287: // threadList.
3288: synchronized (threadsSync) {
3289: // only start a new thread if we have no maximum number of
3290: // threads or the maximum number of threads is not exceeded
3291: if ((maxThreads == 0)
3292: || (threadList.size() < maxThreads)) {
3293: thread = new DRDAConnThread(session, this ,
3294: getTimeSlice(), getLogConnections());
3295: threadList.add(thread);
3296: thread.start();
3297: }
3298: }
3299: }
3300:
3301: // add the session to the run queue if we didn't start a new thread
3302: if (thread == null) {
3303: runQueueAdd(session);
3304: }
3305: }
3306:
3307: /**
3308: * Remove a thread from the thread list. Should be called when a
3309: * <code>DRDAConnThread</code> has been closed.
3310: *
3311: * @param thread the closed thread
3312: */
3313: void removeThread(DRDAConnThread thread) {
3314: threadList.remove(thread);
3315: }
3316:
3317: protected Object getShutdownSync() {
3318: return shutdownSync;
3319: }
3320:
3321: protected boolean getShutdown() {
3322: return shutdown;
3323: }
3324:
3325: public String buildRuntimeInfo(LocalizedResource locallangUtil) {
3326:
3327: String s = locallangUtil
3328: .getTextMessage("DRDA_RuntimeInfoBanner.I")
3329: + "\n";
3330: int sessionCount = 0;
3331: s += locallangUtil
3332: .getTextMessage("DRDA_RuntimeInfoSessionBanner.I")
3333: + "\n";
3334: for (int i = 0; i < threadList.size(); i++) {
3335: String sessionInfo = ((DRDAConnThread) threadList.get(i))
3336: .buildRuntimeInfo("", locallangUtil);
3337: if (!sessionInfo.equals("")) {
3338: sessionCount++;
3339: s += sessionInfo + "\n";
3340: }
3341: }
3342: int waitingSessions = 0;
3343: for (int i = 0; i < runQueue.size(); i++) {
3344: s += ((Session) runQueue.get(i)).buildRuntimeInfo("",
3345: locallangUtil);
3346: waitingSessions++;
3347: }
3348: s += "-------------------------------------------------------------\n";
3349: s += locallangUtil
3350: .getTextMessage("DRDA_RuntimeInfoNumThreads.I")
3351: + threadList.size() + "\n";
3352: s += locallangUtil
3353: .getTextMessage("DRDA_RuntimeInfoNumActiveSessions.I")
3354: + sessionCount + "\n";
3355: s += locallangUtil
3356: .getTextMessage("DRDA_RuntimeInfoNumWaitingSessions.I")
3357: + +waitingSessions + "\n\n";
3358:
3359: Runtime rt = Runtime.getRuntime();
3360: rt.gc();
3361: long totalmem = rt.totalMemory();
3362: long freemem = rt.freeMemory();
3363: s += locallangUtil
3364: .getTextMessage("DRDA_RuntimeInfoTotalMemory.I")
3365: + +totalmem + "\t";
3366: s += locallangUtil
3367: .getTextMessage("DRDA_RuntimeInfoFreeMemory.I")
3368: + +freemem + "\n\n";
3369:
3370: return s;
3371: }
3372:
3373: protected void setClientLocale(String locale) {
3374: clientLocale = locale;
3375: }
3376:
3377: /**
3378: * Retrieve product version information
3379: * We need to make sure that this method gets the stream and passes it to
3380: * ProductVersionHolder, because it lives in the Network Server jar
3381: * and won't be readily available to ProductVersionHolder when running
3382: * under security manager.
3383: */
3384: private ProductVersionHolder getNetProductVersionHolder()
3385: throws Exception {
3386: ProductVersionHolder myPVH = null;
3387: try {
3388: myPVH = (ProductVersionHolder) AccessController
3389: .doPrivileged(new PrivilegedExceptionAction() {
3390:
3391: public Object run()
3392: throws UnknownHostException,
3393: IOException {
3394: InputStream versionStream = getClass()
3395: .getResourceAsStream(
3396: ProductGenusNames.NET_INFO);
3397:
3398: return ProductVersionHolder
3399: .getProductVersionHolderFromMyEnv(versionStream);
3400: }
3401: });
3402:
3403: } catch (PrivilegedActionException e) {
3404: Exception e1 = e.getException();
3405: consolePropertyMessage("DRDA_ProductVersionReadError.S", e1
3406: .getMessage());
3407: }
3408: return myPVH;
3409: }
3410:
3411: /**
3412: * This method returns a timestamp to be used in the messages.
3413: * CheapDateFormatter class, which uses GMT, is used to format timestamps.
3414: * This is to keep the formatting consistent with Derby boot message since
3415: * network server messages and the boot message get written to derby.log.
3416: *
3417: * @return current timestamp formatted in GMT
3418: */
3419: private String getFormattedTimestamp() {
3420: long currentTime = System.currentTimeMillis();
3421: return CheapDateFormatter.formatDate(currentTime);
3422: }
3423: }
|