Added: incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/fostore/GetExtentHandler.java URL: http://svn.apache.org/viewcvs/incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/fostore/GetExtentHandler.java?view=auto&rev=158176 ============================================================================== --- incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/fostore/GetExtentHandler.java (added) +++ incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/fostore/GetExtentHandler.java Fri Mar 18 17:02:29 2005 @@ -0,0 +1,254 @@ +/* + * 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.HashSet; +import java.util.Iterator; + +/** +* Process GetExtent requests. +* +* @author Dave Bristor +*/ +// This is server-side code. It does not need to live in the client. +class GetExtentHandler extends RequestHandler { + + private GetExtentHandler(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 GetExtentHandler(reply, length, con); + }}; + + /** + * The desired extent's class can be specified either by CLID (i.e., the + * class's CLID was already loaded from store to the client) or classname + * and FSUID (in case the CLID wasn't known). In the latter case, look + * up the CLID. + * <br> + * With the CLID, get the set of extents: it will be only one if + * subclasses is false, an aribtrary number otherwise. + * <br> + * Iterate over the extents, writing the items into the reply. + * @see RequestHandler#handleRequest + * @see GetExtentRequest#doRequestBody + */ + RequestFinisher handleRequest() + throws IOException, FOStoreDatabaseException { + + FOStoreInput in = con.getInputFromClient(); + FOStoreDatabase db = con.getDatabase(); + HashSet dbExtents = null; + + // true => resulting extent to include subclasses of indicated class. + boolean subclasses; + + int maxInstances = in.readInt(); + + // By CLID or by classname/fsuid? + boolean isCLID = in.readBoolean(); + + CLID clid = null; + if (isCLID) { + clid = CLID.read(in); + subclasses = in.readBoolean(); + + } else { + // Find the clid for the given name/fsuid. + + String name = in.readUTF(); + FOStoreSchemaUID fsuid = FOStoreSchemaUID.read(in); + subclasses = in.readBoolean(); + + if (logger.isDebugEnabled()) { + logger.debug( + "GetExtentHandler for name=" + name); // NOI18N + } + + for (Iterator i = db.getDBInfo().getDBClasses(); i.hasNext();) { + DBClass dbClass = (DBClass)i.next(); + if (fsuid.equals(dbClass.getFSUID()) && + name.equals(dbClass.getName())) { + clid = dbClass.getCLID(); + // break because there can be only one match. + break; + } + } + } + + // Get the set of CLIDs of extents + if (null != clid) { + dbExtents = getDBExtents(clid, subclasses, db); + } + + if (logger.isDebugEnabled()) { + if (null == dbExtents) { + logger.debug("GetExtentHandler.hr: no extents"); // NOI18N + } else { + for (Iterator i = dbExtents.iterator(); i.hasNext();) { + logger.debug( + "GetExtentHandler.hr: extent=" + i.next()); // NOI18N + } + } + } + + Status status = null; + int extentSize = 0; + int numInstances = 0; + int numOIDs = 0; + + // Write instances into the reply + if (null == dbExtents) { + reply.writeInt(0); + reply.writeInt(0); + reply.writeInt(0); + status = Status.OK; + + } else { + // Save space for writing various counts in the reply. + int extentSizePos = reply.beginStash(); + int numInstancesPos = reply.beginStash(); + int numOIDsPos = reply.beginStash(); + + for (Iterator i = dbExtents.iterator(); i.hasNext();) { + DBExtent extent = (DBExtent)i.next(); + + if (null != extent && extent.size() > 0) { + + // Fetch some of the instances from the store. + // If we fail to get an object, continue, but set status + // to WARN. + for (Iterator j = extent.iterator(); j.hasNext();) { + OID oid = (OID)j.next(); + try { + reply.writeOID(oid); + if (logger.isDebugEnabled()) { + logger.debug( + "GetExtentHandler.hR/2: " + // NOI18N + oid + " " + // NOI18N + Tester.toHex(oid.oid, 16)); + } + + if (numInstances < maxInstances) { + Block block = (Block)db.get(oid); + if (logger.isTraceEnabled()) { + block.dump(); + } + + byte data[] = block.getData(); + reply.writeInt(data.length); + reply.write(data); + numInstances++; + } else { + numOIDs++; + } + extentSize++; + } catch (FOStoreDatabaseException ex) { + status = Status.WARN; + } + } + } + } + + // Write counts. + reply.endStash(extentSize, extentSizePos); + reply.endStash(numInstances, numInstancesPos); + reply.endStash(numOIDs, numOIDsPos); + + if (null == status) { + status = Status.OK; + } + } + reply.setStatus(status); + + if (logger.isDebugEnabled()) { + logger.debug( + "GetExtentHandler.hR/3: " + // NOI18N + "extentSize=" + extentSize + // NOI18N + ", numInstances=" + numInstances + // NOI18N + ", numOIDs=" + numOIDs + " " + status); // NOI18N + } + return null; + } + + /** + * Get a set of CLIDs of extents for the given clid. The set will have at + * most one element if subclasses is false, or an arbitrary number + * otherwise. + * @param clid The CLID of the class whose extent is wanted. + * @param subclasses If true, then include extents for all subclasses of + * the class corresponding to CLID. + * @param db Database in which to find extents. + * @return Set of extents of clid (and possibly its subclasses). + * @throws FOStoreDatabaseException if a database error occurs. + */ + HashSet getDBExtents(CLID clid, boolean subclasses, FOStoreDatabase db) + throws FOStoreDatabaseException { + + HashSet rc = null; + + if (logger.isDebugEnabled()) { + logger.debug( + "GetExtentHandler.getExtents: for " + clid); // NOI18N + } + OID oid = db.getDBInfo().getExtentOID(clid); + DBExtent extent = (DBExtent)db.getIfExists(oid); + if (null != extent) { + rc = new HashSet(); + rc.add(extent); + } + + if (logger.isDebugEnabled()) { + logger.debug( + "GetExtentHandler.getExtents: got " + extent); // NOI18N + } + + if (subclasses) { + OID ssOID = DBInfo.getSubclassSetOID(clid); + SubclassSet ss = (SubclassSet)db.getIfExists(ssOID); + if (null != ss) { + for (Iterator i = ss.iterator(); i.hasNext();) { + CLID subCLID = (CLID)i.next(); + OID subExtentOID = DBInfo.getExtentOID(subCLID); + DBExtent subExtent = + (DBExtent)db.getIfExists(subExtentOID); + if (null != subExtent) { + if (logger.isDebugEnabled()) { + logger.debug( + "GetExtentHandler.getExtents: "+ // NOI18N + "subclass " + subExtent); // NOI18N + } + if (null == rc) { + rc = new HashSet(); + } + rc.add(subExtent); + } + } + } + } + return rc; + } +}
Added: incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/fostore/GetExtentRequest.java URL: http://svn.apache.org/viewcvs/incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/fostore/GetExtentRequest.java?view=auto&rev=158176 ============================================================================== --- incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/fostore/GetExtentRequest.java (added) +++ incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/fostore/GetExtentRequest.java Fri Mar 18 17:02:29 2005 @@ -0,0 +1,263 @@ +/* + * 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.security.AccessController; +import java.security.PrivilegedAction; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Iterator; +import java.util.NoSuchElementException; + +import javax.jdo.Extent; +import javax.jdo.JDOHelper; +import javax.jdo.JDOUserException; +import javax.jdo.PersistenceManager; + +import org.apache.jdo.pm.PersistenceManagerInternal; +import org.apache.jdo.state.StateManagerInternal; + + +/** + * Represents a request to get the extent of a class and possibly its + * subclasses. + * + * @author Dave Bristor + */ +// +// This is client-side code. It does not need to live in the server. +// +class GetExtentRequest extends AbstractRequest { + /** The PersistenceManagerInternal that is making this request. */ + private final PersistenceManagerInternal pm; + + /** The class of the extent sought by this request. */ + private final Class cls; + + /** If true retrieve instances of cls and its subclasses, otherwise just + * of cls. */ + private final boolean subclasses; + + /** Maximum number of instances that should be returned at a time. */ + private static final int maxInstances; + + /** Extent returned to user. */ + private FOStoreExtent extent; + + /** List of returned instances */ + private ArrayList instances; + + /** List of returned object Ids */ + private ArrayList oids; + + static { + int max = 100; + try { + String property = (String)AccessController.doPrivileged( + new PrivilegedAction () { + public Object run () { + return System.getProperty("maxInstances"); // NOI18N + } + } + ); + max = Integer.parseInt(property); + } catch (SecurityException ex) { + // cannot read maxInstances flag => log warning + if (logger.isWarnEnabled()) + logger.warn(msg.msg("MSG_CannotGetSystemProperty", //NOI18N + "maxInstances", ex.toString())); //NOI18N + } catch (Exception ex) { + // use default + } + maxInstances = max; + } + + GetExtentRequest(FOStoreExtent extent, Class cls, boolean subclasses, Message m, + PersistenceManagerInternal pm) { + + super(m, (FOStorePMF)pm.getPersistenceManagerFactory()); + this.pm = pm; + this.cls = cls; + this.subclasses = subclasses; + this.extent = extent; + } + + /** + * @see AbstractRequest#doRequestBody + * @see InsertRequest#doRequestBody + */ + protected void doRequestBody() throws IOException { + // + // The format of this request is variable, depending on whether or not + // we already have in-client metadata for the given class. + // + // If we do have in-client metadata for the given class: + // + // int: maximum number of instances to return + // boolean: true + // clid of the class + // boolean: true if subclasses, false otherwise + // + // Otherwise: + // + // int: maximum number of instances to return + // boolean: flase + // String name of the class + // FOStoreSchemaUID of the class. + // boolean: true if subclasses, false otherwise + // + + FOStoreModel model = pmf.getModel(); + CLID clid = model.getCLID(cls); + out.writeInt(maxInstances); + if (null != clid && ! clid.isProvisional()) { + out.writeBoolean(true); + clid.write(out); + if (logger.isDebugEnabled()) { + logger.debug("GetExtentRequest.dRB: " + clid // NOI18N + + " subclasses=" + subclasses); // NOI18N + } + } else { + out.writeBoolean(false); + out.writeUTF(cls.getName()); + FOStoreSchemaUID fsuid = FOStoreSchemaUID.lookup(cls, model); + fsuid.write(out); + if (logger.isDebugEnabled()) { + logger.debug( + "GetExtentRequest.dRB: " + cls.getName() // NOI18N + + " subclasses=" + subclasses); // NOI18N + } + } + out.writeBoolean(subclasses); + } + + /** + * Handles replies to GetExtentRequests. + * The format of the reply is: + * <pre> + * int: count of number of instances in the extent + * int: count of the number of instances returned + * int: count of the number of oids returned + * that many instances + * </pre> + * The number of instances returned + number of oid returned = number + * of instances in the extent. This is for performance: we don't + * want to return *all* the instances at once: we return some, plus + * information so that we can get the rest, if the user requests so. + * <p> + * The status might be Status.WARN, in which case there were OID's in + * the database extent which were unreadable. But the count should + * be for the actual number of objects returned. + */ + public void handleReply(Status status, DataInput in, int length) + throws IOException { + + + int extentSize = in.readInt(); + int numInstances = in.readInt(); + int numOIDs = in.readInt(); + if (logger.isDebugEnabled()) { + logger.debug( + "GER.hR/1: extentSize=" + extentSize + // NOI18N + ", numInstances=" + numInstances + // NOI18N + ", numOIDs=" + numOIDs); // NOI18N + } + + // Get instances for the extent + instances = + readInstances(in, numInstances, pmf.getModel(), pm, cls); + + // Add OIDs for the extent + oids = new ArrayList(numOIDs); + for (int i = 0; i < numOIDs; i++) { + OID oid = OID.read(in); + if (logger.isDebugEnabled()) { + logger.debug("GER.hR/4: " + oid + // NOI18N + " " + Tester.toHex(oid.oid, 16)); // NOI18N + } + oids.add(oid); + } + } + + + /** + * Reads instances from given DataInput using a [EMAIL PROTECTED] FieldFetcher}. + * @param in DataInput from which instances are read. + * @param numInstances Number of instances to read from <code>in</code>. + * @param model Model required to by [EMAIL PROTECTED] FieldFetcher}. + * @param pm PersistenceManagerInternal required [EMAIL PROTECTED] FieldFetcher}. + * @param cls Candidate Class for which instances are being obtained. + * @return ArrayList of instances read. + */ + static ArrayList readInstances(DataInput in, int numInstances, + FOStoreModel model, + PersistenceManagerInternal pm, + Class cls) + throws IOException { + + ArrayList rc = new ArrayList(numInstances); + for (int i = 0; i < numInstances; i++) { + OID oid = OID.read(in); + if (logger.isDebugEnabled()) { + logger.debug("GER.readInstances/1: " + oid + // NOI18N + " " + Tester.toHex(oid.oid, 16)); // NOI18N + } + + // Fill in pc's fields. + int objLength = in.readInt(); + if (logger.isDebugEnabled()) { + logger.debug( + "GER.readInstances/2: reading " + objLength + // NOI18N + " bytes"); // NOI18N + } + byte data[] = new byte[objLength]; + in.readFully(data); + FieldFetcher ff = + new FieldFetcher(new FOStoreInput(data, 0, objLength), + model, + pm, + cls.getClassLoader()); + StateManagerInternal sm = ff.fetch(oid); + Object pc = sm.getObject(); + rc.add(pc); + } + return rc; + } + + /** Returns max number of instances */ + int getMaxInstances() { + return maxInstances; + } + + /** Returns the list of instances */ + ArrayList getInstances() { + return instances; + } + + /** Returns the list of object id's */ + ArrayList getOIDs() { + return oids; + } + + /** Returns the Extent associated with this request */ + Extent getExtent() { + return extent; + } + +} Added: incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/fostore/GetInstancesHandler.java URL: http://svn.apache.org/viewcvs/incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/fostore/GetInstancesHandler.java?view=auto&rev=158176 ============================================================================== --- incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/fostore/GetInstancesHandler.java (added) +++ incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/fostore/GetInstancesHandler.java Fri Mar 18 17:02:29 2005 @@ -0,0 +1,101 @@ +/* + * 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.HashSet; +import java.util.Iterator; + +/** +* Process GetInstances requests. +* +* @author Dave Bristor +*/ +// This is server-side code. It does not need to live in the client. +class GetInstancesHandler extends RequestHandler { + + private GetInstancesHandler(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 GetInstancesHandler(reply, length, con); + }}; + + /** + * Get some instances from the database, and return them. + */ + RequestFinisher handleRequest() + throws IOException, FOStoreDatabaseException { + + FOStoreInput in = con.getInputFromClient(); + FOStoreDatabase db = con.getDatabase(); + + int numInstances = in.readInt(); + + Status status = null; + + int numReturned = 0; + int numReturnedPos = reply.beginStash(); + + for (int i = 0; i < numInstances; i++) { + try { + OID oid = OID.read(in); + reply.writeOID(oid); + if (logger.isDebugEnabled()) { + logger.debug( + "GetInstancesHandler.hR/2: " + // NOI18N + oid + " " + // NOI18N + Tester.toHex(oid.oid, 16)); + } + + Block block = (Block)db.get(oid); + if (logger.isTraceEnabled()) { + block.dump(); + } + + byte data[] = block.getData(); + reply.writeInt(data.length); + reply.write(data); + numReturned++; + } catch (FOStoreDatabaseException ex) { + status = Status.WARN; + } + } + + reply.endStash(numReturned, numReturnedPos); + + if (null == status) { + status = Status.OK; + } + reply.setStatus(status); + + if (logger.isDebugEnabled()) { + logger.debug( + "GetInstancesHandler.hR/3: " + // NOI18N + "numReturned=" + numReturned + ": " +status); // NOI18N + } + return null; + } +} Added: incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/fostore/GetInstancesRequest.java URL: http://svn.apache.org/viewcvs/incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/fostore/GetInstancesRequest.java?view=auto&rev=158176 ============================================================================== --- incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/fostore/GetInstancesRequest.java (added) +++ incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/fostore/GetInstancesRequest.java Fri Mar 18 17:02:29 2005 @@ -0,0 +1,128 @@ +/* + * 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 org.apache.jdo.pm.PersistenceManagerInternal; + + +/** + * Represents a request to fetch a batch of instances. + * + * @author Dave Bristor + */ +// +// This is client-side code. It does not need to live in the server. +// +class GetInstancesRequest extends AbstractRequest { + /** List of oids from which we get instances. */ + private final ArrayList oids; + + /** Starting position in oids ArrayList. */ + private final int start; + + /** Number of instances to get. */ + private final int numInstances; + + /** The PersistenceManagerInternal that is making this request. */ + private final PersistenceManagerInternal pm; + + /** Candidate class for which instances are being obtained. */ + private final Class cls; + + /** ArrayList returned to user. */ + private ArrayList instances; + + GetInstancesRequest(ArrayList oids, int start, int numInstances, + Message m, PersistenceManagerInternal pm, Class cls) { + + super(m, (FOStorePMF)pm.getPersistenceManagerFactory()); + this.oids = oids; + this.start = start; + this.numInstances = numInstances; + this.pm = pm; + this.cls = cls; + + if (logger.isDebugEnabled()) { + logger.debug("GetInstancesRequest: " + // NOI18N + "start=" + start + // NOI18N + ", numInstances=" + numInstances); // NOI18N + } + } + + ArrayList getInstances() { + return instances; + } + + // + // Methods from AbstractRequest + // + + protected void doRequestBody() throws IOException { + // + // The format of this request is (aside from the request header): + // + // int: numOIDs + // oid: OID... + // + + int numRequested = numInstances; + if (start + numInstances > oids.size()) { + numRequested = oids.size() - start; + } + + out.writeInt(numRequested); + + if (logger.isDebugEnabled()) { + logger.debug( + "GetInstancesRequest.dRB/0: max=" + numRequested); // NOI18N + } + + int end = start + numRequested; + for (int i = start; i < end; i++) { + OID oid = (OID)oids.get(i); + oid.write(out); + } + } + + + // + // Methods from Request + // + + public void handleReply(Status status, DataInput in, int length) + throws IOException { + + // + // The format of this reply is + // + // int: number of instances + // that many instances + // + + int numReturned = in.readInt(); + if (logger.isDebugEnabled()) { + logger.debug("GetInstancesRequest.hR: numReturned=" + // NOI18N + numReturned); + } + instances = GetExtentRequest.readInstances( + in, numReturned, pmf.getModel(), pm, cls); + } +} Added: incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/fostore/I18N.java URL: http://svn.apache.org/viewcvs/incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/fostore/I18N.java?view=auto&rev=158176 ============================================================================== --- incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/fostore/I18N.java (added) +++ incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/fostore/I18N.java Fri Mar 18 17:02:29 2005 @@ -0,0 +1,27 @@ +/* + * 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; + +/** +* Provides I18N information for FOStore. +* +* @author Dave Bristor +*/ +class I18N { + /** Name of the resource bundle for FOStore. */ + static final String NAME = "org.apache.jdo.impl.fostore.Bundle"; // NOI18N +} Added: incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/fostore/InsertHandler.java URL: http://svn.apache.org/viewcvs/incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/fostore/InsertHandler.java?view=auto&rev=158176 ============================================================================== --- incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/fostore/InsertHandler.java (added) +++ incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/fostore/InsertHandler.java Fri Mar 18 17:02:29 2005 @@ -0,0 +1,324 @@ +/* + * 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 javax.jdo.JDOFatalDataStoreException; + +import org.apache.jdo.util.I18NHelper; + +/** +* Process requests to insert objects in the datastore. +* +* @author Dave Bristor +*/ +// +// This is server-side code. It does not need to live in the client. +// +class InsertHandler extends RequestHandler { + + /** I18N help */ + private static final I18NHelper msg = I18NHelper.getInstance(I18N.NAME); + + InsertHandler(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 InsertHandler(reply, length, con); + }}; + + + /** + * Changes all provisional OID's in an instance's in-store Block to real, + * datastore OID's. + */ + private class InsertFinisher implements RequestFinisher { + private final FOStoreDatabase db; + + // This is the oid of the object in which we're going to replace the + // provisional OID with a real OID. + private final OID oid; + + // Offsets into the Block mapped by OID at which provisional OID's + // lie. Replace them with datastore OID's. + private final int oidOffsets[]; + + // Offsets into the Block's CLID appendix at which provisional CLID's + // lie. Replace them with datastore CLID's. + private final int clidOffsets[]; + + InsertFinisher(FOStoreDatabase db, OID oid, DataInput in) + throws IOException { + this.db = db; + this.oid = oid; + + // Read oidOffsets + int numOIDOffsets = in.readInt(); + if (logger.isDebugEnabled()) { + logger.debug("InsertFinisher: numOIDOffsets=" + // NOI18N + numOIDOffsets); + } + + this.oidOffsets = numOIDOffsets > 0 ? new int[numOIDOffsets] : null; + if (numOIDOffsets > 0) { + for (int i = 0; i < numOIDOffsets; i++) { + oidOffsets[i] = in.readInt(); + } + } + + // Read CLID offsets + int numCLIDOffsets = in.readInt(); + if (logger.isDebugEnabled()) { + logger.debug("InsertFinisher: numCLIDOffsets=" + // NOI18N + numCLIDOffsets); + } + this.clidOffsets = numCLIDOffsets > 0 ? new int[numCLIDOffsets] : null; + if (numCLIDOffsets > 0) { + for (int i = 0; i < numCLIDOffsets; i++) { + clidOffsets[i] = in.readInt(); + } + } + } + + /** + * Replace all provisional OIDs in the data with real OIDs. + * Byte array data is the datablock containing provisional OID's which + * need to be converted in-place. + */ + private void finishOIDOffsets(byte data[]) + throws FOStoreDatabaseException, IOException { + + FOStoreInput in = + new FOStoreInput(data, 0, data.length); + FOStoreOutput out = new FOStoreOutput(); + + if (null != oidOffsets) { + int numOIDOffsets = oidOffsets.length; + for (int i = 0; i < numOIDOffsets; i++) { + int offset = oidOffsets[i]; + in.setPos(offset); + OID provOID = OID.read(in); + OID realOID = db.getRealOIDFromProvisional(provOID); + + if (logger.isDebugEnabled()) { + logger.debug("InsertFinisher.finish: i=" + i // NOI18N + + " offset="+ offset); // NOI18N + logger.debug("InsertFinisher.finish: prov" + provOID + + " " // NOI18N + + Tester.toHex(provOID.oid, 16)); + logger.debug("InsertFinisher.finish: real" + realOID + + " " // NOI18N + + Tester.toHex(realOID.oid, 16)); + } + + // Write the real OID + // XXX PERF Best way to convert a long to byte array? + // XXX Conversion should be done by OID itself, since only + // it really knows it's representation is a long. + realOID.write(out); + System.arraycopy(out.getBuf(), 0, data, offset, 8); + out.setPos(0); // Set for next time through. + } + } + } + + /** + * Replace all provisional CLIDs in the data with real CLIDs. + * Byte array data is the datablock containing provisional CLID's + * which need to be converted in-place. + */ + private void finishCLIDOffsets(byte data[]) + throws FOStoreDatabaseException, IOException { + + FOStoreInput in = + new FOStoreInput(data, 0, data.length); + FOStoreOutput out = new FOStoreOutput(); + + if (null != clidOffsets) { + int numCLIDOffsets = clidOffsets.length; + for (int i = 0; i < numCLIDOffsets; i++) { + int offset = clidOffsets[i]; + in.setPos(offset); + CLID provCLID = CLID.read(in); + CLID realCLID = db.getRealCLIDFromProvisional(provCLID); + + if (logger.isDebugEnabled()) { + logger.debug("InsertFinisher.finish: i=" + i // NOI18N + + " offset="+ offset); // NOI18N + logger.debug("InsertFinisher.finish: prov" + provCLID); // NOI18N + logger.debug("InsertFinisher.finish: real" + realCLID); // NOI18N + } + + if (null == realCLID) { + throw new JDOFatalDataStoreException( + msg.msg("ERR_NullRealCLID", provCLID)); // NOI18N + } + + // Write the real CLID + // XXX PERF Best way to convert an int to byte array? + // XXX Conversion should be done by CLID itself, since only + // it really knows its representation is an int. + realCLID.write(out); + System.arraycopy(out.getBuf(), 0, data, offset, 4); + out.setPos(0); + } + } + } + + /** + * @see RequestFinisher#finish + */ + public void finish() { + if (logger.isDebugEnabled()) { + logger.debug("InsertFinisher.finish: " + oid); // NOI18N + } + + if (null != oidOffsets || null != clidOffsets) { + try { + Block block = (Block)db.get(oid); + byte data[] = block.getData(); + + if (logger.isDebugEnabled()) { + logger.debug("InsertFinisher.finish: block length=" + // NOI18N + data.length); + } + + // Replace each provisional OID with a datastore OID. + finishOIDOffsets(data); + + // Replace each provisional CLID with a datastore CLID. + finishCLIDOffsets(data); + + if (logger.isTraceEnabled()) { + logger.trace("InsertFinisher: after finalizing"); // NOI18N + block.dump(); + } + + } catch (FOStoreDatabaseException ex) { + throw new FOStoreFatalInternalException( + this.getClass(), "finish", ex); // NOI18N + } catch (IOException ex) { + throw new FOStoreFatalIOException( + this.getClass(), "finish", ex); // NOI18N + } + } + } + } + + + /** + * @see RequestHandler#handleRequest + */ + RequestFinisher handleRequest() + throws IOException, FOStoreDatabaseException { + + RequestFinisher rc = null; + FOStoreInput in = con.getInputFromClient(); + FOStoreDatabase db = con.getDatabase(); + DBInfo dbInfo = db.getDBInfo(); + + OID oid = OID.read(in); + CLID clid = oid.getCLID(); + if (logger.isDebugEnabled()) { + logger.debug("InsertHandler.hR/1: given" + oid // NOI18N + + " " + Tester.toHex(oid.oid, 16)); // NOI18N + } + + if (clid.isProvisional()) { + clid = db.getRealCLIDFromProvisional(clid); + } + + OID realOID; + if (oid.isProvisional()) { + realOID = db.getRealOIDFromProvisional(oid); + if (null == realOID) { + realOID = dbInfo.newInstanceOID(clid); + } + } else { + realOID = oid; + } + + if (logger.isDebugEnabled()) { + logger.debug("InsertHandler.hR/2: real" + realOID + " " // NOI18N + + Tester.toHex(realOID.oid, 16)); + } + Block block = readBlock(in); + + rc = new InsertFinisher(db, realOID, in); + + try { + updateDB(realOID, oid, block, db); + + reply.writeOID(realOID); + reply.setStatus(Status.OK); + + } catch (Exception ex) { + reply.writeOID(realOID); + reply.setStatus(Status.ERROR, + msg.msg("ERR_InsertException", realOID)); // NOI18N + } + + return rc; + } + + protected Block readBlock(FOStoreInput in) throws IOException { + int blockLength = in.readInt(); + Block block = FOStoreDatabase.createBlock(in, blockLength); + + if (logger.isDebugEnabled()) { + logger.debug("InsertHandler.readBlock: blockLength="+ // NOI18N + blockLength); + if (logger.isTraceEnabled()) { + block.dump(); + } + } + return block; + } + + /** + * Add the block to the database, and to the database's extent. + * @param realOID OID to use as key in the database. + * @param givenOID by which object was inserted, possibly provisional. + * @param block Block to be inserted in database. + * @param db Database into which block is inserted. + */ + protected void updateDB(OID realOID, OID givenOID, Block block, + FOStoreDatabase db) + throws IOException, FOStoreDatabaseException { + + db.add(realOID, block); + if (givenOID.isProvisional()) { + db.mapProvisionalOIDToReal(givenOID, realOID); + } + + // Add instance to its extent + DBInfo dbInfo = db.getDBInfo(); + OID extentOID = dbInfo.getExtentOID(realOID.getCLID()); + DBExtent dbExtent = (DBExtent)db.get(extentOID); + dbExtent.add(realOID); + con.addExtent(dbExtent); + } +} Added: incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/fostore/InsertRequest.java URL: http://svn.apache.org/viewcvs/incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/fostore/InsertRequest.java?view=auto&rev=158176 ============================================================================== --- incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/fostore/InsertRequest.java (added) +++ incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/fostore/InsertRequest.java Fri Mar 18 17:02:29 2005 @@ -0,0 +1,537 @@ +/* + * 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.lang.reflect.Array; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +import org.apache.jdo.model.jdo.PersistenceModifier; +import org.apache.jdo.pm.PersistenceManagerInternal; +import org.apache.jdo.sco.SCOCollection; +import org.apache.jdo.sco.SCOMap; +import org.apache.jdo.state.FieldManager; +import org.apache.jdo.state.StateManagerInternal; + +/** + * Represents a request to write a new object to the datastore. + * + * @author Dave Bristor + * @version 1.0.1 + */ +// +// This is client-side code. It does not need to live in the server. +// +class InsertRequest extends AbstractFieldRequest { + // Provisional object id of object being inserted by this request. + private final OID oid; + + // Metadata for this object. + private final FOStoreModel model; + + private final Class cls; + + // Set of CLID's of the object instances referenced by this object's + // fields (note that Strings are omitted). This is sent to the store + // along with the object values, as an appendix, so that when + // reconstituting the object, information about the referenced classes is + // available. Of course, metadata for the object is available (it was or + // will be sent to the store in an ActivateClassRequest), but that field + // information is about the type of referenced objects, not about their + // class. See storeObjectField(). + private Set clids = null; + + // Set of int arrays of object references in this object which are + // provisional, as offsets of OID's in the request buffer. See the end + // of doRequestBody, and storeObjectField(). Each field may add one + // entry to the ArrayList; each field may have any number of offsets + // within it. totNumOffsets is the total number of offsets, across all + // arrays in the ArrayList. + private ArrayList oidOffsets = new ArrayList(); + private int numOidOffsets = 0; + + private int clidOffsets[]; + private int numClidOffsets; + + InsertRequest(StateManagerInternal sm, Message m, FOStorePMF pmf) { + super(sm, m, pmf); + this.oid = (OID)sm.getInternalObjectId(); + this.model = pmf.getModel(); + this.cls = sm.getPCClass(); + } + + // + // Methods from AbstractRequest + // + + /** + * Provides detail about data being inserted in an InsertRequest. + * The format of this request is (aside from the request header): + * <pre> + * oid: OID + * length of data block containing classname, field values, CLID info: int + * className: String (fully qualified name of class of object) + * fostoreSchemaUID for class + * length of the field values part of this block: int + * fieldValue: Object... (repeats numFields times) + * number of [CLID, classname] pairs for Object fields of this object + * [CLID, classname]... + * (the following are not in the above noted length bytes of this block) + * number of provisional OID's written in this request + * OID offset... + * number of the above written CLIDs that are provisional + * CLID offset... + * </pre> + * + * In the case of both the OID and CLID offsets, the offset is from + * the beginning of the block. In the case of the CLIDs and the OID + * and CLID offsets, the size of each set written may be zero but + * that size is always written. + * <p> + * Note that the number of CLID/classname pairs and the CLID/classname + * pairs themselves are part of the data block that is intended to be + * stored by the database server (and hence later returned upon + * fetch/getExtent). That is, the size written includes those pairs. + * The offsets, however, are not part of the datablock; they are used + * by the database server and discarded. + * @see AbstractRequest#doRequestBody + */ + protected void doRequestBody() throws IOException { + oid.write(out); + + // Write out the values of all the fields. + int startPos = writeBlock(jdoClass.getPersistentFieldNumbers(), false); + + // Now write the oidOffsets, so that the store can turn provisional + // OID's into datastore OID's. + if (logger.isDebugEnabled()) { + logger.debug("InsertRequest.dRB: numoidOffsets=" + // NOI18N + numOidOffsets); + } + out.writeInt(numOidOffsets); + int size = oidOffsets.size(); + for (int i = 0; i < size; i++) { + int fieldOffsets[] = (int[])(oidOffsets.get(i)); + int numOffsets = fieldOffsets.length; + for (int j = 0; j < numOffsets; j++) { + // Write out offset relative to start of the data block. + out.writeInt(fieldOffsets[j] - startPos); + if (logger.isDebugEnabled()) { + logger.debug( + "InsertRequest.dRB: " + oid // NOI18N + + " rawOffset=" + fieldOffsets[j] // NOI18N + + " offset=" + // NOI18N + (fieldOffsets[j] - startPos)); + } + } + } + + // Now write the offsets of the provisional CLID's + if (logger.isDebugEnabled()) { + logger.debug("InsertRequest.dRB: numClidOffsets=" + // NOI18N + numClidOffsets); + } + out.writeInt(numClidOffsets); + for (int i = 0; i < numClidOffsets; i++) { + out.writeInt(clidOffsets[i]); + } + } + + // + // Methods from Request + // + + /** + * Reads the oid, and notifies the persistence manager and state manager + * of the new oid. + * @see Request#handleReply + */ + public void handleReply(Status status, DataInput in, int length) + throws IOException { + + OID replyOid = OID.read(in); + + // We don't have to update the cache, because we will when the + // instance was made persistent, we did a CreateOIDRequest, and it's + // reply updated the cache. + + if (logger.isDebugEnabled()) { + logger.debug("InsertRequest.hR: " + status + ", " + // NOI18N + oid + " " + // NOI18N + Tester.toHex(oid.oid, 16) + + " -> " + replyOid + " " + // NOI18N + Tester.toHex(replyOid.oid, 16)); + } + } + + // + // Methods from FieldManager + // + + /** + * @see org.apache.jdo.state.FieldManager#storeBooleanField(int fieldNum, + * boolean value) + */ + public void storeBooleanField(int fieldNum, boolean value) { + FOStoreTranscriber t = model.getTranscriber(cls, fieldNum); + try { + t.storeBoolean(value, out); + } catch (IOException ex) { + throw new FOStoreFatalIOException( + this.getClass(), "storeBooleanField", ex); // NOI18N + } + } + + /** + * @see org.apache.jdo.state.FieldManager#storeCharField(int fieldNum, + * char value) + */ + public void storeCharField(int fieldNum, char value) { + FOStoreTranscriber t = model.getTranscriber(cls, fieldNum); + try { + t.storeChar(value, out); + } catch (IOException ex) { + throw new FOStoreFatalIOException( + this.getClass(), "storeCharField", ex); // NOI18N + } + } + + /** + * @see org.apache.jdo.state.FieldManager#storeByteField(int fieldNum, + * byte value) + */ + public void storeByteField(int fieldNum, byte value) { + FOStoreTranscriber t = model.getTranscriber(cls, fieldNum); + try { + t.storeByte(value, out); + } catch (IOException ex) { + throw new FOStoreFatalIOException( + this.getClass(), "storeByteField", ex); // NOI18N + } + } + + /** + * @see org.apache.jdo.state.FieldManager#storeShortField(int fieldNum, + * short value) + */ + public void storeShortField(int fieldNum, short value) { + FOStoreTranscriber t = model.getTranscriber(cls, fieldNum); + try { + t.storeShort(value, out); + } catch (IOException ex) { + throw new FOStoreFatalIOException( + this.getClass(), "storeShortField", ex); // NOI18N + } + } + + /** + * @see org.apache.jdo.state.FieldManager#storeIntField(int fieldNum, int value) + */ + public void storeIntField(int fieldNum, int value) { + FOStoreTranscriber t = model.getTranscriber(cls, fieldNum); + try { + t.storeInt(value, out); + } catch (IOException ex) { + throw new FOStoreFatalIOException( + this.getClass(), "storeIntField", ex); // NOI18N + } + } + + /** + * @see org.apache.jdo.state.FieldManager#storeLongField(int fieldNum, + * long value) + */ + public void storeLongField(int fieldNum, long value) { + FOStoreTranscriber t = model.getTranscriber(cls, fieldNum); + try { + t.storeLong(value, out); + } catch (IOException ex) { + throw new FOStoreFatalIOException( + this.getClass(), "storeLongField", ex); // NOI18N + } + } + + /** + * @see org.apache.jdo.state.FieldManager#storeFloatField(int fieldNum, + * float value) + */ + public void storeFloatField(int fieldNum, float value) { + FOStoreTranscriber t = model.getTranscriber(cls, fieldNum); + try { + t.storeFloat(value, out); + } catch (IOException ex) { + throw new FOStoreFatalIOException( + this.getClass(), "storeFloatField", ex); // NOI18N + } + } + + /** + * @see org.apache.jdo.state.FieldManager#storeDoubleField(int fieldNum, + * double value) + */ + public void storeDoubleField(int fieldNum, double value) { + FOStoreTranscriber t = model.getTranscriber(cls, fieldNum); + try { + t.storeDouble(value, out); + } catch (IOException ex) { + throw new FOStoreFatalIOException( + this.getClass(), "storeDoubleField", ex); // NOI18N + } + } + + /** + * @see org.apache.jdo.state.FieldManager#storeStringField(int fieldNum, + * String value) + */ + public void storeStringField(int fieldNum, String value) { + FOStoreTranscriber t = model.getTranscriber(cls, fieldNum); + try { + // Though it is a String, we need to preserve pm value + // in the ObjectTranscriber in case this is request + // to fetch a PC (embedded or because its field is accessed. + PersistenceManagerInternal pm = + (PersistenceManagerInternal)sm.getPersistenceManager(); + t.storeObject(value, out, pm); + } catch (IOException ex) { + throw new FOStoreFatalIOException( + this.getClass(), "storeStringField", ex); // NOI18N + } + } + + /** + * @see org.apache.jdo.state.FieldManager#storeObjectField(int fieldNum, + * Object value) + */ + public void storeObjectField(int fieldNum, Object value) { + FOStoreTranscriber t = model.getTranscriber(cls, fieldNum); + try { + PersistenceManagerInternal pm = + (PersistenceManagerInternal)sm.getPersistenceManager(); + int fieldOffsets[] = t.storeObject(value, out, pm); + + // Add the field's object's CLID. + addCLID(value); + + // If value is Collection, Map, or array then need to add the + // Class's of its elements. + if (null != value) { + if (value instanceof Collection) { + addCollectionCLIDs((Collection)value); + } else if (value instanceof Map) { + addMapCLIDs((Map)value); + } else { + Class cls = value.getClass().getComponentType(); + if (null != cls) { + // We have an array. We have to add the CLIDs for all + // its elements, not just for the component type, + // because the class of the elements could be a + // subclass or implementation of the component type. + addArrayCLIDs(value); + } + } + } + + // Check for and store offsets. + if (null != fieldOffsets) { + oidOffsets.add(fieldOffsets); + numOidOffsets += fieldOffsets.length; + if (logger.isDebugEnabled()) { + logger.debug( + "InsertRequest.sOF: fieldNum=" + fieldNum // NOI18N + + " numOffsets=" + fieldOffsets.length); // NOI18N + } + } + } catch (IOException ex) { + throw new FOStoreFatalIOException( + this.getClass(), "storeObjectField", ex); // NOI18N + } + } + + // + // Implementation detail + // + + protected OID getOID() { + return oid; + } + + /** + * Writes a data block, which consists of the values of the specified + * fields, plus the CLID's and corresponding class names of referenced + * objects. + * @param fields Field numbers of the fields to be written + * @param identifying If true, write before/flushed image fields, + * otherwise write current fields. + * @return Position in output stream at which data block starts + */ + protected int writeBlock(int fields[], boolean identifying) throws IOException { + // Save and overwrite the position where we later write the length of + // the data. + int dataLengthPos = out.getPos(); + out.writeInt(LENGTH_COOKIE); + + // Save start of data block + int startPos = out.getPos(); + + // Initialize list of CLIDs used by this instance + clids = new HashSet(); + + // Fully qualified class name and FOStoreSchemaUID + if (logger.isDebugEnabled()) { + logger.debug("IR: className=" + jdoClass.getName() + // NOI18N + ", fsuid=" + fsuid); // NOI18N + } + out.writeUTF(jdoClass.getName()); + fsuid.write(out); + + if (logger.isDebugEnabled()) { + logger.debug("InsertRequest.writeBlock: classname=" + // NOI18N + jdoClass.getName()); + } + + insertFields(fields, identifying); + + // Write the CLID's and classnames. Some might be provisional, so as + // with the OID's we must write their offsets so they can be turned + // into real CLID's in the store. Here we save the offsets, to write + // them later, after the data block. + int size = clids.size(); + clidOffsets = new int[size]; // Max num of possible provisionals + numClidOffsets = 0; + if (logger.isDebugEnabled()) { + logger.debug( + "InsertRequest.writeBlock: numCLID's=" + // NOI18N + size); + } + out.writeInt(size); + for (Iterator i = clids.iterator(); i.hasNext();) { + CLID c = (CLID)i.next(); + if (c.isProvisional()) { + clidOffsets[numClidOffsets++] = out.getPos() - startPos; + } + c.write(out); + Class clz = CLID.getKnownType(c); + if (null == clz) { + clz = model.getClass(c); + } + if (logger.isDebugEnabled()) { + logger.debug("InsertRequest.writeBlock: " + c); // NOI18N + } + String className = clz.getName(); + if (logger.isDebugEnabled()) { + logger.debug( + "InsertRequest.writeBlock: field type " + // NOI18N + className); + } + out.writeUTF(className); + FOStoreSchemaUID fldUID = FOStoreSchemaUID.lookup(clz, model); + fldUID.write(out); + } + + // Write the length of the block (data + CLID's) + int currentPos = out.getPos(); + int length = currentPos - startPos; + if (logger.isDebugEnabled()) { + logger.debug("InsertRequest.dRB: length=" + length); // NOI18N + } + out.setPos(dataLengthPos); + out.writeInt(length); + out.setPos(currentPos); + + return startPos; + } + + /** + * Writes values of the specified fields. If identifying is true, writes + * identifying fields, otherwise writes current fields. + */ + private void insertFields(int[] fields, boolean identifying) + throws IOException { + + int fieldLengthPos = out.getPos(); + out.writeInt(LENGTH_COOKIE); + int fieldPos = out.getPos(); + + sm.provideFields(fields, this, identifying); + + // Write the length of the field values part of the block. + int currentPos = out.getPos(); + int length = currentPos - fieldPos; + out.setPos(fieldLengthPos); + out.writeInt(length); + out.setPos(currentPos); + + if (logger.isDebugEnabled()) { + logger.debug("InsertRequest.dRB: field values length=" + length); // NOI18N + } + } + + private void addCollectionCLIDs(Collection c) { + Iterator i; + if (c instanceof SCOCollection) { + SCOCollection sco = (SCOCollection)c; + i = sco.eitherIterator(); + } else { + i = c.iterator(); + } + while(i.hasNext()) { + addCLID(i.next()); + } + } + + private void addMapCLIDs(Map m) { + Iterator i = null; + if (m instanceof SCOMap) { + i = ((SCOMap)m).eitherIterator(); + } else { + i = m.entrySet().iterator(); + } + while (i.hasNext()) { + Map.Entry entry = (Map.Entry)i.next(); + addCLID(entry.getKey()); + addCLID(entry.getValue()); + } + } + + private void addArrayCLIDs(Object arr) { + try { + int length = Array.getLength(arr); + for (int i = 0; i < length; i++) { + Object o = Array.get(arr, i); + addCLID(o); + } + } catch (RuntimeException ex) { + throw new FOStoreFatalInternalException( + this.getClass(), "addArrayCLIDs", ex); // NOI18N + } + } + + private void addCLID(Object o) { + if (null != o) { + Class cls = o.getClass(); + if (! CLID.isKnown(cls)) { + clids.add(model.getCLID(cls)); + } + } + } +} Added: incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/fostore/IntTranscriber.java URL: http://svn.apache.org/viewcvs/incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/fostore/IntTranscriber.java?view=auto&rev=158176 ============================================================================== --- incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/fostore/IntTranscriber.java (added) +++ incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/fostore/IntTranscriber.java Fri Mar 18 17:02:29 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 int values. +* +* @author Dave Bristor +*/ +class IntTranscriber extends FOStoreTranscriber { + private static IntTranscriber instance = new IntTranscriber(); + + private IntTranscriber() {} + + static IntTranscriber getInstance() { + return instance; + } + + void storeInt(int value, DataOutput out) throws IOException { + out.writeInt(value); + } + + int fetchInt(DataInput in) throws IOException { + return in.readInt(); + } + + void skip(DataInput in) throws IOException { + in.readInt(); + } +} Added: incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/fostore/LoginHandler.java URL: http://svn.apache.org/viewcvs/incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/fostore/LoginHandler.java?view=auto&rev=158176 ============================================================================== --- incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/fostore/LoginHandler.java (added) +++ incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/fostore/LoginHandler.java Fri Mar 18 17:02:29 2005 @@ -0,0 +1,109 @@ +/* + * 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. + */ + +/* + * Handler for LoginRequests. + * + * LoginHandler.java + * + * Created on June 7, 2001, 4:16 PM + */ + +package org.apache.jdo.impl.fostore; + +import java.io.IOException; +import java.io.DataInput; +import java.io.DataOutput; + +import javax.jdo.JDODataStoreException; +import javax.jdo.JDOFatalDataStoreException; + +import org.apache.jdo.util.I18NHelper; + + +/** + * Handler for LoginRequests. + * @author Craig Russell + * @version 1.0 + */ +class LoginHandler extends RequestHandler { + /** the database name + */ + String dbname; + + /** the user of the database + */ + String user; + + /** a pseudo random number + */ + long timestamp; + + /** a message digest which is a shared secret + */ + byte[] secret; + + /** a flag to tell whether to create the database + */ + boolean create; + + /** Construct an instance of the LoginHandler to service this request. + * @param reply the reply for the request + * @param length the length of the request + * @param con the FOStoreServerConnection with the request + */ + private LoginHandler(Reply reply, int length, + FOStoreServerConnection con) { + + super(reply, length, con); + } + + /** the factory used to create the handler for this request + */ + public static final HandlerFactory factory = + new HandlerFactory() { + public RequestHandler getHandler(Reply reply, int length, + FOStoreServerConnection con) { + return new LoginHandler(reply, length, con); + }}; + + /** Process the request by analyzing the database and user login information + * from the request buffer. This will create the database if needed, open + * it, and verify the user and password. + * + * @throws IOException if any problems + * @return null; there is no need for a finisher. + */ + RequestFinisher handleRequest() + throws IOException, FOStoreDatabaseException { + DataInput in = con.getInputFromClient(); + // read the utf encoded user and password for verification. + dbname = in.readUTF(); + user = in.readUTF(); + timestamp = in.readLong(); + int secretSize = in.readInt(); + secret = new byte[secretSize]; + in.readFully(secret); + create = in.readBoolean(); + try { + con.openDatabase(dbname, user, timestamp, secret, create); + } catch (Exception e) { + throw new FOStoreLoginException(dbname, user, e); + } + reply.setStatus(Status.OK); + return null; + } +} Added: incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/fostore/LoginRequest.java URL: http://svn.apache.org/viewcvs/incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/fostore/LoginRequest.java?view=auto&rev=158176 ============================================================================== --- incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/fostore/LoginRequest.java (added) +++ incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/fostore/LoginRequest.java Fri Mar 18 17:02:29 2005 @@ -0,0 +1,151 @@ +/* + * 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. + */ + +/* + * Request to login to a database. + * + * LoginRequest.java + * + * Created on June 7, 2001, 3:28 PM + */ + +package org.apache.jdo.impl.fostore; + +import java.io.IOException; +import java.io.DataInput; +import java.io.DataOutput; +import java.io.UnsupportedEncodingException; +import java.util.Date; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +import javax.jdo.JDODataStoreException; +import javax.jdo.JDOFatalInternalException; + +import org.apache.jdo.util.I18NHelper; + + +/** + * Request to login to a database. + * @author Craig Russell + * @version 1.0 + */ +class LoginRequest extends AbstractRequest { + /** The database name from the PMF URL property. + */ + String dbname; + + /** The user from the PMF user property. + */ + String user; + + /** The password from the PMF password property. + */ + String password; + + /** This is the Date.getTime() of the time the request + * was created. It is used to construct the shared secret + * to verify the password at the server side, without + * transmitting the password in clear. + */ + long timestamp; + + /** The secret constructed from the user, timestamp, and password. + */ + byte[] secret; + + /** A flag telling whether to create the database + */ + boolean create; + + /** Creates new LoginRequest + * @param m the Message + * @param pmf the PersistenceManagerFactory + * @param user the user + * @param password the password + */ + public LoginRequest(Message m, FOStorePMF pmf, String dbname, String user, String password, boolean create) { + super (m, pmf); + this.dbname = dbname; + this.user = user; + this.password = password; + this.create = create; + timestamp = new Date().getTime(); + MessageDigest md; + try { + md = MessageDigest.getInstance("MD5"); // NOI18N + md.update (dbname.getBytes("UTF-8")); // NOI18N + md.update (user.getBytes("UTF-8")); // NOI18N + md.update ((Long.toString(timestamp)).getBytes("UTF-8")); // NOI18N +// md.update (password.getBytes("UTF-8")); // XXX add back when database can look up password + this.secret = md.digest(); + } catch (NoSuchAlgorithmException nsae) { + throw new JDOFatalInternalException ( + msg.msg("ERR_NoSuchAlgorithmException", dbname, user), // NOI18N + new Exception[]{nsae}); + } catch (UnsupportedEncodingException uee) { + throw new JDOFatalInternalException ( + msg.msg("ERR_UnsupportedEncodingException", dbname), // NOI18N + new Exception[]{uee}); + } + } + + /** + * Subclasses must implement in this method the actual writing of their + * Request type-specific data. + * @throws IOException if any errors constructing the stream + */ + protected void doRequestBody() throws IOException { + // + // The format of this request is (aside from the request header): + // + // dbname: the database name property (part of the URL) of the PMF + // user: the user property of the PMF + // timestamp: the current Date in milliseconds + // XXX change the following to be a shared secret based on the password + // password: the password property of the PMF + // secret.length: the length of the secret + // secret: the secret as a byte array + + if (logger.isDebugEnabled()) logger.debug("LoginRequest.dRB"); // NOI18N + out.writeUTF (dbname); + out.writeUTF (user); + out.writeLong (timestamp); + out.writeInt (secret.length); + out.write (secret); + out.writeBoolean (create); + } + + /** + * 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 + * @param length + * @param status Indication as to the success, failure, etc. of the + * request as handled by the store. + * @throws IOException + */ + public void handleReply(Status status, DataInput in, int length) throws IOException { + if (logger.isDebugEnabled()) logger.debug("LoginRequest.hR"); // NOI18N + + if (!status.equals (Status.OK)) { + throw new JDODataStoreException (msg.msg("EXC_CannotLogin")); // NOI18N + } + } +} Added: incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/fostore/LongTranscriber.java URL: http://svn.apache.org/viewcvs/incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/fostore/LongTranscriber.java?view=auto&rev=158176 ============================================================================== --- incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/fostore/LongTranscriber.java (added) +++ incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/fostore/LongTranscriber.java Fri Mar 18 17:02:29 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 long values. +* +* @author Dave Bristor +*/ +class LongTranscriber extends FOStoreTranscriber { + private static LongTranscriber instance = new LongTranscriber(); + + private LongTranscriber() {} + + static LongTranscriber getInstance() { + return instance; + } + + void storeLong(long value, DataOutput out) throws IOException { + out.writeLong(value); + } + + long fetchLong(DataInput in) throws IOException { + return in.readLong(); + } + + void skip(DataInput in) throws IOException { + in.readLong(); + } +}