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 [11/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/ReplyHandler.java
URL: http://svn.apache.org/viewcvs/incubator/jdo/trunk/fostore20/src/java/org/apache/jdo/impl/fostore/ReplyHandler.java?rev=171355&view=auto
==============================================================================
--- incubator/jdo/trunk/fostore20/src/java/org/apache/jdo/impl/fostore/ReplyHandler.java (added)
+++ incubator/jdo/trunk/fostore20/src/java/org/apache/jdo/impl/fostore/ReplyHandler.java Sun May 22 11:40:13 2005
@@ -0,0 +1,249 @@
+/*
+ * 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.IOException;
+import java.util.ArrayList;
+
+import javax.jdo.JDODataStoreException;
+import javax.jdo.JDOFatalDataStoreException;
+import javax.jdo.JDOFatalUserException;
+import javax.jdo.JDOOptimisticVerificationException;
+import javax.jdo.JDOUserException;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.apache.jdo.state.StateManagerInternal;
+import org.apache.jdo.store.Connector;
+import org.apache.jdo.util.I18NHelper;
+
+/**
+* Processes replies that are received from the store.  Dispatches each one to
+* its corresponding Request.
+* @author Dave Bristor
+*/
+
+// Perhaps this class isn't necessary, and it's functionality should be
+// moved into AbstractRequest.  In any case, either it should be called
+// ReplyProcessor or the single method should be named handleReplies.
+
+// This is client-side code.  It does not need to live in the server.
+abstract class ReplyHandler {
+    /** 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
+    
+    /**
+    * Process all replies in the given input stream.  The format of the
+    * DataInput is<br>
+    * <pre>
+    * Version number of the Reply data (of the whole enchilada, not of the
+    * individual Reply instances).
+    * Status value indicating the overall success by the server in processing
+    * the Message.
+    * </pre>
+    * The expected Status value is either OK or FATAL.  If FATAL, then the
+    * next item is
+    * <pre>
+    * String: message from server (such as exception string or stack trace).
+    * </pre>
+    * Otherwise, the next item is
+    * <pre>
+    * int: number of replies
+    * </pre>
+    * In the FATAL case, all other data is ignored.  Otherwise the remaining
+    * data is, per reply:
+    * <pre>
+    * RequestId: of the request corresponding to the reply data being read
+    * Status: of the individual reply
+    * MessagePos: int which indicates where in the DataInput is a String that
+    * was generated by the processing of the reply's request.  If this is 0,
+    * then there is no message.
+    * length: int indicating the length of the reply's data.
+    * request-specific data: length bytes of data associated with the reply.
+    * </pre>
+    */
+    static void processReplies(DataInput in, Message message) {
+        ArrayList exceptions = new ArrayList();
+        boolean optimistic_failure = false;
+
+        try {
+            try {
+                Reply.verifyVersionNumber(in);
+            } catch (JDOFatalUserException ex) {
+                throw ex;
+            }
+
+            // Overall status of a reply is FATAL, ROLLBACK, LOGIN, or OK.
+            Status replyStatus = new Status(in);
+            if (logger.isDebugEnabled()) {
+                logger.debug(
+                    "ReplyHandler.hR: replyStatus=" + replyStatus); // NOI18N
+            }
+            if (replyStatus.equals(Status.FATAL)) {
+                // Don't process any replies, just throw a
+                // JDOFatalDataStoreException so that upper levels can
+                // rollback, etc. as necessary.
+                Connector c = message.getConnector();
+                if (null != c) {
+                    c.setRollbackOnly();
+                }
+                String str = in.readUTF();
+                // sic: DataStoreException.  This was decided upon after much
+                // email discussion.
+                throw new JDODataStoreException(
+                    msg.msg("ERR_FatalReply", str)); // NOI18N
+
+            } else if (replyStatus.equals(Status.LOGIN)) {
+                if (logger.isDebugEnabled()) {
+                    logger.debug("ReplyHandler: Login failure"); // NOI18N
+                }
+                String str = in.readUTF();
+                throw new JDOFatalDataStoreException(str);
+
+            } else if (replyStatus.equals(Status.ROLLBACK)) {
+                // The rollback was at user request.  Don't process any
+                // replies, but don't throw any exception either.
+                if (logger.isDebugEnabled()) {
+                    logger.debug(
+                        "ReplyHandler.processReplies: ROLLBACK"); // NOI18N
+                }
+                int skipLength = in.readInt();
+                in.skipBytes(skipLength);
+            } else {
+                // Process each reply.
+                int numReplies = in.readInt();
+                if (logger.isDebugEnabled()) {
+                    logger.debug(
+                        "ReplyHandler.processReplies: numReplies=" + // NOI18N
+                        numReplies);
+                }
+
+                for (int i = 0; i < numReplies; i++) {
+                    RequestId requestId = new RequestId(in);
+                    Status status = new Status(in);
+
+                    if (logger.isDebugEnabled()) {
+                        logger.debug(
+                            "ReplyHandler: " + requestId + // NOI18N
+                            ", " + status); // NOI18N
+                    }
+
+                    int messagePos = in.readInt();
+                    Request request = message.getRequest(requestId);
+                    if (null == request) {
+                        // This could happen if a "nested" request is being
+                        // processed, i.e. this requestId was already removed
+                        // from the list but was not finished processing.
+                        int length = in.readInt();
+                        in.skipBytes(length);
+                        continue;
+                        /*
+                        throw new FOStoreFatalInternalException(
+                            ReplyHandler.class, "processReplies", // NOI18N
+                            msg.msg(
+                                "ERR_CannotGetRequest", requestId)); // NOI18N
+                        */
+                    }
+                    int length = in.readInt();
+
+                    if (0 != messagePos) {
+                        if (status.equals(Status.OK) ||
+                            status.equals(Status.WARN)) {
+
+                            // If there's a message with non-erroneus status,
+                            // let request handle reply.  Note: Request
+                            // *MUST* read message in this case!
+                            request.handleReply(status, in, length);
+
+
+                            // The next 2 cases are similar, differing only
+                            // in whether the user could retry the operation
+                            // or not.  In both cases, skip the reply data
+                            // (it might not even be valid, though it's
+                            // length is) and add the message in an
+                            // exception.
+                        } else if (status.equals(Status.FATAL)) {
+                            in.skipBytes(length);
+                            String str = in.readUTF();
+                            throw new JDOFatalDataStoreException(str);
+                        } else if (status.equals(Status.OPTIMISTIC)) {
+                            // overall status is failed due to optimistic verification
+                            optimistic_failure = true;
+                            // when the result is Status.OPTIMISTIC, the result
+                            // contains the oid of the failed operation
+                            OID oid = OID.read(in);
+                            StateManagerInternal sm = request.getStateManager();
+                            Object failed = (sm == null) ? null : sm.getObject();
+                            String str = in.readUTF();
+                            exceptions.add(new JDOOptimisticVerificationException(str, failed));
+                        } else {
+                            in.skipBytes(length);
+                            String str = in.readUTF();
+                            exceptions.add(new JDODataStoreException(str));
+                        }
+                    } else if (status.equals(Status.ERROR) ||
+                               status.equals(Status.FATAL)) {
+                        // If there's no message but the status reports an
+                        // error then we have a bug.
+                        throw new JDOFatalDataStoreException();
+                    } else {
+                        // No message, non-ERROR status: let request handle
+                        // reply
+                        request.handleReply(status, in, length);
+                    }
+                }
+            }
+        } catch (IOException ex) {
+            throw new FOStoreFatalIOException(
+                ReplyHandler.class, "processReplies", ex); // NOI18N
+        }
+
+        int numExceptions = exceptions.size();
+
+        if (logger.isDebugEnabled()) {
+            logger.debug(
+                "ReplyHandler.processReplies: finished; " + // NOI18N
+                "numExceptions=" + numExceptions); // NOI18N
+        }
+
+        if (numExceptions > 0) {
+            // We can't use exceptions.toArray() because you wouldn't have
+            // the debugging output.
+            // XXX use toArray(Throwable[])
+            Throwable t[] = new Throwable[numExceptions];
+            for (int i = 0; i < numExceptions; i++) {
+                t[i] = (Throwable)exceptions.get(i);
+                if (logger.isDebugEnabled()) {
+                    logger.debug("RH.rH: " + t[i]); // NOI18N
+                }
+            }
+            if (optimistic_failure) {
+                throw new JDOOptimisticVerificationException(
+                    msg.msg("EXC_Optimistic"), t); // NOI18N
+            } else {
+                throw new JDODataStoreException(
+                    msg.msg("EXC_Exceptions"), t);  // NOI18N
+            }
+        }
+    }
+}

Added: incubator/jdo/trunk/fostore20/src/java/org/apache/jdo/impl/fostore/Request.java
URL: http://svn.apache.org/viewcvs/incubator/jdo/trunk/fostore20/src/java/org/apache/jdo/impl/fostore/Request.java?rev=171355&view=auto
==============================================================================
--- incubator/jdo/trunk/fostore20/src/java/org/apache/jdo/impl/fostore/Request.java (added)
+++ incubator/jdo/trunk/fostore20/src/java/org/apache/jdo/impl/fostore/Request.java Sun May 22 11:40:13 2005
@@ -0,0 +1,54 @@
+/*
+ * 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.IOException;
+
+import org.apache.jdo.state.StateManagerInternal;
+
+
+/**
+ * Represents the ability to send information to the actual file/object store.
+ *
+ * @author Dave Bristor
+ */
+interface Request {
+    /** Does whatever it is that the kind of request does in actually making a
+     * request of the store.
+     * @throws IOException in case of errors with the stream.
+     */
+    public void doRequest() throws IOException;
+    
+    /** Processes the results of the effect of the request in the store.  To be
+     * invoked after the store has processed the request, and has returned
+     * information about that request, such as its status and any accompanying
+     * data.
+     * @param in the input stream.
+     * @param length the length of data in the stream.
+     * @param status Indication as to the success, failure, etc. of the
+     * request as handled by the store.
+     * @throws IOException if any problems reading the stream.
+     */
+    public void handleReply(Status status, DataInput in, int length)
+        throws IOException;
+    
+    /** Get the StateManager associated with this request, null if none.
+     * @return the StateManager.
+     */
+    public StateManagerInternal getStateManager();
+}

Added: incubator/jdo/trunk/fostore20/src/java/org/apache/jdo/impl/fostore/RequestFactory.java
URL: http://svn.apache.org/viewcvs/incubator/jdo/trunk/fostore20/src/java/org/apache/jdo/impl/fostore/RequestFactory.java?rev=171355&view=auto
==============================================================================
--- incubator/jdo/trunk/fostore20/src/java/org/apache/jdo/impl/fostore/RequestFactory.java (added)
+++ incubator/jdo/trunk/fostore20/src/java/org/apache/jdo/impl/fostore/RequestFactory.java Sun May 22 11:40:13 2005
@@ -0,0 +1,195 @@
+/*
+ * 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.util.ArrayList;
+import java.util.BitSet;
+
+import org.apache.jdo.pm.PersistenceManagerInternal;
+import org.apache.jdo.state.StateManagerInternal;
+
+
+/**
+ * Creates different kinds of requests.
+ *
+ * @author Dave Bristor
+ */
+interface RequestFactory {
+    /**
+     * Creates a request object that will get a datastore OID for a
+     * provisional OID.
+     */
+    public CreateOIDRequest getCreateOIDRequest(
+        StateManagerInternal sm, Message m, FOStorePMF pmf,
+        OID oid, PersistenceManagerInternal pm);
+
+    /**
+     * Creates a request object to activate the class corresponding to the
+     * given oid.
+     * @param cls Class to be activated.
+     * @param m Message by which the request is to be sent to the store.
+     * @param pmf FOStorePMF in which the request is taking place.
+     */
+    public ActivateClassRequest getActivateClassRequest(
+        Class cls, Message m, FOStorePMF pmf);
+
+    /**
+     * Creates a request object to cause a persistent object to be inserted
+     * into the datastore.
+     * @param sm StateManagerInternal of the object to be stored in the
+     * datastore. 
+     * @param m Message by which the request is to be sent to the store.
+     * @param pmf FOStorePMF in which the request is taking place.
+     */
+    public InsertRequest getInsertRequest(
+        StateManagerInternal sm, Message m, FOStorePMF pmf);
+
+    /**
+     * Creates a request object to cause one or more fields of a persistent
+     * object to be updated in the store.
+     * @param sm StateManagerInternal of the object to be updated.
+     * @param m Message by which the request is to be sent to the store.
+     * @param pmf FOStorePMF in which the request is taking place.
+     * @param loadedFields Set of fields loaded from the database.
+     * @param dirtyFields Set of fields that are to be flushed and
+     * verified against the those in the database, if this
+     * <code>update</code> is within the context of an optimistic
+     * transaction.
+     * @param optimistic If true, then update is happening in context of
+     * optimistic transaction, otherwise datastore transaction.
+     */
+    public UpdateRequest getUpdateRequest(
+        StateManagerInternal sm, Message m, FOStorePMF pmf,
+        BitSet loadedFields, BitSet dirtyFields, boolean optimistic);
+
+    /**
+     * Creates a request object to verify that in-memory data is the same as
+     * that in the database.
+     * @param sm StateManagerInternal of the object to be verified.
+     * @param m Message by which the request is to be sent to the store.
+     * @param pmf FOStorePMF in which the request is taking place.
+     * @param verifyFields If true, verify values of object, otherwise verify
+     * only existence (and ignore remaining parameters).
+     * @param loadedFields Set of fields to be verified against those in the
+     * database.
+     */
+    public VerifyRequest getVerifyRequest(
+        StateManagerInternal sm, Message m, FOStorePMF pmf,
+        boolean verifyFields, BitSet loadedFields);
+
+    /**
+     * Creates a request object to cause one or more fields of a persistent
+     * object to be read from the store.
+     * @param sm StateManagerInternal of the object whose field(s) are to be
+     * read. 
+     * @param m Message by which the request is to be sent to the store.
+     * @param pmf FOStorePMF in which the request is taking place.
+     */
+    public FetchRequest getFetchRequest(
+        StateManagerInternal sm, Message m, FOStorePMF pmf);
+
+    /**
+     * Creates a request object to cause a particular class's extent to be
+     * retrieved.
+     * @param extent FOStoreExtent for which the request is being created.
+     * @param pcClass Class of the objects whose extent is sought.  It is
+     * <em>required</em> that the caller ensure that the given pcClass
+     * implement javax.jdo.PersistenceCapable.
+     * @param subclasses If false, retrieve instances of pcClass only; if true
+     * retrieve those plus all instances of subclasses of pcClass.
+     * @param m Message by which the request is to be sent to the store.
+     * @param pm PersistenceManager on whose behalf the request is taking
+     * place.
+     */
+    public GetExtentRequest getGetExtentRequest(
+        FOStoreExtent extent, Class pcClass, 
+        boolean subclasses, Message m,
+        PersistenceManagerInternal pm);
+
+    /**
+     * Creates a request to get instances for some oids.
+     * @param oids List of oids for which instances are needed.
+     * @param start Starting index in oids for which instances are needed.
+     * @param numInstances Number of instances which are needed.
+     * @param pm PersistenceManager on whose behalf the request is taking
+     * place.
+     * @param cls Candidate Class for which instances are being obtained.
+     */
+    public GetInstancesRequest getGetInstancesRequest(
+        ArrayList oids, int start, int numInstances,
+        Message m, PersistenceManagerInternal pm, Class cls);
+
+    /**
+     * Creates a request object to cause a persistent object in the store to
+     * be deleted.
+     * @param sm StateManagerInternal of the object to delete in the store.
+     * @param m Message by which the request is to be sent to the store.
+     * @param pmf FOStorePMF in which the request is taking place.
+     */
+    public DeleteRequest getDeleteRequest(
+        StateManagerInternal sm, Message m, FOStorePMF pmf);
+
+    /**
+     * Creates a request object to cause the java.lang.Class associated with
+     * the given CLID to be provided.
+     * @param clid CLID of the class that is needed.
+     * @param m Message by which the request is to be sent to the store.
+     * @param pmf FOStorePMF in which the request is taking place.
+     * @param pm PersistenceManager used to load the class.
+     * place.
+ */
+    public GetClassRequest getGetClassRequest(
+        CLID clid, Message m, FOStorePMF pmf, PersistenceManagerInternal pm);
+
+    /**
+     * Creates a request object which notifies the store of the kind of
+     * transaction that is starting.
+     * @param m Message by which the request is to be sent to the store.
+     * @param pmf FOStorePMF in which the request is taking place.
+     * @param optimistic Indicates whether an optimistic or datastore
+     * transaction is beginning.
+     */
+    public BeginTxRequest getBeginTxRequest(
+        Message m, FOStorePMF pmf, boolean optimistic);
+
+    /**
+     * Creates a request object which causes previous operations to commit.
+     * @param m Message by which the request is to be sent to the store.
+     * @param pmf FOStorePMF in which the request is taking place.
+     */
+    public CommitRequest getCommitRequest(
+        Message m, FOStorePMF pmf);
+
+    /**
+     * Creates a request object which causes previous operations to rollback.
+     * @param m Message by which the request is to be sent to the store.
+     * @param pmf FOStorePMF in which the request is taking place.
+     */
+    public RollbackRequest getRollbackRequest(
+        Message m, FOStorePMF pmf);
+
+    /**
+     * Creates a request object to get information from the store.
+     * @param option Diagnostic parameter code.
+     * @param className Optional class name.
+     * @param m Message by which the request is to be sent to the store.
+     * @param pmf FOStorePMF in which the request is taking place.
+     */
+    public DumpRequest getDumpRequest(
+        DumpOption option, String className, Message m,
+        FOStorePMF pmf);
+}

Added: incubator/jdo/trunk/fostore20/src/java/org/apache/jdo/impl/fostore/RequestFinisher.java
URL: http://svn.apache.org/viewcvs/incubator/jdo/trunk/fostore20/src/java/org/apache/jdo/impl/fostore/RequestFinisher.java?rev=171355&view=auto
==============================================================================
--- incubator/jdo/trunk/fostore20/src/java/org/apache/jdo/impl/fostore/RequestFinisher.java (added)
+++ incubator/jdo/trunk/fostore20/src/java/org/apache/jdo/impl/fostore/RequestFinisher.java Sun May 22 11:40:13 2005
@@ -0,0 +1,31 @@
+/*
+ * 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;
+
+/**
+ * Means by which a request can indicate that there is work to be done
+ * once all requests have been processed.
+ *
+ * @author Dave Bristor
+ */
+interface RequestFinisher {
+    /**
+     * Does some post-request processing work on behalf of the request which
+     * created it.
+     */
+    public void finish();
+}

Added: incubator/jdo/trunk/fostore20/src/java/org/apache/jdo/impl/fostore/RequestHandler.java
URL: http://svn.apache.org/viewcvs/incubator/jdo/trunk/fostore20/src/java/org/apache/jdo/impl/fostore/RequestHandler.java?rev=171355&view=auto
==============================================================================
--- incubator/jdo/trunk/fostore20/src/java/org/apache/jdo/impl/fostore/RequestHandler.java (added)
+++ incubator/jdo/trunk/fostore20/src/java/org/apache/jdo/impl/fostore/RequestHandler.java Sun May 22 11:40:13 2005
@@ -0,0 +1,391 @@
+/*
+ * 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.util.ArrayList;
+
+import javax.jdo.JDOException;
+import javax.jdo.JDOFatalDataStoreException;
+import javax.jdo.JDOFatalUserException;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.apache.jdo.util.I18NHelper;
+
+/**
+* This dispatches each request received by the store to the appropriate
+* request-type-specific request handler.  It is very dependent on the
+* 'message full of data' means of communicating between client and store.
+*
+* @author Dave Bristor
+*/
+//
+// This is server-side code.  It does not need to live in the client.
+//
+abstract class RequestHandler {
+    /** Subclasses use this Reply instance to send data back to their
+     * corresponding client-side request. */
+    protected final Reply reply;
+
+    /** Length of the data in the Request. */
+    protected final int length;
+
+    /** Connection on which the Request arrived. */
+    protected final FOStoreServerConnection con;
+
+    /** 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    
+    
+    /** Means by which subclasses are created.  Each RequestHandler subclass
+     * should have a static inner class that implements this interface.
+     */
+    interface HandlerFactory {
+        /**
+         * @param reply Reply instance into which the returned handler may
+         * write request/reply - specific information.
+         * @param length Length in bytes of the request data.
+         * @param con Connection from which the request was made.
+         *@return A RequestHandler instance for a specific subclass of
+         * RequestHandler.
+         */
+        public RequestHandler getHandler(Reply reply, int length,
+                                         FOStoreServerConnection con);
+    }
+
+    /**
+    * @param reply Reply to which request handler should write all reply
+    * information.
+    * @param length Number of bytes in the connection's input that are for
+    * this request; subclasses <strong>must</strong> read all bytes so that
+    * other requests can work.
+    * @param con Connection from which request handler reads the
+    * request data.
+    */
+    protected RequestHandler(Reply reply, int length,
+                             FOStoreServerConnection con) {
+        
+        this.reply = reply;
+        this.length = length;
+        this.con = con;
+    }
+
+    /**
+    * The RollbackHandler should override this and return false, so that
+    * finishers are not run when we are rolling back.
+    */
+    protected boolean getOkToFinish() {
+        return true;
+    }
+
+    /**
+    * The CommitHandler should override this and save the given value, then
+    * use it to determine whether or not to commit.
+    */
+    protected void setOkToCommit(boolean okToCommit) { }
+
+    /**
+     * Handles all requests that can be read at this time from the given
+     * connection.  Reads the number of requests, then reads each one and, in
+     * turn, invokes the handleRequest operation on each.
+     * <p>
+     * The data it generates for the client is documented; see {@link
+     * ReplyHandler#processReplies}.
+     * <p>
+     * If after all requests are thusly handled, none have indicated that the
+     * second round of handling, called finishing, is not to be done, then
+     * performs this second round.  The finishers that are invoked are
+     * precisely those which were returned from each request's handleRequest
+     * invocation, and the finishers have their finish() methods invoked in
+     * the same order in which the requests were originally invoked.
+     * <p>
+     * Finally, writes the number of replies (at the beginning of the reply
+     * data, in a spot that was set aside for this purpose), and sends replies
+     * back to the client.
+     * <p>
+     * This method is <em>very</em> paranoid about error checking, which
+     * clutters it up some, but is necessary to ensure that the server keeps
+     * running.
+     */
+    static void handleRequests(FOStoreServerConnection con) {
+        // List of finishers to run after all requests have been handled.
+        // Provides a means for a second phase of operations to run, at the
+        // request of the requests themselves.  Re-initialized each time
+        // handleRequests is run.
+        // @see Message#sendToStore for stream header writer.
+        ArrayList finishers = new ArrayList();
+        
+        DataInput in = con.getInputFromClient();
+        int numRequests = 0;
+
+        FOStoreOutput serverData = con.getOutputForClient();
+        int numReplies = 0;
+
+        // Result of processing all requests.  Written as part of the reply
+        // data's "header".
+        Status overallStatus = Status.OK;
+
+        // Position in serverData where the reply's overall status is written.
+        // Used in case we have to overwrite that value.
+        //
+        // Why do we initialize statusPos?
+        // We have to because if not, the compiler complains that it might
+        // not have been initialized, at it is used as an R-value.  And while
+        // this is technically true, in fact it is initialized right away
+        // inside the first try block, and nothing at or before that
+        // initialization can throw an exception.  (Well, at least not
+        // anything we'd ever recover from.)
+        int statusPos = 0;
+
+        // Position in serverData wher number of replies is written.
+        int numRepliesPos;
+        
+        // If true, then run finishers after handling all requests.
+        // This is set below by invoking okToFinish on each RequestHandler
+        // instance.  Only RollbackHandler should return false.
+        boolean okToFinish = true;
+
+        // The message might contain a CommitRequest.  We assume that all
+        // requests will succeed.  But if one of them sets its reply's status
+        // to Status.ERROR or Status.FATAL, we don't commit.
+        boolean okToCommit = true;
+
+        try {
+            Reply.writeVersionNumber(serverData);
+
+            // Write a default overall status of the reply.  Will be
+            // overwritten later if there are any fatal problems in processing
+            // the requests.
+            statusPos = serverData.getPos();
+            overallStatus.write(serverData);
+            
+            // Write a default number of replies.  Later write the real number
+            // of replies.
+            numRepliesPos = serverData.getPos();
+            serverData.writeInt(0);
+
+            try {
+                Message.verifyVersionNumber(in);
+                numRequests = in.readInt();
+                if (logger.isDebugEnabled()) {
+                    logger.debug("RequestHandler: numRequests=" + // NOI18N
+                                   numRequests);
+                }
+            } catch (JDOFatalUserException ex) {
+                // Message version mismatch
+                throw ex;
+            } catch (IOException ex) {
+                throw new FOStoreFatalIOException(
+                    RequestHandler.class,
+                    "handleRequests/numRequests ", ex); // NOI18N
+            }
+
+            for (int i = 0; i < numRequests; i++) {
+                Reply reply = null; // Prepare for this iteration.
+                RequestId requestId = new RequestId(in);
+                RequestType requestType = new RequestType(in);
+
+                if (logger.isDebugEnabled()) {
+                    logger.debug("RequestHandler: " + // NOI18N
+                                   requestId + "/" + // NOI18N
+                                   requestType);
+                }
+
+                HandlerFactory factory = requestType.getHandlerFactory();
+                if (null == factory) {
+                    throw new FOStoreFatalInternalException(
+                        RequestHandler.class, "handleRequests", // NOI18N
+                        msg.msg("ERR_CannotCreateHandler", // NOI18N
+                                requestType.toString()));
+                }
+                int length = in.readInt();
+                reply = con.createReply(requestId);
+                numReplies++;
+
+                RequestHandler rh =
+                    factory.getHandler(reply, length, con);
+
+                // Run the request, and save it's finisher (if any)
+                rh.setOkToCommit(okToCommit);
+                RequestFinisher rf = rh.handleRequest();
+
+                // Check for commit- and finish- ability
+                Status replyStatus = reply.getStatus();
+                if (Status.ERROR.equals(replyStatus) ||
+                    Status.FATAL.equals(replyStatus) ||
+                    Status.OPTIMISTIC.equals(replyStatus)) {
+
+                    okToCommit = false;
+
+                } else if (Status.ROLLBACK.equals(replyStatus)) {
+                    okToCommit = false;
+
+                     // No point in doing work that'll just be undone
+                    okToFinish = false;
+
+                    // Mark the status as rolled back.
+                    overallStatus = replyStatus;
+                    int pos = serverData.getPos();
+                    serverData.setPos(statusPos);
+                    overallStatus.write(serverData);
+
+                    // Instead of number of replies, write the number of bytes
+                    // in the reply, so that the client can skip them.
+                    serverData.setPos(numRepliesPos);
+                    serverData.writeInt(pos - numRepliesPos);
+
+                    serverData.setPos(pos);
+                    
+                    // Once a rollback has been requested, there's no point in
+                    // processing other replies.
+                    break;
+
+                } else {
+                    okToFinish = rh.getOkToFinish();
+                    if (okToFinish && null != rf) {
+                        finishers.add(rf);
+                    }
+                }
+            }
+
+            if (okToFinish) {
+                // Run the finishers they produced (if any).
+                int numFinishers = finishers.size();
+                for (int i = 0; i < numFinishers; i++) {
+                    RequestFinisher rf = (RequestFinisher)finishers.get(i);
+                    if (logger.isDebugEnabled()) {
+                        logger.debug(
+                            "RequestHandler.hR: finish " + // NOI18N
+                            rf.getClass().getName());
+                    }
+                    rf.finish();
+                }
+            }
+
+            // If rollback, numRepliesPos already overwritten with length of
+            // reply data.
+            if (! overallStatus.equals(Status.ROLLBACK)) {
+                // Write the number of replies produced.
+                int pos = serverData.getPos();
+                serverData.setPos(numRepliesPos);
+                serverData.writeInt(numReplies);
+                serverData.setPos(pos);
+            }
+
+        } catch (FOStoreLoginException ex) {
+            if (logger.isDebugEnabled()) {
+                logger.debug("Login failure"); // NOI18N
+            }
+            try {
+                int pos = serverData.getPos();
+                serverData.setPos(statusPos);
+                overallStatus = Status.LOGIN;
+                overallStatus.write(serverData);
+                serverData.writeUTF(ex.toString());
+                int messagePos = serverData.getPos();
+                if (messagePos > pos) {
+                    pos = messagePos;
+                }
+                serverData.setPos(pos);
+            } catch (IOException ex2) {
+                giveUp(ex2);
+            }
+            
+        } catch (Throwable ex) {
+            // Handle unexpected failure.  Inform client that the replies
+            // could not be processed.  Rollback the database.
+            if (logger.isDebugEnabled()) {
+                ex.printStackTrace();
+            }
+            try {
+                int pos = serverData.getPos();
+                serverData.setPos(statusPos);
+                overallStatus = Status.FATAL;
+                overallStatus.write(serverData);
+                String message = Reply.getExceptionMessage(ex);
+                if (null == message) {
+                    message = msg.msg("EXC_Unknown"); // NOI18N
+                }
+                serverData.writeUTF(message);
+                int messagePos = serverData.getPos();
+                if (messagePos > pos) {
+                    pos = messagePos;
+                }
+                serverData.setPos(pos);
+
+                try {
+                    con.rollback();
+                } catch (FOStoreDatabaseException ex2) {
+                    // Oh well, we tried.
+                }
+            } catch (IOException ex2) {
+                giveUp(ex2);
+            }
+            
+        } finally {
+            try {
+                con.sendToClient();
+
+                if (logger.isDebugEnabled()) {
+                    if (Status.OK == overallStatus) {
+                        logger.debug(
+                            "RequestHandler: " + overallStatus + // NOI18N
+                            ", numReplies=" + numReplies); // NOI18N
+                        
+                    } else {
+                        logger.debug(
+                            "RequestHandler: " + overallStatus); // NOI18N
+                    }
+                }
+            } catch (IOException ex) {
+                giveUp(ex);
+            } catch (FOStoreDatabaseException ex) {
+                giveUp(ex);
+            }
+        }
+    }
+
+    /**
+     * Invoke this when all attempts at communicating errors to the client
+     * have failed.  Hope that someone is watching the console!
+     */
+    private static void giveUp(Throwable ex) {
+        System.err.println(msg.msg("ERR_SendToClient", ex)); // NOI18N
+        // XXX Should log this, not just print.
+        ex.printStackTrace(System.err);
+    }
+    
+    /**
+    * Subclasses implement this to take care of individiual requests.
+    * @return A RequestFinisher or null.  If null, then no further work is
+    * required on behalf of this request.  If a RequestFinisher is returned,
+    * then it is added to a list, and after all requests have been processed,
+    * the finishers in the list have their finish() method invoked on them.
+    * Finishers are invoked in the same order as the requests were.
+    */
+    abstract RequestFinisher handleRequest()
+        throws IOException, FOStoreDatabaseException;
+}
+
+

Added: incubator/jdo/trunk/fostore20/src/java/org/apache/jdo/impl/fostore/RequestId.java
URL: http://svn.apache.org/viewcvs/incubator/jdo/trunk/fostore20/src/java/org/apache/jdo/impl/fostore/RequestId.java?rev=171355&view=auto
==============================================================================
--- incubator/jdo/trunk/fostore20/src/java/org/apache/jdo/impl/fostore/RequestId.java (added)
+++ incubator/jdo/trunk/fostore20/src/java/org/apache/jdo/impl/fostore/RequestId.java Sun May 22 11:40:13 2005
@@ -0,0 +1,120 @@
+/*
+ * 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.util.HashMap;
+
+import javax.jdo.JDOFatalUserException;
+
+import org.apache.jdo.util.I18NHelper;
+
+
+/**
+* Represents a simple id associated with a request.  This is used, for
+* example, to pair up requests and replies: when a Request is written to the
+* store, it is put in a map keyed by RequestId, and when replies are received
+* from the store, they contain a RequestId; this is used to find the
+* corresponding Request which then handles the reply data.
+*
+* @author Dave Bristor
+*/
+class RequestId {
+    // RequestId's are per-pmf; this maps from FOStorePMF to IdFactory.
+    private static HashMap idFactoryTable = new HashMap();
+
+    // The representation of a RequestId.  We use a Long so that we can
+    // support them as keys in Maps.  Note also the need for hashCode() and
+    // equals() for the same reason.
+    private final Long id;
+
+    // When the server is having catastrophic problems, it should use this as
+    // a request id in the reply it creates.
+    public static final RequestId FAILURE = new RequestId(-1L);
+
+    /** I18N support. */
+    private static final I18NHelper msg = I18NHelper.getInstance(I18N.NAME);
+
+    private RequestId(long id) {
+        this.id = new Long(id);
+    }
+
+    /**
+    * Create a new RequestId by reading it's representation from the input.
+    * @param in DataInput from which representation is read.
+    */
+    RequestId(DataInput in) throws IOException {
+        this.id = new Long(in.readLong());
+    }
+
+    /**
+    * Writes its representation to the output stream.
+    * @param out DataOutput stream to which RequestId's representation is
+    * written.
+    */
+    void write(DataOutput out) throws IOException {
+        out.writeLong(id.longValue());
+    }
+
+    // This is how you allocate yourself a RequestId.  Allocates an id from
+    // the factory corresponding to the given PMF.
+    //
+    synchronized static RequestId allocate(FOStorePMF pmf) {
+
+        // IdFactory is in charge of allocating the instances of the
+        // representation of a RequestId.
+        class IdFactory {
+            // Using this might make debug output easier to read:
+            //private long lastAllocated = 0;
+            private long lastAllocated = Long.MIN_VALUE;
+            
+            long allocate() {
+                if (lastAllocated == Long.MAX_VALUE) {
+                    throw new FOStoreFatalInternalException(
+                        RequestId.class, "allocate", // NOI18N
+                        msg.msg("ERR_Overflow", new Long(Long.MAX_VALUE))); // NOI18N
+                }
+                return lastAllocated++;
+            }
+        }
+        
+        IdFactory f = (IdFactory)idFactoryTable.get(pmf);
+        if (null == f) {
+            f = new IdFactory();
+            idFactoryTable.put(pmf, f);
+        }
+        return new RequestId(f.allocate());
+    }
+
+    /**
+    * Returns true if the other id is equal to this one.
+    * @param other RequestId to which this one is compared.
+    */
+    public boolean equals(Object other) {
+        return id.longValue() == ((RequestId)other).id.longValue();
+    }
+
+    public int hashCode() {
+        return id.hashCode();
+    }
+
+    public String toString() {
+        return "ReqId=" + id.toString(); // NOI18N
+    }
+}

Added: incubator/jdo/trunk/fostore20/src/java/org/apache/jdo/impl/fostore/RequestType.java
URL: http://svn.apache.org/viewcvs/incubator/jdo/trunk/fostore20/src/java/org/apache/jdo/impl/fostore/RequestType.java?rev=171355&view=auto
==============================================================================
--- incubator/jdo/trunk/fostore20/src/java/org/apache/jdo/impl/fostore/RequestType.java (added)
+++ incubator/jdo/trunk/fostore20/src/java/org/apache/jdo/impl/fostore/RequestType.java Sun May 22 11:40:13 2005
@@ -0,0 +1,146 @@
+/*
+ * 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.util.HashMap;
+
+/**
+* Represents a kind of Request.  Used to identify request types between
+* client and store; a smaller representation than a Request's
+* java.lang.Class.
+*
+* @author Dave Bristor
+*/
+class RequestType {
+    // We use an Integer so that we can support the requestHandlers HashTable. 
+    private final Integer id;
+
+    private static HashMap requestTypes = new HashMap();
+    private static HashMap requestHandlers = new HashMap();
+
+    private static HashMap debug = new HashMap(); // for debugging only
+
+    private static int nextType = 0;
+    static {
+        RequestType rt = null;
+
+        rt = new RequestType(nextType++, ActivateClassRequest.class, 
+                             ActivateClassHandler.factory);
+
+        rt = new RequestType(nextType++, InsertRequest.class, 
+                             InsertHandler.factory);
+
+        rt = new RequestType(nextType++, UpdateRequest.class, 
+                             UpdateHandler.factory);
+
+        rt = new RequestType(nextType++, VerifyRequest.class, 
+                             VerifyHandler.factory);
+
+        rt = new RequestType(nextType++, DeleteRequest.class, 
+                             DeleteHandler.factory);
+
+        rt = new RequestType(nextType++, FetchRequest.class, 
+                             FetchHandler.factory);
+
+        rt = new RequestType(nextType++, GetExtentRequest.class, 
+                             GetExtentHandler.factory);
+
+        rt = new RequestType(nextType++, GetInstancesRequest.class, 
+                             GetInstancesHandler.factory);
+
+        rt = new RequestType(nextType++, GetClassRequest.class, 
+                             GetClassHandler.factory);
+        
+        rt = new RequestType(nextType++, CommitRequest.class, 
+                             CommitHandler.factory);
+
+        rt = new RequestType(nextType++, RollbackRequest.class, 
+                             RollbackHandler.factory);
+
+        rt = new RequestType(nextType++, CreateOIDRequest.class, 
+                             CreateOIDHandler.factory);
+        
+        rt = new RequestType(nextType++, LoginRequest.class,
+                            LoginHandler.factory);
+        
+        rt = new RequestType(nextType++, BeginTxRequest.class,
+                            BeginTxHandler.factory);
+
+        rt = new RequestType(nextType++, DumpRequest.class,
+                            DumpHandler.factory);
+    }
+
+    RequestType(int id, Class requestClass,
+                RequestHandler.HandlerFactory factory) {
+        this.id = new Integer(id);
+        if (null != requestTypes.put(requestClass, this)) {
+            throw new FOStoreFatalInternalException(
+                this.getClass(), "constructor", // NOI18N
+                "duplicate requestClass entry"); // NOI18N
+        }
+
+        if (null != requestHandlers.put(this, factory)) {
+            throw new FOStoreFatalInternalException(
+                this.getClass(), "constructor", // NOI18N
+                "duplicate factory entry"); // NOI18N
+        }
+
+        debug.put(this.id, requestClass);
+    }
+
+    RequestType(DataInput in) throws IOException {
+        this.id = new Integer(in.readInt());
+    }
+
+    void write(DataOutput out) throws IOException {
+        out.writeInt(id.intValue());
+    }
+
+    /**
+    * @return The RequestType corresponding to the given class.
+    */
+    static RequestType get(Class cls) {
+        return (RequestType)requestTypes.get(cls);
+    }
+    
+    public int hashCode() {
+        return id.hashCode();
+    }
+    
+    public boolean equals (Object other) {
+        return this.id.intValue() == ((RequestType)other).id.intValue();
+    }
+
+    /**
+    * @return The factory that can create a RequestHandler corresponding to
+    * our request type.
+    */
+    RequestHandler.HandlerFactory getHandlerFactory() {
+       return (RequestHandler.HandlerFactory)requestHandlers.get(this);
+    }
+
+    public String toString() {
+        String name = ((Class)(debug.get(id))).getName();
+        name = name.substring(name.lastIndexOf('.')+1);
+        return "ReqType=" + id.toString() + " " + // NOI18N
+            "(" + name + ")"; // NOI18N
+    }
+}
+    

Added: incubator/jdo/trunk/fostore20/src/java/org/apache/jdo/impl/fostore/RollbackHandler.java
URL: http://svn.apache.org/viewcvs/incubator/jdo/trunk/fostore20/src/java/org/apache/jdo/impl/fostore/RollbackHandler.java?rev=171355&view=auto
==============================================================================
--- incubator/jdo/trunk/fostore20/src/java/org/apache/jdo/impl/fostore/RollbackHandler.java (added)
+++ incubator/jdo/trunk/fostore20/src/java/org/apache/jdo/impl/fostore/RollbackHandler.java Sun May 22 11:40:13 2005
@@ -0,0 +1,76 @@
+/*
+ * 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 org.apache.jdo.util.I18NHelper;
+
+
+/**
+* Process Rollback requests.
+*
+* @author Dave Bristor
+*/
+// This is server-side code.  It does not need to live in the client.
+class RollbackHandler extends RequestHandler {
+    /** I18N support. */
+    private static final I18NHelper msg = I18NHelper.getInstance(I18N.NAME);
+
+    private RollbackHandler(Reply reply, int length,
+                         FOStoreServerConnection con) {
+
+        super(reply, length, con);
+    }
+    
+    public static final HandlerFactory factory =
+        new HandlerFactory() {
+                public RequestHandler getHandler(Reply reply, int length,
+                                             FOStoreServerConnection con) {
+                return new RollbackHandler(reply, length, con);
+            }};
+
+    /**
+    * Causes RequestHandler.handleRequests to not run finishers when a
+    * RollbackRequest has been found in a message.  There's no reason to run
+    * them in such a case, as the rollback will undo their effects anyway.
+    */
+    protected boolean getOkToFinish() {
+        return false;
+    }
+
+
+    RequestFinisher handleRequest()
+        throws IOException, FOStoreDatabaseException {
+
+        if (logger.isDebugEnabled()) {
+            logger.debug("RollbackHandler.hR"); // NOI18N
+        }
+        
+        try {
+            con.rollback();
+        } catch (FOStoreDatabaseException ex) {
+            throw new FOStoreFatalInternalException(
+                this.getClass(), "handleRequest", // NOI18N
+                msg.msg("ERR_RollbackFailed", ex)); // NOI18N
+        }
+
+        con.setOkToReleaseDatabase(true);
+        reply.setStatus(Status.ROLLBACK);
+        return null;
+    }
+}

Added: incubator/jdo/trunk/fostore20/src/java/org/apache/jdo/impl/fostore/RollbackRequest.java
URL: http://svn.apache.org/viewcvs/incubator/jdo/trunk/fostore20/src/java/org/apache/jdo/impl/fostore/RollbackRequest.java?rev=171355&view=auto
==============================================================================
--- incubator/jdo/trunk/fostore20/src/java/org/apache/jdo/impl/fostore/RollbackRequest.java (added)
+++ incubator/jdo/trunk/fostore20/src/java/org/apache/jdo/impl/fostore/RollbackRequest.java Sun May 22 11:40:13 2005
@@ -0,0 +1,71 @@
+/*
+ * 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.IOException;
+
+/**
+* Represents a request to cause operations since the previous Commit or
+* Rollback request to rollback.
+*
+* @author Dave Bristor
+*/
+//
+// This is client-side code.  It does not need to live in the server.
+//
+class RollbackRequest extends AbstractRequest {
+    RollbackRequest(Message m, FOStorePMF pmf) {
+        super(m, pmf);
+    }
+
+    //
+    // Methods from AbstractRequest
+    //
+
+    /**
+     * Provides the information ecessary for a RollbackRequest.
+     * The format of this request is (aside from the request header):
+     * <pre>
+     * empty: that's right, there's nothing here; the request's existence
+     * is alone enough to cause a rollback.
+     * </pre>
+     */
+    protected void doRequestBody() throws IOException {
+        if (logger.isDebugEnabled()) {
+            logger.debug("RollbackRequest.dRB"); // NOI18N
+        }
+    }
+
+    //
+    // Methods from Request
+    //
+
+    /**
+     * This should never be executed.  A RollbackRequest causes the reply
+     * data's header's Status value to indicate that the user requested a
+     * rollback, and when the reply handler sees that value, it does not
+     * invoke handleReply on any replies.
+     */
+    public void handleReply(Status status, DataInput in, int length)
+        throws IOException {
+
+        if (logger.isDebugEnabled()) {
+            logger.debug("RollbackRequest.hR"); // NOI18N
+        }
+    }
+}

Added: incubator/jdo/trunk/fostore20/src/java/org/apache/jdo/impl/fostore/ShortTranscriber.java
URL: http://svn.apache.org/viewcvs/incubator/jdo/trunk/fostore20/src/java/org/apache/jdo/impl/fostore/ShortTranscriber.java?rev=171355&view=auto
==============================================================================
--- incubator/jdo/trunk/fostore20/src/java/org/apache/jdo/impl/fostore/ShortTranscriber.java (added)
+++ incubator/jdo/trunk/fostore20/src/java/org/apache/jdo/impl/fostore/ShortTranscriber.java Sun May 22 11:40:13 2005
@@ -0,0 +1,48 @@
+/*
+ * 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;
+
+/**
+* Transcribes short values.
+*
+* @author Dave Bristor
+*/
+class ShortTranscriber extends FOStoreTranscriber {
+    private static ShortTranscriber instance = new ShortTranscriber();
+
+    private ShortTranscriber() {}
+
+    static ShortTranscriber getInstance() {
+        return instance;
+    }
+    
+    void storeShort(short value, DataOutput out) throws IOException {
+        out.writeShort(value);
+    }
+
+    short fetchShort(DataInput in) throws IOException {
+        return in.readShort();
+    }
+
+    void skip(DataInput in) throws IOException { 
+        in.readShort(); 
+    }
+}

Added: incubator/jdo/trunk/fostore20/src/java/org/apache/jdo/impl/fostore/Status.java
URL: http://svn.apache.org/viewcvs/incubator/jdo/trunk/fostore20/src/java/org/apache/jdo/impl/fostore/Status.java?rev=171355&view=auto
==============================================================================
--- incubator/jdo/trunk/fostore20/src/java/org/apache/jdo/impl/fostore/Status.java (added)
+++ incubator/jdo/trunk/fostore20/src/java/org/apache/jdo/impl/fostore/Status.java Sun May 22 11:40:13 2005
@@ -0,0 +1,134 @@
+/*
+ * 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 org.apache.jdo.util.I18NHelper;
+
+
+/**
+* Represents a quick summary of the result of processing a request.
+*
+* @author Dave Bristor
+*/
+class Status {
+    /** I18N support. */
+    private static final I18NHelper msg = I18NHelper.getInstance(I18N.NAME);
+
+    /** Request is being processed.  Should be used only by Reply's
+     * constructor, to write a value which will be overwritten once the
+     * requests's actual status is known.  */
+    private static final Status UNKNOWN = new Status(-1);
+
+    /** Request was successfully processed. */
+    static final Status OK       = new Status(0);
+
+    /** Request succeeded, but with warnings. */
+    static final Status WARN     = new Status(1);
+
+    /** Request failed. */
+    static final Status ERROR    = new Status(2);
+
+    /** Request failed fatally in server. */
+    static final Status FATAL    = new Status(3);
+
+    /** Database rolled back due to user request. */
+    static final Status ROLLBACK = new Status(4);
+
+    /** Could not login to database. */
+    static final Status LOGIN = new Status(5);
+
+    /** Optimistic failure. Verify, delete, or duplicate oid. */
+    static final Status OPTIMISTIC = new Status(6);
+    
+    /** Minimum status value. */
+    private static final int MIN_STATUS = -1;
+
+    /** Maximum status value. */
+    private static final int MAX_STATUS = 6;
+
+    /** Status value. */
+    private final int status;
+
+    /**
+    * Used to create the public static final elements.
+    */
+    private Status(int status) {
+        this.status = status;
+    }
+
+    /**
+     * Used to initialize a reply
+     */
+    static void initialize(Reply reply) throws IOException {
+        UNKNOWN.write(reply);
+    }
+
+    /**
+    * Used to 'reconstitute' a Status value from a DataInput.
+    */
+    Status(DataInput in) throws IOException {
+        this.status = in.readInt();
+        if (status < MIN_STATUS || status > MAX_STATUS){
+            throw new FOStoreFatalInternalException(
+                this.getClass(), "constructor(DataInput)", // NOI18N
+                msg.msg("ERR_OutOfRange", new Integer(status), // NOI18N
+                        new Integer(MIN_STATUS), new Integer(MAX_STATUS)));
+        }
+    }
+
+    /**
+    * Used to externalize a Status value.
+    */
+    void write(DataOutput out) throws IOException {
+        out.writeInt(status);
+    }
+
+    /**
+    * Returns length of a Status value's representation in bytes.
+    */
+    int getLength() {
+        return 4;
+    }
+
+    /**
+    * Compares this to another Status.
+    */
+    public boolean equals(Object other) {
+        return this.status == ((Status)other).status;
+    }
+
+    public String toString() {
+        String rc = msg.msg("MSG_Invalid"); // NOI18N
+        switch (status) {
+          case -1: rc = msg.msg("MSG_Unknown"); break; // NOI18N
+          case 0: rc = msg.msg("MSG_Ok"); break; // NOI18N
+          case 1: rc = msg.msg("MSG_Warn"); break; // NOI18N
+          case 2: rc = msg.msg("MSG_Error"); break; // NOI18N
+          case 3: rc = msg.msg("MSG_Fatal"); break; // NOI18N
+          case 4: rc = msg.msg("MSG_Rollback"); break; // NOI18N
+          case 5: rc = msg.msg("MSG_Login"); break; // NOI18N
+          case 6: rc = msg.msg("MSG_Optimistic"); break; // NOI18N
+          default: break;
+        }
+        return rc;
+    }
+}
+    

Added: incubator/jdo/trunk/fostore20/src/java/org/apache/jdo/impl/fostore/SubclassSet.java
URL: http://svn.apache.org/viewcvs/incubator/jdo/trunk/fostore20/src/java/org/apache/jdo/impl/fostore/SubclassSet.java?rev=171355&view=auto
==============================================================================
--- incubator/jdo/trunk/fostore20/src/java/org/apache/jdo/impl/fostore/SubclassSet.java (added)
+++ incubator/jdo/trunk/fostore20/src/java/org/apache/jdo/impl/fostore/SubclassSet.java Sun May 22 11:40:13 2005
@@ -0,0 +1,115 @@
+/*
+ * 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.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.InputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.HashSet;
+import java.util.Iterator;
+
+import org.netbeans.mdr.persistence.Streamable;
+import org.netbeans.mdr.persistence.StorageException;
+import org.netbeans.mdr.persistence.StorageIOException;
+
+/**
+* Represents a set of CLIDs of subclasses of a given class.
+* <p>
+* This class is <code>public</code> so that it can be used as a
+* <code>Streamable</code> and stored in the database.
+*
+* @author Dave Bristor
+*/
+public class SubclassSet implements Streamable {
+    /** The oid of this list of subclasses. */
+    private OID oid;
+
+    /** List of the clids which represent classes that are subclasses of the
+    * class indicated by the CLID in our oid. */
+    private HashSet clids;
+
+    /**
+    * Given clid is the first entry in the list.
+    */
+    private SubclassSet(OID oid, CLID clid) {
+        this.oid = oid;
+        clids = new HashSet();
+        clids.add(clid);
+    }
+
+    static SubclassSet create(OID oid, CLID clid) {
+        return new SubclassSet(oid, clid);
+    }
+
+    /**
+    * Add the given clid to the list.
+    */
+    void add(CLID clid) {
+        clids.add(clid);
+    }
+
+    /**
+    * @return Iterator over the CLID's in this subclass list.
+    */
+    Iterator iterator() {
+        return clids.iterator();
+    }        
+
+    //
+    // Implement Streamable
+    //
+
+    public SubclassSet() { }
+
+    /**
+    * Write this SubclassSet to the given stream.
+    */
+    public void write(OutputStream os) throws StorageException {
+        DataOutputStream dos = new DataOutputStream(os);
+
+        try {
+            oid.write(dos);
+            int size = clids.size();
+            dos.writeInt(size);
+            for (Iterator i = clids.iterator(); i.hasNext();) {
+                CLID clid = (CLID)i.next();
+                clid.write(dos);
+            }
+        } catch (IOException ex) {
+            throw new StorageIOException(ex);
+        }
+    }
+
+    /**
+    * Initialize this SubclassSet from the given stream.
+    */
+    public void read(InputStream is) throws StorageException {
+        DataInputStream dis = new DataInputStream(is);
+        try {
+            this.oid = OID.read(dis);
+            int size = dis.readInt();
+            clids = new HashSet(size);
+            for (int i = 0; i < size; i++) {
+                clids.add(CLID.read(dis));
+            }
+        } catch (IOException ex) {
+            throw new StorageIOException(ex);
+        }
+    }
+}

Added: incubator/jdo/trunk/fostore20/src/java/org/apache/jdo/impl/fostore/Tester.java
URL: http://svn.apache.org/viewcvs/incubator/jdo/trunk/fostore20/src/java/org/apache/jdo/impl/fostore/Tester.java?rev=171355&view=auto
==============================================================================
--- incubator/jdo/trunk/fostore20/src/java/org/apache/jdo/impl/fostore/Tester.java (added)
+++ incubator/jdo/trunk/fostore20/src/java/org/apache/jdo/impl/fostore/Tester.java Sun May 22 11:40:13 2005
@@ -0,0 +1,132 @@
+/*
+ * 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.text.NumberFormat;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+* Provides a relatively convient way to print debug messages.
+*
+* @author Dave Bristor
+*/
+class Tester {
+    static final Log logger = LogFactory.getFactory().getInstance(
+        "org.apache.jdo.impl.fostore"); // NOI18N
+
+    private static boolean threadPrinting = 
+        Boolean.getBoolean("org.apache.jdo.impl.fostore.threadprinting");
+   
+    private static final NumberFormat nf;
+    static { 
+        nf = NumberFormat.getInstance();
+        nf.setMinimumIntegerDigits(4);
+        nf.setMaximumIntegerDigits(4);
+        nf.setMaximumFractionDigits(0);
+        nf.setGroupingUsed(false);
+    }
+
+    // This method checks that TIME is enabled, unlike the other methods.
+    // Reason: it doesn't take an arguments, and therefore the runtime of
+    // calling this is very small (no String args to construct as w/ print
+    // methods).
+    static Object startTime() {
+        Timer timer = new Timer();
+        timer.start();
+        return timer;
+    }
+
+    static void printTime(Object o, String msg) {
+        if (null != o && o instanceof Timer) {
+            Timer timer = (Timer)o;
+            timer.println(msg);
+        }
+    }
+
+    static String toHex(long n, int len) {
+        String rc = Long.toHexString(n);
+        StringBuffer zeroes = new StringBuffer("0000000000000000"); // NOI18N
+        int length = rc.length();
+        if (length > len) {
+            rc = rc.substring(length - len);
+        } else if (len > length) {
+            rc = zeroes.substring(0, len - length) + rc;
+        }
+        return rc;
+    }
+
+    static void dump(String label, byte data[], int length) {
+        dump(label, data, 0, length);
+        
+    }
+    
+    static void dump(String label, byte data[], int offset, int length) {
+        // Determine number of lines to print
+        final int bytesPerLine = 16;
+        int lines = length / bytesPerLine;
+        if (0 != length % bytesPerLine) {
+            lines++;
+        }
+        int line = 0; // Address at start of line
+
+        int addr = offset;
+        int max = offset + length;
+
+        logger.trace("dumping " + length + " bytes"); // NOI18N
+        
+        for (int i = 0; i < lines; i++) {
+            StringBuffer buf = new StringBuffer();
+            
+            if (threadPrinting) {
+                buf.append(Thread.currentThread().toString() + ": "); // NOI18N
+            }
+            buf.append(label + " " + nf.format((long)line) + ": "); // NOI18N
+            line += bytesPerLine;
+            for (int j = 0; j < bytesPerLine; j++) {
+                if (addr >= max) {
+                    break;
+                } else {
+                    buf.append(toHex((long)data[addr], 2) + " "); // NOI18N
+                }
+                addr++;
+            }
+            logger.trace(buf.toString());
+        }
+    }
+
+    static void dump(String label, FOStoreOutput out, int offset, int length) {
+        byte data[] = out.getBuf();
+        dump(label, data, offset, length);
+    }
+
+    static class Timer {
+        long start;
+    
+        public void start() {
+            start = System.currentTimeMillis();
+        }
+    
+        public void println(String msg) {
+            long end;
+            end = System.currentTimeMillis();
+            System.out.println(msg + ": " + (end - start)); // NOI18N
+            start = end;
+        }
+    }
+}

Added: incubator/jdo/trunk/fostore20/src/java/org/apache/jdo/impl/fostore/UpdateHandler.java
URL: http://svn.apache.org/viewcvs/incubator/jdo/trunk/fostore20/src/java/org/apache/jdo/impl/fostore/UpdateHandler.java?rev=171355&view=auto
==============================================================================
--- incubator/jdo/trunk/fostore20/src/java/org/apache/jdo/impl/fostore/UpdateHandler.java (added)
+++ incubator/jdo/trunk/fostore20/src/java/org/apache/jdo/impl/fostore/UpdateHandler.java Sun May 22 11:40:13 2005
@@ -0,0 +1,211 @@
+/*
+ * 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.util.Arrays;
+
+import javax.jdo.JDOUserException;
+
+import org.apache.jdo.util.I18NHelper;
+
+
+/**
+* Process requests to update instances in the datastore.
+*
+* @author Dave Bristor
+*/
+//
+// This is server-side code.  It does not need to live in the client.
+//
+class UpdateHandler extends InsertHandler {
+    /** I18N Support */
+    // Note that in this file we're using keys with the "EXC" prefix, but
+    // outside the context of throwing an exception: this is because the
+    // client side will take these messages and put them into exceptions.  See
+    // ReplyHandler.handleReplies.
+    private static final I18NHelper msg = I18NHelper.getInstance(I18N.NAME);
+
+    /** Construct a new handler for processing updates.
+     * @param reply the Reply.
+     * @param length the length of data in the stream.
+     * @param con the connection to use.
+     */    
+    protected UpdateHandler(Reply reply, int length,
+                          FOStoreServerConnection con) {
+
+        super(reply, length, con);
+    }
+
+    static final HandlerFactory factory =
+        new HandlerFactory() {
+            public RequestHandler getHandler(Reply reply, int length,
+                                             FOStoreServerConnection con) {
+                return new UpdateHandler(reply, length, con);
+            }};
+
+    RequestFinisher handleRequest()
+        throws IOException, FOStoreDatabaseException {
+
+        RequestFinisher rc = null;
+        FOStoreInput in = con.getInputFromClient();
+
+        // True if verify() returns true or datastore transaction.
+        boolean okToUpdate = true;
+
+        // True if verify throws an exception.  In that case, okToUpdate
+        // *will* be false.
+        boolean verifyException = true;
+
+        if (logger.isDebugEnabled()) {
+            logger.debug("UpdateHandler.hR: begin"); // NOI18N
+        }
+
+        OID oid = null;
+        try {
+            boolean optimistic = in.readBoolean();
+            if (optimistic) {
+                oid = OID.read(in);
+                Block block = readBlock(in);
+                okToUpdate = verify(oid, true, block);
+            }
+            verifyException = false;
+        } catch(DoesNotExistException ex) {
+            okToUpdate = false;
+            reply.writeOID(oid);
+            reply.setStatus(
+                Status.OPTIMISTIC, 
+                msg.msg("EXC_OptimisticDoesNotExist", oid)); // NOI18N
+        } catch(Exception ex) {
+            okToUpdate = false;
+            reply.writeOID(oid);
+            reply.setStatus(
+                Status.ERROR,
+                msg.msg("ERR_VerifyException", oid), ex); // NOI18N
+        }
+
+        int length = in.readInt();
+        if (okToUpdate) {
+            // datastore image exists and matches before image
+            rc = super.handleRequest();
+        } else {
+            in.skipBytes(length);
+            if ( ! verifyException) {
+                reply.writeOID(oid);
+                // mismatch between before image and datastore image
+                // status has not yet been set by exception handling
+                reply.setStatus(
+                    Status.OPTIMISTIC,
+                    msg.msg("EXC_OptimisticVerifyFailed", oid)); // NOI18N
+            }
+        }
+
+        if (logger.isDebugEnabled()) {
+            logger.debug("UpdateHandler.hR: end"); // NOI18N
+        }
+
+        return rc;
+    }
+
+    /** Verify before image versus database state of an object.
+     * @return true if verify succeeds as per <code>verifyFields</code>.
+     * @param oid OID of object to be verified
+     * @param verifyFields If true, verify that values of the data
+     * corresponding to <code>oid</code> in the database match those in the
+     * given block; if false verify that object corresponding to
+     * <code>oid</code> exists.
+     * @param block Block of data for value verification; may be null if
+     * <code>verifyFields</code> is false.
+     * @throws IOException if stream errors.
+     * @throws FOStoreDatabaseException if any errors in the database.
+     * @throws DoesNotExistException if the object to be updated does not exist.
+     */
+    protected boolean verify(OID oid, boolean verifyFields, Block block)
+        throws IOException, FOStoreDatabaseException, DoesNotExistException {
+
+        if (logger.isDebugEnabled()) {
+            logger.debug("UpdateHandler.verify for: " + oid + // NOI18N
+                           ", verifyFields=" + verifyFields); // NOI18N
+        }
+
+        boolean rc = false;
+        
+        FOStoreDatabase fodb = con.getDatabase();
+        Block current = (Block)fodb.getIfExists(oid);
+
+        rc = (null != current);
+
+        if (verifyFields) {
+            byte blockData[] = block.getData();
+
+            if (logger.isTraceEnabled()) {
+                Tester.dump("BLK", blockData, blockData.length); // NOI18N
+            }
+
+            // Reset to false so that if we get an error, client won't try to
+            // set status.
+            rc = false; 
+
+            if (null == current) {
+                throw new DoesNotExistException();
+            } else {
+                byte currentData[] = current.getData();
+                if (logger.isTraceEnabled()) {
+                    logger.debug(
+                        "UpdateHandler.verify current data:"); // NOI18N
+                    Tester.dump("CUR", currentData, currentData.length); // NOI18N
+                }
+
+                // XXX TBD Compare and report based on field values
+                rc = Arrays.equals(blockData, currentData);
+            }
+        }
+
+        if (logger.isDebugEnabled()) {
+            logger.debug("UpdateHandler.verify returns: " + rc); // NOI18N
+        }
+
+        return rc;
+    }
+
+    /**
+     * Thrown by {@link UpdateHandler#verify} if an object which should exist
+     * in the database, does not.
+     */
+    private class DoesNotExistException extends Exception { }
+
+    /** Replace a block in the database.
+     * @see InsertHandler#updateDB
+     * @param provOID provisional OID.
+     * @param realOID OID to use as key in the database.
+     * @param block Block to be inserted in database.
+     * @param db Database into which block is replaced.
+     * @throws IOException for stream errors.
+     * @throws FOStoreDatabaseException for any database error except for object not found.
+     */
+    protected void updateDB(OID realOID, OID provOID, Block block,
+                            FOStoreDatabase db)
+        throws IOException, FOStoreDatabaseException {
+
+        if (logger.isDebugEnabled()) {
+            logger.debug("UpdateHandler.updateDb: " + realOID); // NOI18N
+        }
+        db.replace(realOID, block);
+    }
+}

Added: incubator/jdo/trunk/fostore20/src/java/org/apache/jdo/impl/fostore/UpdateRequest.java
URL: http://svn.apache.org/viewcvs/incubator/jdo/trunk/fostore20/src/java/org/apache/jdo/impl/fostore/UpdateRequest.java?rev=171355&view=auto
==============================================================================
--- incubator/jdo/trunk/fostore20/src/java/org/apache/jdo/impl/fostore/UpdateRequest.java (added)
+++ incubator/jdo/trunk/fostore20/src/java/org/apache/jdo/impl/fostore/UpdateRequest.java Sun May 22 11:40:13 2005
@@ -0,0 +1,144 @@
+/*
+ * 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.IOException;
+import java.util.BitSet;
+
+import javax.jdo.PersistenceManager;
+import javax.jdo.Transaction;
+
+import org.apache.jdo.model.jdo.PersistenceModifier;
+import org.apache.jdo.state.StateManagerInternal;
+import org.apache.jdo.util.I18NHelper;
+
+
+
+/**
+ * Represents a request to change one or more fields of a persistent object in
+ * the store.
+ *
+ * @author Dave Bristor
+ */
+//
+// This is client-side code.  It does not need to live in the server.
+//
+class UpdateRequest extends InsertRequest {
+    /** Fields which are to be updated. */
+    private final BitSet loadedFields;
+
+    /** Fields which are to be verified in database, in case of
+     * optimistic transaction. */
+    private final BitSet dirtyFields;
+
+    /** If true, update happens in context of an optimistic transaction. */
+    private final boolean optimistic;
+
+    UpdateRequest(StateManagerInternal sm, Message m, FOStorePMF pmf,
+                  BitSet loadedFields, BitSet dirtyFields, boolean optimistic) {
+
+        super(sm, m, pmf);
+        this.loadedFields = loadedFields;
+        this.dirtyFields = dirtyFields;
+        this.optimistic = optimistic;
+    }
+
+    //
+    // Methods from AbstractRequest
+    //
+
+    /**
+     * Provides the information necessary to do an UpdateRequest.
+     * The format of this request is (aside from the request header):
+     * <pre>
+     * optimistic: boolean
+     * oid: OID
+     * data block
+     * length: int
+     * InsertRequest's doRequestBody - written data
+     * </pre>
+     * The oid and data block are written only if optimistic is true.
+     * @see AbstractRequest#doRequestBody
+     */
+    protected void doRequestBody() throws IOException {
+        OID oid = (OID)sm.getInternalObjectId();
+        if (oid.isProvisional()) {
+            throw new FOStoreFatalInternalException(
+                this.getClass(), "doRequestBody", // NOI18N
+                msg.msg("ERR_OidIsProvisional", oid)); // NOI18N
+        }
+
+        if (logger.isDebugEnabled()) {
+            logger.debug("UpdateRequest.dRB: begin: " + optimistic); // NOI18N
+        }
+
+        out.writeBoolean(optimistic);
+
+        if (optimistic) {
+            oid.write(out);
+            // XXX For now, verify the values of all the fields.
+            writeBlock(jdoClass.getPersistentFieldNumbers(), true);
+        }
+
+        // Save space to write the length of the data written by
+        // InsertRequest.  The reason is, that on the Handler side, if we're
+        // running an optimistic transaction and the verify fails, we just
+        // want to skip over InsertRequest's bytes.
+        //
+        int lengthPos = out.getPos();
+        out.writeInt(LENGTH_COOKIE);
+        int startPos = out.getPos();
+
+        super.doRequestBody();
+
+        int currentPos = out.getPos();
+        int length = currentPos - startPos;
+        if (logger.isDebugEnabled()) {
+            logger.debug("UpdateRequest.dRB: length=" + length); // NOI18N
+        }
+        out.setPos(lengthPos);
+        out.writeInt(length);
+        out.setPos(currentPos);
+
+        if (logger.isDebugEnabled()) {
+            logger.debug("UpdateRequest.dRB: end"); // NOI18N
+        }
+    }
+
+    //
+    // Methods from Request
+    //
+
+    /**
+     * Handles reply data from an UpdateReply.
+     * The format of this reply is
+     * <pre>
+     * oid: OID
+     * </pre>
+     */
+    public void handleReply(Status status, DataInput in, int length)
+        throws IOException {
+
+        OID replyOid = OID.read(in);
+
+        if (logger.isDebugEnabled()) {
+            logger.debug("UpdateRequest.hR: " + replyOid + // NOI18N
+                ", " + status); // NOI18N
+        }
+    }
+}

Added: incubator/jdo/trunk/fostore20/src/java/org/apache/jdo/impl/fostore/VerifyHandler.java
URL: http://svn.apache.org/viewcvs/incubator/jdo/trunk/fostore20/src/java/org/apache/jdo/impl/fostore/VerifyHandler.java?rev=171355&view=auto
==============================================================================
--- incubator/jdo/trunk/fostore20/src/java/org/apache/jdo/impl/fostore/VerifyHandler.java (added)
+++ incubator/jdo/trunk/fostore20/src/java/org/apache/jdo/impl/fostore/VerifyHandler.java Sun May 22 11:40:13 2005
@@ -0,0 +1,97 @@
+/*
+ * 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.util.Arrays;
+
+import javax.jdo.JDOUserException;
+
+import org.apache.jdo.util.I18NHelper;
+
+
+/**
+* Process requests to verify instances in the datastore.
+*
+* @author Dave Bristor
+*/
+//
+// This is server-side code.  It does not need to live in the client.
+//
+class VerifyHandler extends UpdateHandler {
+    /** I18N Support */
+    // Note that in this file we're using keys with the "EXC" prefix, but
+    // outside the context of throwing an exception: this is because the
+    // client side will take these messages and put them into exceptions.  See
+    // ReplyHandler.handleReplies.
+    private static final I18NHelper msg = I18NHelper.getInstance(I18N.NAME);
+
+    private VerifyHandler(Reply reply, int length,
+                          FOStoreServerConnection con) {
+
+        super(reply, length, con);
+    }
+
+    static final HandlerFactory factory =
+        new HandlerFactory() {
+            public RequestHandler getHandler(Reply reply, int length,
+                                             FOStoreServerConnection con) {
+                return new VerifyHandler(reply, length, con);
+            }};
+
+    /**
+     * Verify that instance exists and/or its values match those in the
+     * client.
+     */
+    RequestFinisher handleRequest()
+        throws IOException, FOStoreDatabaseException {
+
+        FOStoreInput in = con.getInputFromClient();
+
+        if (logger.isDebugEnabled()) {
+            logger.debug("VerifyHandler.hR: begin"); // NOI18N
+        }
+
+        boolean rc = false;
+
+        OID oid = OID.read(in);
+        boolean verifyFields = in.readBoolean();
+
+        try {
+            if (verifyFields) {
+                Block block = readBlock(in);
+                rc = verify(oid, true, block);
+            } else {
+                rc = verify(oid, false, null);
+            }
+            reply.writeBoolean(rc);
+            reply.setStatus(Status.OK);
+        } catch (Exception ex) {
+            reply.writeBoolean(false);
+            reply.setStatus(Status.ERROR,
+                            msg.msg("ERR_VerifyException", oid, ex)); // NOI18N
+        }
+
+        if (logger.isDebugEnabled()) {
+            logger.debug("VerifyHandler.hR: end, rc=" + rc); // NOI18N
+        }
+
+        return null;
+    }
+}