blob: bcaf2aadeae79d82d3838f259e86163b60910f73 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.yoko.orb.OB;
import java.io.Serializable;
import java.lang.reflect.Array;
import java.util.Hashtable;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.rmi.CORBA.ValueHandler;
import org.apache.yoko.orb.CORBA.InputStream;
import org.apache.yoko.orb.CORBA.OutputStream;
import org.apache.yoko.orb.OCI.Buffer;
import org.omg.CORBA.Any;
import org.omg.CORBA.CompletionStatus;
import org.omg.CORBA.CustomMarshal;
import org.omg.CORBA.DataInputStream;
import org.omg.CORBA.MARSHAL;
import org.omg.CORBA.StringHolder;
import org.omg.CORBA.SystemException;
import org.omg.CORBA.TCKind;
import org.omg.CORBA.TypeCode;
import org.omg.CORBA.TypeCodePackage.BadKind;
import org.omg.CORBA.TypeCodePackage.Bounds;
import org.omg.CORBA.VM_CUSTOM;
import org.omg.CORBA.VM_NONE;
import org.omg.CORBA.VM_TRUNCATABLE;
import org.omg.CORBA.WStringValueHelper;
import org.omg.CORBA.portable.BoxedValueHelper;
import org.omg.CORBA.portable.IndirectionException;
import org.omg.CORBA.portable.StreamableValue;
import org.omg.CORBA.portable.ValueFactory;
import org.omg.SendingContext.CodeBase;
public final class ValueReader {
private static final Logger logger = Logger.getLogger(ValueReader.class.getName());
//
// Chunk data
//
private static class ChunkState {
boolean chunked;
int nestingLevel;
int chunkStart;
int chunkSize;
ChunkState() {
}
ChunkState(ChunkState s) {
copyFrom(s);
}
void copyFrom(ChunkState s) {
chunked = s.chunked;
nestingLevel = s.nestingLevel;
chunkStart = s.chunkStart;
chunkSize = s.chunkSize;
}
}
//
// Valuetype header data
//
private static class Header {
int tag;
int headerPos;
int dataPos;
String[] ids;
final ChunkState state;
Header next; // Java only
String codebase; // Java only
Header() {
ids = new String[0];
state = new ChunkState();
}
boolean isRMIValue() {
return (ids != null) && (ids.length > 0) && ids[0].startsWith("RMI:");
}
}
private final ORBInstance orbInstance_;
private final InputStream in_;
private final Buffer buf_;
private final Map<Integer, Serializable> instanceTable_;
private final Map<Integer, Header> headerTable_;
private Map<Integer, Integer> positionTable_;
private final ChunkState chunkState_ = new ChunkState();
private Header currentHeader_;
private ValueHandler valueHandler;
private CodeBase remoteCodeBase;
// ------------------------------------------------------------------
// Valuetype creation strategies
// ------------------------------------------------------------------
private abstract static class CreationStrategy {
final ValueReader reader_;
final InputStream is_;
CreationStrategy(ValueReader reader, InputStream is) {
reader_ = reader;
is_ = is;
}
abstract Serializable create(Header h);
}
//
// Create a valuebox using a BoxedValueHelper
//
private static class BoxCreationStrategy extends CreationStrategy {
private final BoxedValueHelper helper_;
BoxCreationStrategy(ValueReader reader, InputStream is, BoxedValueHelper helper) {
super(reader, is);
helper_ = helper;
}
Serializable create(Header h) {
Assert._OB_assert((h.tag >= 0x7fffff00) && (h.tag != -1));
final Serializable result = helper_.read_value(is_);
if (result != null) {
reader_.addInstance(h.headerPos, result);
return result;
}
throw new MARSHAL(MinorCodes.describeMarshal(MinorCodes.MinorNoValueFactory) + ": " + helper_.get_id(), MinorCodes.MinorNoValueFactory,
CompletionStatus.COMPLETED_NO);
}
}
//
// Create a value using a class
//
private static class ClassCreationStrategy extends CreationStrategy {
private final Class<? extends Serializable> clz_;
ClassCreationStrategy(ValueReader reader, InputStream is, Class<? extends Serializable> clz) {
super(reader, is);
clz_ = clz;
}
Serializable create(Header h) {
if (logger.isLoggable(Level.FINE))
logger.fine(String.format("Creating a value object with tag value 0x%08x", h.tag));
Assert._OB_assert((h.tag >= 0x7fffff00) && (h.tag != -1));
if (h.isRMIValue()) {
return reader_.readRMIValue(h, clz_, h.ids[0]);
}
try {
Serializable result = clz_.newInstance();
reader_.addInstance(h.headerPos, result);
try {
reader_.unmarshalValueState(result);
} catch (SystemException ex) {
reader_.removeInstance(h.headerPos);
throw ex;
}
return result;
} catch (ClassCastException | InstantiationException | IllegalAccessException ignored) {
}
throw new MARSHAL(MinorCodes.describeMarshal(MinorCodes.MinorNoValueFactory) + ": " + clz_.getName(), MinorCodes.MinorNoValueFactory,
CompletionStatus.COMPLETED_NO);
}
}
//
// Create a value using a factory
//
private class FactoryCreationStrategy extends CreationStrategy {
private final String id_;
private final ORBInstance orbInstance_;
FactoryCreationStrategy(ValueReader reader, InputStream is, String id) {
super(reader, is);
id_ = id;
orbInstance_ = is._OB_ORBInstance();
}
private ValueFactory findFactory(Header h, StringHolder id) {
ValueFactory f = null;
if (orbInstance_ != null) {
final ValueFactoryManager manager = orbInstance_.getValueFactoryManager();
if (h.ids.length > 0) {
for (int i = 0; i < h.ids.length; i++) {
f = manager.lookupValueFactoryWithClass(h.ids[i]);
if (f != null) {
id.value = h.ids[i];
break;
}
//
// If we have a formal ID, and we haven't found
// a factory yet, then give up
//
if ((id_ != null) && h.ids[i].equals(id_)) {
break;
}
}
} else if (id_ != null) {
f = manager.lookupValueFactoryWithClass(id_);
id.value = id_;
}
}
return f;
}
private Serializable createWithFactory(Header h, ValueFactory factory) {
//
// The factory's read_value method is expected to create
// an instance of the valuetype and then call the method
// InputStream.read_value(java.io.Serializable) to read
// the valuetype state. We use a stack to remember the
// Header information for use by initializeValue(), which
// is called by read_value().
//
try {
reader_.pushHeader(h);
return factory.read_value(is_);
} finally {
reader_.popHeader();
}
}
private BoxedValueHelper getBoxedHelper(String id) {
if (WStringValueHelper.id().equals(id))
return new WStringValueHelper();
final Class helperClass = Util.idToClass(id, "Helper");
if (helperClass != null) {
try {
return (BoxedValueHelper) helperClass.newInstance();
} catch (ClassCastException | InstantiationException | IllegalAccessException ignored) {
}
throw new MARSHAL(MinorCodes.describeMarshal(MinorCodes.MinorNoValueFactory) + ": invalid BoxedValueHelper for " + id,
MinorCodes.MinorNoValueFactory, CompletionStatus.COMPLETED_NO);
}
return null;
}
Serializable create(Header h) {
final StringHolder idH = new StringHolder();
return create(h, idH);
}
Serializable create(Header h, StringHolder id) {
Assert._OB_assert((h.tag >= 0x7fffff00) && (h.tag != -1));
if (h.isRMIValue()) {
final Serializable result = readRMIValue(h, null, h.ids[0]);
addInstance(h.headerPos, result);
return result;
}
//
// See if a factory exists that can create the value
//
final ValueFactory factory = findFactory(h, id);
if (factory != null) {
final Serializable result = createWithFactory(h, factory);
reader_.addInstance(h.headerPos, result);
return result;
}
//
// Another possibility is that we're unmarshalling a valuebox,
// so we'll try to load the Helper class dynamically
//
BoxedValueHelper helper = null;
if (h.ids.length > 0) {
//
// If it's a valuebox, at most one id will be marshalled
//
helper = getBoxedHelper(h.ids[0]);
if (helper != null) {
id.value = h.ids[0];
}
}
if ((helper == null) && (id_ != null)) {
helper = getBoxedHelper(id_);
if (helper != null) {
id.value = id_;
}
}
if (helper != null) {
final Serializable result = helper.read_value(is_);
reader_.addInstance(h.headerPos, result);
return result;
}
String type = "<unknown>";
if (h.ids.length > 0) {
type = h.ids[0];
} else if (id_ != null) {
type = id_;
}
throw new MARSHAL(MinorCodes.describeMarshal(MinorCodes.MinorNoValueFactory) + ": " + type, MinorCodes.MinorNoValueFactory,
CompletionStatus.COMPLETED_NO);
}
}
// ------------------------------------------------------------------
// Private and protected members
// ------------------------------------------------------------------
// Java only
private void addInstance(int pos, Serializable instance) {
// only add this if we have a real value
if (instance != null) {
instanceTable_.put(pos, instance);
}
}
// Java only
private void removeInstance(int pos) {
instanceTable_.remove(pos);
}
private void readHeader(Header h) {
if (logger.isLoggable(Level.FINE))
logger.fine(String.format("Reading header with tag value 0x%08x at pos=0x%x", h.tag, in_.buf_.pos_));
//
// Special cases are handled elsewhere
//
Assert._OB_assert((h.tag != 0) && (h.tag != -1));
//
// Check if the value is chunked
//
if ((h.tag & 0x00000008) == 8) {
h.state.chunked = true;
} else {
h.state.chunked = false;
}
//
// Check for presence of codebase URL
//
if ((h.tag & 0x00000001) == 1) {
//
// Check for indirection tag
//
final int save = buf_.pos_;
final int indTag = in_.read_long();
if (indTag == -1) {
final int offs = in_.read_long();
if (offs >= -4) {
throw new MARSHAL(MinorCodes.describeMarshal(MinorCodes.MinorReadInvalidIndirection), MinorCodes.MinorReadInvalidIndirection,
CompletionStatus.COMPLETED_NO);
}
final int tmp = buf_.pos_;
buf_.pos_ = (buf_.pos_ - 4) + offs;
if (buf_.pos_ < 0) {
throw new MARSHAL(MinorCodes.describeMarshal(MinorCodes.MinorReadInvalidIndirection), MinorCodes.MinorReadInvalidIndirection,
CompletionStatus.COMPLETED_NO);
}
h.codebase = in_.read_string();
buf_.pos_ = tmp;
} else {
buf_.pos_ = save;
h.codebase = in_.read_string();
}
if (logger.isLoggable(Level.FINER))
logger.finer(String.format("Value header codebase value is \"%s\"", h.codebase));
}
//
// Extract repository ID information
//
if ((h.tag & 0x00000006) == 0) {
logger.finer("No type information was included");
//
// No type information was marshalled
//
} else if ((h.tag & 0x00000006) == 6) {
logger.finer("Multiple types included in header");
//
// Extract a list of repository IDs, representing the
// truncatable types for this value
//
//
// Check for indirection tag (indirected list)
//
int saveList = buf_.pos_;
int indTag = in_.read_long();
final boolean indList = (indTag == -1);
if (indList) {
final int offs = in_.read_long();
if (offs > -4) {
throw new MARSHAL(MinorCodes.describeMarshal(MinorCodes.MinorReadInvalidIndirection), MinorCodes.MinorReadInvalidIndirection,
CompletionStatus.COMPLETED_NO);
}
saveList = buf_.pos_;
buf_.pos_ = (buf_.pos_ - 4) + offs;
if (buf_.pos_ < 0) {
throw new MARSHAL(MinorCodes.describeMarshal(MinorCodes.MinorReadInvalidIndirection), MinorCodes.MinorReadInvalidIndirection,
CompletionStatus.COMPLETED_NO);
}
} else {
buf_.pos_ = saveList;
}
final int count = in_.read_long();
h.ids = new String[count];
for (int i = 0; i < count; i++) {
//
// Check for indirection tag (indirected list entry)
//
int saveRep = buf_.pos_;
indTag = in_.read_long();
if (indTag == -1) {
final int offs = in_.read_long();
if (offs > -4) {
throw new MARSHAL(MinorCodes.describeMarshal(MinorCodes.MinorReadInvalidIndirection), MinorCodes.MinorReadInvalidIndirection,
CompletionStatus.COMPLETED_NO);
}
saveRep = buf_.pos_;
buf_.pos_ = (buf_.pos_ - 4) + offs;
if (buf_.pos_ < 0) {
throw new MARSHAL(MinorCodes.describeMarshal(MinorCodes.MinorReadInvalidIndirection), MinorCodes.MinorReadInvalidIndirection,
CompletionStatus.COMPLETED_NO);
}
h.ids[i] = in_.read_string();
buf_.pos_ = saveRep;
} else {
buf_.pos_ = saveRep;
h.ids[i] = in_.read_string();
}
if (logger.isLoggable(Level.FINER))
logger.finer(String.format("Value header respoitory id added \"%s\"", h.ids[i]));
}
//
// Restore buffer position (case of indirected list)
//
if (indList) {
buf_.pos_ = saveList;
}
} else if ((h.tag & 0x00000006) == 2) {
//
// Extract a single repository ID
//
final String id;
//
// Check for indirection tag
//
int save = buf_.pos_;
final int indTag = in_.read_long();
if (indTag == -1) {
final int offs = in_.read_long();
if (offs > -4) {
throw new MARSHAL(MinorCodes.describeMarshal(MinorCodes.MinorReadInvalidIndirection), MinorCodes.MinorReadInvalidIndirection,
CompletionStatus.COMPLETED_NO);
}
save = buf_.pos_;
buf_.pos_ = (buf_.pos_ - 4) + offs;
if (buf_.pos_ < 0) {
throw new MARSHAL(MinorCodes.describeMarshal(MinorCodes.MinorReadInvalidIndirection), MinorCodes.MinorReadInvalidIndirection,
CompletionStatus.COMPLETED_NO);
}
id = in_.read_string();
buf_.pos_ = save;
} else {
buf_.pos_ = save;
id = in_.read_string();
}
h.ids = new String[1];
h.ids[0] = id;
if (logger.isLoggable(Level.FINER))
logger.finer(String.format("Single header repository id read \"%s\"", id));
}
//
// Record beginning of value data
//
h.dataPos = buf_.pos_;
//
// Add entry to header table
//
headerTable_.put(h.headerPos, h);
}
private void readChunk(ChunkState state) {
//
// Check for a chunk size
//
final int size = in_._OB_readLongUnchecked();
if (logger.isLoggable(Level.FINEST))
logger.finest(String.format(
"Reading new chunk. Size value is 0x%x current nest is %d current position=0x%x",
size, state.nestingLevel, buf_.pos_));
if ((size >= 0) && (size < 0x7fffff00)) // chunk size
{
state.chunkStart = buf_.pos_;
state.chunkSize = size;
} else if (size < 0) // end tag
{
buf_.pos_ -= 4; // rewind
state.chunkStart = buf_.pos_;
state.chunkSize = 0;
} else {
buf_.pos_ -= 4; // rewind
state.chunkStart = 0;
state.chunkSize = 0;
}
if (logger.isLoggable(Level.FINEST))
logger.finest(String.format("Chunk read. start=0x%x, size=0x%x buffer position=0x%x",
state.chunkStart, state.chunkSize, buf_.pos_));
}
private void initHeader(Header h) {
//
// Null values and indirections must be handled by caller
//
Assert._OB_assert((h.tag != 0) && (h.tag != -1));
h.headerPos = buf_.pos_ - 4; // adjust for alignment
h.state.copyFrom(chunkState_);
//
// Read value header info
//
readHeader(h);
chunkState_.copyFrom(h.state);
//
// Increment our nesting level if we are chunked
//
if (chunkState_.chunked) {
// logger.finest("Reading chunk for chunked value. Header tag=" + Integer.toHexString(h.tag) + " current position=" + buf_.pos_);
readChunk(chunkState_);
chunkState_.nestingLevel++;
// logger.fine("Chunk nesting level is " + chunkState_.nestingLevel + " current position=" + buf_.pos_ + " chunk size=" + chunkState_.chunkSize);
}
}
private void skipChunk() {
if (chunkState_.chunked) {
if (logger.isLoggable(Level.FINE))
logger.fine(String.format("Skipping a chunked value. nesting level=%d current position is 0x%x chunk end is 0x%x",
chunkState_.nestingLevel, buf_.pos_, (chunkState_.chunkStart + chunkState_.chunkSize)));
//
// At this point, the unmarshalling code has finished. However,
// we may have a truncated value, or we may have unmarshalled a
// custom value. In either case, we can't be sure that the
// unmarshalling code has positioned the stream at the end of
// this value.
//
// Therefore we will advance, chunk by chunk, until we reach
// the end of the value.
//
//
// Skip to the end of the current chunk (if necessary)
//
if (chunkState_.chunkStart > 0) {
buf_.pos_ = chunkState_.chunkStart;
in_._OB_skip(chunkState_.chunkSize);
if (logger.isLoggable(Level.FINEST))
logger.finest(String.format("Skipping to end of current chunk. New position is 0x%x", buf_.pos_));
}
chunkState_.chunkStart = 0;
chunkState_.chunkSize = 0;
//
// Loop until we have reached the end of this value. We may
// have to process nested values, chunks, etc.
//
int level = chunkState_.nestingLevel;
int tag = in_._OB_readLongUnchecked();
if (logger.isLoggable(Level.FINEST))
logger.finest(String.format("Skipping chunk: read tag value =0x%08x", tag));
while ((tag >= 0) || ((tag < 0) && (tag < -chunkState_.nestingLevel))) {
if (tag >= 0x7fffff00) {
logger.finest("Skipping chunk: reading a nested chunk value");
//
// This indicates a nested value. We read the header
// information and store it away, in case a subsequent
// indirection refers to this value.
//
level++;
final Header nest = new Header();
nest.tag = tag;
nest.headerPos = buf_.pos_ - 4; // adjust for alignment
nest.state.nestingLevel = level;
readHeader(nest);
} else if (tag >= 0) {
if (logger.isLoggable(Level.FINEST))
logger.finest(String.format("Skipping chunk: skipping over a chunk for length 0x%x", tag));
//
// Chunk size - advance the stream past the chunk
//
in_._OB_skip(tag);
} else {
if (logger.isLoggable(Level.FINEST))
logger.finest(String.format("Skipping chunk: chunk end tag=0x%08x current level=%d",
tag, level));
//
// tag is less than 0, so this is an end tag for a nested
// value
//
// this can terminate more than a single level.
level = (-tag) - 1;
}
//
// Read the next tag
//
tag = in_._OB_readLongUnchecked();
if (logger.isLoggable(Level.FINEST))
logger.finest(String.format("Skipping chunk: read tag value=0x%08x", tag));
}
//
// If the tag is greater than our nesting level, then this
// value coterminates with an outer value. We rewind the
// stream so that the outer value can read this tag.
//
if (tag > -chunkState_.nestingLevel) {
buf_.pos_ -= 4;
}
chunkState_.nestingLevel--;
if (logger.isLoggable(Level.FINEST))
logger.finest(String.format("New chunk nesting level is %d", chunkState_.nestingLevel));
if (chunkState_.nestingLevel == 0) {
chunkState_.chunked = false;
} else {
//
// We're chunked and still processing nested values, so
// another chunk may follow
//
logger.finest("Reading chunk for skipping to end of a chunk");
readChunk(chunkState_);
}
if (logger.isLoggable(Level.FINEST))
logger.finest(String.format(
"Final chunk state is nesting level=%d current position is 0x%x chunk end is 0x%x",
chunkState_.nestingLevel, buf_.pos_, (chunkState_.chunkStart + chunkState_.chunkSize)));
}
}
//
// Invoke the valuetype to unmarshal its state
//
// Java only
//
private void unmarshalValueState(Serializable v) {
if (v instanceof StreamableValue) {
((StreamableValue) v)._read(in_);
} else if (v instanceof CustomMarshal) {
final DataInputStream dis = new org.apache.yoko.orb.CORBA.DataInputStream(in_);
((CustomMarshal) v).unmarshal(dis);
} else {
throw new MARSHAL("Valuetype does not implement " + "StreamableValue or " + "CustomMarshal");
}
}
private Serializable readIndirection(CreationStrategy strategy) {
final int offs = in_.read_long();
int pos = (buf_.pos_ - 4) + offs;
pos += 3; // adjust for alignment
pos -= (pos & 0x3);
final Integer posObj = pos;
//
// Check the history for a value that was seen at the specified
// position. Generally, we should expect the value to be present.
// However, in the case of chunking, it's possible for an
// indirection to refer to a nested value in the truncated state
// of an enclosing value, or to a value that we could not
// instantiate.
//
Serializable v = (Serializable) instanceTable_.get(posObj);
if (v != null) {
return v;
} else {
final int save = buf_.pos_;
//
// Check for indirection to null value
//
buf_.pos_ = pos; // rewind to offset position
if (in_._OB_readLongUnchecked() == 0) {
buf_.pos_ = save;
// Can't put a null in a Hashtable
// instanceTable_.put(posObj, null);
return null;
}
//
// If it's not null and it's not in our history, then
// there's no hope
//
final Header nest = headerTable_.get(posObj);
if (nest == null) {
throw new MARSHAL(MinorCodes.describeMarshal(MinorCodes.MinorNoValueFactory) + ": cannot instantiate value for indirection",
MinorCodes.MinorNoValueFactory, CompletionStatus.COMPLETED_NO);
}
/*
* Maybe we have an indirection to an object that is being
* deserialized. We throw an IndirectionException which signals the
* RMI implementation to handle the indirection.
*/
if (nest.isRMIValue()) {
buf_.pos_ = save;
throw new IndirectionException(pos);
}
//
// Create the value
//
buf_.pos_ = nest.dataPos;
final ChunkState saveState = new ChunkState(chunkState_);
chunkState_.copyFrom(nest.state);
if (chunkState_.chunked) {
readChunk(chunkState_);
}
try {
v = strategy.create(nest);
} finally {
//
// Restore state
//
buf_.pos_ = save;
chunkState_.copyFrom(saveState);
}
return v;
}
}
private java.io.Serializable read(CreationStrategy strategy) {
Header h = new Header();
h.tag = in_.read_long();
// logger.fine("Read tag value " + Integer.toHexString(h.tag));
if (h.tag == 0) {
return null;
} else if (h.tag == -1) {
return readIndirection(strategy);
} else if (h.tag < 0x7fffff00) {
throw new org.omg.CORBA.MARSHAL(String.format(
"Illegal valuetype tag 0x%08x",
h.tag));
} else {
initHeader(h);
// read_value() may be called to skip over a secondary custom valuetype.
// If so, return an internal marker object so it shows up if misused.
final Serializable result = isSecondaryCustomValuetype(h) ?
SecondaryValuetypeMarker.ATTEMPT_TO_READ_CUSTOM_DATA_AS_VALUE
: strategy.create(h);
skipChunk();
return result;
}
}
private enum SecondaryValuetypeMarker {
ATTEMPT_TO_READ_CUSTOM_DATA_AS_VALUE
}
private boolean isSecondaryCustomValuetype(Header h) {
// there must be exactly one repository ID
if (h.ids.length != 1)
return false;
// the repository ID must start with one of the Java-to-IDL spec prefixes
final String repId = h.ids[0];
if (!(repId.startsWith("RMI:org.omg.custom.") || repId.startsWith("RMI:org.omg.customRMI.")))
return false;
// it matched enough of the rules, so treat it as a secondary custom valuetype
// there should not be a codebase (tolerate but log this)
if (h.codebase != null) {
if (logger.isLoggable(Level.FINE))
logger.fine(String.format(
"Secondary custom marshal valuetype found with non-null codebase: \"%s\", repId: \"%s\"",
h.codebase, repId));
}
return true;
}
//
// Remarshal each valuetype member
//
private void copyValueState(TypeCode tc, OutputStream out) {
try {
if (tc.kind() == TCKind.tk_value) {
//
// First copy the state of the concrete base type, if any
//
final TypeCode base = tc.concrete_base_type();
if (base != null) {
copyValueState(base, out);
}
for (int i = 0; i < tc.member_count(); i++) {
// logger.fine("writing member of typecode " + tc.member_type(i).kind().value());
out.write_InputStream(in_, tc.member_type(i));
}
} else if (tc.kind() == TCKind.tk_value_box) {
out.write_InputStream(in_, tc.content_type());
} else {
Assert._OB_assert(false);
}
} catch (BadKind | Bounds ex) {
logger.log(Level.FINER, "Invalid type kind", ex);
Assert._OB_assert(ex);
}
}
// Java only
private void pushHeader(Header h) {
h.next = currentHeader_;
currentHeader_ = h;
}
// Java only
private void popHeader() {
Assert._OB_assert(currentHeader_ != null);
currentHeader_ = currentHeader_.next;
}
//
// Search up the valuetype's inheritance hierarchy for a TypeCode
// with the given repository ID
//
private TypeCode findTypeCode(String id, TypeCode tc) {
TypeCode result = null;
TypeCode t = tc;
// logger.finer("Locating type code for id " + id);
while (result == null) {
try {
final TypeCode t2 = org.apache.yoko.orb.CORBA.TypeCode._OB_getOrigType(t);
// logger.finer("Checking typecode " + id + " against " + t2.id());
if (id.equals(t2.id())) {
result = t;
} else if ((t2.kind() == TCKind.tk_value) && (t2.type_modifier() == VM_TRUNCATABLE.value)) {
t = t2.concrete_base_type();
// logger.finer("Iterating with concrete type " + t.id());
} else {
break;
}
} catch (BadKind ex) {
Assert._OB_assert(ex);
}
}
return result;
}
// ------------------------------------------------------------------
// Public methods
// ------------------------------------------------------------------
public ValueReader(InputStream in) {
in_ = in;
buf_ = in._OB_buffer();
orbInstance_ = in._OB_ORBInstance();
instanceTable_ = new Hashtable<Integer, Serializable>(131);
headerTable_ = new Hashtable<Integer, Header>(131);
}
private Serializable readRMIValue(Header h, Class<? extends Serializable> clz, String repid) {
if (logger.isLoggable(Level.FINE))
logger.fine(String.format("Reading RMI value of type \"%s\"", repid));
if (valueHandler == null) {
valueHandler = javax.rmi.CORBA.Util.createValueHandler();
}
if (repid == null) {
repid = h.ids[0];
if (repid == null) {
throw new MARSHAL("missing repository id");
}
}
final String className = Util.idToClassName(repid, "");
String codebase = h.codebase;
if (codebase == null) {
codebase = in_.__getCodeBase();
}
Class repoClass = resolveRepoClass(className, codebase);
// if we have a non-null codebase and can't resolve this, throw an
// exception now. Otherwise, we'll try again after grabbing the remote
// codebase.
if ((repoClass == null) && (codebase != null) && !codebase.isEmpty()) {
throw new MARSHAL("class " + className + " not found (cannot load from " + codebase + ")");
}
if (remoteCodeBase == null) {
remoteCodeBase = in_.__getSendingContextRuntime();
}
if (repoClass == null) {
if ((codebase == null) && (remoteCodeBase != null)) {
try {
codebase = remoteCodeBase.implementation(repid);
} catch (SystemException ignored) {
}
}
if (codebase == null) {
// TODO: add minor code
throw new MARSHAL("class " + className + " not found (no codebase provided)");
} else {
repoClass = resolveRepoClass(className, codebase);
if (repoClass == null) {
throw new MARSHAL("class " + className + " not found (cannot load from " + codebase + ")");
}
}
}
/*
* Suns crappy ValueHandler implementation narrows the remote CodeBase
* to a com.sun.org.omg.SendingContext.CodeBase. Narrowing CodeBaseProxy
* is not possible, we need a stub.
*/
if (remoteCodeBase instanceof CodeBaseProxy) {
remoteCodeBase = ((CodeBaseProxy) remoteCodeBase).getCodeBase();
}
Serializable serobj = null;
try {
serobj = valueHandler.readValue(in_, h.headerPos, repoClass, repid, remoteCodeBase);
} catch (RuntimeException ex) {
if (logger.isLoggable(Level.FINE)) {
logger.fine(String.format(
"RuntimeException happens when reading GIOP stream coming to pos_=0x%x",
in_.buf_.pos_));
logger.fine(String.format("Wrong data section:%n%s", in_.dumpData()));
final int currentpos = in_.buf_.pos_;
in_.buf_.pos_ = 0;
logger.fine(String.format("Full GIOP stream dump:%n%s", in_.dumpData()));
in_.buf_.pos_ = currentpos;
}
throw ex;
}
return serobj;
}
private Class resolveRepoClass(String name, String codebase) {
if (logger.isLoggable(Level.FINE))
logger.fine(String.format(
"Attempting to resolve class \"%s\" from codebase \"%s\"",
name, codebase));
if (name.startsWith("[")) {
int levels = 0;
for (int i = 0; name.charAt(i) == '['; i++) {
levels++;
}
Class elementClass = null;
// now resolve the element descriptor to a class
switch (name.charAt(levels)) {
case 'Z':
elementClass = Boolean.TYPE;
break;
case 'B':
elementClass = Byte.TYPE;
break;
case 'S':
elementClass = Short.TYPE;
break;
case 'C':
elementClass = Character.TYPE;
break;
case 'I':
elementClass = Integer.TYPE;
break;
case 'J':
elementClass = Long.TYPE;
break;
case 'F':
elementClass = Float.TYPE;
break;
case 'D':
elementClass = Double.TYPE;
break;
case 'L':
// extract the class from the name and resolve that.
elementClass = resolveRepoClass(name.substring(levels + 1, name.indexOf(';')), codebase);
if (elementClass == null) {
return null;
}
break;
}
// ok, we need to recurse and resolve the base array element class
Object arrayInstance;
// this is easier with a single level
if (levels == 1) {
arrayInstance = Array.newInstance(elementClass, 0);
} else {
// all elements will be zero
final int[] dimensions = new int[levels];
arrayInstance = Array.newInstance(elementClass, dimensions);
}
// return the class associated with this array
return arrayInstance.getClass();
} else {
try {
return javax.rmi.CORBA.Util.loadClass(name, codebase, Util.getContextClassLoader());
} catch (ClassNotFoundException ex) {
// this will be sorted out later
return null;
}
}
}
public Serializable readValue() {
final FactoryCreationStrategy strategy = new FactoryCreationStrategy(this, in_, null);
return read(strategy);
}
public Serializable readValue(String id) {
if (logger.isLoggable(Level.FINE))
logger.fine(String.format("Reading value of type \"%s\"", id));
final FactoryCreationStrategy strategy = new FactoryCreationStrategy(this, in_, id);
return read(strategy);
}
public Serializable readValue(Class<? extends Serializable> clz) {
if (logger.isLoggable(Level.FINE))
logger.fine(String.format("Reading value of type \"%s\"", clz.getName()));
if (clz.equals(String.class)) {
return WStringValueHelper.read(in_);
}
final ClassCreationStrategy strategy = new ClassCreationStrategy(this, in_, clz);
Serializable result;
try {
result = read(strategy);
} catch (MARSHAL marshalex) {
logger.severe(String.format("MARSHAL \"%s\", at pos=0x%x", marshalex.getMessage(), (in_.buf_.pos_ - 4)));
if ("true".equalsIgnoreCase(System.getProperty("org.apache.yoko.ignoreInvalidValueTag"))) {
result = read(strategy);
} else {
throw marshalex;
}
}
return result;
}
public Serializable readValueBox(BoxedValueHelper helper) {
final BoxCreationStrategy strategy = new BoxCreationStrategy(this, in_, helper);
return read(strategy);
}
public void initializeValue(Serializable value) {
//
// We should have previously pushed a Header on the stack
//
Assert._OB_assert(currentHeader_ != null);
//
// Now that we have an instance, we can put it in our history
//
addInstance(currentHeader_.headerPos, value);
//
// Unmarshal the value's state
//
try {
unmarshalValueState(value);
} catch (SystemException ex) {
removeInstance(currentHeader_.headerPos);
throw ex;
}
}
public Object readAbstractInterface() {
//
// Abstract interfaces are marshalled like a union with a
// boolean discriminator - if true, an objref follows,
// otherwise a valuetype follows
//
if (in_.read_boolean()) {
return in_.read_Object();
} else {
return readValue();
}
}
public Object readAbstractInterface(Class clz) {
//
// Abstract interfaces are marshalled like a union with a
// boolean discriminator - if true, an objref follows,
// otherwise a valuetype follows
//
if (in_.read_boolean()) {
return in_.read_Object(clz);
} else {
return readValue(clz);
}
}
//
// Copy a value from out InputStream to the given OutputStream.
// The return value is the most-derived TypeCode of the value
// written to the stream. This may not be the same as the TypeCode
// argument if the value is truncated. Furthermore, the TypeCode
// may not represent the actual most-derived type, since a more-derived
// value may have been read.
//
public TypeCode remarshalValue(TypeCode tc, OutputStream out) {
//
// TODO: We've removed the reset of the position table at each top
// level call. We need to perform more testing and analysis to verify
// that this wasn't broken. remarshalDepth was also removed since it
// wasn't being used anywhere except to determine when the table should
// be reset.
//
//
// Create a new Hashtable for each top-level call to remarshalValue
//
if (positionTable_ == null) {
positionTable_ = new Hashtable<Integer, Integer>(131);
}
final TypeCode origTC = org.apache.yoko.orb.CORBA.TypeCode._OB_getOrigType(tc);
final Header h = new Header();
h.tag = in_.read_long();
if (logger.isLoggable(Level.FINE))
logger.fine(String.format("Read tag value 0x%08x", h.tag));
h.headerPos = buf_.pos_ - 4; // adjust for alignment
h.state.copyFrom(chunkState_);
//
// Remember starting position of this valuetype
//
final int pos = h.headerPos;
//
// Check for special cases (null values and indirections)
//
TypeCode result;
if (h.tag == 0) {
out.write_long(0);
result = tc;
} else if (h.tag == -1) {
//
// Since offsets can change as we remarshal valuetypes to the
// output stream, we use a table to map old positions to new ones
//
int offs = in_.read_long();
int oldPos = (buf_.pos_ - 4) + offs;
oldPos += 3; // adjust alignment to start of value
oldPos -= (oldPos & 0x3);
//
// If we find the position in the table, write the translated
// offset to the output stream. Otherwise, the indirection refers
// to a valuetype that we were unable to create and we therefore
// raise MARSHAL.
//
@SuppressWarnings("UnnecessaryBoxing")
final Integer newPos = positionTable_.get(oldPos);
if (newPos != null) {
out.write_long(h.tag);
offs = newPos - out._OB_pos();
out.write_long(offs);
//
// TODO: The TypeCode may not necessarily reflect the
// TypeCode of the indirected value.
//
result = tc;
} else {
throw new MARSHAL("Cannot find value for indirection");
}
} else {
if (h.tag < 0x7fffff00) {
throw new MARSHAL("Illegal valuetype tag 0x" + Integer.toHexString(h.tag));
}
if (logger.isLoggable(Level.FINE))
logger.fine(String.format("Remarshalling header with tag value 0x%08x", h.tag));
//
// Add valuetype to position map
//
int outPos = out._OB_pos();
outPos += 3; // adjust alignment to start of value
outPos -= (outPos & 0x3);
positionTable_.put(pos, outPos);
//
// Read value header info
//
readHeader(h);
chunkState_.copyFrom(h.state);
if (chunkState_.chunked) {
logger.finest("Reading chunk in remarshal value()");
readChunk(chunkState_);
chunkState_.nestingLevel++;
}
String tcId = null;
short mod = VM_NONE.value;
try {
tcId = origTC.id();
if (origTC.kind() == TCKind.tk_value) {
mod = origTC.type_modifier();
}
} catch (BadKind ex) {
Assert._OB_assert(ex);
}
//
// We have two methods of extracting the state of a valuetype:
//
// 1) Use the TypeCode
// 2) Use a valuetype factory
//
// Which method we use is determined by the repository IDs
// in the valuetype header.
//
// Our goal is to preserve as much information as possible.
// If the TypeCode describes a more-derived type than any
// available factory, we will use the TypeCode. Otherwise,
// we use a factory to create a temporary instance of the
// valuetype, and subsequently marshal that instance to the
// OutputStream.
//
if (logger.isLoggable(Level.FINE))
logger.fine(String.format("Attempting to resolve typeId \"%s\"", tcId));
//
// See if the TypeCode ID matches any of the valuetype's IDs -
// stop at the first match
//
String id = null;
int idPos;
for (idPos = 0; idPos < h.ids.length; idPos++) {
if (logger.isLoggable(Level.FINER))
logger.finer(String.format(
"Comparing type id \"%s\" against \"%s\"",
tcId, h.ids[idPos]));
if (tcId.equals(h.ids[idPos])) {
id = h.ids[idPos];
break;
}
}
// if this is null, then try again to see if we can find a class in the ids list
// that is compatible with the base type. This will require resolving the classes.
if (id == null) {
// see if we can resolve the type for the stored type code
final Class<?> baseType = Util.idToClass(tcId, "");
if (baseType != null) {
for (idPos = 0; idPos < h.ids.length; idPos++) {
if (logger.isLoggable(Level.FINER))
logger.finer(String.format(
"Considering base types of id \"%s\" against \"%s\"",
tcId, h.ids[idPos]));
final Class idType = Util.idToClass(h.ids[idPos], "");
if (idType != null) {
// if these classes are assignment compatible, go with that as the type.
if (logger.isLoggable(Level.FINER))
logger.finer(String.format(
"Comparing type id \"%s\" against \"%s\"",
baseType.getName(), idType.getName()));
if (baseType.isAssignableFrom(idType)) {
id = h.ids[idPos];
break;
}
}
}
}
}
//
// See if a factory exists for any of the valuetype's IDs -
// stop at the first match
//
String factoryId = null;
int factoryPos = 0;
ValueFactory factory = null;
if (orbInstance_ != null) {
final ValueFactoryManager manager = orbInstance_.getValueFactoryManager();
for (factoryPos = 0; factoryPos < h.ids.length; factoryPos++) {
factory = manager.lookupValueFactoryWithClass(h.ids[factoryPos]);
if (factory != null) {
factoryId = h.ids[factoryPos];
break;
}
}
}
//
// If no ID matched the TypeCode, and no factory was found,
// then we have no way to remarshal the data
//
if ((h.ids.length > 0) && (id == null) && (factoryId == null)) {
if (logger.isLoggable(Level.FINE))
logger.fine(String.format("Unable to resolve a factory for type \"%s\"", tcId));
throw new MARSHAL(MinorCodes.describeMarshal(MinorCodes.MinorNoValueFactory) + ": insufficient information to copy valuetype",
MinorCodes.MinorNoValueFactory, CompletionStatus.COMPLETED_NO);
}
//
// If value is custom and there is no factory, then we have
// no way to remarshal the data
//
if ((mod == VM_CUSTOM.value) && (factoryId == null)) {
throw new MARSHAL(MinorCodes.describeMarshal(MinorCodes.MinorNoValueFactory) + ": unable to copy custom valuetype",
MinorCodes.MinorNoValueFactory, CompletionStatus.COMPLETED_NO);
}
//
// If the TypeCode is more descriptive than any available
// factory, or if no identifiers were provided, then use the
// TypeCode, otherwise use the factory
//
// NOTE: (Java only) We also use the TypeCode for boxed values,
// because we don't have the BoxedHelper and may not be
// able to locate one via the class loader.
//
if ((idPos < factoryPos) || (h.ids.length == 0) || (origTC.kind() == TCKind.tk_value_box)) {
//
// We may need to truncate the state of this value, which
// means we need to revise the list of repository IDs
//
final int numIds = h.ids.length - idPos;
final String[] ids = new String[numIds];
System.arraycopy(h.ids, idPos, ids, idPos - idPos, h.ids.length - idPos);
logger.fine("Copying value state of object using truncated type");
out._OB_beginValue(h.tag, ids, h.state.chunked);
copyValueState(origTC, out);
out._OB_endValue();
result = tc;
} else {
//
// Create a temporary instance to use for marshalling
//
try {
pushHeader(h);
final Serializable vb = factory.read_value(in_);
logger.fine("Creating a temporary copy of the object for marshalling");
try {
out.write_value(vb);
} finally {
removeInstance(h.headerPos);
}
} finally {
popHeader();
}
//
// Determine the TypeCode that is equivalent to the
// factory in use
//
result = findTypeCode(h.ids[factoryPos], tc);
if (result == null) {
result = tc;
}
}
skipChunk();
}
Assert._OB_assert(result != null);
return result;
}
public void readValueAny(Any any, TypeCode tc) {
//
// In constrast to other complex types, valuetypes and value boxes
// in Anys cannot simply be "remarshalled". The reason is that
// indirection may occur across multiple Any values in the same
// stream. Therefore, if possible, we should attempt to construct
// the values using a factory so that any shared values will be
// handled correctly.
//
// If a factory cannot be found, we should still remarshal.
// However, if an indirection is encountered which refers to
// a value we were unable to construct, an exception will be
// raised.
//
org.apache.yoko.orb.CORBA.Any obAny = null;
try {
obAny = (org.apache.yoko.orb.CORBA.Any) any;
} catch (ClassCastException ex) {
//
// Any may have been created by a foreign ORB
//
}
final TypeCode origTC = org.apache.yoko.orb.CORBA.TypeCode._OB_getOrigType(tc);
if (logger.isLoggable(Level.FINE))
logger.fine(String.format(
"Reading an Any value of kind=%d from position 0x%x",
origTC.kind().value(), buf_.pos_));
//
// Check if the Any contains an abstract interface
//
if (origTC.kind() == TCKind.tk_abstract_interface) {
final boolean b = in_.read_boolean();
if (b) {
logger.fine("Reading an object reference for an abstract interface");
//
// The abstract interface represents an object reference
//
any.insert_Object(in_.read_Object(), tc);
return;
} else {
logger.fine("Reading an object value for an abstract interface");
//
// The abstract interface represents a valuetype. The
// readValue() method will raise an exception if an
// instance of the valuetype could not be created.
// We cannot remarshal in this case because we don't
// have a TypeCode for the valuetype.
//
any.insert_Value(readValue(), tc);
return;
}
}
//
// If the TypeCode's repository ID is that of CORBA::ValueBase,
// then we try to create an instance. The Any could contain a
// valuetype *or* a boxed valuetype.
//
// If creation fails, we cannot remarshal the value, since
// CORBA::ValueBase is not a truncatable base type, and we
// have no other TypeCode information. Truncating to
// CORBA::ValueBase doesn't seem very useful anyway.
//
try {
final String id = origTC.id();
if (logger.isLoggable(Level.FINE))
logger.fine(String.format("Reading an Any value of id=\"%s\"", id));
if ("IDL:omg.org/CORBA/ValueBase:1.0".equals(id)) {
any.insert_Value(readValue(), tc);
return;
}
} catch (BadKind ex) {
Assert._OB_assert(ex);
}
//
// At this point, the Any contains a valuetype or a boxed valuetype,
// and we have a TypeCode that can be used for remarshalling.
//
//
// Save some state so that we can restore things prior to
// remarshalling
//
final int startPos = buf_.pos_;
final ChunkState startState = new ChunkState(chunkState_);
//
// No need to worry about truncation for boxed valuetypes
//
if (origTC.kind() == TCKind.tk_value_box) {
try {
any.insert_Value(readValue(tc.id()), tc);
return;
} catch (MARSHAL ex) {
//
// Creation failed - restore our state and try remarshalling
//
buf_.pos_ = startPos;
chunkState_.copyFrom(startState);
final Buffer buf = new Buffer();
final OutputStream out = new OutputStream(buf);
out._OB_ORBInstance(orbInstance_);
remarshalValue(origTC, out);
final InputStream in = (InputStream) out.create_input_stream();
Assert._OB_assert(obAny != null);
obAny.replace(tc, in);
return;
} catch (BadKind ex) {
Assert._OB_assert(ex);
}
} else {
//
// Read valuetype header tag
//
final Header h = new Header();
h.tag = in_.read_long();
if (logger.isLoggable(Level.FINE))
logger.fine(String.format("Read tag value 0x%08x", h.tag));
//
// Check tag for special cases
//
if (h.tag == 0) {
any.insert_Value(null, tc);
return;
}
if ((h.tag != -1) && (h.tag < 0x7fffff00)) {
throw new MARSHAL("Illegal valuetype tag 0x" + Integer.toHexString(h.tag));
}
//
// Try to create an instance of the valuetype using a factory
//
final FactoryCreationStrategy strategy = new FactoryCreationStrategy(this, in_, null);
try {
if (h.tag == -1) {
//
// TODO: The TypeCode may not necessarily reflect
// the one that was used for the indirected value
// (i.e., the value may have been truncated).
// Fixing this probably requires maintaining a
// map of stream position to TypeCode.
//
logger.fine("Handling a value type indirection value");
any.insert_Value(readIndirection(strategy), tc);
return;
} else {
initHeader(h);
final StringHolder idH = new StringHolder();
final Serializable vb = strategy.create(h, idH);
if (logger.isLoggable(Level.FINE))
logger.fine(String.format("Obtained a value of type \"%s\"", vb.getClass().getName()));
skipChunk();
//
// Find the TypeCode for the repository ID that was
// used to instantiate the valuetype. Three things
// can happen:
//
// 1) The TypeCode is equal to tc.
//
// 2) The TypeCode is null. In this case, the instance
// is of a more-derived type than tc, so the best
// we can do is to use tc.
//
// 3) The TypeCode is a base type of tc. In this case,
// the valuetype was truncated.
//
TypeCode t = null;
if (idH.value != null) {
t = findTypeCode(idH.value, tc);
}
if (t != null) {
any.insert_Value(vb, t);
} else {
any.insert_Value(vb, tc);
}
return;
}
} catch (MARSHAL ex) {
logger.log(Level.FINE, "Marshaling exception occurred, attempting to remarshal", ex);
//
// Creation failed - restore our state and try remarshalling
//
buf_.pos_ = startPos;
chunkState_.copyFrom(startState);
final Buffer buf = new Buffer();
final OutputStream out = new OutputStream(buf);
out._OB_ORBInstance(orbInstance_);
final TypeCode t = remarshalValue(origTC, out);
final InputStream in = (InputStream) out.create_input_stream();
Assert._OB_assert(obAny != null);
obAny.replace(t, in);
return;
}
}
Assert._OB_assert(false); // should never reach this point
}
public void beginValue() {
final Header h = new Header();
h.tag = in_.read_long();
if (logger.isLoggable(Level.FINE))
logger.fine(String.format("Read tag value 0x%08x", h.tag));
Assert._OB_assert((h.tag != 0) && (h.tag != -1));
initHeader(h);
}
public void endValue() {
skipChunk();
}
public void checkChunk() {
if (!chunkState_.chunked) {
return;
}
// logger.finest("Checking chunk position. end=" + (chunkState_.chunkStart + chunkState_.chunkSize) + " buffer position=" + buf_.pos_);
//
// If we've reached the end of the current chunk, then check
// for the start of a new chunk
//
if ((chunkState_.chunkStart > 0) && ((chunkState_.chunkStart + chunkState_.chunkSize) == buf_.pos_)) {
// logger.finest("Reading chunk from check chunk");
readChunk(chunkState_);
}
}
}