/*************************************************************************** * Copyright (C) by Fabrizio Montesi * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU Library General Public License as * * published by the Free Software Foundation; either version 2 of the * * License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU Library General Public * * License along with this program; if not, write to the * * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * * * For details about the authors of this software, see the AUTHORS file. * ***************************************************************************/ package jolie.net; import java.io.ByteArrayOutputStream; import java.io.DataInput; import java.io.DataInputStream; import java.io.DataOutput; import java.io.DataOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.Writer; import java.nio.charset.Charset; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.zip.GZIPInputStream; import java.util.zip.GZIPOutputStream; import jolie.net.protocols.ConcurrentCommProtocol; import jolie.runtime.ByteArray; import jolie.runtime.FaultException; import jolie.runtime.Value; import jolie.runtime.ValueVector; import jolie.runtime.VariablePath; public class SodepProtocol extends ConcurrentCommProtocol { private static class DataTypeHeaderId { private static final int NULL = 0; private static final int STRING = 1; private static final int INT = 2; private static final int DOUBLE = 3; private static final int BYTE_ARRAY = 4; private static final int BOOL = 5; private static final int LONG = 6; } public String name() { return "sodep"; } private Charset stringCharset = Charset.forName( "UTF8" ); private String readString( DataInput in ) throws IOException { int len = in.readInt(); if ( len > 0 ) { byte[] bb = new byte[ len ]; in.readFully( bb ); return new String( bb, stringCharset ); } return ""; } private void writeString( DataOutput out, String str ) throws IOException { if ( str.isEmpty() ) { out.writeInt( 0 ); } else { ByteArrayOutputStream bos = new ByteArrayOutputStream(); Writer writer = new OutputStreamWriter( bos, stringCharset ); writer.write( str ); writer.close(); byte[] bb = bos.toByteArray(); out.writeInt( bb.length ); out.write( bb ); } } private ByteArray readByteArray( DataInput in ) throws IOException { int size = in.readInt(); ByteArray ret = null; if ( size > 0 ) { byte[] bytes = new byte[ size ]; in.readFully( bytes, 0, size ); ret = new ByteArray( bytes ); } else { ret = new ByteArray( new byte[0] ); } return ret; } private void writeByteArray( DataOutput out, ByteArray byteArray ) throws IOException { int size = byteArray.size(); out.writeInt( size ); if ( size > 0 ) { out.write( byteArray.getBytes() ); } } private void writeFault( DataOutput out, FaultException fault ) throws IOException { writeString( out, fault.faultName() ); writeValue( out, fault.value() ); } private void writeValue( DataOutput out, Value value ) throws IOException { Object valueObject = value.valueObject(); if ( valueObject == null ) { out.writeByte( DataTypeHeaderId.NULL ); } else if ( valueObject instanceof String ) { out.writeByte( DataTypeHeaderId.STRING ); writeString( out, (String)valueObject ); } else if ( valueObject instanceof Integer ) { out.writeByte( DataTypeHeaderId.INT ); out.writeInt( ((Integer)valueObject).intValue() ); } else if ( valueObject instanceof Double ) { out.writeByte( DataTypeHeaderId.DOUBLE ); out.writeDouble( ((Double)valueObject).doubleValue() ); } else if ( valueObject instanceof ByteArray ) { out.writeByte( DataTypeHeaderId.BYTE_ARRAY ); writeByteArray( out, (ByteArray)valueObject ); } else if ( valueObject instanceof Boolean ) { out.writeByte( DataTypeHeaderId.BOOL ); out.writeBoolean( ((Boolean)valueObject).booleanValue() ); } else if ( valueObject instanceof Long ) { out.writeByte( DataTypeHeaderId.LONG ); out.writeLong( ((Long)valueObject).longValue() ); } else { out.writeByte( DataTypeHeaderId.NULL ); } Map< String, ValueVector > children = value.children(); List< Entry< String, ValueVector > > entries = new LinkedList< Entry< String, ValueVector > >(); for( Entry< String, ValueVector > entry : children.entrySet() ) { if ( !entry.getKey().startsWith( "@" ) ) { entries.add( entry ); } } out.writeInt( entries.size() ); for( Entry< String, ValueVector > entry : entries ) { writeString( out, entry.getKey() ); out.writeInt( entry.getValue().size() ); for( Value v : entry.getValue() ) { writeValue( out, v ); } } } private void writeMessage( DataOutput out, CommMessage message ) throws IOException { out.writeLong( message.id() ); writeString( out, message.resourcePath() ); writeString( out, message.operationName() ); FaultException fault = message.fault(); if ( fault == null ) { out.writeBoolean( false ); } else { out.writeBoolean( true ); writeFault( out, fault ); } writeValue( out, message.value() ); } private Value readValue( DataInput in ) throws IOException { Value value = Value.create(); Object valueObject = null; byte b = in.readByte(); switch( b ) { case DataTypeHeaderId.STRING: valueObject = readString( in ); break; case DataTypeHeaderId.INT: valueObject = Integer.valueOf( in.readInt() ); break; case DataTypeHeaderId.LONG: valueObject = Long.valueOf( in.readLong() ); break; case DataTypeHeaderId.DOUBLE: valueObject = Double.valueOf( in.readDouble() ); break; case DataTypeHeaderId.BYTE_ARRAY: valueObject = readByteArray( in ); break; case DataTypeHeaderId.BOOL: valueObject = Boolean.valueOf( in.readBoolean() ); break; case DataTypeHeaderId.NULL: default: break; } value.setValue( valueObject ); Map< String, ValueVector > children = value.children(); String s; int n, i, size, k; n = in.readInt(); // How many children? ValueVector vec; for( i = 0; i < n; i++ ) { s = readString( in ); vec = ValueVector.create(); size = in.readInt(); for( k = 0; k < size; k++ ) { vec.add( readValue( in ) ); } children.put( s, vec ); } return value; } private FaultException readFault( DataInput in ) throws IOException { String faultName = readString( in ); Value value = readValue( in ); return new FaultException( faultName, value ); } private CommMessage readMessage( DataInput in ) throws IOException { Long id = in.readLong(); String resourcePath = readString( in ); String operationName = readString( in ); FaultException fault = null; if ( in.readBoolean() == true ) { fault = readFault( in ); } Value value = readValue( in ); return new CommMessage( id, operationName, resourcePath, value, fault ); } public SodepProtocol( VariablePath configurationPath ) { super( configurationPath ); } public void send( OutputStream ostream, CommMessage message, InputStream istream ) throws IOException { channel().setToBeClosed( !checkBooleanParameter( "keepAlive", true ) ); String charset = getStringParameter( "charset" ); if ( !charset.isEmpty() ) { stringCharset = Charset.forName( charset ); } GZIPOutputStream gzip = null; String compression = getStringParameter( "compression" ); if ( "gzip".equals( compression ) ) { gzip = new GZIPOutputStream( ostream ); ostream = gzip; } DataOutputStream oos = new DataOutputStream( ostream ); writeMessage( oos, message ); if ( gzip != null ) gzip.finish(); } public CommMessage recv( InputStream istream, OutputStream ostream ) throws IOException { channel().setToBeClosed( !checkBooleanParameter( "keepAlive", true ) ); String charset = getStringParameter( "charset" ); if ( !charset.isEmpty() ) { stringCharset = Charset.forName( charset ); } String compression = getStringParameter( "compression" ); if ( "gzip".equals( compression ) ) { istream = new GZIPInputStream( istream ); } DataInputStream ios = new DataInputStream( istream ); return readMessage( ios ); } }