You are viewing a plain text version of this content. The canonical link for it is here.
Posted to jdo-commits@db.apache.org by mb...@apache.org on 2005/05/22 20:40:21 UTC

svn commit: r171355 [9/31] - in /incubator/jdo/trunk/fostore20: ./ src/ src/conf/ src/java/ src/java/org/ src/java/org/apache/ src/java/org/apache/jdo/ src/java/org/apache/jdo/impl/ src/java/org/apache/jdo/impl/fostore/ test/ test/conf/ test/fsuid2/ test/fsuid2/org/ test/fsuid2/org/apache/ test/fsuid2/org/apache/jdo/ test/fsuid2/org/apache/jdo/pc/ test/java/ test/java/org/ test/java/org/apache/ test/java/org/apache/jdo/ test/java/org/apache/jdo/impl/ test/java/org/apache/jdo/impl/fostore/ test/java/org/apache/jdo/pc/ test/java/org/apache/jdo/pc/appid/ test/java/org/apache/jdo/pc/empdept/ test/java/org/apache/jdo/pc/serializable/ test/java/org/apache/jdo/pc/xempdept/ test/java/org/apache/jdo/test/ test/java/org/apache/jdo/test/query/ test/java/org/apache/jdo/test/util/ test/jdo/ test/jdo/org/ test/jdo/org/apache/ test/jdo/org/apache/jdo/ test/jdo/org/apache/jdo/pc/ test/jdo/org/apache/jdo/pc/appid/ test/jdo/org/apache/jdo/pc/empdept/ test/jdo/org/apache/jdo/pc/serializable/ test/jdo/org/apache/jdo/pc/xempdept/

Added: incubator/jdo/trunk/fostore20/src/java/org/apache/jdo/impl/fostore/Main.java
URL: http://svn.apache.org/viewcvs/incubator/jdo/trunk/fostore20/src/java/org/apache/jdo/impl/fostore/Main.java?rev=171355&view=auto
==============================================================================
--- incubator/jdo/trunk/fostore20/src/java/org/apache/jdo/impl/fostore/Main.java (added)
+++ incubator/jdo/trunk/fostore20/src/java/org/apache/jdo/impl/fostore/Main.java Sun May 22 11:40:13 2005
@@ -0,0 +1,472 @@
+/*
+ * Copyright 2005 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at 
+ * 
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software 
+ * distributed under the License is distributed on an "AS IS" BASIS, 
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+ * See the License for the specific language governing permissions and 
+ * limitations under the License.
+ */
+
+/*
+ * Main.java
+ *
+ * Created on June 4, 2001, 9:59 AM
+ */
+
+package org.apache.jdo.impl.fostore;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.EOFException;
+import java.io.InputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.InetAddress;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Iterator;
+
+import javax.jdo.JDOFatalInternalException;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.apache.jdo.util.I18NHelper;
+
+/**
+ * Standalone server for FOStore databases.
+ * <p>
+ * This class is <code>public</code> because it has a <code>main</code> entry
+ * point for running as a standalone program.
+ *
+ * @author  Craig Russell
+ * @version 1.0
+ */
+public class Main {
+   
+    /** I18N support. */
+    private static final I18NHelper msg = I18NHelper.getInstance(I18N.NAME);
+
+    /** Logger */
+    static final Log logger = LogFactory.getFactory().getInstance(
+        "org.apache.jdo.impl.fostore"); // NOI18N
+    
+    /** The port number to use for incoming connections.
+     */    
+    int port;
+
+    /** The backlog for Socket.listen.
+     */    
+    int backlog;
+
+    /** Default backlog.
+     */
+    private static final int DEFAULT_BACKLOG = 5;
+    
+    /** The number of seconds with no activity before shutting down.
+     */    
+    int timeout; 
+    
+    /** Default timeout.
+     */
+    private static final int DEFAULT_TIMEOUT = 60;
+    
+    /** The root of the file system for database path names.
+     * Currently this property is ignored.
+     */    
+    String root = null;
+
+    /** The time this server started running.
+     */    
+    final Date startTime = new Date();
+    
+    /** A flag indicating that the server is shutting down.
+     */    
+    static boolean shutdown = false;
+    
+    /** The synchronizing Object for the shutdown flag.
+     */    
+    static Object shutdownSynchronizer = new Object();
+    
+    /** The Thread responsible for detecting inactivity.
+     */    
+    static Thread timeoutThread = null;
+    
+    /** The Thread responsible for listening for incoming connection requests.
+     */    
+    Thread listenerThread = null;
+    
+    /** The set of Threads servicing incoming connections.
+     */    
+    HashSet serviceThreads = new HashSet ();
+    
+    /** Creates new Main */
+    public Main () {
+    }
+
+    /** The main routine.
+     * @param args the command line arguments
+     */
+    public static void main (String args[]) {
+        if (args.length == 0) {
+            usage();
+        }
+        Main main = new Main();
+        main.run (args);
+    }
+    
+    /** Print the usage message on standard output.
+     */    
+    static void usage () {
+        // Turn int into a String to avoid having the formatter localize it by
+        // (e.g., with EN_US) putting in a comma (i.e., print 9919, not 9,919).
+        print(msg.msg("MSG_MainUsage1", // NOI18N
+                      new Integer(FOStoreRemoteConnection.DEFAULT_PORT).toString()));
+        print(msg.msg("MSG_MainUsage2", DEFAULT_BACKLOG)); // NOI18N
+        print(msg.msg("MSG_MainUsage3", DEFAULT_TIMEOUT)); // NOI18N
+    }
+    
+    /** Run the main program.
+     * @param args the command line arguments
+     */    
+    void run (String args[]) {
+        boolean debug = logger.isDebugEnabled();
+        if (debug) {
+            logger.debug("FOStore Main started: " + startTime); // NOI18N
+        }
+        timeout = Integer.getInteger(
+            "timeout", DEFAULT_TIMEOUT).intValue(); // NOI18N
+        port = Integer.getInteger(
+            "port", FOStoreRemoteConnection.DEFAULT_PORT).intValue(); // NOI18N
+        backlog = Integer.getInteger(
+            "backlog", DEFAULT_BACKLOG).intValue(); // NOI18N
+        root = System.getProperty("root"); // NOI18N
+        if ((root == null) || root.equals("")) { // NOI18N
+            root = System.getProperty("user.dir"); // NOI18N
+        }
+        
+        if (debug) {
+            logger.debug("\ttimeout = " + timeout); // NOI18N
+            logger.debug("\tport = " + port); // NOI18N
+            logger.debug("\tbacklog = " + backlog); // NOI18N
+            logger.debug("\troot = " + root); // NOI18N
+        }
+        
+        startTimeoutThread();
+        startListenerThread();
+        try {
+            timeoutThread.join();
+            setShutdown();
+            listenerThread.interrupt();
+            listenerThread.join();
+        } catch (InterruptedException ie) {
+            // do nothing
+            if (debug) {
+                logger.debug("Main: timeoutThread.join() caught InterruptedException."); // NOI18N
+            }
+        } finally {
+            if (debug) {
+                logger.debug("Main: FOStore timeout thread ended: " + 
+                               new Date().toString()); // NOI18N
+            }
+            setShutdown();
+            for (Iterator serviceThreadIterator = serviceThreads.iterator();
+                 serviceThreadIterator.hasNext();) {
+                try {
+                    Thread serviceThread = (Thread) serviceThreadIterator.next();
+                    serviceThread.join();
+                } catch (InterruptedException ie) {
+                    if (debug) {
+                        logger.debug("Main: serviceThread.join() caught InterruptedException."); // NOI18N
+                        }
+                }
+            }
+        }
+        if (debug) {
+            logger.debug("Main: FOStore shutdown."); // NOI18N
+        }
+    }
+    
+    /** Start the TimeoutThread.
+     */    
+    void startTimeoutThread() {
+        Runnable timeoutRunnable = new TimeoutRunnable (timeout); 
+        timeoutThread = new Thread (timeoutRunnable, "TimeoutThread"); // NOI18N
+        timeoutThread.start();
+        if (logger.isDebugEnabled()) {
+            logger.debug("Main: TimeoutThread started."); // NOI18N
+        }
+    }
+    
+    static void resetTimeout() {
+        timeoutThread.interrupt();
+    }
+    
+    /** The Timeout Runnable class.  This class watches a timer,
+     * and whent the timer expires, the thread terminates.
+     * This causes the Main thread to fall through its join on
+     * the timeout thread and completes the shutdown process.
+     */    
+    class TimeoutRunnable implements Runnable {
+        /** The number of milliseconds to sleep before terminating this thread.
+         * Another thread wishing to reset the timeout will
+         * interrupt this thread.
+         */        
+        int timeoutMillis = timeout * 1000;
+        /** Construct an instance of the TimeoutRunnable with the specified
+         * number of seconds to sleep before terminating.
+         * @param timeout the number of seconds before timeout.
+         */        
+        TimeoutRunnable (int timeout) {
+            timeoutMillis = timeout * 1000;
+        }
+        
+        /** Run the timeout thread.
+         */        
+        public void run() {
+            boolean debug = logger.isDebugEnabled();
+            
+            boolean awake = false;
+            if (debug) {
+                logger.debug("TimeoutThread using: " + 
+                               timeoutMillis + " milliseconds"); // NOI18N
+            }
+                while (!awake) {
+                    try {
+                        Thread.sleep (timeoutMillis);
+                        awake = true;
+                    } catch (InterruptedException ie) {
+                        if (debug) {
+                            logger.debug("TimeoutThread caught InterruptedException; continuing to sleep"); // NOI18N
+                        }
+                    }
+                }
+                if (debug) {
+                    logger.debug("TimeoutThread ending."); // NOI18N
+                }
+        }
+    }
+
+    /** Start the Listener Thread.
+     */    
+    void startListenerThread() {
+        Runnable listenerRunnable = new ListenerRunnable (port); 
+        listenerThread = new Thread (listenerRunnable, "ListenerThread"); // NOI18N
+        listenerThread.start();
+            if (logger.isDebugEnabled()) {
+                logger.debug("Main: ListenerThread started."); // NOI18N
+            }
+    }
+    
+    /** The Listener Thread class.  This class creates an
+     * incoming Socket and listens on it.  When a connection
+     * comes in, create a service thread using the new Socket
+     * and run it.
+     */    
+    class ListenerRunnable implements Runnable {
+        /** The port number to listen on.
+         */        
+        int port;
+        /** The Runnable class for the Listener Thread.
+         * @param port the port number to listen on.
+         */        
+        ListenerRunnable (int port) {
+            this.port = port;
+        }
+        /** Run the listener thread.  Create a ServerSocket using the port
+         * and backlog parameters and listen on it.  For each incoming
+         * request, create a ConnectionRunnable and start a thread to
+         * service the request.  
+         * This thread continues to accept incoming connections until the
+         * shutdown flag is set, at which point it terminates.
+         */        
+        public void run() {
+            boolean debug = logger.isDebugEnabled();
+            try {
+                if (debug) {
+                    logger.debug("ListenerThread using port: " + port); // NOI18N
+                }
+                ServerSocket listener = new ServerSocket (port, backlog);
+                if (debug) {
+                    logger.debug("ListenerThread using ServerSocket: " + 
+                                   listener); // NOI18N
+                }
+                while (true) {
+                    if (getShutdown()) break;
+                    if (debug) {
+                        logger.debug("ListenerThread accepting new connections."); // NOI18N
+                    }
+                    final Socket connection = listener.accept();
+                    if (debug) {
+                        logger.debug("ListenerThread accepted " + connection); // NOI18N
+                    }
+                    if (connection.getLocalPort() == 0 &
+                        connection.getPort() == 0) {
+                            // must be a bogus shutdown connection
+                    if (debug) {
+                        logger.debug("Bugus connection ignored: " + connection); // NOI18N
+                    }
+                        continue;
+                    }
+                    Runnable connectionRunnable = 
+                        new ConnectionRunnable (connection);
+                    Thread connectionThread = 
+                        new Thread (connectionRunnable, "Connection"); // NOI18N
+                    serviceThreads.add(connectionThread);
+                    connectionThread.start();
+                }
+            } catch (java.net.UnknownHostException uhe) {
+                if (debug) {
+                    logger.debug("ListenerThread caught UnknownHostException"); // NOI18N
+                }
+            } catch (java.net.BindException ioe) {
+                if (debug) {
+                    logger.debug("ListenerThread caught BindException"); // NOI18N
+                }
+                ioe.printStackTrace();
+            } catch (java.io.IOException ioe) {
+                if (debug) {
+                    logger.debug("ListenerThread caught IOException"); // NOI18N
+                }
+                ioe.printStackTrace();
+            } finally {
+                if (debug) {
+                    logger.debug("ListenerThread ending."); // NOI18N
+                }
+            }
+        }
+    }
+        
+    /** The Runnable class for incoming connections.
+     */    
+    class ConnectionRunnable implements Runnable {
+        /** The Socket that received an incoming request.
+         */        
+        Socket socket;
+        /** The Runnable class for incoming connections.
+         * @param conn the socket which received a connection request.
+         */        
+        ConnectionRunnable (Socket conn) {
+            socket = conn;
+        }
+        /** Run the Connection Thread.  This handles the incoming
+         * connection in the handleConnection method.  
+         */        
+        public void run () {
+            if (logger.isDebugEnabled()) {
+                logger.debug("ConnectionRunnable started."); // NOI18N
+            }
+            handleConnection (socket);
+            serviceThreads.remove(this);
+            if (logger.isDebugEnabled()) {
+                logger.debug("ConnectionRunnable ending."); // NOI18N
+            }
+        }
+    }
+    
+    /** Handle the incoming connection.  This method should create a new
+     * handler instance to read the messages from the connection, parse
+     * the message, determine which database is being used, and handle
+     * the requests.
+     * @param socket the socket connected by the listener
+     */    
+    void handleConnection (Socket socket) {
+        boolean info = logger.isInfoEnabled();
+        FOStoreServerConnectionImpl server =
+            new FOStoreServerConnectionImpl(socket, root);
+        boolean connected = true;
+        while (connected) {
+
+            if (info) {
+                logger.info("Main.handleConnection"); // NOI18N
+            }
+            
+            try {
+                resetTimeout(); // reset the timeout thread on each message received
+                server.readInputFromClient();
+            } catch (EOFException ioe) {
+                connected = false; // normal case of EOF indicating remote side closed
+                break;
+            } catch (IOException ioe) {
+                connected = false;
+                throw new JDOFatalInternalException (
+                    msg.msg("ERR_HandleConnectionReadIOException"), ioe); // NOI18N
+            }
+
+            if (info) {
+                logger.info("Main.handleConnection: processRequests"); // NOI18N
+            }
+            server.processRequests();
+
+            try {
+                if (info) {
+                    logger.info("Main.handleConnection: release & write"); // NOI18N
+                }
+                server.releaseDatabase();
+                server.writeOutputToClient();
+            } catch (IOException ioe) {
+                connected = false;
+                ioe.printStackTrace(); // should not occur
+                throw new JDOFatalInternalException (
+                    msg.msg("ERR_HandleConnectionWriteIOException"), ioe); // NOI18N
+            } catch (InterruptedException ioe) {
+                connected = false;
+                ioe.printStackTrace(); // should not occur
+                throw new JDOFatalInternalException (
+                    msg.msg("ERR_HandleConnectionWriteInterruptedException"), 
+                    ioe); // NOI18N
+            }
+        }
+        try {          
+            if (info) {
+                logger.info("Main.handleConnection: close server, socket"); // NOI18N
+            }
+            server.close();
+            socket.close();
+        } catch (Exception e) {
+            if (logger.isDebugEnabled()) {
+                e.printStackTrace();
+            }
+        }
+    }
+
+    /** Test if the server is shutting down.
+     * @return if the server is shutting down.
+     */    
+    static boolean getShutdown() {
+        synchronized (shutdownSynchronizer) {
+        return shutdown;
+        }
+    }
+    
+    /** Set the shutdown flag.
+     */    
+    static void setShutdown() {
+        synchronized (shutdownSynchronizer) {
+            shutdown = true;
+        }
+    }
+
+    /** Print a message on the standard output.
+     * @param s the message to print.
+     */    
+    static void print (String s) {
+        System.out.println (s);
+    }
+    
+    /** Flush the standard output.
+     */    
+    static void flush() {
+        System.out.flush();
+    }
+
+}

Added: incubator/jdo/trunk/fostore20/src/java/org/apache/jdo/impl/fostore/Message.java
URL: http://svn.apache.org/viewcvs/incubator/jdo/trunk/fostore20/src/java/org/apache/jdo/impl/fostore/Message.java?rev=171355&view=auto
==============================================================================
--- incubator/jdo/trunk/fostore20/src/java/org/apache/jdo/impl/fostore/Message.java (added)
+++ incubator/jdo/trunk/fostore20/src/java/org/apache/jdo/impl/fostore/Message.java Sun May 22 11:40:13 2005
@@ -0,0 +1,321 @@
+/*
+ * Copyright 2005 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at 
+ * 
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software 
+ * distributed under the License is distributed on an "AS IS" BASIS, 
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+ * See the License for the specific language governing permissions and 
+ * limitations under the License.
+ */
+
+package org.apache.jdo.impl.fostore;
+
+import java.io.IOException;
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map.Entry;
+import java.util.Map;
+
+import javax.jdo.JDOFatalException;
+import javax.jdo.JDOFatalInternalException;
+import javax.jdo.JDOFatalDataStoreException;
+import javax.jdo.JDOFatalUserException;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.apache.jdo.store.Connector;
+import org.apache.jdo.util.I18NHelper;
+
+/**
+* Represents a set of one or more requests that will be sent to the store.
+* @see Request
+*
+* @author Dave Bristor
+*/
+class Message {
+    /** Connector for which this message acts as a transport. */
+    private final Connector connector;
+
+    /** Set of request objects that are in the process of carrying out their
+     * function; maps from a RequestId to Request.  When a request is
+     * created, it adds itself to this map.  When a request's reply is
+     * received, it is looked up by RequestId in this map, removed, and the
+     * Request object given the reply.
+     */
+    private final HashMap requests = new HashMap();
+
+    /** Set of CLIDs associated with ActivateClass requests that are in this
+     * Message. It is cleared by initOutput. */
+    private final HashSet clids = new HashSet();
+
+    /**
+     * This contains the Message's actual data for the store.
+     */
+    private final FOStoreOutput out = new FOStoreOutput();
+
+    /**
+     * Indicates the number of requests that have been written into this
+     * Message.
+     */
+    private int numRequests = 0;
+    
+    /**
+     * Contains the position in the output of the numRequest stashed by
+     * initOutput.  This is modified and used only by initOutput and 
+     * finishOutput.
+     */
+    private int numRequestStash = 0;
+    
+    /** 
+     * The version number of the current protocol.  In future, this version 
+     * number can be used to identify mismatches in protocol.
+     * The format is (short)major; (byte)minor; (byte)patch
+     * Only use major for compatibility checks; always bump major when
+     * incompatibly changing protocol.
+     */
+    private static final int VERSION_NUMBER = 0x00010000; // version 1.0.0
+
+    /** I18N support. */
+    private static final I18NHelper msg = I18NHelper.getInstance(I18N.NAME);
+
+    /** Logger */
+    static final Log logger = LogFactory.getFactory().getInstance(
+        "org.apache.jdo.impl.fostore"); // NOI18N
+
+    /** Constructor for Message.  The output stream is initialized to
+     * contain the version number and a stash for the number of requests.
+     * @param connector The Connector on whose behalf this Message is serving
+     * as a transport.  May be null.
+     */
+    Message(Connector connector) {
+        this.connector = connector;
+        initOutput();
+    }
+
+    Message() {
+        this(null);
+    }
+
+    /**
+     * @return The connector associated with this Message.
+     */
+    Connector getConnector() {
+        return connector;
+    }
+    
+    /** Return the FOStoreOutput stream for requests to be inserted.
+     * @return the FOStoreOutput under construction
+     */
+    public FOStoreOutput getOutput() {
+        return out;
+    }
+
+    /**
+     * Write this message to the given connection, and read replies from that
+     * connection, processing replies as they are read.
+     * @see RequestHandler#handleRequests for stream header reader.
+     * @param con the FOStoreClientConnection for this message
+     */
+    void processInStore(FOStoreClientConnection con,
+                        boolean okToReleaseConnection) {
+
+        try {
+            sendToStore (con);
+        } finally {
+            try {
+                receiveFromStore (con, okToReleaseConnection);
+            } finally {
+                initOutput(); // Prepare for next send.
+            }
+        }
+    }
+    
+    /** Send the current Message buffer to the store.  The data contained
+     * in out is written as one block of data.  The connection's 
+     * sendToStore is responsible for sending the data and handling the
+     * processing at the server side.  
+     * @param con the FOStoreClientConnection
+     */
+    private void sendToStore(FOStoreClientConnection con) {
+        try {
+            finishOutput();
+            if (logger.isTraceEnabled()) {
+                Tester.dump("MsTS", out.getBuf(), out.getPos()); // NOI18N
+            }
+            con.sendToStore(out.getBuf(), 0, out.getPos());
+        } catch (IOException ex) {
+            throw new JDOFatalDataStoreException(
+                msg.msg("ERR_SendToStore"), ex); // NOI18N
+        }
+    }
+
+    /** Receive the replies from the store and process them.
+     * @param con the FOStoreConnection with the replies.
+     */
+    private void receiveFromStore(FOStoreClientConnection con,
+                                  boolean okToReleaseConnection) {
+        
+        try {
+            DataInput di = con.getInputFromServer();
+
+            // Process the replies *and then* close the connection, to prevent
+            // the byte array underlying data input (which is property of the
+            // connection) from being overwritten by another thread.
+            ReplyHandler.processReplies(di, this);
+        } catch (IOException ioe) {
+            throw new JDOFatalDataStoreException (
+                msg.msg("ERR_ReceiveFromStore"), ioe); // NOI18N
+        } finally {
+            if (okToReleaseConnection) {
+                if (logger.isDebugEnabled()) {
+                    logger.debug(
+                        "Message.receiveFromStore: closing connection"); //NOI18N
+                }
+                closeConnection(con);
+            }
+        }
+    }
+
+
+    /**
+     * Maps the given request to the given requestId.
+     * @param requestId Identifies a request within a JVM.
+     * @param request A request for some operation on the store for which a
+     * reply is expected. */
+    void putRequest(RequestId requestId, Request request) {
+        if (logger.isDebugEnabled()) {
+            logger.debug("Msg.putRequest: " + requestId + // NOI18N
+                           " " + request.getClass().getName()); // NOI18N
+        }
+        if (requests.containsKey(requestId)) {
+            throw new FOStoreFatalInternalException(
+                this.getClass(), "putRequest", // NOI18N
+                msg.msg("ERR_DuplicateRequestID", requestId)); // NOI18N
+        } else {
+            numRequests++;
+            requests.put(requestId, request);
+        }
+    }
+
+    /**
+    * Provides the Request corresponding to the give requestId, removing it
+    * from the internal map of requests (i.e., subsequent getRequest
+    * invocations for the same requestId will return null).
+    * @param requestId Identifier for a particular request in this JVM.
+    * @return The Request identified by the given identifier or null if there
+    * is no such Request.
+    */
+    Request getRequest(RequestId requestId) {
+        if (logger.isDebugEnabled()) {
+            logger.debug ("Msg.getRequest: " + requestId); // NOI18N
+        }
+        Request rc = (Request)requests.remove(requestId);
+        if (rc == null) { // oops, this should never happen
+        if (logger.isDebugEnabled()) {
+            logger.debug ("Msg.getRequest: unable to find: " + requestId); // NOI18N
+                for (Iterator it = requests.entrySet().iterator();it.hasNext();) {
+                    Map.Entry entry = (Map.Entry) it.next();
+                    logger.debug ("Msg.getRequest: found: " +  // NOI18N
+                                    "entry.key: " + entry.getKey() + // NOI18N
+                                    " entry.Value: " + entry.getValue()); // NOI18N
+                }
+            }
+        }
+        return rc;
+    }
+
+    /**
+     * Returns true if this message has requests for the store.
+     * @return true if there are any messages.
+     */
+    boolean hasRequests() {
+        return numRequests > 0;
+    }
+
+    /** Initialize the output buffer with version number and a stash for number
+     * of requests.
+     */
+    private void initOutput() {
+        try {
+            out.setPos(0);
+            out.writeInt (VERSION_NUMBER);
+            numRequests = 0;
+            numRequestStash = out.beginStash();
+            clids.clear();
+        } catch (IOException ioe) {
+            throw new JDOFatalInternalException (
+                msg.msg("ERR_InitOutput"), ioe); // NOI18N
+        }
+    }
+    
+    /** Finish the output buffer by updating the stash with number of requests.
+     */
+    private void finishOutput () {
+        try {
+            out.endStash (numRequests, numRequestStash);
+        } catch (IOException ioe) {
+            throw new JDOFatalInternalException (
+                msg.msg("ERR_FinishOutput"), ioe); // NOI18N
+        }
+    }
+    
+    /** Close the connection to the store.
+     * @param con the connection to close.
+     */
+    private void closeConnection(FOStoreClientConnection con) {
+        if (logger.isDebugEnabled()) {
+            logger.debug ("Msg.closeConnection:" + con); // NOI18N
+        }
+        try {
+            con.close();
+        } catch (IOException ioe) {
+            throw new JDOFatalInternalException (
+                msg.msg("ERR_CloseConnection"), ioe); // NOI18N
+        }
+    }
+            
+    /**
+     * Verify a Message's version number.
+     * @throws JDOFatalUserException if the version number does not match
+     * that in the caller's JVM.
+     */
+    static void verifyVersionNumber(DataInput in) throws IOException {
+        int verNum = in.readInt();
+        if (VERSION_NUMBER != verNum) {
+            throw new JDOFatalUserException(
+                msg.msg("EXC_MessageVersionMismatch", // NOI18N
+                        new Integer(verNum), new Integer(VERSION_NUMBER)));
+        }
+    }
+
+    /**
+     * Add the given CLID to the set of CLIDs maintained by this Message.
+     */
+    void addCLID(CLID clid) {
+        clids.add(clid);
+    }
+
+    /**
+     * Returns true if the given CLID is in this Message's set of CLIDs.
+     */
+    boolean containsCLID(CLID clid) {
+        return clids.contains(clid);
+    }
+
+    // Debug support
+    /** Dump the complete current contents of the message buffer.
+     */    
+    public void dump() {
+        Tester.dump("MSG", out.getBuf(), out.getPos()); // NOI18N
+    }
+}

Added: incubator/jdo/trunk/fostore20/src/java/org/apache/jdo/impl/fostore/OID.java
URL: http://svn.apache.org/viewcvs/incubator/jdo/trunk/fostore20/src/java/org/apache/jdo/impl/fostore/OID.java?rev=171355&view=auto
==============================================================================
--- incubator/jdo/trunk/fostore20/src/java/org/apache/jdo/impl/fostore/OID.java (added)
+++ incubator/jdo/trunk/fostore20/src/java/org/apache/jdo/impl/fostore/OID.java Sun May 22 11:40:13 2005
@@ -0,0 +1,462 @@
+/*
+ * Copyright 2005 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at 
+ * 
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software 
+ * distributed under the License is distributed on an "AS IS" BASIS, 
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+ * See the License for the specific language governing permissions and 
+ * limitations under the License.
+ */
+
+package org.apache.jdo.impl.fostore;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+
+import java.util.StringTokenizer;
+
+import javax.jdo.JDOFatalException;
+import javax.jdo.JDOUserException;
+import javax.jdo.spi.PersistenceCapable;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.apache.jdo.state.StateManagerInternal;
+import org.apache.jdo.util.I18NHelper;
+import org.netbeans.mdr.persistence.btreeimpl.btreestorage.BtreeFactory;
+import org.netbeans.mdr.persistence.Storage;
+import org.netbeans.mdr.persistence.MOFID;
+
+/**
+ * Represents the identity of a JDO object in the File/Object store.  This
+ * implementation uses datastore identity.  The identity is based on the class
+ * of the object and a unique identifier within that class.  These OID's are
+ * unique only within a single datastore.
+ * <p>
+ * This class is <code>public</code> as required by the JDO specification.
+ *
+ * @author Dave Bristor
+ * @version 1.0.1
+ */
+// XXX TBD Remote: Allocate provisional OID's per-PMF w/ remote store.
+public class OID implements Serializable, Comparable {
+    //
+    // Make sure that the number of bits uses here add up to the right number
+    // of bits as per the size of the OID.
+    //
+    // In the initial implementation, we are using a long as the size of the
+    // OID.  The layout is:
+    // [reserved: 2] [provisional-CLID: 1] [provisional-UID: 1] [class id: 12] [unique id: 48]
+    //
+    static final long RESERVED_MASK =    0xc000000000000000L;
+    static final long PROV_UID_MASK =    0x1000000000000000L;
+    static final long PROV_CLID_MASK =   0x2000000000000000L;
+    static final long CLID_MASK =        0x0fff000000000000L;
+    static final long UID_MASK =         0x0000ffffffffffffL;
+
+    // Shift a clid's id by this much to "or" it into an oid.
+    static final int CLID_SHIFT = 48;
+
+    // Maximum value for a CLID and UID.
+    static final int MAX_CLID = (int)(CLID_MASK >> CLID_SHIFT);
+    static final long MAX_UID = UID_MASK;
+
+    // Shift the reserved bits over by this much to "or" them into an oid.
+    static final int RESERVED_SHIFT = 61;
+
+    /**
+    * The 'value' of this OID.
+    */
+    // JDO spec - required.
+    public long oid;
+
+    // Hashcode uniquely identifying this CLID within this JVM.
+    private int hashCode;
+
+    // The value for oid that will be used by the next-created provisional OID.
+    private static long nextProvisional = 0;
+
+    // The Class that defined this OID
+    private Class pcClass = null;
+
+    // For synchronizing access to nextProvisional;
+    private static final Integer lock = new Integer(1);
+
+    /** I18N support. */
+    static final I18NHelper msg = I18NHelper.getInstance(I18N.NAME);
+
+    /** Logger */
+    static final Log logger = LogFactory.getFactory().getInstance(
+        "org.apache.jdo.impl.fostore"); // NOI18N
+
+    //
+    // OID's are created 3 ways:
+    // * Creating an "empty" Oid and then (presumably) setting
+    //   the long oid value.
+    // * Creating a filled-in OID from a long value.
+    // * Creting an OID from a CLID.
+    //
+
+    /**
+    * Creates an OID with the no value.
+    */
+    // JDO spec - required.
+    public OID() { }
+
+    /**
+     * Constructor that takes the result of toString() and creates a new
+     * OID.  Currently only the CLID and UID are used.  The provisional
+     * bits are ignored.
+     * @see org.apache.jdo.impl.fostore.OID#toString
+     */
+    // JDO spec - required.
+    public OID (String str) {
+        StringTokenizer st = new StringTokenizer (str, "OID: -()"); // NOI18N
+        String clid = st.nextToken();
+        String uid = st.nextToken();
+        long clidBits = Long.parseLong (clid);
+        long uidBits = Long.parseLong (uid);
+        oid = (clidBits << CLID_SHIFT) | uidBits;
+    }
+
+    /**
+    * Creates an OID with the given value.
+    */
+    // public for user convenience
+    public OID(long oid) {
+        this.oid = oid;
+    }
+
+    // Returns a new OID for the given CLID.  The OID is provisional if the
+    // CLID is provisional.
+    private OID(CLID clid) {
+        synchronized(lock) {
+            long clidBits = clid.getId();
+            clidBits <<= CLID_SHIFT;
+            oid = clidBits | ++nextProvisional;
+
+            if (clid.isProvisional()) {
+                oid |= (PROV_UID_MASK | PROV_CLID_MASK);
+            }
+        }
+    }
+
+    //
+    // I prefer to avoid using constructors outside of a given class, and to
+    // instead use factory methods.  The above constructors which are public
+    // are that way for the sake of the JDO spec.  Code within fostore uses
+    // these factory methods instead.
+    //
+
+    /**
+    * Create and return a provisional OID
+    * @return A Provisional OID.
+    */
+    static OID create(CLID clid) {
+        OID rc = new OID(clid);
+        if (clid.isProvisional()) {
+            rc.oid |= PROV_CLID_MASK;
+        }
+        rc.oid |= PROV_UID_MASK;
+        return rc;
+    }
+
+    /**
+    * Create and return a based on a given representation.
+    * @return A real, non-provisional OID.
+    * @exception JDOFatalException Thrown if given oid has its provisional bit set.
+    */
+    OID create(long oid) {
+        OID rc = new OID(oid);
+        if (rc.isProvisional()) {
+            throw new FOStoreFatalInternalException(
+                this.getClass(), "create(oid)", // NOI18N
+                msg.msg("ERR_InvalidAttemptToCreateOID", new Long(oid))); // NOI18N
+        }
+        return rc;
+    }
+
+    /**
+    * Provides an OID for the given CLID and UID.  The given CLID must not be
+    * provisional, or a JDOFatalException will result.
+    * @param clid CLID for the OID.
+    * @param uid UID part of the OID.
+    * @return A new OID based on the given clid and uid.
+    * @exception JDOFatalException Thrown if given CLID is provisional.
+    */
+    static OID create(CLID clid, long uid) {
+        if (clid.isProvisional()) {
+            throw new FOStoreFatalInternalException(
+                OID.class, "create(clid, oid)", // NOI18N
+                msg.msg("ERR_InvalidAttemptToCreateOID", clid, new Long(uid))); // NOI18N
+        }
+        long clidBits = clid.getId();
+        clidBits = clidBits << CLID_SHIFT;
+        return new OID(clidBits | uid);
+    }
+
+    //
+    // Provide access to information about an OID.
+    //
+
+    /**
+    * Indicates whether this OID is provisional.
+    * @return true if this OID is provisional, false otherwise.
+    */
+    public boolean isProvisional() {
+        boolean rc = false;
+        // If CLID is provisional, it *must* be provisional.  If not, it can
+        // still be a real CLID with a provisional UID part.
+        if ((oid & PROV_CLID_MASK) != 0) {
+            rc = true;
+        } else if ((oid & PROV_UID_MASK) != 0) {
+            rc = true;
+        }
+        return rc;
+    }
+
+    /**
+    * Provides the CLID part of this OID.  The resulting CLID is
+    * provisional if it is provisional within this OID.  I.e., this might be a
+    * provisional OID, but the CLID part could still be datastore-assigned.
+    * @return The CLID part of this OID.
+    */
+    public CLID getCLID() {
+        long clidBits = oid & CLID_MASK;
+        clidBits >>= CLID_SHIFT;
+        return CLID.create((int)clidBits, (oid & PROV_CLID_MASK) != 0);
+    }
+
+    /**
+    * Provides the unique id part of this OID.
+    * @return The unique id part of this OID.
+    */
+    public long getUID() {
+        return oid & UID_MASK;
+    }
+
+    /**
+    * Provides a JVM-unique hashCode for this OID.
+    */
+    public int hashCode() {
+        if (0 == hashCode) {
+            hashCode = new Long(oid).hashCode();
+        }
+        return hashCode;
+    }
+
+    /**
+    * Determines if this OID is equal to another.
+    * @param other The other OID in the equality comparison.
+    * @return True if they are equal, false otherwise.
+    */
+    public boolean equals(Object other) {
+        boolean rc = false;
+        if (other instanceof OID) {
+            rc = oid == ((OID)other).oid;
+        }
+        return rc;
+    }
+
+    /**
+    * Returns a String representation of this OID.  Includes whether or not
+    * the OID is provisional, and its reserved bits, if they are set.
+    */
+    public String toString() {
+        StringBuffer rc =
+            new StringBuffer(
+                "OID: " + // NOI18N
+                ((oid & CLID_MASK) >> CLID_SHIFT) +
+                "-" + (oid & UID_MASK)); // NOI18N
+        if (isProvisional()) {
+            rc.append(" (provisional)"); // NOI18N
+        }
+        long res = oid & RESERVED_MASK;
+        if (res > 0) {
+            res = res >> RESERVED_SHIFT;
+            rc.append(" (reserved=" + res + ")"); // NOI18N
+        }
+        return rc.toString();
+    }
+
+    /**
+    * Returns the id itself in String form, for debugging.
+    */
+    public String oidString() {
+        return "" + oid; // NOI18N
+    }
+
+    //
+    // Serialization
+    // We provide the {write,read}Object methods for java.io.Serialization, so
+    // that we know exactly what's being written and read.  We also have
+    // methods used elsewhere in the fostore package that don't rely
+    // ObjectOutput stuff.
+    //
+
+    /**
+    * Writes this OID to the output stream.
+    */
+    private void writeObject(ObjectOutputStream out) throws IOException {
+        write(out);
+    }
+
+    /**
+    * Reads this OID's value from the input stream.
+    */
+    private void readObject(ObjectInputStream in) throws IOException {
+        boolean appIdType = in.readBoolean();
+        oid = in.readLong();
+    }
+
+    void write(DataOutput out) throws IOException {
+        if (logger.isDebugEnabled()) {
+            logger.debug("OID.write: " + oid);  // NOI18N
+        }
+        out.writeBoolean(false);
+        out.writeLong(oid);
+    }
+
+    static OID read(DataInput in) throws IOException {
+        boolean appIdType = in.readBoolean();
+        long oid = in.readLong();
+        if (logger.isDebugEnabled()) {
+            logger.debug("OID.read: " + oid + " appIdType: " + appIdType);  // NOI18N
+        }
+        OID rc = null;
+        if (appIdType) {
+            rc = new AID(oid);
+            ((AID)rc).readBuffer(in);
+        } else {
+            rc = new OID(oid);
+        }
+        return rc;
+    }
+
+    /**
+     * Skip OID bytes from the input.
+     * @param in DataInput.
+     * @throws IOException.
+     */
+    static void skip(DataInput in) throws IOException {
+        boolean appIdType = in.readBoolean();
+        long oid = in.readLong();
+        if (logger.isDebugEnabled()) {
+            logger.debug("OID.skip: " + oid + " appIdType: " + appIdType);  // NOI18N
+        }
+        if (appIdType) {
+            int length = in.readInt();
+            in.skipBytes(length);
+        }
+    }
+
+    //
+    // Implementation methods.
+    //
+
+    /**
+     * Returns copy of the requested oid to be accessed by the user.
+     */
+    Object getExternalObjectId(PersistenceCapable pc) {
+        return new OID(oid);
+    }
+
+    /**
+    * Provides the OID in a form that can be used by the database as a key.
+    */
+    MOFID keyValue(FOStoreBtreeStorage storage) {
+        return storage.createMOFID(getCLID().getId(), getUID());
+    }
+
+    /** Replaces provisional oid with real oid (datastore identity only)
+     * @param realOID as OID instance
+     * @param pmf as FOStorePMF
+     * @param sm as StateManagerInternal
+     */
+    void replaceProvisionalOIDWithReal(OID realOID, FOStorePMF pmf,
+        StateManagerInternal sm) {
+
+        pmf.mapProvisionalOIDToReal(this, realOID);
+        sm.setObjectId(realOID);
+    }
+
+    /**
+     * Returns copy of the requested oid.
+     */
+    OID copy() {
+        return new OID(oid);
+    }
+
+    /**
+     * Copy key fields from OID into PC instance. No-op for the
+     * datastore identity type for this OID.
+     * @param sm as StateManagerInternal
+     * @param pmf as FOStorePMF
+     * @param pcClass Class of the PC instance.
+     * @param pkfields array of PK field numbers.
+     */
+    void copyKeyFieldsToPC(StateManagerInternal sm, FOStorePMF pmf,
+                                    Class pcClass, int[] pkfields) {}
+
+    /**
+     * Returns Class that defined OID.
+     * @param pmf as FOStorePMF
+     */
+    Class getPCClass(FOStorePMF pmf) {
+        if (pcClass == null) {
+            FOStoreModel model = pmf.getModel();
+            pcClass = model.getClass(getCLID());
+            if (logger.isDebugEnabled()) {
+                logger.debug("OID.getPCClass: " + getCLID() + " " + pcClass);  // NOI18N
+            }
+        }
+        return pcClass;
+    }
+
+    /**
+     * Returns false for application identity type for this OID.
+     */
+    boolean isApplicationIdentity() {
+        return false;
+    }
+
+    /**
+     * Returns true for datastore identity type for this OID.
+     */
+    boolean isDataStoreIdentity() {
+        return true;
+    }
+    
+    /** Compare this OID to another OID. This is needed to implement an
+     * absolute ordering of OIDs. The ordering must provide for comparing
+     * provisional OIDs to permanent OIDs, with all provisional OIDs 
+     * comparing greater than all permanent OIDs.
+     * @since 1.0.1
+     */
+    public int compareTo(Object obj) {
+        if (!(obj instanceof OID)) {
+            throw new JDOUserException(msg.msg("EXC_CannotCompareNonOID")); // NOI18N
+        }
+        OID other = (OID)obj;
+        // if other is provisional and we're not, other is bigger
+        if (other.isProvisional() & !this.isProvisional()) {
+            return -1;
+        } else if (this.isProvisional() & !other.isProvisional()) {
+            return 1;
+        } else // compare UIDs
+            // both are provisional or both not; which is bigger UID?
+            return (this.getUID() < other.getUID())?-1:1;
+    }
+    
+}