All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jdiameter.client.impl.parser.MessageImpl Maven / Gradle / Ivy

The newest version!
 /*
  * TeleStax, Open Source Cloud Communications
  * Copyright 2011-2016, TeleStax Inc. and individual contributors
  * by the @authors tag.
  *
  * This program is free software: you can redistribute it and/or modify
  * under the terms of the GNU Affero General Public License as
  * published by the Free Software Foundation; either version 3 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 Affero General Public License for more details.
  *
  * You should have received a copy of the GNU Affero General Public License
  * along with this program.  If not, see 
  *
  * This file incorporates work covered by the following copyright and
  * permission notice:
  *
  *   JBoss, Home of Professional Open Source
  *   Copyright 2007-2011, Red Hat, Inc. and individual contributors
  *   by the @authors tag. See the copyright.txt in the distribution for a
  *   full listing of individual contributors.
  *
  *   This is free software; you can redistribute it and/or modify it
  *   under the terms of the GNU Lesser General Public License as
  *   published by the Free Software Foundation; either version 2.1 of
  *   the License, or (at your option) any later version.
  *
  *   This software 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
  *   Lesser General Public License for more details.
  *
  *   You should have received a copy of the GNU Lesser General Public
  *   License along with this software; if not, write to the Free
  *   Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
  *   02110-1301 USA, or see the FSF site: http://www.fsf.org.
  */

package org.jdiameter.client.impl.parser;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import org.jdiameter.api.Answer;
import org.jdiameter.api.ApplicationId;
import org.jdiameter.api.Avp;
import org.jdiameter.api.AvpDataException;
import org.jdiameter.api.AvpSet;
import org.jdiameter.api.InternalException;
import org.jdiameter.client.api.IEventListener;
import org.jdiameter.client.api.IMessage;
import org.jdiameter.client.api.controller.IPeer;
import org.jdiameter.client.impl.router.RouterImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Represents a Diameter message.
 *
 * @author [email protected]
 * @author  Alexandre Mendonca 
 * @author  Bartosz Baranowski 
 */
public class MessageImpl implements IMessage {

  private static final long serialVersionUID = 1L;

  private static final Logger logger = LoggerFactory.getLogger(MessageImpl.class);
  private static final MessageParser parser = new MessageParser();
  int state = STATE_NOT_SENT;

  short version = 1, flags;
  int commandCode;
  long applicationId;
  long hopByHopId;
  boolean notMutableHopByHop;
  long endToEndId;

  AvpSetImpl avpSet;

  boolean isNetworkRequest = false;

  transient IPeer peer;
  transient TimerTask timerTask;
  transient IEventListener listener;

  // Cached result for getApplicationIdAvps() method. It is called extensively and takes some time.
  // Potential place for dirt, but Application IDs don't change during message life time.
  transient List applicationIds;

  /**
   * Create empty message
   *
   * @param parser
   * @param commandCode
   * @param appId
   */
  MessageImpl(int commandCode, long appId) {
    this.commandCode = commandCode;
    this.applicationId = appId;

    this.avpSet = new AvpSetImpl();
    this.endToEndId = parser.getNextEndToEndId();
  }

  /**
   * Create empty message
   *
   * @param parser
   * @param commandCode
   * @param applicationId
   * @param flags
   * @param hopByHopId
   * @param endToEndId
   * @param avpSet
   */
  MessageImpl(int commandCode, long applicationId, short flags, long hopByHopId, long endToEndId, AvpSetImpl avpSet) {
    this(commandCode, applicationId);
    this.flags = flags;
    this.hopByHopId = hopByHopId;
    this.endToEndId = endToEndId;
    if (avpSet != null) {
      this.avpSet = avpSet;
    }
  }

  //  /**
  //   * Create empty message
  //   *
  //   * @param metaData
  //   * @param parser
  //   * @param commandCode
  //   * @param appId
  //   */
  //  MessageImpl(MetaData metaData, MessageParser parser, int commandCode, long appId) {
  //    this(commandCode, appId);
  //    try {
  //      getAvps().addAvp(Avp.ORIGIN_HOST, metaData.getLocalPeer().getUri().getFQDN(), true, false, true);
  //      getAvps().addAvp(Avp.ORIGIN_REALM, metaData.getLocalPeer().getRealmName(), true, false, true);
  //    }
  //    catch (Exception e) {
  //      logger.debug("Can not create message", e);
  //    }
  //  }

  /**
   * Create Answer
   *
   * @param request parent request
   */
  private MessageImpl(MessageImpl request) {
    this(request.getCommandCode(), request.getHeaderApplicationId());
    copyHeader(request);
    setRequest(false);
    parser.copyBasicAvps(this, request, true);
    // if we set REQUEST_TABLE_SIZE to 0, we store routing info at answer
    if (RouterImpl.REQUEST_TABLE_SIZE == 0) {
      addRoutingInfo(request);
    }
  }

  private String[] routingInfo = {null, null};

  private void addRoutingInfo(MessageImpl request) {
    for (Avp a :request.getAvps()) {
      if (a.getCode() == Avp.ORIGIN_HOST) {
        try {
          routingInfo[0] = a.getDiameterIdentity();
          if (routingInfo[1] != null) {
            return;
          }
        }
        catch (AvpDataException e) {
          logger.error("Unable to read Origin-Host AVP value for storing Routing Info", e);
        }
      }
      else if (a.getCode() == Avp.ORIGIN_REALM) {
        try {
          routingInfo[1] = a.getDiameterIdentity();
          if (routingInfo[0] != null) {
            return;
          }
        }
        catch (AvpDataException e) {
          logger.error("Unable to read Origin-Realm AVP value for storing Routing Info", e);
        }
      }
    }
  }

  public String[] getRoutingInfo() {
    return routingInfo;
  }

  @Override
  public byte getVersion() {
    return (byte) version;
  }

  @Override
  public boolean isRequest() {
    return (flags & 0x80) != 0;
  }

  @Override
  public void setRequest(boolean b) {
    if (b) {
      flags |= 0x80;
    }
    else {
      flags &= 0x7F;
    }
  }

  @Override
  public boolean isProxiable() {
    return (flags & 0x40) != 0;
  }

  @Override
  public void setProxiable(boolean b) {
    if (b) {
      flags |= 0x40;
    }
    else {
      flags &= 0xBF;
    }
  }

  @Override
  public boolean isError() {
    return (flags & 0x20) != 0;
  }

  @Override
  public void setError(boolean b) {
    if (b) {
      flags |= 0x20;
    }
    else {
      flags &= 0xDF;
    }
  }

  @Override
  public boolean isReTransmitted() {
    return (flags & 0x10) != 0;
  }

  @Override
  public void setReTransmitted(boolean b) {
    if (b) {
      flags |= 0x10;
    }
    else {
      flags &= 0xEF;
    }
  }

  @Override
  public int getCommandCode() {
    return this.commandCode;
  }

  @Override
  public String getSessionId() {
    try {
      Avp avpSessionId = avpSet.getAvp(Avp.SESSION_ID);
      return avpSessionId != null ? avpSessionId.getUTF8String() : null;
    }
    catch (AvpDataException ade) {
      logger.error("Failed to fetch Session-Id", ade);
      return null;
    }
  }

  @Override
  public Answer createAnswer() {
    MessageImpl answer = new MessageImpl(this);
    return answer;
  }

  @Override
  public Answer createAnswer(long resultCode) {
    MessageImpl answer = new MessageImpl(this);
    try {
      answer.getAvps().addAvp(Avp.RESULT_CODE, resultCode, true, false, true);
    }
    catch (Exception e) {
      logger.debug("Can not create answer message", e);
    }
    //Its set in constructor.
    //answer.setRequest(false);
    return answer;
  }

  @Override
  public Answer createAnswer(long vendorId, long experimentalResultCode) {
    MessageImpl answer = new MessageImpl(this);
    try {
      AvpSet exp_code = answer.getAvps().addGroupedAvp(297, true, false);
      exp_code.addAvp(Avp.VENDOR_ID, vendorId, true, false, true);
      exp_code.addAvp(Avp.EXPERIMENTAL_RESULT_CODE, experimentalResultCode, true, false, true);
    }
    catch (Exception e) {
      logger.debug("Can not create answer message", e);
    }
    answer.setRequest(false);
    return answer;
  }

  @Override
  public long getApplicationId() {
    return applicationId;
  }

  @Override
  public ApplicationId getSingleApplicationId() {
    return getSingleApplicationId(this.applicationId);
  }

  @Override
  public List getApplicationIdAvps() {
    if (this.applicationIds != null) {
      return this.applicationIds;
    }

    List rc = new ArrayList();
    try {
      AvpSet authAppId = avpSet.getAvps(Avp.AUTH_APPLICATION_ID);
      for (Avp anAuthAppId : authAppId) {
        rc.add(ApplicationId.createByAuthAppId((anAuthAppId).getInteger32()));
      }
      AvpSet accAppId = avpSet.getAvps(Avp.ACCT_APPLICATION_ID);
      for (Avp anAccAppId : accAppId) {
        rc.add(ApplicationId.createByAccAppId((anAccAppId).getInteger32()));
      }
      AvpSet specAppId = avpSet.getAvps(Avp.VENDOR_SPECIFIC_APPLICATION_ID);
      for (Avp aSpecAppId : specAppId) {
        long vendorId = 0, acctApplicationId = 0, authApplicationId = 0;
        AvpSet avps = (aSpecAppId).getGrouped();
        for (Avp localAvp : avps) {
          if (localAvp.getCode() == Avp.VENDOR_ID) {
            vendorId = localAvp.getUnsigned32();
          }
          if (localAvp.getCode() == Avp.AUTH_APPLICATION_ID) {
            authApplicationId = localAvp.getUnsigned32();
          }
          if (localAvp.getCode() == Avp.ACCT_APPLICATION_ID) {
            acctApplicationId = localAvp.getUnsigned32();
          }
        }
        if (authApplicationId != 0) {
          rc.add(ApplicationId.createByAuthAppId(vendorId, authApplicationId));
        }
        if (acctApplicationId != 0) {
          rc.add(ApplicationId.createByAccAppId(vendorId, acctApplicationId));
        }
      }
    }
    catch (Exception exception) {
      return new ArrayList();
    }

    this.applicationIds = rc;
    return this.applicationIds;
  }

  @Override
  public ApplicationId getSingleApplicationId(long applicationId) {
    logger.debug("In getSingleApplicationId for application id [{}]", applicationId);
    List appIds = getApplicationIdAvps();
    logger.debug("Application Ids in this message are:");
    ApplicationId firstOverall = null;
    ApplicationId firstWithZeroVendor = null;
    ApplicationId firstWithNonZeroVendor = null;
    for (ApplicationId id : appIds) {
      logger.debug("[{}]", id);
      if (firstOverall == null) {
        firstOverall = id;
      }
      if (applicationId != 0) {
        if (firstWithZeroVendor == null && id.getVendorId() == 0 && (applicationId == id.getAuthAppId() || applicationId == id.getAcctAppId())) {
          firstWithZeroVendor = id;
        }
        if (firstWithNonZeroVendor == null && id.getVendorId() != 0 && (applicationId == id.getAuthAppId() || applicationId == id.getAcctAppId())) {
          firstWithNonZeroVendor = id;
          break;
        }
      }
    }
    ApplicationId toReturn = null;
    if (firstWithNonZeroVendor != null) {
      toReturn = firstWithNonZeroVendor;
      logger.debug("Returning [{}] as the first application id because its the first vendor specific one found", toReturn);
    }
    else if (firstWithZeroVendor != null) {
      toReturn = firstWithZeroVendor;
      logger.debug("Returning [{}] as the first application id because there are no vendor specific ones found", toReturn);
    }
    else {
      toReturn = firstOverall;
      logger.debug("Returning [{}] as the first application id because none with the requested app ids were found", toReturn);
    }

    if (toReturn == null) {
      // TODO: ammendonca: improve this (find vendor? use common app list map?)
      logger.debug("There are no Application-Id AVPs. Using the value in the header and assuming as Auth Application-Id [{}]", this.applicationId);
      toReturn = ApplicationId.createByAuthAppId(this.applicationId);
    }

    return toReturn;
  }

  @Override
  public long getHopByHopIdentifier() {
    return hopByHopId;
  }

  @Override
  public long getEndToEndIdentifier() {
    return endToEndId;
  }

  @Override
  public AvpSet getAvps() {
    return avpSet;
  }

  protected void copyHeader(MessageImpl request) {
    endToEndId = request.endToEndId;
    hopByHopId = request.hopByHopId;
    version    = request.version;
    flags      = request.flags;
    peer       = request.peer;
  }

  @Override
  public Avp getResultCode() {
    return getAvps().getAvp(Avp.RESULT_CODE);
  }

  @Override
  public void setNetworkRequest(boolean isNetworkRequest) {
    this.isNetworkRequest = isNetworkRequest;
  }

  @Override
  public boolean isNetworkRequest() {
    return isNetworkRequest;
  }

  @Override
  public boolean isWrapperFor(Class aClass) throws InternalException {
    return false;
  }

  @Override
  public  T unwrap(Class aClass) throws InternalException {
    return null;
  }

  // Inner API
  @Override
  public void setHopByHopIdentifier(long hopByHopId) {
    if (hopByHopId < 0) {
      this.hopByHopId = -hopByHopId;
      this.notMutableHopByHop = true;
    }
    else {
      if (!this.notMutableHopByHop) {
        this.hopByHopId = hopByHopId;
      }
    }
  }

  @Override
  public void setEndToEndIdentifier(long endByEndId) {
    this.endToEndId = endByEndId;
  }

  @Override
  public IPeer getPeer() {
    return peer;
  }

  @Override
  public void setPeer(IPeer peer) {
    this.peer = peer;
  }

  @Override
  public int getState() {
    return state;
  }

  @Override
  public long getHeaderApplicationId() {
    return applicationId;
  }

  @Override
  public void setHeaderApplicationId(long applicationId) {
    this.applicationId = applicationId;
  }

  @Override
  public int getFlags() {
    return flags;
  }

  @Override
  public void setState(int newState) {
    state = newState;
  }

  @Override
  public void createTimer(ScheduledExecutorService scheduledFacility, long timeOut, TimeUnit timeUnit) {
    timerTask = new TimerTask(this);
    timerTask.setTimerHandler(scheduledFacility, scheduledFacility.schedule(timerTask, timeOut, timeUnit));
  }

  @Override
  public void runTimer() {
    if (timerTask != null && !timerTask.isDone() && !timerTask.isCancelled()) {
      timerTask.run();
    }
  }

  @Override
  public boolean isTimeOut() {
    return timerTask != null && timerTask.isDone() && !timerTask.isCancelled();
  }

  @Override
  public void setListener(IEventListener listener) {
    this.listener = listener;
  }

  @Override
  public IEventListener getEventListener() {
    return listener;
  }

  @Override
  public void clearTimer() {
    if (timerTask != null) {
      timerTask.cancel();
    }
  }

  @Override
  public String toString() {
    return "MessageImpl{" + "commandCode=" + commandCode + ", flags=" + flags + '}';
  }

  @Override
  public boolean equals(Object o) {
    if (this == o) {
      return true;
    }
    if (o == null || getClass() != o.getClass()) {
      return false;
    }

    MessageImpl message = (MessageImpl) o;

    return applicationId == message.applicationId && commandCode == message.commandCode &&
        endToEndId == message.endToEndId && hopByHopId == message.hopByHopId;
  }

  @Override
  public int hashCode() {
    long result;
    result = commandCode;
    result = 31 * result + applicationId;
    result = 31 * result + hopByHopId;
    result = 31 * result + endToEndId;
    return new Long(result).hashCode();
  }

  @Override
  public String getDuplicationKey() {
    try {
      return getDuplicationKey(getAvps().getAvp(Avp.ORIGIN_HOST).getDiameterIdentity(), getEndToEndIdentifier());
    }
    catch (AvpDataException e) {
      throw new IllegalArgumentException(e);
    }
  }


  @Override
  public String getDuplicationKey(String host, long endToEndId) {
    return host + endToEndId;
  }

  @Override
  public Object clone() {
    try {
      return parser.createMessage(parser.encodeMessage(this));
    }
    catch (Exception e) {
      throw new IllegalArgumentException(e);
    }
  }

  protected static class TimerTask implements Runnable {

    ScheduledFuture timerHandler;
    MessageImpl message;
    ScheduledExecutorService scheduledFacility;

    public TimerTask(MessageImpl message) {
      this.message = message;
    }

    public void setTimerHandler(ScheduledExecutorService scheduledFacility, ScheduledFuture timerHandler) {
      this.scheduledFacility = scheduledFacility;
      this.timerHandler = timerHandler;
    }

    @Override
    public void run() {
      try {
        if (message != null && message.state != STATE_ANSWERED) {
          IEventListener listener = null;
          if (message.listener  instanceof IEventListener) {
            listener = message.listener;
          }
          if (listener != null && listener.isValid()) {
            if (message.peer != null) {
              message.peer.remMessage(message);
            }
            message.listener.timeoutExpired(message);
          }
        }
      }
      catch (Throwable e) {
        logger.debug("Can not process timeout", e);
      }
    }

    public void cancel() {
      if (timerHandler != null) {
        timerHandler.cancel(true);
        if (scheduledFacility instanceof ThreadPoolExecutor && timerHandler instanceof Runnable) {
          ((ThreadPoolExecutor) scheduledFacility).remove((Runnable) timerHandler);
        }
      }
      message = null;
    }

    public boolean isDone() {
      return timerHandler != null && timerHandler.isDone();
    }

    public boolean isCancelled() {
      return timerHandler == null || timerHandler.isCancelled();
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy