PageRenderTime 46ms CodeModel.GetById 11ms RepoModel.GetById 0ms app.codeStats 0ms

/servers/diameter/core/jdiameter/impl/src/main/java/org/jdiameter/client/impl/router/RouterImpl.java

http://mobicents.googlecode.com/
Java | 859 lines | 622 code | 68 blank | 169 comment | 172 complexity | 62cde183a4ce0f7c4154daa4f25a5545 MD5 | raw file
Possible License(s): LGPL-3.0, GPL-3.0, LGPL-2.1, GPL-2.0, CC-BY-SA-3.0, CC0-1.0, Apache-2.0, BSD-3-Clause
  1. /*
  2. * JBoss, Home of Professional Open Source
  3. * Copyright 2006-2011, Red Hat, Inc. and individual contributors
  4. * by the @authors tag. See the copyright.txt in the distribution for a
  5. * full listing of individual contributors.
  6. *
  7. * This is free software; you can redistribute it and/or modify it
  8. * under the terms of the GNU Lesser General Public License as
  9. * published by the Free Software Foundation; either version 2.1 of
  10. * the License, or (at your option) any later version.
  11. *
  12. * This software is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  15. * Lesser General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU Lesser General Public
  18. * License along with this software; if not, write to the Free
  19. * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
  20. * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
  21. */
  22. package org.jdiameter.client.impl.router;
  23. import static org.jdiameter.client.impl.helpers.Parameters.AcctApplId;
  24. import static org.jdiameter.client.impl.helpers.Parameters.Agent;
  25. import static org.jdiameter.client.impl.helpers.Parameters.ApplicationId;
  26. import static org.jdiameter.client.impl.helpers.Parameters.AuthApplId;
  27. import static org.jdiameter.client.impl.helpers.Parameters.OwnRealm;
  28. import static org.jdiameter.client.impl.helpers.Parameters.RealmEntry;
  29. import static org.jdiameter.client.impl.helpers.Parameters.RealmTable;
  30. import static org.jdiameter.client.impl.helpers.Parameters.VendorId;
  31. import static org.jdiameter.server.impl.helpers.Parameters.RealmEntryExpTime;
  32. import static org.jdiameter.server.impl.helpers.Parameters.RealmEntryIsDynamic;
  33. import static org.jdiameter.server.impl.helpers.Parameters.RealmHosts;
  34. import static org.jdiameter.server.impl.helpers.Parameters.RealmLocalAction;
  35. import static org.jdiameter.server.impl.helpers.Parameters.RealmName;
  36. import static org.jdiameter.server.impl.helpers.Parameters.RequestTable;
  37. import static org.jdiameter.server.impl.helpers.Parameters.RequestTableClearSize;
  38. import static org.jdiameter.server.impl.helpers.Parameters.RequestTableSize;
  39. import java.io.IOException;
  40. import java.net.URISyntaxException;
  41. import java.net.UnknownServiceException;
  42. import java.util.ArrayList;
  43. import java.util.Arrays;
  44. import java.util.HashMap;
  45. import java.util.List;
  46. import java.util.Map;
  47. import java.util.concurrent.locks.ReadWriteLock;
  48. import java.util.concurrent.locks.ReentrantReadWriteLock;
  49. import org.jdiameter.api.ApplicationId;
  50. import org.jdiameter.api.Avp;
  51. import org.jdiameter.api.AvpDataException;
  52. import org.jdiameter.api.AvpSet;
  53. import org.jdiameter.api.Configuration;
  54. import org.jdiameter.api.IllegalDiameterStateException;
  55. import org.jdiameter.api.InternalException;
  56. import org.jdiameter.api.LocalAction;
  57. import org.jdiameter.api.MetaData;
  58. import org.jdiameter.api.RouteException;
  59. import org.jdiameter.api.URI;
  60. import org.jdiameter.client.api.IAnswer;
  61. import org.jdiameter.client.api.IContainer;
  62. import org.jdiameter.client.api.IMessage;
  63. import org.jdiameter.client.api.IRequest;
  64. import org.jdiameter.client.api.controller.IPeer;
  65. import org.jdiameter.client.api.controller.IPeerTable;
  66. import org.jdiameter.client.api.controller.IRealm;
  67. import org.jdiameter.client.api.controller.IRealmTable;
  68. import org.jdiameter.client.api.router.IRouter;
  69. import org.jdiameter.client.impl.helpers.AppConfiguration;
  70. import org.jdiameter.client.impl.helpers.Parameters;
  71. import org.jdiameter.common.api.concurrent.IConcurrentFactory;
  72. import org.jdiameter.server.api.agent.IAgentConfiguration;
  73. import org.slf4j.Logger;
  74. import org.slf4j.LoggerFactory;
  75. /**
  76. * Diameter Routing Core
  77. *
  78. * @author <a href="mailto:brainslog@gmail.com"> Alexandre Mendonca </a>
  79. * @author <a href="mailto:baranowb@gmail.com"> Bartosz Baranowski </a>
  80. */
  81. public class RouterImpl implements IRouter {
  82. public static final int DONT_CACHE = 0;
  83. public static final int ALL_SESSION = 1;
  84. public static final int ALL_REALM = 2;
  85. public static final int REALM_AND_APPLICATION = 3;
  86. public static final int ALL_APPLICATION = 4;
  87. public static final int ALL_HOST = 5;
  88. public static final int ALL_USER = 6;
  89. //
  90. private static final Logger logger = LoggerFactory.getLogger(RouterImpl.class);
  91. protected MetaData metaData;
  92. //
  93. //private ConcurrentHashMap<String, String[]> network = new ConcurrentHashMap<String, String[]>();
  94. protected IRealmTable realmTable;
  95. // Redirection feature
  96. public final int REDIRECT_TABLE_SIZE = 1024;
  97. //TODO: index it differently.
  98. protected List<RedirectEntry> redirectTable = new ArrayList<RedirectEntry>(REDIRECT_TABLE_SIZE);
  99. protected IConcurrentFactory concurrentFactory;
  100. protected IContainer container;
  101. // Answer routing feature
  102. public static int REQUEST_TABLE_SIZE = 10 * 1024;
  103. public static int REQUEST_TABLE_CLEAR_SIZE = 2 * 1024;
  104. protected ReadWriteLock requestEntryTableLock = new ReentrantReadWriteLock();
  105. protected ReadWriteLock redirectTableLock = new ReentrantReadWriteLock();
  106. protected Map<Long, AnswerEntry> requestEntryTable ;
  107. protected List<Long> requestSortedEntryTable = new ArrayList<Long>();
  108. protected boolean isStopped = true;
  109. public RouterImpl(IContainer container,IConcurrentFactory concurrentFactory, IRealmTable realmTable,Configuration config, MetaData aMetaData) {
  110. this.concurrentFactory = concurrentFactory;
  111. this.metaData = aMetaData;
  112. this.realmTable = realmTable;
  113. this.container = container;
  114. logger.debug("Constructor for RouterImpl: Calling loadConfiguration");
  115. loadConfiguration(config);
  116. }
  117. protected void loadConfiguration(Configuration config) {
  118. logger.debug("Loading Router Configuration. Populating Realms, Application IDs, etc");
  119. //add local realm : this might not be good
  120. String localRealm = config.getStringValue(OwnRealm.ordinal(),null);
  121. String localHost = config.getStringValue(Parameters.OwnDiameterURI.ordinal(),null);
  122. try {
  123. this.realmTable.addLocalRealm(localRealm, new URI(localHost).getFQDN());
  124. }
  125. catch (UnknownServiceException use) {
  126. throw new RuntimeException("Unable to create URI from Own URI config value:" + localHost, use);
  127. }
  128. catch (URISyntaxException use) {
  129. throw new RuntimeException("Unable to create URI from Own URI config value:" + localHost, use);
  130. }
  131. if (config.getChildren(RequestTable.ordinal()) != null) {
  132. AppConfiguration requestTableConfig = (AppConfiguration) config.getChildren(org.jdiameter.server.impl.helpers.Parameters.RequestTable.ordinal())[0];
  133. int tSize = (int)requestTableConfig.getIntValue(RequestTableSize.ordinal(),(Integer) RequestTableSize.defValue());
  134. int tClearSize = (int)requestTableConfig.getIntValue(RequestTableClearSize.ordinal(),(Integer) RequestTableClearSize.defValue());
  135. if(tClearSize >= tSize) {
  136. logger.warn("Configuration entry RequestTable, attribute 'clear_size' [{}] should not be greater than 'size' [{}]. Adjusting.", tSize, tClearSize);
  137. while (tClearSize >= tSize) {
  138. tSize *= 10;
  139. }
  140. }
  141. REQUEST_TABLE_SIZE = (int) tSize;
  142. REQUEST_TABLE_CLEAR_SIZE = (int) tClearSize;
  143. }
  144. this.requestEntryTable = new HashMap<Long, AnswerEntry>(REQUEST_TABLE_SIZE);
  145. logger.debug("Configured Request Table with size[{}] and clear size[{}].", REQUEST_TABLE_SIZE, REQUEST_TABLE_CLEAR_SIZE);
  146. //add realms based on realm table.
  147. if (config.getChildren(RealmTable.ordinal()) != null) {
  148. logger.debug("Going to loop through configured realms and add them into a network map");
  149. for (Configuration items : config.getChildren(RealmTable.ordinal())) {
  150. if (items != null) {
  151. Configuration[] m = items.getChildren(RealmEntry.ordinal());
  152. for (Configuration c : m) {
  153. try {
  154. String name = c.getStringValue(RealmName.ordinal(), "");
  155. logger.debug("Getting config for realm [{}]", name);
  156. ApplicationId appId = null;
  157. {
  158. Configuration[] apps = c.getChildren(ApplicationId.ordinal());
  159. if (apps != null) {
  160. for (Configuration a : apps) {
  161. if (a != null) {
  162. long vnd = a.getLongValue(VendorId.ordinal(), 0);
  163. long auth = a.getLongValue(AuthApplId.ordinal(), 0);
  164. long acc = a.getLongValue(AcctApplId.ordinal(), 0);
  165. if (auth != 0) {
  166. appId = org.jdiameter.api.ApplicationId.createByAuthAppId(vnd, auth);
  167. }
  168. else {
  169. appId = org.jdiameter.api.ApplicationId.createByAccAppId(vnd, acc);
  170. }
  171. if (logger.isDebugEnabled()) {
  172. logger.debug("Realm [{}] has application Acct [{}] Auth [{}] Vendor [{}]", new Object[]{name, appId.getAcctAppId(), appId.getAuthAppId(), appId.getVendorId()});
  173. }
  174. break;
  175. }
  176. }
  177. }
  178. }
  179. String[] hosts = c.getStringValue(RealmHosts.ordinal(), (String) RealmHosts.defValue()).split(",");
  180. logger.debug("Adding realm [{}] with hosts [{}] to network map", name, hosts);
  181. LocalAction locAction = LocalAction.valueOf(c.getStringValue(RealmLocalAction.ordinal(), "0"));
  182. boolean isDynamic = c.getBooleanValue(RealmEntryIsDynamic.ordinal(), false);
  183. long expirationTime = c.getLongValue(RealmEntryExpTime.ordinal(), 0);
  184. //check if there is Agent, ATM we support only props there.
  185. IAgentConfiguration agentConfImpl = null;
  186. Configuration[] confs = c.getChildren(Agent.ordinal());
  187. if(confs != null && confs.length > 0) {
  188. Configuration agentConfiguration = confs[0]; //only one!
  189. agentConfImpl = this.container.getAssemblerFacility().getComponentInstance(IAgentConfiguration.class);
  190. if(agentConfImpl != null) {
  191. agentConfImpl = agentConfImpl.parse(agentConfiguration);
  192. }
  193. }
  194. this.realmTable.addRealm(name, appId, locAction, agentConfImpl, isDynamic, expirationTime, hosts);
  195. }
  196. catch (Exception e) {
  197. logger.warn("Unable to append realm entry", e);
  198. }
  199. }
  200. }
  201. }
  202. }
  203. }
  204. public void registerRequestRouteInfo(IRequest request) {
  205. logger.debug("Entering registerRequestRouteInfo");
  206. try {
  207. requestEntryTableLock.writeLock().lock();
  208. long hopByHopId = request.getHopByHopIdentifier();
  209. Avp hostAvp = request.getAvps().getAvp(Avp.ORIGIN_HOST);
  210. Avp realmAvp = request.getAvps().getAvp(Avp.ORIGIN_REALM);
  211. AnswerEntry entry = new AnswerEntry(hopByHopId, hostAvp != null ? hostAvp.getDiameterIdentity() : null,
  212. realmAvp != null ? realmAvp.getDiameterIdentity() : null);
  213. logger.debug("Adding Hop-by-Hop id [{}] into request entry table for routing answers back to the requesting peer", hopByHopId);
  214. requestEntryTable.put(hopByHopId, entry);
  215. requestSortedEntryTable.add(hopByHopId);
  216. if (requestEntryTable.size() > REQUEST_TABLE_SIZE) {
  217. List<Long> toRemove = requestSortedEntryTable.subList(0, REQUEST_TABLE_CLEAR_SIZE);
  218. // removing from keyset removes from hashmap too
  219. requestEntryTable.keySet().removeAll(toRemove);
  220. // instead of wasting time removing, just make a new one, much faster
  221. requestSortedEntryTable = new ArrayList<Long>(requestSortedEntryTable.subList(REQUEST_TABLE_CLEAR_SIZE, requestSortedEntryTable.size()));
  222. // help garbage collector
  223. toRemove = null;
  224. if (logger.isDebugEnabled()) {
  225. logger.debug("Request entry table has now [{}] entries.", requestEntryTable.size());
  226. }
  227. }
  228. }
  229. catch (Exception e) {
  230. logger.warn("Unable to store route info", e);
  231. }
  232. finally {
  233. requestEntryTableLock.writeLock().unlock();
  234. }
  235. }
  236. public String[] getRequestRouteInfo(long hopByHopIdentifier) {
  237. requestEntryTableLock.readLock().lock();
  238. AnswerEntry ans = requestEntryTable.get(hopByHopIdentifier);
  239. requestEntryTableLock.readLock().unlock();
  240. if (ans != null) {
  241. if (logger.isDebugEnabled()) {
  242. logger.debug("getRequestRouteInfo found host [{}] and realm [{}] for Hop-by-Hop Id [{}]", new Object[]{ans.getHost(), ans.getRealm(), hopByHopIdentifier});
  243. }
  244. return new String[] {ans.getHost(), ans.getRealm()};
  245. }
  246. else {
  247. if(logger.isWarnEnabled()) {
  248. logger.warn("Could not find route info for Hop-by-Hop Id [{}]. Table size is [{}]", hopByHopIdentifier, requestEntryTable.size());
  249. }
  250. return null;
  251. }
  252. }
  253. public IPeer getPeer(IMessage message, IPeerTable manager) throws RouteException, AvpDataException {
  254. logger.debug("Getting a peer for message [{}]", message);
  255. //FIXME: add ability to send without matching realm+peer pair?, that is , route based on peer table entries?
  256. //that is, if msg.destHost != null > getPeer(msg.destHost).sendMessage(msg);
  257. String destRealm = null;
  258. String destHost = null;
  259. IRealm matchedRealm = null;
  260. String[] info = null;
  261. // Get destination information
  262. if(message.isRequest()) {
  263. Avp avpRealm = message.getAvps().getAvp(Avp.DESTINATION_REALM);
  264. if (avpRealm == null) {
  265. throw new RouteException("Destination realm avp is empty");
  266. }
  267. destRealm = avpRealm.getDiameterIdentity();
  268. Avp avpHost = message.getAvps().getAvp(Avp.DESTINATION_HOST);
  269. if (avpHost != null) {
  270. destHost = avpHost.getDiameterIdentity();
  271. }
  272. if(logger.isDebugEnabled()) {
  273. logger.debug("Looking up peer for request: [{}], DestHost=[{}], DestRealm=[{}]", new Object[] {message,destHost, destRealm});
  274. }
  275. matchedRealm = (IRealm) this.realmTable.matchRealm(message);
  276. }
  277. else {
  278. //answer, search
  279. info = getRequestRouteInfo(message.getHopByHopIdentifier());
  280. if (info != null) {
  281. destHost = info[0];
  282. destRealm = info[1];
  283. logger.debug("Message is an answer. Host is [{}] and Realm is [{}] as per hopbyhop info from request", destHost, destRealm);
  284. if (destRealm == null) {
  285. logger.warn("Destination-Realm was null for hopbyhop id " + message.getHopByHopIdentifier());
  286. }
  287. }
  288. else {
  289. logger.debug("No Host and realm found based on hopbyhop id of the answer associated request");
  290. }
  291. //FIXME: if no info, should not send it ?
  292. //FIXME: add strict deff in route back table so stack does not have to lookup?
  293. if(logger.isDebugEnabled()) {
  294. logger.debug("Looking up peer for answer: [{}], DestHost=[{}], DestRealm=[{}]", new Object[] {message,destHost, destRealm});
  295. }
  296. matchedRealm = (IRealm) this.realmTable.matchRealm((IAnswer)message,destRealm);
  297. }
  298. // IPeer peer = getPeerPredProcessing(message, destRealm, destHost);
  299. //
  300. // if (peer != null) {
  301. // logger.debug("Found during preprocessing...[{}]", peer);
  302. // return peer;
  303. // }
  304. // Check realm name
  305. //TODO: check only if it exists?
  306. if (matchedRealm == null) {
  307. throw new RouteException("Unknown realm name [" + destRealm + "]");
  308. }
  309. // THIS IS GET PEER, NOT ROUTE!!!!!!!
  310. // Redirect processing
  311. //redirectProcessing(message, destRealm, destHost);
  312. // Check previous context information, this takes care of most answers.
  313. if (message.getPeer() != null && destHost != null && destHost.equals(message.getPeer().getUri().getFQDN()) && message.getPeer().hasValidConnection()) {
  314. if(logger.isDebugEnabled()) {
  315. logger.debug("Select previous message usage peer [{}]", message.getPeer());
  316. }
  317. return message.getPeer();
  318. }
  319. // Balancing procedure
  320. IPeer c = (IPeer) (destHost != null ? manager.getPeer(destHost) : null);
  321. if (c != null && c.hasValidConnection()) {
  322. logger.debug("Found a peer using destination host avp [{}] peer is [{}] with a valid connection.", destHost, c);
  323. //here matchedRealm MAY
  324. return c;
  325. }
  326. else {
  327. logger.debug("Finding peer by destination host avp [host={}] did not find anything. Now going to try finding one by destination realm [{}]", destRealm, destHost);
  328. String peers[] = matchedRealm.getPeerNames();
  329. if (peers == null || peers.length == 0) {
  330. throw new RouteException("Unable to find context by route information [" + destRealm + " ," + destHost + "]");
  331. }
  332. // Collect peers
  333. ArrayList<IPeer> availablePeers = new ArrayList<IPeer>(5);
  334. logger.debug("Looping through peers in realm [{}]", destRealm);
  335. for (String peerName : peers) {
  336. IPeer localPeer = (IPeer) manager.getPeer(peerName);
  337. if(logger.isDebugEnabled()) {
  338. logger.debug("Checking peer [{}] for name [{}]", new Object[]{localPeer,peerName});
  339. }
  340. if (localPeer != null) {
  341. if(localPeer.hasValidConnection()) {
  342. if(logger.isDebugEnabled()) {
  343. logger.debug("Found available peer to add to available peer list with uri [{}] with a valid connection", localPeer.getUri().toString());
  344. }
  345. availablePeers.add(localPeer);
  346. }
  347. else {
  348. if(logger.isDebugEnabled()) {
  349. logger.debug("Found a peer with uri [{}] with no valid connection", localPeer.getUri());
  350. }
  351. }
  352. }
  353. }
  354. if(logger.isDebugEnabled()) {
  355. logger.debug("Performing Realm routing. Realm [{}] has the following peers available [{}] from list [{}]", new Object[] {destRealm, availablePeers, Arrays.asList(peers)});
  356. }
  357. // Balancing
  358. IPeer peer = selectPeer(availablePeers);
  359. if (peer == null) {
  360. throw new RouteException("Unable to find valid connection to peer[" + destHost + "] in realm[" + destRealm + "]");
  361. }
  362. else {
  363. if (logger.isDebugEnabled()) {
  364. logger.debug("Load balancing selected peer with uri [{}]", peer.getUri());
  365. }
  366. }
  367. return peer;
  368. }
  369. }
  370. public IRealmTable getRealmTable() {
  371. return this.realmTable;
  372. }
  373. public void processRedirectAnswer(IRequest request, IAnswer answer, IPeerTable table) throws InternalException, RouteException {
  374. try {
  375. Avp destinationRealmAvp = request.getAvps().getAvp(Avp.DESTINATION_REALM);
  376. if(destinationRealmAvp == null) {
  377. throw new RouteException("Request to be routed has no Destination-Realm AVP!"); // sanity check... if user messes with us
  378. }
  379. String destinationRealm = destinationRealmAvp.getDiameterIdentity();
  380. String[] redirectHosts = null;
  381. if (answer.getAvps().getAvps(Avp.REDIRECT_HOST) != null) {
  382. AvpSet avps = answer.getAvps().getAvps(Avp.REDIRECT_HOST);
  383. redirectHosts = new String[avps.size()];
  384. int i = 0;
  385. // loop detected
  386. for (Avp avp : avps) {
  387. String r = avp.getDiameterIdentity();
  388. if (r.equals(metaData.getLocalPeer().getUri().getFQDN())) {
  389. throw new RouteException("Loop detected");
  390. }
  391. redirectHosts[i++] = r;
  392. }
  393. }
  394. //
  395. int redirectUsage = DONT_CACHE;
  396. Avp redirectHostUsageAvp = answer.getAvps().getAvp(Avp.REDIRECT_HOST_USAGE);
  397. if (redirectHostUsageAvp != null) {
  398. redirectUsage = redirectHostUsageAvp.getInteger32();
  399. }
  400. if (redirectUsage != DONT_CACHE) {
  401. long redirectCacheTime = 0;
  402. Avp redirectCacheMaxTimeAvp = answer.getAvps().getAvp(Avp.REDIRECT_MAX_CACHE_TIME);
  403. if (redirectCacheMaxTimeAvp != null) {
  404. redirectCacheTime = redirectCacheMaxTimeAvp.getUnsigned32();
  405. }
  406. String primaryKey = null;
  407. ApplicationId secondaryKey = null;
  408. switch (redirectUsage) {
  409. case ALL_SESSION:
  410. primaryKey = request.getSessionId();
  411. break;
  412. case ALL_REALM:
  413. primaryKey = destinationRealm;
  414. break;
  415. case REALM_AND_APPLICATION:
  416. primaryKey = destinationRealm;
  417. secondaryKey = ((IMessage)request).getSingleApplicationId();
  418. break;
  419. case ALL_APPLICATION:
  420. secondaryKey = ((IMessage)request).getSingleApplicationId();
  421. break;
  422. case ALL_HOST:
  423. Avp destinationHostAvp = ((IRequest)request).getAvps().getAvp(Avp.DESTINATION_HOST);
  424. if(destinationHostAvp == null) {
  425. throw new RouteException("Request to be routed has no Destination-Host AVP!"); // sanity check... if user messes with us
  426. }
  427. primaryKey = destinationHostAvp.getDiameterIdentity();
  428. break;
  429. case ALL_USER:
  430. Avp userNameAvp = answer.getAvps().getAvp(Avp.USER_NAME);
  431. if (userNameAvp == null) {
  432. throw new RouteException("Request to be routed has no User-Name AVP!"); // sanity check... if user messes with us
  433. }
  434. primaryKey = userNameAvp.getUTF8String();
  435. break;
  436. }
  437. //
  438. if(redirectTable.size()> REDIRECT_TABLE_SIZE) {
  439. try {
  440. //yes, possible that this will trigger this procedure twice, but thats worst than locking always.
  441. redirectTableLock.writeLock().lock();
  442. trimRedirectTable();
  443. }
  444. finally {
  445. redirectTableLock.writeLock().unlock();
  446. }
  447. }
  448. if (REDIRECT_TABLE_SIZE > redirectTable.size()) {
  449. RedirectEntry e = new RedirectEntry(primaryKey, secondaryKey, redirectCacheTime, redirectUsage, redirectHosts, destinationRealm);
  450. redirectTable.add(e);
  451. //redirectProcessing(answer,destRealm.getOctetString(),destHost !=null ? destHost.getOctetString():null);
  452. //we dont have to elect?
  453. updateRoute(request,e.getRedirectHost());
  454. }
  455. else {
  456. if (redirectHosts != null && redirectHosts.length > 0) {
  457. String destHost = redirectHosts[0];
  458. //setRouteInfo(answer, getRealmForPeer(destHost), destHost);
  459. updateRoute(request,destHost);
  460. }
  461. }
  462. }
  463. else {
  464. if (redirectHosts != null && redirectHosts.length > 0) {
  465. String destHost = redirectHosts[0];
  466. //setRouteInfo(answer, getRealmForPeer(destHost), destHost);
  467. updateRoute(request,destHost);
  468. }
  469. }
  470. //now send
  471. table.sendMessage((IMessage)request);
  472. }
  473. catch (AvpDataException exc) {
  474. throw new InternalException(exc);
  475. }
  476. catch (IllegalDiameterStateException e) {
  477. throw new InternalException(e);
  478. }
  479. catch (IOException e) {
  480. throw new InternalException(e);
  481. }
  482. }
  483. /**
  484. *
  485. */
  486. private void trimRedirectTable() {
  487. for(int index = 0; index < redirectTable.size(); index++) {
  488. try{
  489. if(redirectTable.get(index).getExpiredTime() <= System.currentTimeMillis()) {
  490. redirectTable.remove(index);
  491. index--; //a trick :)
  492. }
  493. }
  494. catch(Exception e) {
  495. logger.debug("Error in redirect task cleanup.", e);
  496. break;
  497. }
  498. }
  499. }
  500. /**
  501. * @param request
  502. * @param destHost
  503. */
  504. private void updateRoute(IRequest request, String destHost) {
  505. // Realm does not change I think... :)
  506. request.getAvps().removeAvp(Avp.DESTINATION_HOST);
  507. request.getAvps().addAvp(Avp.DESTINATION_HOST, destHost, true, false, true);
  508. }
  509. public boolean updateRoute(IRequest message) throws RouteException, AvpDataException {
  510. AvpSet set = message.getAvps();
  511. Avp destRealmAvp = set.getAvp(Avp.DESTINATION_REALM);
  512. Avp destHostAvp = set.getAvp(Avp.DESTINATION_HOST);
  513. if(destRealmAvp == null) {
  514. throw new RouteException("Request does not have Destination-Realm AVP!");
  515. }
  516. String destRealm = destRealmAvp.getDiameterIdentity();
  517. String destHost = destHostAvp != null ? destHostAvp.getDiameterIdentity() : null;
  518. boolean matchedEntry = false;
  519. String userName = null;
  520. // get Session id
  521. String sessionId = message.getSessionId();
  522. //
  523. Avp avpUserName = message.getAvps().getAvp(Avp.USER_NAME);
  524. // Get application id
  525. ApplicationId appId = ((IMessage)message).getSingleApplicationId();
  526. // User name
  527. if (avpUserName != null)
  528. userName = avpUserName.getUTF8String();
  529. // Processing table
  530. try{
  531. redirectTableLock.readLock().lock();
  532. for (int index = 0;index<redirectTable.size();index++) {
  533. RedirectEntry e = redirectTable.get(index);
  534. switch (e.getUsageType()) {
  535. case ALL_SESSION: // Usage type: ALL SESSION
  536. matchedEntry = sessionId != null && e.primaryKey != null & sessionId.equals(e.primaryKey);
  537. break;
  538. case ALL_REALM: // Usage type: ALL REALM
  539. matchedEntry = destRealm != null && e.primaryKey != null & destRealm.equals(e.primaryKey);
  540. break;
  541. case REALM_AND_APPLICATION: // Usage type: REALM AND APPLICATION
  542. matchedEntry = destRealm != null & appId != null & e.primaryKey != null & e.secondaryKey != null & destRealm.equals(e.primaryKey)
  543. & appId.equals(e.secondaryKey);
  544. break;
  545. case ALL_APPLICATION: // Usage type: ALL APPLICATION
  546. matchedEntry = appId != null & e.secondaryKey != null & appId.equals(e.secondaryKey);
  547. break;
  548. case ALL_HOST: // Usage type: ALL HOST
  549. matchedEntry = destHost != null & e.primaryKey != null & destHost.equals(e.primaryKey);
  550. break;
  551. case ALL_USER: // Usage type: ALL USER
  552. matchedEntry = userName != null & e.primaryKey != null & userName.equals(e.primaryKey);
  553. break;
  554. }
  555. // Update message redirect information
  556. if (matchedEntry) {
  557. String newDestHost = e.getRedirectHost();
  558. //String newDestRealm = getRealmForPeer(destHost);
  559. //setRouteInfo(message, destRealm, newDestHost);
  560. updateRoute(message, newDestHost);
  561. logger.debug("Redirect message from host={}; to new-host={}, realm={} ", new Object[] { destHost, newDestHost,destRealm});
  562. return true;
  563. }
  564. }
  565. }
  566. finally {
  567. redirectTableLock.readLock().unlock();
  568. }
  569. return false;
  570. }
  571. protected IPeer getPeerPredProcessing(IMessage message, String destRealm, String destHost) {
  572. return null;
  573. }
  574. public void start() {
  575. if (isStopped) {
  576. //redirectScheduler = concurrentFactory.getScheduledExecutorService(RedirectMessageTimer.name());
  577. //redirectEntryHandler = redirectScheduler.scheduleAtFixedRate(redirectTask, 1, 1, TimeUnit.SECONDS);
  578. isStopped = false;
  579. }
  580. }
  581. public void stop() {
  582. isStopped = true;
  583. // if (redirectEntryHandler != null) {
  584. // redirectEntryHandler.cancel(true);
  585. //}
  586. if (redirectTable != null) {
  587. redirectTable.clear();
  588. }
  589. if (requestEntryTable != null) {
  590. requestEntryTable.clear();
  591. }
  592. if (requestSortedEntryTable != null) {
  593. requestSortedEntryTable.clear();
  594. }
  595. //if (redirectScheduler != null) {
  596. // concurrentFactory.shutdownNow(redirectScheduler);
  597. //}
  598. }
  599. public void destroy() {
  600. try {
  601. if (!isStopped) {
  602. stop();
  603. }
  604. }
  605. catch (Exception exc) {
  606. logger.error("Unable to stop router", exc);
  607. }
  608. //redirectEntryHandler = null;
  609. //redirectScheduler = null;
  610. redirectTable = null;
  611. requestEntryTable = null;
  612. requestEntryTable = null;
  613. }
  614. protected IPeer selectPeer(List<IPeer> availablePeers) {
  615. IPeer p = null;
  616. for (IPeer c : availablePeers) {
  617. if (p == null || c.getRating() >= p.getRating()) {
  618. p = c;
  619. }
  620. }
  621. return p;
  622. }
  623. // protected void redirectProcessing(IMessage message, final String destRealm, final String destHost) throws AvpDataException {
  624. // String userName = null;
  625. // // get Session id
  626. // String sessionId = message.getSessionId();
  627. // //
  628. // Avp avpUserName = message.getAvps().getAvp(Avp.USER_NAME);
  629. // // Get application id
  630. // ApplicationId appId = message.getSingleApplicationId();
  631. // // User name
  632. // if (avpUserName != null)
  633. // userName = avpUserName.getUTF8String();
  634. // // Processing table
  635. // for (RedirectEntry e : redirectTable.values()) {
  636. // boolean matchedEntry = false;
  637. // switch (e.getUsageType()) {
  638. // case ALL_SESSION: // Usage type: ALL SESSION
  639. // matchedEntry = sessionId != null && e.primaryKey != null &
  640. // sessionId.equals(e.primaryKey);
  641. // break;
  642. // case ALL_REALM: // Usage type: ALL REALM
  643. // matchedEntry = destRealm != null && e.primaryKey != null &
  644. // destRealm.equals(e.primaryKey);
  645. // break;
  646. // case REALM_AND_APPLICATION: // Usage type: REALM AND APPLICATION
  647. // matchedEntry = destRealm != null & appId != null & e.primaryKey != null & e.secondaryKey != null &
  648. // destRealm.equals(e.primaryKey) & appId.equals(e.secondaryKey);
  649. // break;
  650. // case ALL_APPLICATION: // Usage type: ALL APPLICATION
  651. // matchedEntry = appId != null & e.secondaryKey != null &
  652. // appId.equals(e.secondaryKey);
  653. // break;
  654. // case ALL_HOST: // Usage type: ALL HOST
  655. // matchedEntry = destHost != null & e.primaryKey != null &
  656. // destHost.equals(e.primaryKey);
  657. // break;
  658. // case ALL_USER: // Usage type: ALL USER
  659. // matchedEntry = userName != null & e.primaryKey != null &
  660. // userName.equals(e.primaryKey);
  661. // break;
  662. // }
  663. // // Update message redirect information
  664. // if (matchedEntry) {
  665. // String newDestHost = e.getRedirectHost();
  666. // // FIXME: Alexandre: Should use newDestHost?
  667. // String newDestRealm = getRealmForPeer(destHost);
  668. // setRouteInfo(message, destRealm, newDestHost);
  669. // logger.debug("Redirect message from host={}; realm={} to new-host={}; new-realm={}",
  670. // new Object[] {destHost, destRealm, newDestHost, newDestRealm});
  671. // return;
  672. // }
  673. // }
  674. // }
  675. //
  676. // private void setRouteInfo(IMessage message, String destRealm, String destHost) {
  677. // message.getAvps().removeAvp(Avp.DESTINATION_REALM);
  678. // message.getAvps().removeAvp(Avp.DESTINATION_HOST);
  679. // if (destRealm != null)
  680. // message.getAvps().addAvp(Avp.DESTINATION_REALM, destRealm, true, false, true);
  681. // if (destHost != null)
  682. // message.getAvps().addAvp(Avp.DESTINATION_HOST, destHost, true, false, true);
  683. // }
  684. //does not make sense, there can be multple realms :/
  685. // public String getRealmForPeer(String destHost) {
  686. // for (String key : getRealmsName()) {
  687. // for (String h : getRealmPeers(key)) {
  688. // if (h.trim().equals(destHost.trim()))
  689. // return key;
  690. // }
  691. // }
  692. // return null;
  693. // }
  694. protected class RedirectEntry {
  695. final long createTime = System.currentTimeMillis();
  696. String primaryKey;
  697. ApplicationId secondaryKey;
  698. long liveTime;
  699. int usageType;
  700. String[] hosts;
  701. String destinationRealm;
  702. public RedirectEntry(String key1, ApplicationId key2, long time, int usage, String[] aHosts, String destinationRealm) throws InternalError {
  703. // Check arguments
  704. if (key1 == null && key2 == null) {
  705. throw new InternalError("Incorrect redirection key.");
  706. }
  707. if (aHosts == null || aHosts.length == 0) {
  708. throw new InternalError("Incorrect redirection hosts.");
  709. }
  710. // Set values
  711. this.primaryKey = key1;
  712. this.secondaryKey = key2;
  713. this.liveTime = time * 1000;
  714. this.usageType = usage;
  715. this.hosts = aHosts;
  716. this.destinationRealm = destinationRealm;
  717. }
  718. public int getUsageType() {
  719. return usageType;
  720. }
  721. public String[] getRedirectHosts() {
  722. return hosts;
  723. }
  724. public String getRedirectHost() {
  725. return hosts[hosts.length - 1];
  726. }
  727. public long getExpiredTime() {
  728. return createTime + liveTime;
  729. }
  730. public int hashCode() {
  731. int result = (primaryKey != null ? primaryKey.hashCode() : 0);
  732. result = 31 * result + (secondaryKey != null ? secondaryKey.hashCode() : 0);
  733. result = 31 * result + (int) (liveTime ^ (liveTime >>> 32));
  734. result = 31 * result + usageType;
  735. result = 31 * result + (hosts != null ? hosts.hashCode() : 0);
  736. return result;
  737. }
  738. public boolean equals(Object other) {
  739. if (other == this) {
  740. return true;
  741. }
  742. if (other instanceof RedirectEntry) {
  743. RedirectEntry that = (RedirectEntry) other;
  744. return liveTime == that.liveTime && usageType == that.usageType &&
  745. Arrays.equals(hosts, that.hosts) && !(primaryKey != null ? !primaryKey.equals(that.primaryKey) : that.primaryKey != null) &&
  746. !(secondaryKey != null ? !secondaryKey.equals(that.secondaryKey) : that.secondaryKey != null);
  747. }
  748. else {
  749. return false;
  750. }
  751. }
  752. }
  753. protected class AnswerEntry {
  754. final long createTime = System.nanoTime();
  755. Long hopByHopId;
  756. String host, realm;
  757. public AnswerEntry(Long hopByHopId) {
  758. this.hopByHopId = hopByHopId;
  759. }
  760. public AnswerEntry(Long hopByHopId, String host, String realm) throws InternalError {
  761. this.hopByHopId = hopByHopId;
  762. this.host = host;
  763. this.realm = realm;
  764. }
  765. public long getCreateTime() {
  766. return createTime;
  767. }
  768. public Long getHopByHopId() {
  769. return hopByHopId;
  770. }
  771. public String getHost() {
  772. return host;
  773. }
  774. public String getRealm() {
  775. return realm;
  776. }
  777. public boolean equals(Object o) {
  778. if (this == o) {
  779. return true;
  780. }
  781. if (o == null || getClass() != o.getClass()) {
  782. return false;
  783. }
  784. AnswerEntry that = (AnswerEntry) o;
  785. return hopByHopId == that.hopByHopId;
  786. }
  787. public String toString() {
  788. return "AnswerEntry{" + "createTime=" + createTime + ", hopByHopId=" + hopByHopId + '}';
  789. }
  790. }
  791. }