Package com.ericsson.ssa.router

Source Code of com.ericsson.ssa.router.DefaultApplicationRouter$RouterInfoBean

/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 1997-2009 Sun Microsystems, Inc. All rights reserved.
* Copyright (c) Ericsson AB, 2004-2008. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License").  You
* may not use this file except in compliance with the License. You can obtain
* a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
* or glassfish/bootstrap/legal/LICENSE.txt.  See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
* Sun designates this particular file as subject to the "Classpath" exception
* as provided by Sun in the GPL Version 2 section of the License file that
* accompanied this code.  If applicable, add the following below the License
* Header, with the fields enclosed by brackets [] replaced by your own
* identifying information: "Portions Copyrighted [year]
* [name of copyright owner]"
*
* Contributor(s):
*
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license."  If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above.  However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/

package com.ericsson.ssa.router;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.util.HashMap;
import java.util.List;
import java.util.Properties;
import java.util.StringTokenizer;
import java.util.Vector;
import java.util.logging.Level;

import javax.servlet.sip.ServletParseException;
import javax.servlet.sip.SipServletRequest;
import javax.servlet.sip.ar.SipApplicationRouter;
import javax.servlet.sip.ar.SipApplicationRouterInfo;
import javax.servlet.sip.ar.SipApplicationRoutingDirective;
import javax.servlet.sip.ar.SipApplicationRoutingRegion;
import javax.servlet.sip.ar.SipApplicationRoutingRegionType;
import javax.servlet.sip.ar.SipRouteModifier;
import javax.servlet.sip.ar.SipTargetedRequestInfo;

import org.jvnet.glassfish.comms.util.LogUtil;

import java.util.logging.Logger;

/**
* Default Application Router implementation according to JSR289.
*
* TODO:
*  - adapt to latest JSR289 interfaces from Sankar
*  - implement destroy method
*  - investigate if getNextApplication may return null
*  - currently only config files which are accessible by getResourceAsStream
*    are supported, is this enough?
*  - internationalized logging
*
* @author elnyvbo
*/
public class DefaultApplicationRouter implements SipApplicationRouter {

  private static final Logger theirLog = LogUtil.AR_LOGGER.getLogger();
  private static final String LOGPREFIX =
    "com.ericsson.ssa.router.DefaultApplicationRouter.";
 
  // Application Router info is a cached double HashMap
  //
  // key: SIP request method name
  // value: hashMap which maps stateinfo to router info
  //
  // TODO OK to use HashMap, memorywise?
  private HashMap<String, HashMap<Integer, RouterInfoBean>> itsRouterInfoMap =
    new HashMap<String, HashMap<Integer, RouterInfoBean>>();
 
  private static final String CONFIGFILE_PROPERTY =
    "javax.servlet.sip.dar.configuration";
    private static final String[] EMPTY_STRING_ARRAY = new String[0];
 
  /*
   * (non-Javadoc)
   *
   * @see javax.servlet.sip.SipApplicationRouter#deployedApplications(java.util.List)
   */
  public void applicationDeployed(List<String> applications) {
    if (theirLog.isLoggable(Level.INFO)) {
      for (String app : applications) {
        theirLog.log(Level.INFO, LOGPREFIX + "applicationdeployed", app);
      }
    }

    // An application was added to the chain, this typically implies
    // that application router configuration has also changed. So refresh.
    this.readConfiguration();
  }

  /*
   * (non-Javadoc)
   *
   * @see javax.servlet.sip.SipApplicationRouter#destroy()
   */
  public void destroy() {
    if (theirLog.isLoggable(Level.INFO)) {
      theirLog.log(Level.INFO, LOGPREFIX + "destroy");
    }
   
    // TODO what? when is this called? Let's just clean up some memory...
    itsRouterInfoMap.clear();
  }

  /*
   * TODO improve method performance
   *
   * (non-Javadoc)
   * @see javax.servlet.sip.SipApplicationRouter#getNextApplication(javax.servlet.sip.SipServletReadOnlyRequest, boolean, javax.servlet.sip.SipApplicationRoutingDirective, java.lang.Object)
   */
  public SipApplicationRouterInfo getNextApplication(
      SipServletRequest initialRequest,
      SipApplicationRoutingRegion region,
      SipApplicationRoutingDirective directive,
                       SipTargetedRequestInfo targetedRequestInfo,
      Serializable stateInfo) {
   
    if (theirLog.isLoggable(Level.FINEST)) {
        theirLog.log(Level.FINEST, LOGPREFIX + "getNextApplication");
    }
   
          if (targetedRequestInfo != null && targetedRequestInfo.getApplicationName() != null) {
              if (theirLog.isLoggable(Level.FINEST)) {
                  theirLog.log(Level.FINEST, LOGPREFIX + "targeted_request_type",
                          new Object[] {targetedRequestInfo.getType().name(), targetedRequestInfo.getApplicationName()});
              }
              return new SipApplicationRouterInfo(targetedRequestInfo.getApplicationName(), region, initialRequest.getRequestURI().toString(), EMPTY_STRING_ARRAY, SipRouteModifier.NO_ROUTE, targetedRequestInfo.getApplicationName());
          }

   
    Integer order = new Integer(0);
    // Upon first invocation, stateInfo will not yet be set.
    if (stateInfo != null){
      // only Integer stateInfo supported
      try {
        order = new Integer(stateInfo.toString());
      }
      catch (NumberFormatException e){
        theirLog.log(Level.SEVERE, LOGPREFIX + "wrongstateinfo");
        return null;
      }
    }
   
    // lookup request method in cache 
    String method = initialRequest.getMethod();
    theirLog.log(Level.INFO, LOGPREFIX + "infolookup", new Object[] { method, order });
   
    // lookup routing info in cache for this method
    RouterInfoBean infoBean = null;
    HashMap<Integer, RouterInfoBean> infoMap = itsRouterInfoMap.get(method);
   
    if (infoMap == null){
      theirLog.log(Level.INFO, LOGPREFIX + "noinfofound", method);
      return null; // TODO is this allowed?
    } else {
      // stateinfo contains the Integer we last put in. Increase the
      // Integer by one, lookup the next routing info in cache.
      order++;
      infoBean = infoMap.get(order);
      if (infoBean == null){
        theirLog.log(Level.INFO, LOGPREFIX + "noinfofoundforthisorder", new Object[] { method, order });
        return null; // TODO is this allowed? 
       
        // TODO return appname=null
       
      }
      theirLog.log(Level.INFO, LOGPREFIX + "infofound");
    }

    // Determine subscriber URI
    String subId = infoBean.getSubscriberId();
    String subUri = null;
    if (subId.startsWith("DAR:")) {
      // subscriber URI is to be retrieved from request
      String header = subId.substring(4).trim();
      // (IndexOutOfBounds exception was checked at config-time)
      try {
        subUri = initialRequest.getAddressHeader(header).getURI().toString();
      } catch (ServletParseException e){
        theirLog.log(Level.SEVERE, LOGPREFIX + "invalidsubscriberuri", header);
        theirLog.log(Level.SEVERE, LOGPREFIX + e.getMessage(), e);
      }
    }
   
    // Determine route info
    String[] routes = new String[] {infoBean.getRouteUri()};
    SipRouteModifier modifier =
      SipRouteModifier.valueOf(infoBean.getRouteModifier());
   
    // Determine routing region
    String regionOutStr = infoBean.getRegion();
    SipApplicationRoutingRegionType regionOutType =
      SipApplicationRoutingRegionType.valueOf(regionOutStr);
    SipApplicationRoutingRegion regionOut =
      new SipApplicationRoutingRegion(regionOutStr, regionOutType);
   
    SipApplicationRouterInfo info =
      new SipApplicationRouterInfo(
        infoBean.getApplicationName(),
        regionOut,
        subUri,
        routes,
        modifier,
        "" + infoBean.getStateInfo());
       
   
    if (theirLog.isLoggable(Level.FINEST)) {
        theirLog.log(Level.FINEST, LOGPREFIX + "returninfo", info);
    }
    return info;
  }

  /*
   * (non-Javadoc)
   * @see javax.servlet.sip.SipApplicationRouter#init()
   */
  public void init(){
    theirLog.log(Level.INFO, LOGPREFIX + "initempty");
    this.readConfiguration();
  }
 
  /*
   * (non-Javadoc)
   * @see javax.servlet.sip.SipApplicationRouter#init(java.util.List)
   */
  public void init(Properties props) {
        if (theirLog.isLoggable(Level.FINE)) {
          theirLog.log(Level.FINE, LOGPREFIX + "init");   
        }
    init();
  }

  /*
   * (non-Javadoc)
   * @see javax.servlet.sip.SipApplicationRouter#undeployedApplications(java.util.List)
   */
  public void applicationUndeployed(List<String> applications) {
    for (String app : applications) {
      if (theirLog.isLoggable(Level.FINE)) {
        theirLog.log(Level.FINE, LOGPREFIX + "applicationundeployed", app)
      }
    }
   
    // An application was removed from the chain, this typically implies
    // that application router configuration has also changed. So refresh.
    this.readConfiguration();
  }
 
  private void readConfiguration(){
   
    itsRouterInfoMap.clear();
   
    // read configuration file as specified in system property
    String filename = System.getProperty(CONFIGFILE_PROPERTY);
   
    if (filename==null || "".equals(filename)){
      theirLog.log(Level.WARNING, LOGPREFIX + "noconfigurationfound", CONFIGFILE_PROPERTY);
      return;
    }
    else {
      theirLog.log(Level.INFO, LOGPREFIX + "configurationfound", filename);
    }

    // TODO what kind of file references are supported?
    // Could this be a URL as well?
    // For now assuming local file system
   
    // open config file
    try {
      FileReader input = new FileReader(filename);
      BufferedReader bufRead = new BufferedReader(input);

      // parse file and populate itsRouterInfoMap based on each line.
      String line = bufRead.readLine().trim();
      int linenr = 1;
      while (line != null) {
        if (theirLog.isLoggable(Level.FINE)) {
          theirLog.log(Level.FINE, LOGPREFIX + "lineread", line);
        }
        try {
          // skip empty lines
          String trimmedLine = line.trim();
          if (!("".equals(trimmedLine))){
            this.parseLine(trimmedLine);
          }
          line = bufRead.readLine();
        }
        catch (Exception e){
          theirLog.log(Level.SEVERE, LOGPREFIX + "lineparsingfailed", linenr);
          theirLog.log(Level.SEVERE, LOGPREFIX + e.getMessage(), e);
          break;
        }
        linenr++;
      }
      bufRead.close();
    } catch (IOException e) {
      theirLog.log(Level.SEVERE, LOGPREFIX + "configfileopenfailed");
      theirLog.log(Level.SEVERE, LOGPREFIX + e.getMessage(), e);
    }
  }
 
  /**
   * Helper method for parsing lines in the DAR configuration
   * file, as specified in Appendix C of JSR289. Each line shall be
   * of the form:
   *
   *  METHOD: (sip-router-info-1), (sip-router-info-2), ...etcetera...  
   * 
   * Any deviations in this format will result in the line being ignored.
   *
   * TODO adapt to latest JSR289 code from Sankar
   *
   * @param aLine line to parse
   */
  private void parseLine(String aLine) throws RouterConfigException {
    // Find first colon to determine method
    int colonindex = aLine.indexOf(':');
    String method = aLine.substring(0, colonindex);
    if (theirLog.isLoggable(Level.FINE)) { 
      theirLog.log(Level.FINE, LOGPREFIX + "parsingformethod", method);
    }
    HashMap<Integer, RouterInfoBean> infoMap =
      new HashMap<Integer, RouterInfoBean>();
   
    // Look for '(' occurances to determine next sip-router-info String
    StringTokenizer st =
      new StringTokenizer(aLine.substring(colonindex + 1).trim(), "(");
    while (st.hasMoreElements()){
      String s_tmp= st.nextToken();
      // only consider what is between brackets
      String s = s_tmp.substring(0, s_tmp.indexOf(')'));
      // s should be a comma separated list where each
      // element should be enclosed in double quotes
      StringTokenizer st2 = new StringTokenizer(s, ",");
      Vector<String> tokens = new Vector<String>();
      while (st2.hasMoreElements()){
        String quotedToken = st2.nextToken().trim();
        // find closing quotes
        String token = quotedToken.substring(
          1, quotedToken.lastIndexOf('\"'));
        tokens.add(token);
      }
     
      RouterInfoBean infoBean = new RouterInfoBean();
      infoBean.setApplicationName(tokens.get(0));
      infoBean.setSubscriberId(tokens.get(1));
      infoBean.setRegion(tokens.get(2));
      infoBean.setRouteUri(tokens.get(3));
      infoBean.setRouteModifier(tokens.get(4));
     
      // stateinfo should be parsed 
      String stateInfo = tokens.get(5);
      infoBean.setStateInfo(stateInfo);
      infoMap.put(infoBean.getStateInfo(), infoBean);
    }   
    itsRouterInfoMap.put(method, infoMap);
  }
 
  /**
   * RouterInfoBean introduced to hold routerInfo as parsed from the DAR
   * config file. Actual SipApplicationRouterInfo objects will only be
   * generated when a request is passed in.
   *
   * @author elnyvbo
   */
  private class RouterInfoBean {
    private String applicationName;
    private String subscriberId;
    private String region;
    private String routeUri;
    private String routeModifier;
    private Integer stateInfo;
   
    public String getApplicationName() {
      return applicationName;
    }
    public void setApplicationName(String applicationName) {
      this.applicationName = applicationName;
    }
    public String getRegion() {
      return region;
    }
    public void setRegion(String region) {
      this.region = region;
    }
    public String getRouteModifier() {
      return routeModifier;
    }
    public void setRouteModifier(String routeModifier) {
      this.routeModifier = routeModifier;
    }
    public String getRouteUri() {
      return routeUri;
    }
    public void setRouteUri(String routeUri) {
      this.routeUri = routeUri;
    }
    public Integer getStateInfo() {
      return stateInfo;
    }
    public void setStateInfo(String stateInfo)
      throws RouterConfigException {
      // stateInfo should be an integer number
      try {
        this.stateInfo = new Integer(stateInfo);
      }
      catch (NumberFormatException e) {
        throw new RouterConfigException(
          "Malformed stateInfo in DAR configuration: "
          + stateInfo, e);
      }
    }
    public String getSubscriberId() {
      return subscriberId;
    }
    public void setSubscriberId(String subscriberId)
      throws RouterConfigException {
      // verify correct format in case of DAR: directive
      if (subscriberId.startsWith("DAR:")){
        try {
          subscriberId.substring(4);
        }
        catch (IndexOutOfBoundsException e){
          throw new RouterConfigException (
            "Malformed subscriberId in DAR configuration: "
            + subscriberId, e);
        }
      }
      this.subscriberId = subscriberId;
    }
   
    public String toString() {
      return "appName=" + applicationName +
        ", subId=" + subscriberId +
        ", region=" + region +
        ", routeUri=" + routeUri +
        ", routeModifier=" + routeModifier +
        ", stateInfo=" + stateInfo;
    }
  }

  public void configurationChanged(InputStream configInputStream) {
    // TODO Auto-generated method stub
   
  }
}
TOP

Related Classes of com.ericsson.ssa.router.DefaultApplicationRouter$RouterInfoBean

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.