blob: 2618167112c5c380e44b8547b6583b3229ae12b4 [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 java.util.HashMap;
import javax.jdo.PersistenceManager;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.jdo.model.jdo.JDOClass;
import org.apache.jdo.state.StateManagerInternal;
import org.apache.jdo.util.I18NHelper;
/**
* Base implementation for all Request subtypes. Provides RequestId, and
* representation of Request types for all subclasses. Ergo, when you add a
* Request type you must add it's request type representation here.
*
* @author Dave Bristor
*/
abstract class AbstractRequest implements Request {
//
// Several members are accessible from subclasses
//
/**
* The state manager which is the subject of this request.
*/
// Please treat this as final after constructors have run.
protected /* final */ StateManagerInternal sm;
/**
* PersistenceManagerFactory via which request is being done.
*/
protected final FOStorePMF pmf;
/**
* Stream to which request writes itself.
*/
protected final FOStoreOutput out;
/**
* Class meta data of the object represented by the constructor's given
* state manager
*/
// Please treat this as final after constructors (including those of
// subclasses, which might set this) have run.
protected /* final */ JDOClass jdoClass;
/**
* uid corresponding to the same java.lang.Class that initializes
* jdoClass.
*/
// Please treat this as final after constructors (including those of
// subclasses, which might set this) have run.
protected /* final */ FOStoreSchemaUID fsuid;
protected static final I18NHelper msg = I18NHelper.getInstance(I18N.NAME);
/** Logger */
static final Log logger = LogFactory.getFactory().getInstance(
"org.apache.jdo.impl.fostore"); // NOI18N
//
// Requests are written into the FOStoreOutput, and have this
// format:
//
// * RequestId: Per-PMF, one per request.
// * RequestType: See above table requestTypes.
// * Length: Length in bytes of the RequestType-specific data
// * RequestType-specific data, Length bytes long.
//
// The methods in this class provide the means of writing the RequestId
// and RequestType (via beginRequest()) and the Length (via
// endRequest()). All AbstractRequest subclasses should invoke these
// methods at the beginning and end of their operations which create the
// actual RequestType-specific data.
//
// Position in the output at which we will write the length of the
// request. Set when we beginRequest(). Referenced when we
// endRequest().
private int lengthPos = 0;
// Written at lengthPos to advance the output over the length field.
protected static final int LENGTH_COOKIE = 0x10badbad;
// Identifies this request uniquely within a PMF.
private final RequestId requestId;
// Identifies the type of the request. Please treat as 'final' after
// beginRequest.
private RequestType requestType;
// When true, indicates that the request has written all its information
// to the FOStoreOutput. See endRequest().
private boolean sealed = false;
protected AbstractRequest(StateManagerInternal sm, Message m,
FOStorePMF pmf) {
this(m, pmf);
this.sm = sm;
FOStoreModel model = pmf.getModel();
if (sm != null) {
Class cls = sm.getPCClass();
this.fsuid = FOStoreSchemaUID.lookup(cls, model);
this.jdoClass = model.getJDOClass(cls);
}
}
protected AbstractRequest(Message m, FOStorePMF pmf) {
this.out = m.getOutput();
this.pmf = pmf;
this.requestId = RequestId.allocate(pmf);
m.putRequest(requestId, this);
}
//
// NOTE: when initialized by the second constructor, the sm and jdoClass
// fields are not filled in. Therefore, methods in this class must *not*
// assume they have valid values!
//
protected RequestId getId() {
return requestId;
}
/**
* @see Request#doRequest
*/
public final void doRequest() throws IOException {
// We must be the only one writing to the output to ensure we
// write a valid request
synchronized (out) {
beginRequest();
doRequestBody();
endRequest();
}
}
/**
* Writes the header of the request. The header is always:<p>
* request id<br>
* request type<br>
* length<br>
*
* No, we don't really know the length yet. We write out any old
* integer, and then later (in endRequest) come back and write in the
* real length.
*/
// Final to enforce the above rules
private final void beginRequest() throws IOException {
if (lengthPos != 0) {
throw new FOStoreFatalInternalException(
this.getClass(), "beginRequest", // NOI18N
msg.msg("ERR_RequestAlreadyBegun")); // NOI18N
}
requestId.write(out);
requestType = RequestType.get(this.getClass());
if (null == requestType) {
throw new FOStoreFatalInternalException(
this.getClass(), "beginRequest", // NOI18N
msg.msg("ERR_NoRequestType", this.getClass().getName())); // NOI18N
}
requestType.write(out);
lengthPos = out.getPos();
out.writeInt(LENGTH_COOKIE);
}
/**
* Subclasses must implement in this method the actual writing of their
* Request type-specific data.
*/
protected abstract void doRequestBody() throws IOException;
/**
* Write the length. Remember, in beginRequest we only wrote a
* placeholder.
*/
// Final to enforce the rules noted in beginRequest().
private final void endRequest() throws IOException {
if (sealed) {
throw new FOStoreFatalInternalException(
this.getClass(), "endRequest", // NOI18N
msg.msg("ERR_RequestAlreadyEnded")); // NOI18N
}
int currentPos = out.getPos();
// Subtract off an extra 4, which is the size of the length itself.
int length = currentPos - lengthPos - 4;
out.setPos(lengthPos);
out.writeInt(length);
out.setPos(currentPos);
sealed = true;
if (logger.isTraceEnabled()) {
logger.trace("Request " + requestId + "/" + requestType); // NOI18N
Tester.dump("REQ", out, lengthPos, length); // NOI18N
}
}
/** Get the StateManager associated with this request, null if none.
*@return the StateManager.
*/
public StateManagerInternal getStateManager() {
return sm;
}
}