blob: 16b9bd3a7e2c971b11b00b172f5b50fe5a9c7646 [file] [log] [blame]
/*
* 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);
}
}