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;
+ }
+
+}