Package freenet.io.comm

Source Code of freenet.io.comm.Message$FieldNotSetException

/*
* Dijjer - A Peer to Peer HTTP Cache
* Copyright (C) 2004,2005 Change.Tv, Inc
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU 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 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
*/

package freenet.io.comm;

import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.EOFException;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;

import freenet.support.ByteBufferInputStream;
import freenet.support.Fields;
import freenet.support.LogThresholdCallback;
import freenet.support.Logger;
import freenet.support.Serializer;
import freenet.support.ShortBuffer;
import freenet.support.Logger.LogLevel;

/**
* A Message which can be read from and written to a DatagramPacket.
*
* SECURITY REDFLAG WARNING: Messages should normally be recreated rather
* than passed on. Messages can contain sub-messages, these are used to
* avoid having to add whole new message types every time we add one field
* to a message... Passing on a message as-is means it includes the
* sub-messages, which could lead to e.g. labelling, communication between
* colluding nodes along a request route, and just wasting bytes.
*
* FIXME we should get rid of sub-messages.
*
* @author ian
*/
public class Message {

  public static final String VERSION = "$Id: Message.java,v 1.11 2005/09/15 18:16:04 amphibian Exp $";
  private static volatile boolean logMINOR;
  private static volatile boolean logDEBUG;

  static {
    Logger.registerLogThresholdCallback(new LogThresholdCallback(){
      @Override
      public void shouldUpdate(){
        logMINOR = Logger.shouldLog(LogLevel.MINOR, this);
        logDEBUG = Logger.shouldLog(LogLevel.DEBUG, this);
      }
    });
  }

  private final MessageType _spec;
  private final WeakReference<? extends PeerContext> _sourceRef;
  private final boolean _internal;
  private final HashMap<String, Object> _payload = new HashMap<String, Object>(8);
  private List<Message> _subMessages;
  public final long localInstantiationTime;
  final int _receivedByteCount;
  short priority;
  private boolean needsLoadRT;
  private boolean needsLoadBulk;
 
  public static Message decodeMessageFromPacket(byte[] buf, int offset, int length, PeerContext peer, int overhead) {
    ByteBufferInputStream bb = new ByteBufferInputStream(buf, offset, length);
    return decodeMessage(bb, peer, length + overhead, true, false, false);
  }
 
  public static Message decodeMessageLax(byte[] buf, PeerContext peer, int overhead) {
    ByteBufferInputStream bb = new ByteBufferInputStream(buf);
    return decodeMessage(bb, peer, buf.length + overhead, true, false, true);
  }

  private static Message decodeMessage(ByteBufferInputStream bb, PeerContext peer, int recvByteCount,
          boolean mayHaveSubMessages, boolean inSubMessage, boolean veryLax) {
    MessageType mspec;
    try {
      mspec = MessageType.getSpec(bb.readInt(), veryLax);
    } catch (IOException e1) {
      if (logMINOR) Logger.minor(Message.class,"Failed to read message type: "+e1, e1);
      return null;
    }
    if (mspec == null) {
      if (logMINOR) Logger.minor(Message.class, "Bogus message type");
      return null;
    }
    if (mspec.isInternalOnly()) {
      if(logMINOR) Logger.minor(Message.class, "Internal only message");
      return null; // silently discard internal-only messages
    }
    Message m = new Message(mspec, peer, recvByteCount);
    try {
      for (String name : mspec.getOrderedFields()) {
        Class<?> type = mspec.getFields().get(name);
        if (type.equals(LinkedList.class)) { // Special handling for LinkedList to deal with element type
          m.set(name, Serializer
                .readListFromDataInputStream(mspec.getLinkedListTypes().get(name), bb));
        } else {
          m.set(name, Serializer.readFromDataInputStream(type, bb));
        }
      }
      if (mayHaveSubMessages) {
        while (bb.remaining() > 2) { // sizeof(unsigned short) == 2
          ByteBufferInputStream bb2;
          try {
            int size = bb.readUnsignedShort();
            if (bb.remaining() < size) return m;
            bb2 = bb.slice(size);
          } catch (EOFException e) {
            if (logMINOR) Logger.minor(Message.class, "No submessages, returning: "+m);
            return m;
          }
          try {
            Message subMessage = decodeMessage(bb2, peer, 0, false, true, veryLax);
            if (subMessage == null) return m;
            if (logMINOR) Logger.minor(Message.class, "Adding submessage: "+subMessage);
            m.addSubMessage(subMessage);
          } catch (Throwable t) {
            Logger.error(Message.class, "Failed to read sub-message: "+t, t);
          }
        }
      }
    } catch (EOFException e) {
      String msg = peer.getPeer()+" sent a message packet that ends prematurely while deserialising "+mspec.getName();
      if (inSubMessage) {
        if (logMINOR) Logger.minor(Message.class, msg+" in sub-message", e);
      } else Logger.error(Message.class, msg, e);
      return null;
    } catch (IOException e) {
      Logger.error(Message.class, "Unexpected IOException: "+e+" reading from buffer stream", e);
      return null;
    }
    if (logMINOR) Logger.minor(Message.class, "Returning message: "+m+" from "+m.getSource());
    return m;
  }

  public Message(MessageType spec) {
    this(spec, null, 0);
  }

  private Message(MessageType spec, PeerContext source, int recvByteCount) {
    localInstantiationTime = System.currentTimeMillis();
    _spec = spec;
    if (source == null) {
      _internal = true;
      _sourceRef = null;
    } else {
      _internal = false;
      _sourceRef = source.getWeakRef();
    }
    _receivedByteCount = recvByteCount;
    priority = spec.getDefaultPriority();
  }

  /** Drops sub-messages, and makes it locally originated */
  private Message(Message m) {
    _spec = m._spec;
    _sourceRef = null;
    _internal = m._internal;
    _payload.putAll(m._payload);
    _subMessages = null;
    localInstantiationTime = System.currentTimeMillis();
    _receivedByteCount = 0;
    priority = m.priority;
    needsLoadRT = m.needsLoadRT;
    needsLoadBulk = m.needsLoadBulk;
  }

  public boolean getBoolean(String key) {
    return (Boolean) _payload.get(key);
  }

  public byte getByte(String key) {
    return (Byte) _payload.get(key);
  }

  public short getShort(String key) {
    return (Short) _payload.get(key);
  }

  public int getInt(String key) {
    return (Integer) _payload.get(key);
  }

  public long getLong(String key) {
    return (Long) _payload.get(key);
  }

  public double getDouble(String key) {
    return (Double) _payload.get(key);
  }

  public float getFloat(String key) {
    return (Float) _payload.get(key);
  }

  public double[] getDoubleArray(String key) {
    return ((double[]) _payload.get(key));
  }

  public float[] getFloatArray(String key) {
    return (float[]) _payload.get(key);
  }

  public String getString(String key) {
    return (String)_payload.get(key);
  }

  public Object getObject(String key) {
    return _payload.get(key);
  }
 
  public byte[] getShortBufferBytes(String key) {
    ShortBuffer buffer = (ShortBuffer) getObject(key);
    return buffer.getData();
  }

  public void set(String key, boolean b) {
    set(key, Boolean.valueOf(b));
  }

  public void set(String key, byte b) {
    set(key, Byte.valueOf(b));
  }

  public void set(String key, short s) {
    set(key, Short.valueOf(s));
  }

  public void set(String key, int i) {
    set(key, Integer.valueOf(i));
  }

  public void set(String key, long l) {
    set(key, Long.valueOf(l));
  }

  public void set(String key, double d) {
    set(key, Double.valueOf(d));
  }

  public void set(String key, float f) {
    set(key, Float.valueOf(f));
  }

  public void set(String key, Object value) {
    if (!_spec.checkType(key, value)) {
      if (value == null) {
        throw new IncorrectTypeException("Got null for " + key);
      }
      throw new IncorrectTypeException("Got " + value.getClass() + ", expected " + _spec.typeOf(key));
    }
    _payload.put(key, value);
  }

  public byte[] encodeToPacket() {
    return encodeToPacket(true, false);
  }

  private byte[] encodeToPacket(boolean includeSubMessages, boolean isSubMessage) {

    if (logDEBUG) Logger.debug(this, "My spec code: "+_spec.getName().hashCode()+" for "+_spec.getName());
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    DataOutputStream dos = new DataOutputStream(baos);
    try {
      dos.writeInt(_spec.getName().hashCode());
      for (String name : _spec.getOrderedFields()) {
        Serializer.writeToDataOutputStream(_payload.get(name), dos);
      }
      dos.flush();
    } catch (IOException e) {
      e.printStackTrace();
      throw new IllegalStateException(e.getMessage());
    }

    if (_subMessages != null && includeSubMessages) {
      for (Message _subMessage : _subMessages) {
        byte[] temp = _subMessage.encodeToPacket(false, true);
        try {
          dos.writeShort(temp.length);
          dos.write(temp);
        } catch (IOException e) {
          e.printStackTrace();
          throw new IllegalStateException(e.getMessage());
        }
      }
    }

    byte[] buf = baos.toByteArray();
    if (logDEBUG) Logger.debug(this, "Length: "+buf.length+", hash: "+Fields.hashCode(buf));
    return buf;
  }

  @Override
  public String toString() {
    StringBuilder ret = new StringBuilder(1000);
    String comma = "";
    ret.append(_spec.getName()).append(" {");
    for (String name : _spec.getFields().keySet()) {
      ret.append(comma);
      ret.append(name).append('=').append(_payload.get(name));
      comma = ", ";
    }
    ret.append('}');
    return ret.toString();
  }

  public PeerContext getSource() {
    return _sourceRef == null ? null : _sourceRef.get();
  }

  public boolean isInternal() {
      return _internal;
  }

  public MessageType getSpec() {
    return _spec;
  }

  public boolean isSet(String fieldName) {
    return _payload.containsKey(fieldName);
  }

  public Object getFromPayload(String fieldName) throws FieldNotSetException {
    Object r =  _payload.get(fieldName);
    if (r == null) {
      throw new FieldNotSetException(fieldName+" not set");
    }
    return r;
  }

  public static class FieldNotSetException extends RuntimeException {
    private static final long serialVersionUID = 1L;

    public FieldNotSetException(String message) {
      super(message);
    }
  }

  /**
   * Set fields for a routed-to-a-specific-node message.
   * @param nodeIdentity
   */
  public void setRoutedToNodeFields(long uid, double targetLocation, short htl, byte[] nodeIdentity) {
    set(DMT.UID, uid);
    set(DMT.TARGET_LOCATION, targetLocation);
    set(DMT.HTL, htl);
    set(DMT.NODE_IDENTITY, new ShortBuffer(nodeIdentity));
  }

  public int receivedByteCount() {
    return _receivedByteCount;
  }

  public void addSubMessage(Message subMessage) {
    if (_subMessages == null) _subMessages = new ArrayList<Message>();
    _subMessages.add(subMessage);
  }

  public Message getSubMessage(MessageType t) {
    if (_subMessages == null) return null;
    for (Message m : _subMessages) {
      if (m.getSpec() == t) return m;
    }
    return null;
  }

  public Message grabSubMessage(MessageType t) {
    if (_subMessages == null) return null;
    for (int i=0;i<_subMessages.size();i++) {
      Message m = _subMessages.get(i);
      if (m.getSpec() == t) {
        _subMessages.remove(i);
        return m;
      }
    }
    return null;
  }

  public long age() {
    return System.currentTimeMillis() - localInstantiationTime;
  }

  public short getPriority() {
    return priority;
  }
 
  public void boostPriority() {
    priority--;
  }

  public boolean needsLoadRT() {
    return needsLoadRT;
  }
 
  public boolean needsLoadBulk() {
    return needsLoadBulk;
  }
 
  public void setNeedsLoadRT() {
    needsLoadRT = true;
  }
 
  public void setNeedsLoadBulk() {
    needsLoadBulk = true;
  }

  /** Clone the message, clear sub-messages and set originator to self. */
  public Message cloneAndDropSubMessages() {
    return new Message(this);
  }

}
TOP

Related Classes of freenet.io.comm.Message$FieldNotSetException

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.