/*
* Copyright (c) 2001-2007 Sun Microsystems, Inc. All rights reserved.
*
* The Sun Project JXTA(TM) Software License
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. The end-user documentation included with the redistribution, if any, must
* include the following acknowledgment: "This product includes software
* developed by Sun Microsystems, Inc. for JXTA(TM) technology."
* Alternately, this acknowledgment may appear in the software itself, if
* and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Sun", "Sun Microsystems, Inc.", "JXTA" and "Project JXTA" must
* not be used to endorse or promote products derived from this software
* without prior written permission. For written permission, please contact
* Project JXTA at http://www.jxta.org.
*
* 5. Products derived from this software may not be called "JXTA", nor may
* "JXTA" appear in their name, without prior written permission of Sun.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SUN
* MICROSYSTEMS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* JXTA is a registered trademark of Sun Microsystems, Inc. in the United
* States and other countries.
*
* Please see the license information page at :
* <http://www.jxta.org/project/www/license.html> for instructions on use of
* the license in source files.
*
* ====================================================================
*
* This software consists of voluntary contributions made by many individuals
* on behalf of Project JXTA. For more information on Project JXTA, please see
* http://www.jxta.org.
*
* This license is based on the BSD license adopted by the Apache Foundation.
*/
package net.jxta.impl.endpoint;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.SequenceInputStream;
import java.nio.ByteBuffer;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.Signature;
import java.security.SignatureException;
import java.security.cert.Certificate;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import net.jxta.document.MimeMediaType;
import net.jxta.endpoint.ByteArrayMessageElement;
import net.jxta.endpoint.EndpointAddress;
import net.jxta.endpoint.Message;
import net.jxta.endpoint.MessageElement;
import net.jxta.endpoint.WireFormatMessage;
import net.jxta.endpoint.WireFormatMessageFactory;
import net.jxta.id.IDFactory;
import net.jxta.impl.endpoint.router.EndpointRouterMessage;
import net.jxta.impl.membership.pse.PSECredential;
import net.jxta.impl.membership.pse.PSEMembershipService;
import net.jxta.logging.Logger;
import net.jxta.logging.Logging;
import net.jxta.peergroup.PeerGroup;
import net.jxta.util.LimitInputStream;
/**
* A Wire Format Message which encodes the message into MIME Type
* "application/x-jxta-msg".
* <p/>
* This implementation does nothing with encodings.
* <p/>
* This implementation does not use any MIME parameters attached to the
* requesting mime type.
*
* @see net.jxta.endpoint.WireFormatMessageFactory
* @see <a href="https://jxta-spec.dev.java.net/nonav/JXTAProtocols.html#msgs-fmts-jbm" target="_blank">JXTA Protocols Specification : Binary Message Format</a>
*/
public class WireFormatMessageBinary implements WireFormatMessage {
private final static transient Logger LOG = Logging.getLogger(WireFormatMessageBinary.class.getName());
// Flag bits
protected static final byte HAS_TYPE = 0x01;
protected static final byte HAS_ENCODING = 0x02;
protected static final byte HAS_SIGNATURE = 0x04;
protected static final int MESSAGE_VERSION = 0;
/**
* Our Mime Media Type(s)
*/
private static final MimeMediaType[] myTypes = {
MimeMediaType.valueOf("application/x-jxta-msg") };
/**
* These are the content encodings we support.
*/
private static final MimeMediaType[] myContentEncodings = {
// we support raw binary!
null
};
/**
* Our instantiator for the factory.
*/
public static final WireFormatMessageFactory.Instantiator INSTANTIATOR = new Instantiator();
/**
* Our instantiator.
*/
static class Instantiator implements WireFormatMessageFactory.Instantiator {
/**
* Creates new WireFormatMessageBinary Instantiator
*/
public Instantiator() {}
/**
* {@inheritDoc}
*/
public MimeMediaType[] getSupportedMimeTypes() {
return myTypes;
}
/**
* {@inheritDoc}
*/
public MimeMediaType[] getSupportedContentEncodings() {
return myContentEncodings;
}
/**
* {@inheritDoc}
*/
public Message fromWire(InputStream is, MimeMediaType type, MimeMediaType contentEncoding) throws IOException {
return fromWireExternal(is, type, contentEncoding, WireFormatMessageFactory.CBJX_DISABLE, null, false);
}
public Message fromBuffer(ByteBuffer buffer, MimeMediaType type, MimeMediaType contentEncoding) throws IOException {
return fromBufferExternal(buffer, type, contentEncoding, WireFormatMessageFactory.CBJX_DISABLE, null, false);
}
/**
* {@inheritDoc}
*/
public WireFormatMessage toWire(Message msg, MimeMediaType type, MimeMediaType[] preferedContentEncoding) {
try {
return new WireFormatMessageBinary(msg, type, preferedContentEncoding);
} catch (IOException caught) {
throw new IllegalStateException("Could not build wire format for message due to " + caught.getMessage());
}
}
/**
* Read in a message header from the provided data stream.
*
* @param dis the data stream to read from
* @return hashmap containing the namespace id to namespace values
* @throws IOException if EOF or other IOException is encountered
* during the reading of the header.
*/
private static HashMap readHeader(DataInputStream dis) throws IOException {
// Read message signature
char[] msgsig = new char[4];
try {
msgsig[0] = (char) dis.readByte();
} catch (EOFException failed) {
// LOGGING: was Finer
Logging.logCheckedDebug(LOG, "EOF reading message at first byte of header.\n" + failed);
throw failed;
}
msgsig[1] = (char) dis.readByte();
msgsig[2] = (char) dis.readByte();
msgsig[3] = (char) dis.readByte();
if (msgsig[0] != 'j' || msgsig[1] != 'x' || msgsig[2] != 'm' || msgsig[3] != 'g') {
IOException failure = new IOException(
"Not a message (incorrect signature '" + msgsig[0] + msgsig[1] + msgsig[2] + msgsig[3] + "') ");
Logging.logCheckedError(LOG, failure);
throw failure;
}
// Message version
if (dis.readByte() != MESSAGE_VERSION) {
IOException failure = new IOException("Message not version " + MESSAGE_VERSION);
Logging.logCheckedError(LOG, failure);
throw failure;
}
int namespaceCnt = dis.readShort();
if (namespaceCnt > 253) {
IOException failure = new IOException("Message contains too many namespaces (>253)");
Logging.logCheckedError(LOG, failure);
throw failure;
}
HashMap<Integer, String> id2namespace = new HashMap<Integer, String>(2 + namespaceCnt);
id2namespace.put(0, "");
id2namespace.put(1, "jxta");
int id = 2;
for (int i = 0; i < namespaceCnt; ++i) {
try {
String namespace = readString(dis);
id2namespace.put(id++, namespace);
} catch (IOException caught) {
Logging.logCheckedWarning(LOG, "Error Processing namespace\n", caught);
throw caught;
}
}
// LOGGING: was Finer
Logging.logCheckedDebug(LOG, "Read Message Header with ", (namespaceCnt + 2), " namespaces from ", dis);
return id2namespace;
}
/**
* Read in a message header from the provided data stream.
*
* @param buffer the data buffer to read from
* @return hashmap containing the namespace id to namespace values
* @throws IOException if EOF or other IOException is encountered
* during the reading of the header.
*/
private static HashMap readHeader(ByteBuffer buffer) throws IOException {
// Read message signature
char[] msgsig = new char[4];
msgsig[0] = (char) buffer.get();
msgsig[1] = (char) buffer.get();
msgsig[2] = (char) buffer.get();
msgsig[3] = (char) buffer.get();
if (msgsig[0] != 'j' || msgsig[1] != 'x' || msgsig[2] != 'm' || msgsig[3] != 'g') {
IOException failure = new IOException(
"Not a message (incorrect signature '" + msgsig[0] + msgsig[1] + msgsig[2] + msgsig[3] + "') ");
Logging.logCheckedError(LOG, failure);
throw failure;
}
// Message version
if (buffer.get() != MESSAGE_VERSION) {
IOException failure = new IOException("Message not version " + MESSAGE_VERSION);
Logging.logCheckedError(LOG, failure);
throw failure;
}
int namespaceCnt = buffer.getShort();
// LOGGING: was Finer
Logging.logCheckedDebug(LOG, MessageFormat.format("Message defines {0} namespaces buffer stats{1}", namespaceCnt, buffer.toString()));
if (namespaceCnt > 253) {
IOException failure = new IOException(MessageFormat.format("Message contains too many namespaces ({0} >253)", namespaceCnt));
Logging.logCheckedError(LOG, failure);
throw failure;
}
HashMap<Integer, String> id2namespace = new HashMap<Integer, String>(2 + namespaceCnt);
id2namespace.put(0, "");
id2namespace.put(1, "jxta");
int id = 2;
for (int i = 0; i < namespaceCnt; ++i) {
try {
String namespace = readString(buffer);
id2namespace.put(id++, namespace);
} catch (IOException caught) {
Logging.logCheckedWarning(LOG, "Error Processing namespace\n", caught);
throw caught;
}
}
// LOGGING: was Finer
Logging.logCheckedDebug(LOG, "Read Message Header with ", (namespaceCnt + 2), " namespaces from ", buffer);
return id2namespace;
}
/**
* Read in a message element from the provided data stream.
*
* @param dis the data stream to read from
* @param is the underlying inputstream that dis is based on
* @return object array containing two objects, index[0] contains an
* Integer which identifies the namespace to which this element belongs
* and index[1] contains a MessageElement. If null is returned then
* the DataInputStream reached EOF before reading the first byte of the
* element.
* @throws IOException if EOF or other IOException is encountered
* during the reading of the element.
*/
private Object[] readMessageElement(DataInputStream dis, InputStream is) throws IOException {
// Read message signature
char[] elsig = new char[4];
// if we EOF before the first byte, return null. EOF anywhere else
// and its an error.
try {
elsig[0] = (char) dis.readByte();
} catch (EOFException allDone) {
return null;
}
elsig[1] = (char) dis.readByte();
elsig[2] = (char) dis.readByte();
elsig[3] = (char) dis.readByte();
if (elsig[0] != 'j' || elsig[1] != 'x' || elsig[2] != 'e' || elsig[3] != 'l') {
IOException failure = new IOException("Not a message element (incorrect signature '" + elsig[0] + elsig[1] + elsig[2] + elsig[3] + "') ");
Logging.logCheckedError(LOG, failure);
throw failure;
}
// Namespace id
int nsid = dis.readByte();
// flags
byte flags = dis.readByte();
// Name
String name = readString(dis);
// Mime type
MimeMediaType type;
if ((flags & HAS_TYPE) != 0) {
String typeString = readString(dis);
try {
type = new MimeMediaType(typeString);
} catch (IllegalArgumentException uhoh) {
throw new IOException("Bad MIME type in message element header : " + uhoh.getMessage());
}
} else {
type = MimeMediaType.AOS;
}
int dataLen = dis.readInt();
// LOGGING: was Finer
Logging.logCheckedDebug(LOG, "element : nsid = ", nsid, " name = \'", name, "\' type = \'", type, "\' flags = ",
Integer.toBinaryString(flags), " datalen = ", dataLen);
Object[] res = new Object[2];
res[0] = nsid & 0x000000FF;
byte[] value = null;
Message submsg = null;
// Value
if (type.equalsIngoringParams(myTypes[0])) {
InputStream subis = new LimitInputStream(is, dataLen);
submsg = WireFormatMessageFactory.fromWire(subis, type, null);
} else {
value = new byte[dataLen];
String mayFail = null;
if (Logging.SHOW_WARNING && LOG.isWarnEnabled()) {
mayFail = is.toString();
}
try {
dis.readFully(value);
} catch (EOFException failed) {
Logging.logCheckedWarning(LOG, "had tried to read ", dataLen, " from ", mayFail, " which is now ", is);
throw failed;
}
}
MessageElement sig = null;
if ((flags & HAS_SIGNATURE) != 0) {
Object[] sigRes = readMessageElement(dis, is);
sig = (MessageElement) sigRes[1];
}
if (null != value) {
res[1] = new ByteArrayMessageElement(name, type, value, sig);
} else {
res[1] = new JxtaMessageMessageElement(name, type, submsg, sig);
}
return res;
}
/**
* Read in a message element from the provided data stream.
*
* @param buffer the data buffer to read from
* @return object array containing two objects, index[0] contains an
* Integer which identifies the namespace to which this element belongs
* and index[1] contains a MessageElement. If null is returned then
* the DataInputStream reached EOF before reading the first byte of the
* element.
* @throws IOException if EOF or other IOException is encountered
* during the reading of the element.
*/
private Object[] readMessageElement(ByteBuffer buffer) throws IOException {
// Read message signature
char[] elsig = new char[4];
// if we EOF before the first byte, return null. EOF anywhere else
// and its an error.
elsig[0] = (char) buffer.get();
elsig[1] = (char) buffer.get();
elsig[2] = (char) buffer.get();
elsig[3] = (char) buffer.get();
if (elsig[0] != 'j' || elsig[1] != 'x' || elsig[2] != 'e' || elsig[3] != 'l') {
IOException failure = new IOException("Not a message element (incorrect signature '" + elsig[0] + elsig[1] + elsig[2] + elsig[3] + "') ");
Logging.logCheckedError(LOG, failure);
throw failure;
}
// Namespace id
int nsid = buffer.get();
// flags
byte flags = buffer.get();
// Name
String name = readString(buffer);
// Mime type
MimeMediaType type;
if ((flags & HAS_TYPE) != 0) {
String typeString = readString(buffer);
try {
type = new MimeMediaType(typeString);
} catch (IllegalArgumentException uhoh) {
throw new IOException("Bad MIME type in message element header : " + uhoh.getMessage());
}
} else {
type = MimeMediaType.AOS;
}
int dataLen = buffer.getInt();
// LOGGING: was Finer
Logging.logCheckedDebug(LOG, "element : nsid = ", nsid, " name = \'", name,
"\' type = \'", type, "\' flags = ", Integer.toBinaryString(flags),
" datalen = ", dataLen);
Object[] res = new Object[2];
res[0] = nsid & 0x000000FF;
byte[] value = null;
Message submsg = null;
// Value
if (type.equalsIngoringParams(myTypes[0])) {
byte[] tempB = new byte[dataLen];
buffer.get(tempB);
InputStream subis = new ByteArrayInputStream(tempB);
submsg = WireFormatMessageFactory.fromWire(subis, type, null);
// buffer.position(buffer.position() + dataLen);
} else {
value = new byte[dataLen];
// LOGGING: was Finer
Logging.logCheckedDebug(LOG, MessageFormat.format("expecting {0} bytes, Buffer stats {1}", dataLen, buffer.toString()));
buffer.get(value);
}
MessageElement sig = null;
if ((flags & HAS_SIGNATURE) != 0) {
Object[] sigRes = readMessageElement(buffer);
sig = (MessageElement) sigRes[1];
}
if (null != value) {
res[1] = new ByteArrayMessageElement(name, type, value, sig);
} else {
res[1] = new JxtaMessageMessageElement(name, type, submsg, sig);
}
return res;
}
/**
* Read and construct a string from the data stream.
*
* @param dis the stream to read from
* @return the String which was read.
* @throws IOException if EOF or other IOException is encountered
* during the reading of the string.
*/
private static String readString(DataInputStream dis) throws IOException {
int len = dis.readShort();
if (len < 0) {
throw new IOException("Bad string length in message");
}
byte[] bytes = new byte[len];
dis.readFully(bytes);
return new String(bytes, "UTF8");
}
/**
* Read and construct a string from the data stream.
*
* @param buffer the ByteBuffer to read from
* @return the String which was read.
* @throws IOException if EOF or other IOException is encountered
* during the reading of the string.
*/
private static String readString(ByteBuffer buffer) throws IOException {
int len = buffer.getShort();
if (len < 0) {
throw new IOException("Bad string length in message");
}
byte[] bytes = new byte[len];
buffer.get(bytes);
return new String(bytes, "UTF8");
}
private static byte[] readBytes(DataInputStream dis) throws IOException
{
int tempN = dis.readInt();
byte[] tempRet = new byte[tempN];
dis.readFully(tempRet);
return tempRet;
}
private static byte[] readBytes(ByteBuffer paramBuf) throws IOException
{
int len = paramBuf.getInt();
byte[] bytes = new byte[len];
paramBuf.get(bytes);
return bytes;
}
private static Message emptyMsg()
{
Message tempMsg = new Message();
tempMsg.setMessageProperty(EndpointServiceImpl.MESSAGE_LOOPBACK, false);
tempMsg.setMessageProperty(EndpointServiceImpl.VERIFIED_ADDRESS_SET, new HashSet<EndpointAddress>());
tempMsg.setMessageProperty(EndpointServiceImpl.MESSAGE_SIGNER_SET,new HashSet<X509Certificate>());
return tempMsg;
}
private static Message enforceCbjxOnIncoming(Message paramMsg, MimeMediaType paramType, DataInputStream paramDIS, PeerGroup paramGroup, boolean isTls) throws IOException
{
return enforceCbjxOnIncoming(paramMsg, paramType, readBytes(paramDIS), readBytes(paramDIS), readBytes(paramDIS), paramGroup, isTls);
}
private static Message enforceCbjxOnIncoming(Message paramMsg, MimeMediaType paramType, ByteBuffer paramBuf, PeerGroup paramGroup) throws IOException
{
return enforceCbjxOnIncoming(paramMsg, paramType, readBytes(paramBuf), readBytes(paramBuf), readBytes(paramBuf), paramGroup, false);
}
private static Message enforceCbjxOnIncoming(Message paramMsg, MimeMediaType paramType, byte[] tempCertFromWire, byte[] tempSrcFromWire, byte[] tempSigFromWire, PeerGroup paramGroup, boolean isTLS) throws IOException
{
paramMsg.setMessageProperty(EndpointServiceImpl.MESSAGE_LOOPBACK, false);
WireFormatMessage tempWFM = WireFormatMessageFactory.toWire(paramMsg,paramType, null);
try {
//fingerprint
CertificateFactory tempCF = CertificateFactory.getInstance( "X.509" );
Certificate tempCert = tempCF.generateCertificate( new ByteArrayInputStream(tempCertFromWire));
tempCert.verify(tempCert.getPublicKey());
Signature tempSig = Signature.getInstance(WireFormatMessageFactory.CBJX_SIG_ALG);
tempSig.initVerify(tempCert);
ByteBuffer[] tempBBs =tempWFM.getUnsignedByteBuffers();
for(ByteBuffer tempBB:tempBBs)
{
tempSig.update(tempBB);
}
tempSig.update(tempCertFromWire);
tempSig.update(tempSrcFromWire);
boolean tempVerified = tempSig.verify(tempSigFromWire);
if(tempVerified)
{
EndpointAddress tempEA = new EndpointAddress(new String(tempSrcFromWire));
Set<EndpointAddress> tempSet = (Set)paramMsg.getMessageProperty(EndpointServiceImpl.VERIFIED_ADDRESS_SET);
if(tempSet==null)
{
tempSet = new HashSet<EndpointAddress>();
}
if (isTLS) {
EndpointAddress tempTLSEA = new EndpointAddress("jxtatls", tempEA.getProtocolAddress(), tempEA.getServiceName(), tempEA.getServiceParameter());
tempSet.add(tempTLSEA);
} else {
tempSet.add(tempEA);
}
Set<X509Certificate> tempCertSet = (Set)paramMsg.getMessageProperty(EndpointServiceImpl.MESSAGE_SIGNER_SET);
if(tempCertSet==null)
{
tempCertSet = new HashSet<X509Certificate>();
}
tempCertSet.add((X509Certificate)tempCert);
MessageElement tempERM = paramMsg.getMessageElement(EndpointRouterMessage.MESSAGE_NS, EndpointRouterMessage.MESSAGE_NAME+"-fingerprint");
if(tempERM==null)
{
paramMsg.setMessageProperty(EndpointServiceImpl.VERIFIED_ADDRESS_SET, tempSet);
paramMsg.setMessageProperty(EndpointServiceImpl.MESSAGE_SIGNER_SET, tempCertSet);
}
else
{
DataInputStream tempDIS = new DataInputStream(tempERM.getStream());
byte[] tempPayloadFromERM = readBytes(tempDIS);
byte[] tempCertFromERM = readBytes(tempDIS);
byte[] tempSigFromERM = readBytes(tempDIS);
Certificate tempCertERM = tempCF.generateCertificate( new ByteArrayInputStream(tempCertFromERM));
tempCertERM.verify(tempCertERM.getPublicKey());
tempSig.initVerify(tempCertERM);
tempSig.update(tempPayloadFromERM);
tempSig.update(tempCertFromERM);
boolean tempVerifiedERM = tempSig.verify(tempSigFromERM);
if(tempVerifiedERM)
{
EndpointRouterMessage tempMsgERM = new EndpointRouterMessage(paramMsg, false, paramGroup.getMembershipService());
net.jxta.impl.id.CBID.PeerID tempSupposedToBe = (net.jxta.impl.id.CBID.PeerID) IDFactory.newPeerID(paramGroup.getPeerGroupID(), tempCertERM.getPublicKey().getEncoded());
EndpointAddress tempEASuposedToBe = new EndpointAddress(tempSupposedToBe.toURI());
if(tempMsgERM.getSrcAddress().equals(tempEASuposedToBe))
{
//Passed the security check
if (isTLS) {
EndpointAddress tempTLSEASuposedToBe = new EndpointAddress("jxtatls", tempEASuposedToBe.getProtocolAddress(), tempEASuposedToBe.getServiceName(), tempEASuposedToBe.getServiceParameter());
tempSet.add(tempTLSEASuposedToBe);
} else {
tempSet.add(tempEASuposedToBe);
}
tempCertSet.add((X509Certificate)tempCertERM);
paramMsg.setMessageProperty(EndpointServiceImpl.VERIFIED_ADDRESS_SET, tempSet);
paramMsg.setMessageProperty(EndpointServiceImpl.MESSAGE_SIGNER_SET, tempCertSet);
}
else
{
LOG.error("EndpointRouterMsg declared src address does not match the sender's address. tempMsgERM.getSrcAddress()="+tempMsgERM.getSrcAddress()+", tempEASuposedToBe="+tempEASuposedToBe);
return emptyMsg();
}
}
else
{
LOG.error("EndpointRouterMsg signature cannot be verified.");
return emptyMsg();
}
}
return paramMsg;
}
else
{
LOG.error("The signature of the message from the wire cannot be verified.");
return emptyMsg();
}
} catch (NoSuchAlgorithmException ex) {
LOG.error(null, ex);
return emptyMsg();
} catch (CertificateException ex) {
LOG.error(null, ex);
return emptyMsg();
} catch (InvalidKeyException ex) {
LOG.error(null, ex);
return emptyMsg();
} catch (NoSuchProviderException ex) {
LOG.error(null, ex);
return emptyMsg();
} catch (SignatureException ex) {
LOG.error(null, ex);
return emptyMsg();
}
}
public Message fromWireExternal(InputStream is, MimeMediaType type, MimeMediaType contentEncoding, boolean paramDisableCbjx, PeerGroup paramGroup, boolean isTls) throws IOException {
return fromWireExternal(is, type, contentEncoding, paramDisableCbjx, paramGroup, true, isTls);
}
private Message fromWireExternal(InputStream is, MimeMediaType type, MimeMediaType contentEncoding, boolean paramDisableCbjx, PeerGroup paramGroup, boolean isEnforce, boolean isTls) throws IOException {
Message msg = new Message();
if (Logging.SHOW_DEBUG && LOG.isDebugEnabled()) {
LOG.debug("Reading " + msg + " from " + is);
}
DataInputStream dis = new DataInputStream(is);
HashMap idToNamespace = readHeader(dis);
int elementCnt = dis.readShort();
// LOGGING: was FINER
if (Logging.SHOW_DEBUG && LOG.isDebugEnabled()) {
LOG.debug("Message element count " + elementCnt + " from " + is);
}
int eachElement = 0;
do {
// LOGGING: was FINER
if (Logging.SHOW_DEBUG && LOG.isDebugEnabled()) {
LOG.debug("Read element " + eachElement + " of " + elementCnt + " from " + is + " for " + msg);
}
Object[] anElement;
try {
anElement = readMessageElement(dis, is);
} catch (IOException failed) {
if (Logging.SHOW_ERROR && LOG.isErrorEnabled()) {
LOG.error("Failure reading element " + eachElement + " of " +
elementCnt + " from " + is + " for " + msg,
failed);
}
throw failed;
}
if (null == anElement) {
break;
}
String namespace = (String) idToNamespace.get(anElement[0]);
if (null == namespace) {
if (Logging.SHOW_ERROR && LOG.isErrorEnabled()) {
LOG.error("Element identified a namespace which was not defined for this message.");
}
throw new IOException("Element identified a namespace which was not defined for this message.");
}
msg.addMessageElement(namespace, (MessageElement) anElement[1]);
eachElement++;
// LOGGING: was FINER
if (Logging.SHOW_DEBUG && LOG.isDebugEnabled()) {
LOG.debug(
"Add element (name=\'" + ((MessageElement) anElement[1]).getElementName() + "\') #" + eachElement
+ " of #" + elementCnt + " elements from " + dis.toString());
}
} while (((0 == elementCnt) || (eachElement < elementCnt)));
if ((elementCnt != 0) && (eachElement != elementCnt)) {
throw new IOException("Found wrong number of elements in message.");
}
if(paramDisableCbjx)
{
return msg;
}
else
{
if (isEnforce && paramGroup != null) { //paramGroup != null - to aid MessageTest.testMessageSerialization()
return enforceCbjxOnIncoming(msg, type, dis, paramGroup, isTls);
} else
return msg;
}
}
public Message fromBufferExternal(ByteBuffer buffer, MimeMediaType type, MimeMediaType contentEncoding, boolean paramDisableCbjx, PeerGroup paramGroup) throws IOException {
return fromBufferExternal(buffer, type, contentEncoding, paramDisableCbjx, paramGroup, true);
}
public Message fromBufferExternal(ByteBuffer buffer, MimeMediaType type, MimeMediaType contentEncoding, boolean paramDisableCbjx, PeerGroup paramGroup, boolean isEnforce) throws IOException {
// FIXME 20020504 bondolo@jxta.org Ignores type and contentEncoding completely.
Message msg = new Message();
if (Logging.SHOW_DEBUG && LOG.isDebugEnabled()) {
LOG.debug("Reading " + msg + " from " + buffer);
}
HashMap idToNamespace = readHeader(buffer);
int elementCnt = buffer.getShort();
// LOGGING: was FINER
if (Logging.SHOW_DEBUG && LOG.isDebugEnabled()) {
LOG.debug("Message element count " + elementCnt + " from " + buffer);
}
int eachElement = 0;
do {
// LOGGING: was FINER
if (Logging.SHOW_DEBUG && LOG.isDebugEnabled()) {
LOG.debug("Read element " + eachElement + " of " + elementCnt + " from " + buffer + " for " + msg);
}
Object[] anElement;
try {
anElement = readMessageElement(buffer);
// LOGGING: was FINER
if (Logging.SHOW_DEBUG && LOG.isDebugEnabled()) {
LOG.debugParams("Read element of size {}, [{}] {}", anElement.length, anElement.toString(), buffer.toString());
}
} catch (IOException failed) {
if (Logging.SHOW_ERROR && LOG.isErrorEnabled()) {
LOG.error("Failure reading element " + eachElement + " of " + elementCnt + " from " + buffer + " for " + msg, failed);
}
throw failed;
}
if (null == anElement) {
break;
}
String namespace = (String) idToNamespace.get(anElement[0]);
if (null == namespace) {
if (Logging.SHOW_ERROR && LOG.isErrorEnabled()) {
LOG.error("Element identified a namespace which was not defined for this message.");
}
throw new IOException("Element identified a namespace which was not defined for this message.");
}
msg.addMessageElement(namespace, (MessageElement) anElement[1]);
eachElement++;
// LOGGING: was FINER
if (Logging.SHOW_DEBUG && LOG.isDebugEnabled()) {
LOG.debug("Add element (name=\'" + ((MessageElement) anElement[1]).getElementName() + "\') #" + eachElement+ " of #" + elementCnt + " elements from " + buffer.toString());
}
} while (((0 == elementCnt) || (eachElement < elementCnt)));
if ((elementCnt != 0) && (eachElement != elementCnt)) {
throw new IOException("Found wrong number of elements in message.");
}
if(paramDisableCbjx)
{
return msg;
}
else
{
if (isEnforce) {
return enforceCbjxOnIncoming(msg, type, buffer, paramGroup);
} else
return msg;
}
}
public WireFormatMessage toWireExternal(Message msg, MimeMediaType type, MimeMediaType[] preferedContentEncoding, boolean paramDisableCbjx, PeerGroup paramGroup) {
try {
return new WireFormatMessageBinary(msg, type, preferedContentEncoding, paramDisableCbjx, paramGroup, false);
} catch (IOException caught) {
throw new IllegalStateException("Could not build wire format for message due to " + caught.getMessage());
}
}
public WireFormatMessage toWireExternalWithTls(Message msg, MimeMediaType type, MimeMediaType[] preferedContentEncoding, boolean paramDisableCbjx, PeerGroup paramGroup) {
try {
return new WireFormatMessageBinary(msg, type, preferedContentEncoding, paramDisableCbjx, paramGroup, true);
} catch (IOException caught) {
throw new IllegalStateException("Could not build wire format for message due to " + caught.getMessage());
}
}
}
/**
* Internal representation for a binary format wire message. Implemented
* as an inner class to allow content encodings to be easily mapped on
* top of the streams this class produces.
*/
static class binaryMessageProxy implements WireFormatMessage {
final Message message;
final MimeMediaType type;
final List<binaryElementProxy> elements = new ArrayList<binaryElementProxy>();
final Map<String, Integer> namespaceIDs = new HashMap<String, Integer>();
final List<String> namespaces = new ArrayList<String>();
byte[] header;
private boolean disableCbjx;
private PeerGroup group;
private boolean isTls = false;
binaryMessageProxy(Message msg, MimeMediaType type, boolean paramDisableCbjx, PeerGroup paramGroup, boolean isTls) throws IOException {
message = msg;
this.isTls = isTls;
this.type = type; // we may generate different content based upon the type.
this.disableCbjx=paramDisableCbjx;
this.group=paramGroup;
assignNamespaceIds();
// build the element proxies
Message.ElementIterator eachElement = message.getMessageElements();
while (eachElement.hasNext()) {
MessageElement anElement = eachElement.next();
byte namespaceid = namespaceIDs.get(eachElement.getNamespace()).byteValue();
elements.add(new binaryElementProxy(namespaceid, anElement));
}
buildHeader();
}
/**
* {@inheritDoc}
*/
public String getFileExtension() {
return "???";
}
/**
* {@inheritDoc}
*/
public MimeMediaType getMimeType() {
return type;
}
/**
* {@inheritDoc}
*/
public ByteBuffer[] getByteBuffers() {
ByteBuffer[] byteBuffers = getUnsignedByteBuffers();
if(this.disableCbjx)
{
}
else
{
byteBuffers = enforceCbjxOnOutgoingWithByteBuffer(byteBuffers);
}
return byteBuffers;
}
/**
* {@inheritDoc}
*/
public ByteBuffer[] getUnsignedByteBuffers() {
List<ByteBuffer> partBuffers = new ArrayList<ByteBuffer>();
partBuffers.add(ByteBuffer.wrap(header));
for (binaryElementProxy anElement : elements) {
partBuffers.addAll(Arrays.asList(anElement.getByteBuffers()));
}
// LOGGING: was Finer
Logging.logCheckedDebug(LOG, MessageFormat.format("Returning {0} buffers for {1}", partBuffers.size(), message));
return partBuffers.toArray(new ByteBuffer[partBuffers.size()]);
}
private ByteBuffer[] enforceCbjxOnOutgoingWithByteBuffer(ByteBuffer[] localByteBuffers)
{
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DataOutputStream tempDOS = new DataOutputStream(baos);
try {
try {
PSEMembershipService tempPSE = (PSEMembershipService) this.group.getMembershipService();
PSECredential tempCred = (PSECredential) tempPSE.getDefaultCredential();
//Cert
byte[] tempCert = tempCred.getCertificate().getEncoded();
tempDOS.writeInt(tempCert.length);
tempDOS.write(tempCert);
byte[] tempSrc;
//Source
if (isTls) {
tempSrc = new String("jxtatls://" + (String)this.group.getPeerID().getUniqueValue()).getBytes();
tempDOS.writeInt(tempSrc.length);
tempDOS.write(tempSrc);
} else {
tempSrc = this.group.getPeerID().toURI().toString().getBytes();
tempDOS.writeInt(tempSrc.length);
tempDOS.write(tempSrc);
}
ByteBuffer[] tempBBs = this.getUnsignedByteBuffers();
CbjxSigInputStream mbais = new CbjxSigInputStream(tempCert, tempSrc, tempBBs);
WireFormatMessageBinarySignatureBridge wireFormatMessageBinarySignatureBridge = new WireFormatMessageBinarySignatureBridge(WireFormatMessageFactory.CBJX_SIG_ALG, mbais);
byte[] tempSigned = tempPSE.signWireFormatMessageBinary(wireFormatMessageBinarySignatureBridge);
tempDOS.writeInt(tempSigned.length);
tempDOS.write(tempSigned);
} catch (InvalidKeyException ex){
tempDOS.writeInt(0);
LOG.error(null, ex);
} catch (SignatureException ex) {
tempDOS.writeInt(0);
LOG.error(null, ex);
} catch (CertificateEncodingException ex) {
tempDOS.writeInt(0);
LOG.error(null, ex);
}
tempDOS.flush();
} catch (IOException ex) {
LOG.error(null, ex);
return localByteBuffers;
}
ByteBuffer[] tmpByteBuffers = new ByteBuffer[localByteBuffers.length + 1];
for (int i=0;i<localByteBuffers.length;i++)
tmpByteBuffers[i] = localByteBuffers[i];
tmpByteBuffers[tmpByteBuffers.length-1] = ByteBuffer.wrap(baos.toByteArray());
return tmpByteBuffers;
}
/**
* {@inheritDoc}
*/
public InputStream getStream() throws IOException {
Logging.logCheckedDebug(LOG, "Getting stream for ", message);
List<InputStream> streamParts = new ArrayList<InputStream>();
streamParts.add(new ByteArrayInputStream(header));
for (binaryElementProxy anElement : elements) {
streamParts.add(anElement.getStream());
}
if(this.disableCbjx)
{
}
else
{
this.enforceCbjxOnOutgoing(streamParts);
}
InputStream theStream = new SequenceInputStream(Collections.enumeration(streamParts));
// LOGGING: was Finer
Logging.logCheckedDebug(LOG, MessageFormat.format("Returning {0}@{1} for {2}", theStream.getClass().getName(),
System.identityHashCode(theStream), message));
return theStream;
}
private void enforceCbjxOnOutgoing(List<InputStream> streamParts)
{
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DataOutputStream tempDOS = new DataOutputStream(baos);
try {
try {
PSEMembershipService tempPSE = (PSEMembershipService) this.group.getMembershipService();
PSECredential tempCred = (PSECredential) tempPSE.getDefaultCredential();
//Cert
byte[] tempCert = tempCred.getCertificate().getEncoded();
tempDOS.writeInt(tempCert.length);
tempDOS.write(tempCert);
byte[] tempSrc;
//Source
if (isTls) {
tempSrc = new String("jxtatls://" + (String)this.group.getPeerID().getUniqueValue()).getBytes();
tempDOS.writeInt(tempSrc.length);
tempDOS.write(tempSrc);
} else {
tempSrc = this.group.getPeerID().toURI().toString().getBytes();
tempDOS.writeInt(tempSrc.length);
tempDOS.write(tempSrc);
}
ByteBuffer[] tempBBs = this.getUnsignedByteBuffers();
CbjxSigInputStream mbais = new CbjxSigInputStream(tempCert, tempSrc, tempBBs);
WireFormatMessageBinarySignatureBridge wireFormatMessageBinarySignatureBridge = new WireFormatMessageBinarySignatureBridge(WireFormatMessageFactory.CBJX_SIG_ALG, mbais);
byte[] tempSigned = tempPSE.signWireFormatMessageBinary(wireFormatMessageBinarySignatureBridge);
tempDOS.writeInt(tempSigned.length);
tempDOS.write(tempSigned);
} catch (InvalidKeyException ex){
tempDOS.writeInt(0);
LOG.error(null, ex);
} catch (SignatureException ex) {
tempDOS.writeInt(0);
LOG.error(null, ex);
} catch (CertificateEncodingException ex) {
tempDOS.writeInt(0);
LOG.error(null, ex);
}
tempDOS.flush();
} catch (IOException ex) {
LOG.error(null, ex);
}
streamParts.add(new ByteArrayInputStream(baos.toByteArray()));
}
/**
* {@inheritDoc}
*/
public void sendToStream(OutputStream sendTo) throws IOException {
Logging.logCheckedDebug(LOG, "Sending ", message, " to ",
sendTo.getClass().getName(), "@", System.identityHashCode(sendTo));
DataOutputStream tempDOS = new DataOutputStream(sendTo);
tempDOS.write(header);
Iterator eachElement = elements.listIterator();
while (eachElement.hasNext()) {
binaryElementProxy anElement = (binaryElementProxy) eachElement.next();
anElement.sendToStream(tempDOS);
}
// added to aid MessageTest.testMessageSerialization() - group not set up ....
if (this.group != null) {
this.enforceCbjxOnOutgoing(tempDOS);
} else {
}
}
private void enforceCbjxOnOutgoing(DataOutputStream tempDOS) throws IOException
{
if(this.disableCbjx)
{
}
else
{
try {
PSEMembershipService tempPSE = (PSEMembershipService) this.group.getMembershipService();
PSECredential tempCred = (PSECredential) tempPSE.getDefaultCredential();
//Cert
byte[] tempCert = tempCred.getCertificate().getEncoded();
tempDOS.writeInt(tempCert.length);
tempDOS.write(tempCert);
byte[] tempSrc;
//Source
if (isTls) {
tempSrc = new String("jxtatls://" + (String)this.group.getPeerID().getUniqueValue()).getBytes();
tempDOS.writeInt(tempSrc.length);
tempDOS.write(tempSrc);
} else {
tempSrc = this.group.getPeerID().toURI().toString().getBytes();
tempDOS.writeInt(tempSrc.length);
tempDOS.write(tempSrc);
}
ByteBuffer[] tempBBs = this.getUnsignedByteBuffers();
CbjxSigInputStream mbais = new CbjxSigInputStream(tempCert, tempSrc, tempBBs);
WireFormatMessageBinarySignatureBridge wireFormatMessageBinarySignatureBridge = new WireFormatMessageBinarySignatureBridge(WireFormatMessageFactory.CBJX_SIG_ALG, mbais);
byte[] tempSigned = tempPSE.signWireFormatMessageBinary(wireFormatMessageBinarySignatureBridge);
tempDOS.writeInt(tempSigned.length);
tempDOS.write(tempSigned);
} catch (InvalidKeyException ex){
tempDOS.writeInt(0);
LOG.error(null, ex);
} catch (SignatureException ex) {
tempDOS.writeInt(0);
LOG.error(null, ex);
} catch (CertificateEncodingException ex) {
tempDOS.writeInt(0);
LOG.error(null, ex);
}
tempDOS.flush();
}
}
private class CbjxSigInputStream extends InputStream {
private int available;
private boolean closed=false;
private int cur_offset;
private int cur_array;
private Object[] arrays;
private CbjxSigInputStream(byte[] cert, byte[] src, ByteBuffer[] bbs) {
this.arrays = new Object[bbs.length+2];
this.available = cert.length + src.length;;
for(int i=0;i<bbs.length;i++)
{
arrays[i] = bbs[i];
available = available + (bbs[i].remaining() - bbs[i].position());
}
arrays[arrays.length-2] = cert;
arrays[arrays.length-1] = src;
}
@Override
public int read() throws IOException {
if (available<1)
return -1;
if (closed)
throw new EOFException("CbjxSigInputStream is closed!");
int c;
if (arrays[cur_array] instanceof ByteBuffer) {
ByteBuffer bb = (ByteBuffer)arrays[cur_array];
c = (int)(bb.get(cur_offset) & 0xff);
cur_offset++;
if (cur_offset == (bb.remaining() - bb.position())) {
cur_offset = 0;
cur_array++;
}
} else {
byte[] ba = (byte[])arrays[cur_array];
c = (int)(ba[cur_offset] & 0xff);
cur_offset++;
if (cur_offset == ba.length) {
cur_offset = 0;
cur_array++;
}
}
available--;
return c;
}
public synchronized int read(byte b[]) throws IOException {
return read(b, 0, b.length);
}
public synchronized int read(byte b[], int off, int len) throws IOException {
if (available<1)
return -1;
if (closed)
throw new EOFException("CbjxSigInputStream is closed!");
int n = off;
int total = 0;
int last = Math.min(off+len, b.length);
while ((cur_array < arrays.length) && (n < last)) {
if (arrays[cur_array] instanceof ByteBuffer) {
ByteBuffer bb = (ByteBuffer)arrays[cur_array];
int num_left = (bb.remaining() - bb.position()) - cur_offset;
int tocopy = Math.min(num_left, last - n);
//System.arraycopy(ba, cur_offset, b, n, tocopy);
int bbOffset = cur_offset+bb.position();
for (int i=0; i<tocopy; i++)
b[i+n] = bb.get(bbOffset+i);
total += tocopy;
n += tocopy;
cur_offset += tocopy;
available -= tocopy;
if (cur_offset == (bb.remaining() - bb.position())) {
cur_offset = 0;
cur_array++;
}
} else {
byte[] ba = (byte[])arrays[cur_array];
int num_left = ba.length - cur_offset;
int tocopy = Math.min(num_left, last - n);
System.arraycopy(ba, cur_offset, b, n, tocopy);
total += tocopy;
n += tocopy;
cur_offset += tocopy;
available -= tocopy;
if (cur_offset == ba.length) {
cur_offset = 0;
cur_array++;
}
}
}
return total;
}
/**
* Return the number of bytes available for reading.
*/
public synchronized int available() throws IOException {
return available;
}
/**
* Close this stream.
*/
public synchronized void close() throws IOException {
closed = true;
}
}
/**
* {@inheritDoc}
*/
public long getByteLength() {
long size = 0;
size += header.length;
for (binaryElementProxy element : elements) {
binaryElementProxy anElement = element;
size += anElement.getByteLength();
}
return size;
}
/**
* {@inheritDoc}
*/
public MimeMediaType getContentEncoding() {
return null;
}
/**
* Scans the source message to build a HashMap of the namespaces used
* in the message and assign and id to each namespace.
*/
private void assignNamespaceIds() {
int id = 0;
Iterator tempNamespaces = message.getMessageNamespaces();
// insert the predefined namespaces.
namespaceIDs.put("", id++);
this.namespaces.add("");
namespaceIDs.put("jxta", id++);
this.namespaces.add("jxta");
// insert items in the vector if they are not found in the map
while (tempNamespaces.hasNext()) {
String namespace = (String) tempNamespaces.next();
if (namespaceIDs.get(namespace) == null) {
namespaceIDs.put(namespace, id++);
this.namespaces.add(namespace);
}
}
if (id >= 256) {
throw new IllegalStateException("WireFormatMessageBinary does not support more than 255 namespaces");
}
}
/**
* Builds the wire format header for the message.
*
* @throws IOException if for some reason the header cannot be built.
*/
private void buildHeader() throws IOException {
ByteArrayOutputStream headerBytes = new ByteArrayOutputStream(256);
DataOutputStream tempHeader = new DataOutputStream(headerBytes);
tempHeader.writeBytes("jxmg");
tempHeader.writeByte(MESSAGE_VERSION);
tempHeader.writeShort(namespaces.size() - 2);
for (int eachNamespace = 2; eachNamespace < namespaces.size(); eachNamespace++) {
byte[] elementName = namespaces.get(eachNamespace).getBytes("UTF8");
tempHeader.writeShort(elementName.length);
tempHeader.write(elementName, 0, elementName.length);
}
tempHeader.writeShort(elements.size());
tempHeader.flush();
tempHeader.close();
headerBytes.flush();
headerBytes.close();
this.header = headerBytes.toByteArray();
}
}
public final static class WireFormatMessageBinarySignatureBridge {
private String signatureAlgorithm = null;
private InputStream signStream = null;
private WireFormatMessageBinarySignatureBridge(String signatureAlgorithm, InputStream signStream) {
this.signatureAlgorithm = signatureAlgorithm;
this.signStream = signStream;
}
public String getSignatureAlgorithm() {
return signatureAlgorithm;
}
public InputStream getInputStream() {
return signStream;
}
}
/**
* Proxy for a message element. Handles the serialization of the element
* meta information.
*/
static class binaryElementProxy {
final byte namespaceid;
binaryElementProxy sig;
MessageElement element;
byte[] header;
binaryElementProxy(byte namespaceid, MessageElement element) throws IOException {
this.namespaceid = namespaceid;
this.element = element;
MessageElement tempSig = element.getSignature();
if (null != tempSig) {
this.sig = new binaryElementProxy(namespaceid, tempSig);
}
buildHeader();
}
void buildHeader() throws IOException {
byte[] elementName = element.getElementName().getBytes("UTF8");
byte[] elementType = null;
if (!MimeMediaType.AOS.equals(element.getMimeType())) {
elementType = element.getMimeType().toString().getBytes("UTF8");
}
// FIXME 20020504 bondolo@jxta.org Do something with encodings.
ByteArrayOutputStream headerBytes = new ByteArrayOutputStream(256);
DataOutputStream tempHeader = new DataOutputStream(headerBytes);
tempHeader.writeBytes("jxel");
tempHeader.writeByte(namespaceid);
tempHeader.writeByte(((null != elementType) ? HAS_TYPE : 0) | ((null != sig) ? HAS_SIGNATURE : 0));
tempHeader.writeShort(elementName.length);
tempHeader.write(elementName, 0, elementName.length);
if (null != elementType) {
tempHeader.writeShort(elementType.length);
tempHeader.write(elementType, 0, elementType.length);
}
// FIXME content encoding should go here
long dataLen = element.getByteLength();
if (dataLen > Integer.MAX_VALUE) {
throw new IllegalStateException("WireFormatMessageBinary does not support elements longer than 4GB");
}
tempHeader.writeInt((int) dataLen);
tempHeader.flush();
tempHeader.close();
headerBytes.flush();
headerBytes.close();
this.header = headerBytes.toByteArray();
}
public long getByteLength() {
long size = 0;
size += header.length;
size += element.getByteLength();
if (null != sig) {
size += sig.getByteLength();
}
return size;
}
public ByteBuffer[] getByteBuffers() {
List<ByteBuffer> partBuffers = new ArrayList<ByteBuffer>();
partBuffers.add(ByteBuffer.wrap(header));
partBuffers.add(ByteBuffer.wrap(element.getBytes(false)));
if (null != sig) {
partBuffers.addAll(Arrays.asList(sig.getByteBuffers()));
}
return partBuffers.toArray(new ByteBuffer[partBuffers.size()]);
}
public InputStream getStream() throws IOException {
List<InputStream> streamParts = new ArrayList<InputStream>();
streamParts.add(new ByteArrayInputStream(header));
streamParts.add(element.getStream());
if (null != sig) {
streamParts.add(sig.getStream());
}
return new SequenceInputStream(Collections.enumeration(streamParts));
}
public void sendToStream(OutputStream sendTo) throws IOException {
sendTo.write(header);
element.sendToStream(sendTo);
if (null != sig) {
sig.sendToStream(sendTo);
}
}
}
/**
* The message we are serializing.
*/
private final Message msg;
/**
* The mod count of the message when we started. Used for detecting
* (illegal) modifications.
*/
private final int msgModCount;
/**
* The mime type of the encoded message.
*/
private final MimeMediaType type;
/**
* The mime type of the content encoding for this message.
*/
private final MimeMediaType contentEncoding;
/**
* The serialization peer to the Message.
*/
private final binaryMessageProxy msgProxy;
/**
* Creates a new instance of WireFormatMessageBinary. Called only by the
* Instantiator.
*
* @param msg the message being serialized
* @param type the mime mediatype being requested.
* @param preferedContentEncodings The ranked content encodings preferred by the recipient.
* @throws java.io.IOException if an io error occurs
*/
WireFormatMessageBinary(Message msg, MimeMediaType type, MimeMediaType[] preferedContentEncodings) throws IOException {
if (null == msg) {
throw new IllegalArgumentException("Null message!");
}
this.msg = msg;
this.msgModCount = msg.getMessageModCount();
if (null == type) {
throw new IllegalArgumentException("Null mime type!");
}
int matchedIdx = -1;
for (int eachType = 0; eachType < myTypes.length; eachType++) {
if (type.equalsIngoringParams(myTypes[eachType])) {
matchedIdx = eachType;
break;
}
}
if (-1 == matchedIdx) {
throw new IllegalArgumentException("Unsupported mime type!");
}
// FIXME 20020504 bondolo@jxta.org Check the mimetype params to make
// sure we can support them.
this.type = type;
// FIXME 20020504 bondolo@jxta.org Do something with encodings.
this.contentEncoding = myContentEncodings[0];
msgProxy = new binaryMessageProxy(msg, type, false, null, false);
}
WireFormatMessageBinary(Message msg, MimeMediaType type, MimeMediaType[] preferedContentEncodings, boolean paramDisableCbjx, PeerGroup paramGroup, boolean isTls) throws IOException {
if (null == msg) {
throw new IllegalArgumentException("Null message!");
}
this.msg = msg;
this.msgModCount = msg.getMessageModCount();
if (null == type) {
throw new IllegalArgumentException("Null mime type!");
}
int matchedIdx = -1;
for (int eachType = 0; eachType < myTypes.length; eachType++) {
if (type.equalsIngoringParams(myTypes[eachType])) {
matchedIdx = eachType;
break;
}
}
if (-1 == matchedIdx) {
throw new IllegalArgumentException("Unsupported mime type!");
}
// FIXME 20020504 bondolo@jxta.org Check the mimetype params to make
// sure we can support them.
this.type = type;
// FIXME 20020504 bondolo@jxta.org Do something with encodings.
this.contentEncoding = myContentEncodings[0];
msgProxy = new binaryMessageProxy(msg, type, paramDisableCbjx, paramGroup, isTls);
}
/**
* {@inheritDoc}
*/
@Override
public boolean equals(Object obj) {
throw new UnsupportedOperationException("don't do this");
}
/*
* The cost of just having a finalize routine is high. The finalizer is
* a bottleneck and can delay garbage collection all the way to heap
* exhaustion. Leave this comment as a reminder to future maintainers.
* Below is the reason why finalize is not needed here.
*
* No critical non-memory resource held.
protected void finalize() {
}
*/
/**
* {@inheritDoc}
*/
@Override
public int hashCode() {
throw new UnsupportedOperationException("don't do this");
}
/**
* {@inheritDoc}
*/
public String getFileExtension() {
return "???";
}
/**
* {@inheritDoc}
*/
public MimeMediaType getMimeType() {
return type;
}
/**
* {@inheritDoc}
*/
public InputStream getStream() throws IOException {
if (msg.getMessageModCount() != msgModCount) {
throw new IllegalStateException("message was unexpectedly modified!");
}
msg.modifiable = false;
try {
InputStream result = msgProxy.getStream();
return result;
} finally {
msg.modifiable = true;
}
}
/**
* {@inheritDoc}
*/
public ByteBuffer[] getByteBuffers() {
if (msg.getMessageModCount() != msgModCount) {
throw new IllegalStateException("message was unexpectedly modified!");
}
msg.modifiable = false;
try {
ByteBuffer[] result = msgProxy.getByteBuffers();
return result;
} finally {
msg.modifiable = true;
}
}
/**
* {@inheritDoc}
*/
public ByteBuffer[] getUnsignedByteBuffers() {
if (msg.getMessageModCount() != msgModCount) {
throw new IllegalStateException("message was unexpectedly modified!");
}
msg.modifiable = false;
try {
ByteBuffer[] result = msgProxy.getUnsignedByteBuffers();
return result;
} finally {
msg.modifiable = true;
}
}
/**
* {@inheritDoc}
*/
public void sendToStream(OutputStream sendTo) throws IOException {
if (msg.getMessageModCount() != msgModCount) {
throw new IllegalStateException("message was unexpectedly modified!");
}
msg.modifiable = false;
try {
msgProxy.sendToStream(sendTo);
} finally {
msg.modifiable = true;
}
}
/**
* {@inheritDoc}
*/
public long getByteLength() {
if (msg.getMessageModCount() != msgModCount) {
throw new IllegalStateException("message was unexpectedly modified!");
}
return msgProxy.getByteLength();
}
/**
* {@inheritDoc}
*/
public MimeMediaType getContentEncoding() {
return contentEncoding;
}
}