PageRenderTime 29ms CodeModel.GetById 0ms RepoModel.GetById 0ms app.codeStats 1ms

/servers/diameter/core/jdiameter/impl/src/main/java/org/jdiameter/client/impl/controller/PeerTableImpl.java

http://mobicents.googlecode.com/
Java | 402 lines | 317 code | 34 blank | 51 comment | 53 complexity | 06f06cd054b433bb29754e2a572bb692 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 2010, 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.controller;
  23. import static org.jdiameter.client.impl.helpers.Parameters.PeerIp;
  24. import static org.jdiameter.client.impl.helpers.Parameters.PeerLocalPortRange;
  25. import static org.jdiameter.client.impl.helpers.Parameters.PeerName;
  26. import static org.jdiameter.client.impl.helpers.Parameters.PeerRating;
  27. import static org.jdiameter.client.impl.helpers.Parameters.StopTimeOut;
  28. import java.io.IOException;
  29. import java.net.URISyntaxException;
  30. import java.net.UnknownServiceException;
  31. import java.util.ArrayList;
  32. import java.util.List;
  33. import java.util.Map;
  34. import java.util.concurrent.ConcurrentHashMap;
  35. import java.util.concurrent.ThreadFactory;
  36. import java.util.concurrent.atomic.AtomicLong;
  37. import org.jdiameter.api.Avp;
  38. import org.jdiameter.api.AvpDataException;
  39. import org.jdiameter.api.Configuration;
  40. import org.jdiameter.api.IllegalDiameterStateException;
  41. import org.jdiameter.api.InternalException;
  42. import org.jdiameter.api.MetaData;
  43. import org.jdiameter.api.NetworkReqListener;
  44. import org.jdiameter.api.Peer;
  45. import org.jdiameter.api.RouteException;
  46. import org.jdiameter.api.URI;
  47. import org.jdiameter.api.validation.AvpNotAllowedException;
  48. import org.jdiameter.api.validation.Dictionary;
  49. import org.jdiameter.client.api.IAssembler;
  50. import org.jdiameter.client.api.IContainer;
  51. import org.jdiameter.client.api.IMessage;
  52. import org.jdiameter.client.api.IMetaData;
  53. import org.jdiameter.client.api.IRequest;
  54. import org.jdiameter.client.api.controller.IPeer;
  55. import org.jdiameter.client.api.controller.IPeerTable;
  56. import org.jdiameter.client.api.fsm.IFsmFactory;
  57. import org.jdiameter.client.api.io.ITransportLayerFactory;
  58. import org.jdiameter.client.api.io.TransportException;
  59. import org.jdiameter.client.api.parser.IMessageParser;
  60. import org.jdiameter.client.api.router.IRouter;
  61. import org.jdiameter.client.impl.DictionarySingleton;
  62. import org.jdiameter.client.impl.helpers.Parameters;
  63. import org.jdiameter.common.api.concurrent.IConcurrentFactory;
  64. import org.jdiameter.common.api.data.ISessionDatasource;
  65. import org.jdiameter.common.api.statistic.IStatisticManager;
  66. import org.jdiameter.common.api.statistic.IStatisticRecord;
  67. import org.slf4j.Logger;
  68. import org.slf4j.LoggerFactory;
  69. /**
  70. *
  71. * @author erick.svenson@yahoo.com
  72. * @author <a href="mailto:baranowb@gmail.com"> Bartosz Baranowski </a>
  73. * @author <a href="mailto:brainslog@gmail.com"> Alexandre Mendonca </a>
  74. */
  75. public class PeerTableImpl implements IPeerTable {
  76. private static final Logger logger = LoggerFactory.getLogger(PeerTableImpl.class);
  77. // Peer table
  78. protected ConcurrentHashMap<String, Peer> peerTable = new ConcurrentHashMap<String, Peer>();
  79. protected boolean isStarted;
  80. protected long stopTimeOut;
  81. protected IAssembler assembler;
  82. protected IRouter router;
  83. protected MetaData metaData;
  84. protected IConcurrentFactory concurrentFactory;
  85. // XXX: FT/HA // protected ConcurrentHashMap<String, NetworkReqListener> sessionReqListeners = new ConcurrentHashMap<String, NetworkReqListener>();
  86. protected ISessionDatasource sessionDatasource;
  87. protected final Dictionary dictionary = DictionarySingleton.getDictionary();
  88. protected PeerTableImpl() {
  89. }
  90. public PeerTableImpl(Configuration globalConfig, MetaData metaData, IContainer stack,IRouter router, IFsmFactory fsmFactory,
  91. ITransportLayerFactory transportFactory, IStatisticManager statisticFactory,
  92. IConcurrentFactory concurrentFactory, IMessageParser parser) {
  93. init(stack,router, globalConfig, metaData, fsmFactory, transportFactory, statisticFactory, concurrentFactory, parser);
  94. }
  95. protected void init( IContainer stack,IRouter router, Configuration globalConfig, MetaData metaData, IFsmFactory fsmFactory,
  96. ITransportLayerFactory transportFactory, IStatisticManager statisticFactory,
  97. IConcurrentFactory concurrentFactory, IMessageParser parser) {
  98. logger.debug("Initializing Peer Table.");
  99. this.router = router;
  100. this.metaData = metaData;
  101. this.concurrentFactory = concurrentFactory;
  102. this.stopTimeOut = globalConfig.getLongValue(StopTimeOut.ordinal(), (Long) StopTimeOut.defValue());
  103. this.sessionDatasource = stack.getAssemblerFacility().getComponentInstance(ISessionDatasource.class);
  104. logger.debug("Populating peerTable from configuration");
  105. Configuration[] peers = globalConfig.getChildren(Parameters.PeerTable.ordinal());
  106. if (peers != null && peers.length > 0) {
  107. for (Configuration peerConfig : peers) {
  108. if (peerConfig.isAttributeExist(PeerName.ordinal())) {
  109. String uri = peerConfig.getStringValue(PeerName.ordinal(), null);
  110. int rating = peerConfig.getIntValue(PeerRating.ordinal(), 0);
  111. String ip = peerConfig.getStringValue(PeerIp.ordinal(), null);
  112. String portRange = peerConfig.getStringValue(PeerLocalPortRange.ordinal(), null);
  113. try {
  114. // create predefined peer
  115. IPeer peer = (IPeer) createPeer(rating, uri, ip, portRange, metaData, globalConfig, peerConfig, fsmFactory, transportFactory, statisticFactory, concurrentFactory, parser);
  116. if (peer != null) {
  117. //NOTE: this depends on conf, in normal case realm is younger part of FQDN, but in some cases
  118. //conf peers may contain IPs only... sucks.
  119. peer.setRealm(router.getRealmTable().getRealmForPeer(peer.getUri().getFQDN()));
  120. peerTable.put(peer.getUri().getFQDN(), peer);
  121. logger.debug("Appended peer [{}] to peer table", peer);
  122. }
  123. }
  124. catch (Exception e) {
  125. logger.warn("Unable to create peer [" + uri + "]", e);
  126. }
  127. }
  128. }
  129. }
  130. }
  131. protected Peer createPeer(int rating, String uri, String ip, String portRange, MetaData metaData, Configuration config, Configuration peerConfig,
  132. IFsmFactory fsmFactory, ITransportLayerFactory transportFactory, IStatisticManager statisticFactory, IConcurrentFactory concurrentFactory, IMessageParser parser)
  133. throws InternalException, TransportException, URISyntaxException, UnknownServiceException {
  134. return new PeerImpl(this, rating, new URI(uri), ip, portRange, metaData.unwrap(IMetaData.class), config,
  135. peerConfig, fsmFactory, transportFactory, statisticFactory, concurrentFactory, parser, this.sessionDatasource);
  136. }
  137. public List<Peer> getPeerTable() {
  138. List<Peer> p = new ArrayList<Peer>();
  139. p.addAll(peerTable.values());
  140. return p;
  141. }
  142. public void sendMessage(IMessage message) throws IllegalDiameterStateException, RouteException, AvpDataException, IOException {
  143. if (!isStarted) {
  144. throw new IllegalDiameterStateException("Stack is down");
  145. }
  146. // Get context
  147. IPeer peer;
  148. if (message.isRequest()) {
  149. if (logger.isDebugEnabled()) {
  150. logger.debug("Send request {} [destHost={}; destRealm={}]", new Object[] {message,
  151. message.getAvps().getAvp(Avp.DESTINATION_HOST) != null ? message.getAvps().getAvp(Avp.DESTINATION_HOST).getOctetString() : "",
  152. message.getAvps().getAvp(Avp.DESTINATION_REALM) != null ? message.getAvps().getAvp(Avp.DESTINATION_REALM).getOctetString() : ""});
  153. }
  154. // Check local request
  155. if(router.updateRoute((IRequest)message)) {
  156. if(logger.isDebugEnabled()) {
  157. logger.debug("Updated route on message {} [destHost={}; destRealm={}]", new Object[] {message,
  158. message.getAvps().getAvp(Avp.DESTINATION_HOST) != null ? message.getAvps().getAvp(Avp.DESTINATION_HOST).getOctetString() : "",
  159. message.getAvps().getAvp(Avp.DESTINATION_REALM) != null ? message.getAvps().getAvp(Avp.DESTINATION_REALM).getOctetString() : ""});
  160. }
  161. }
  162. peer = router.getPeer(message, this);
  163. logger.debug("Selected peer [{}] for sending message [{}]", peer, message);
  164. if (peer == metaData.getLocalPeer()) {
  165. logger.debug("Request [{}] will be processed by local service", message);
  166. }
  167. else {
  168. message.setHopByHopIdentifier(peer.getHopByHopIdentifier());
  169. peer.addMessage(message);
  170. message.setPeer(peer);
  171. }
  172. }
  173. else {
  174. logger.debug("Message is an answer");
  175. peer = message.getPeer();
  176. if (peer == null) {
  177. logger.debug("Peer is null so we will use router.getPeer to find a peer");
  178. peer = router.getPeer(message, this);
  179. if (peer == null) {
  180. throw new RouteException( "Cannot found remote context for sending message" );
  181. }
  182. logger.debug("Found a peer [{}] and setting it as the peer in the message", peer);
  183. message.setPeer(peer);
  184. }
  185. }
  186. try {
  187. logger.debug("Calling sendMessage on peer [{}]", peer);
  188. if (!peer.sendMessage(message)) {
  189. throw new IOException("Can not send message");
  190. }
  191. else {
  192. logger.debug("Message was submitted to be sent, now adding statistics");
  193. if (message.isRequest()) {
  194. if(peer.getStatistic().isEnabled())
  195. peer.getStatistic().getRecordByName(IStatisticRecord.Counters.AppGenRequest.name()).inc();
  196. }
  197. else {
  198. if(peer.getStatistic().isEnabled())
  199. peer.getStatistic().getRecordByName(IStatisticRecord.Counters.AppGenResponse.name()).inc();
  200. }
  201. }
  202. }
  203. catch (Exception e) {
  204. logger.error("Can not send message", e);
  205. if (message.isRequest()) {
  206. if(peer.getStatistic().isEnabled())
  207. peer.getStatistic().getRecordByName(IStatisticRecord.Counters.AppGenRejectedRequest.name()).inc();
  208. }
  209. else {
  210. if(peer.getStatistic().isEnabled())
  211. peer.getStatistic().getRecordByName(IStatisticRecord.Counters.AppGenRejectedResponse.name()).inc();
  212. }
  213. if(e instanceof AvpNotAllowedException) {
  214. throw (AvpNotAllowedException) e;
  215. }
  216. else {
  217. throw new IOException(e.getMessage());
  218. }
  219. }
  220. }
  221. public void addSessionReqListener(String sessionId, NetworkReqListener listener) {
  222. // XXX: FT/HA // sessionReqListeners.put(sessionId, listener);
  223. logger.debug("Adding sessionId [{}] to sessionDatasource", sessionId);
  224. sessionDatasource.setSessionListener(sessionId, listener);
  225. }
  226. public Map<String, NetworkReqListener> getSessionReqListeners() {
  227. // XXX: FT/HA // return sessionReqListeners;
  228. return null;
  229. }
  230. public IPeer getPeer(String fqdn) {
  231. logger.debug("In getPeer for peer with FQDN [{}]. Going to find a matching entry in peerTable", fqdn);
  232. IPeer peer = (IPeer) peerTable.get(fqdn);
  233. if (peer == null) {
  234. logger.debug("No peer found in getPeer for peer [{}] will return null", fqdn);
  235. return null;
  236. }
  237. logger.debug("Found matching peer [{}]. Is connection open ? {}.", peer.getUri(), peer.hasValidConnection());
  238. return peer;
  239. }
  240. public void removeSessionListener(String sessionId) {
  241. // XXX: FT/HA // sessionReqListeners.remove(sessionId);
  242. sessionDatasource.removeSessionListener(sessionId);
  243. }
  244. public void setAssembler(IAssembler assembler) {
  245. this.assembler = assembler;
  246. }
  247. // Life cycle
  248. public void start() throws IllegalDiameterStateException, IOException {
  249. logger.debug("Starting PeerTable. Going to call connect on all peers in the peerTable");
  250. for(Peer peer : peerTable.values()) {
  251. try {
  252. peer.connect();
  253. }
  254. catch (Exception e) {
  255. logger.warn("Can not start connect procedure to peer [" + peer + "]", e);
  256. }
  257. }
  258. logger.debug("Calling start on the router");
  259. router.start();
  260. isStarted = true;
  261. }
  262. public void stopped() {
  263. logger.debug("Calling stopped() on PeerTableImpl");
  264. // XXX: FT/HA // if (sessionReqListeners != null) {
  265. // XXX: FT/HA // sessionReqListeners.clear();
  266. // XXX: FT/HA // }
  267. for (Peer p : peerTable.values()) {
  268. for (IMessage m : ((IPeer) p).remAllMessage()) {
  269. try {
  270. m.runTimer();
  271. }
  272. catch(Exception e) {
  273. logger.debug("Unable to stop timer on message", e);
  274. }
  275. }
  276. }
  277. if (concurrentFactory != null) {
  278. try {
  279. // Wait for some threads which may take longer...
  280. // FIXME: Change this once we get rid of ThreadGroup and hard interrupting threads.
  281. boolean interrupted = false;
  282. long remWaitTime = 2000;
  283. logger.debug("Stopping thread group and waiting a max of {}ms for all threads to finish", remWaitTime);
  284. while(concurrentFactory.getThreadGroup().activeCount() > 0 && remWaitTime > 0) {
  285. long waitTime = 250;
  286. Thread.sleep(waitTime);
  287. remWaitTime -= waitTime;
  288. // it did not terminated, let's interrupt
  289. // FIXME: remove ASAP, this is very bad, it kills threads in middle of op,
  290. // killing FSM of peer for instance, after that its not usable.
  291. if(remWaitTime <= 0 && !interrupted) {
  292. interrupted = true;
  293. remWaitTime = 2000;
  294. logger.debug("Stopping thread group did not work. Interrupting and waiting a max of {}ms for all threads to finish", remWaitTime);
  295. concurrentFactory.getThreadGroup().interrupt();
  296. }
  297. }
  298. }
  299. catch (Exception e) {
  300. logger.warn("Unable to stop executor");
  301. }
  302. }
  303. router.stop();
  304. }
  305. public void stopping(int disconnectCause) {
  306. logger.debug("In stopping. Going to disconnect all peers in peer table");
  307. isStarted = false;
  308. for (Peer peer : peerTable.values()) {
  309. try {
  310. peer.disconnect(disconnectCause);
  311. }
  312. catch (Exception e) {
  313. logger.warn("Failure disconnecting peer [" + peer.getUri().toString() + "]", e);
  314. }
  315. }
  316. }
  317. public void destroy() {
  318. logger.debug("In destroy. Going to destroy concurrentFactory's thread group");
  319. if (concurrentFactory != null) {
  320. try {
  321. concurrentFactory.getThreadGroup().stop(); //had to add it to make testStartStopStart pass....
  322. concurrentFactory.getThreadGroup().destroy();
  323. }
  324. catch (IllegalThreadStateException itse) {
  325. if(logger.isDebugEnabled()) {
  326. logger.debug("Failure trying to destroy ThreadGroup probably due to existing active threads. Use stop() before destroy(). (nr_threads={})", concurrentFactory.getThreadGroup().activeCount());
  327. }
  328. }
  329. catch(ThreadDeath td) {
  330. // The class ThreadDeath is specifically a subclass of Error rather than Exception, even though it is a
  331. // "normal occurrence", because many applications catch all occurrences of Exception and then discard the
  332. // exception. ....
  333. }
  334. }
  335. if (router != null) {
  336. logger.debug("Calling destroy on router");
  337. router.destroy();
  338. }
  339. router = null;
  340. peerTable = null;
  341. assembler = null;
  342. }
  343. // Extension interface
  344. public boolean isWrapperFor(Class<?> aClass) throws InternalException {
  345. return false;
  346. }
  347. public <T> T unwrap(Class<T> aClass) throws InternalException {
  348. return null;
  349. }
  350. protected class PeerTableThreadFactory implements ThreadFactory {
  351. public final AtomicLong sequence = new AtomicLong(0);
  352. private int priority = Thread.NORM_PRIORITY;
  353. private ThreadGroup factoryThreadGroup = new ThreadGroup("JDiameterThreadGroup[" + sequence.incrementAndGet() + "]");
  354. public PeerTableThreadFactory(int priority) {
  355. super();
  356. this.priority = priority;
  357. }
  358. public Thread newThread(Runnable r) {
  359. Thread t = new Thread(this.factoryThreadGroup, r);
  360. if(logger.isDebugEnabled()) {
  361. logger.debug("Creating new thread in thread group JDiameterThreadGroup. Thread name is [{}]", t.getName());
  362. }
  363. t.setPriority(this.priority);
  364. // TODO ? t.start();
  365. return t;
  366. }
  367. }
  368. }