001/** 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.activemq.broker; 018 019import java.io.EOFException; 020import java.io.IOException; 021import java.net.SocketException; 022import java.net.URI; 023import java.util.Collection; 024import java.util.HashMap; 025import java.util.Iterator; 026import java.util.LinkedList; 027import java.util.List; 028import java.util.Map; 029import java.util.Properties; 030import java.util.concurrent.ConcurrentHashMap; 031import java.util.concurrent.CopyOnWriteArrayList; 032import java.util.concurrent.CountDownLatch; 033import java.util.concurrent.TimeUnit; 034import java.util.concurrent.atomic.AtomicBoolean; 035import java.util.concurrent.atomic.AtomicInteger; 036import java.util.concurrent.atomic.AtomicReference; 037import java.util.concurrent.locks.ReentrantReadWriteLock; 038 039import javax.transaction.xa.XAResource; 040 041import org.apache.activemq.advisory.AdvisorySupport; 042import org.apache.activemq.broker.region.ConnectionStatistics; 043import org.apache.activemq.broker.region.RegionBroker; 044import org.apache.activemq.command.ActiveMQDestination; 045import org.apache.activemq.command.BrokerInfo; 046import org.apache.activemq.command.Command; 047import org.apache.activemq.command.CommandTypes; 048import org.apache.activemq.command.ConnectionControl; 049import org.apache.activemq.command.ConnectionError; 050import org.apache.activemq.command.ConnectionId; 051import org.apache.activemq.command.ConnectionInfo; 052import org.apache.activemq.command.ConsumerControl; 053import org.apache.activemq.command.ConsumerId; 054import org.apache.activemq.command.ConsumerInfo; 055import org.apache.activemq.command.ControlCommand; 056import org.apache.activemq.command.DataArrayResponse; 057import org.apache.activemq.command.DestinationInfo; 058import org.apache.activemq.command.ExceptionResponse; 059import org.apache.activemq.command.FlushCommand; 060import org.apache.activemq.command.IntegerResponse; 061import org.apache.activemq.command.KeepAliveInfo; 062import org.apache.activemq.command.Message; 063import org.apache.activemq.command.MessageAck; 064import org.apache.activemq.command.MessageDispatch; 065import org.apache.activemq.command.MessageDispatchNotification; 066import org.apache.activemq.command.MessagePull; 067import org.apache.activemq.command.ProducerAck; 068import org.apache.activemq.command.ProducerId; 069import org.apache.activemq.command.ProducerInfo; 070import org.apache.activemq.command.RemoveInfo; 071import org.apache.activemq.command.RemoveSubscriptionInfo; 072import org.apache.activemq.command.Response; 073import org.apache.activemq.command.SessionId; 074import org.apache.activemq.command.SessionInfo; 075import org.apache.activemq.command.ShutdownInfo; 076import org.apache.activemq.command.TransactionId; 077import org.apache.activemq.command.TransactionInfo; 078import org.apache.activemq.command.WireFormatInfo; 079import org.apache.activemq.network.DemandForwardingBridge; 080import org.apache.activemq.network.MBeanNetworkListener; 081import org.apache.activemq.network.NetworkBridgeConfiguration; 082import org.apache.activemq.network.NetworkBridgeFactory; 083import org.apache.activemq.security.MessageAuthorizationPolicy; 084import org.apache.activemq.state.CommandVisitor; 085import org.apache.activemq.state.ConnectionState; 086import org.apache.activemq.state.ConsumerState; 087import org.apache.activemq.state.ProducerState; 088import org.apache.activemq.state.SessionState; 089import org.apache.activemq.state.TransactionState; 090import org.apache.activemq.thread.Task; 091import org.apache.activemq.thread.TaskRunner; 092import org.apache.activemq.thread.TaskRunnerFactory; 093import org.apache.activemq.transaction.Transaction; 094import org.apache.activemq.transport.DefaultTransportListener; 095import org.apache.activemq.transport.ResponseCorrelator; 096import org.apache.activemq.transport.TransmitCallback; 097import org.apache.activemq.transport.Transport; 098import org.apache.activemq.transport.TransportDisposedIOException; 099import org.apache.activemq.util.IntrospectionSupport; 100import org.apache.activemq.util.MarshallingSupport; 101import org.slf4j.Logger; 102import org.slf4j.LoggerFactory; 103import org.slf4j.MDC; 104 105public class TransportConnection implements Connection, Task, CommandVisitor { 106 private static final Logger LOG = LoggerFactory.getLogger(TransportConnection.class); 107 private static final Logger TRANSPORTLOG = LoggerFactory.getLogger(TransportConnection.class.getName() + ".Transport"); 108 private static final Logger SERVICELOG = LoggerFactory.getLogger(TransportConnection.class.getName() + ".Service"); 109 // Keeps track of the broker and connector that created this connection. 110 protected final Broker broker; 111 protected final TransportConnector connector; 112 // Keeps track of the state of the connections. 113 // protected final ConcurrentHashMap localConnectionStates=new 114 // ConcurrentHashMap(); 115 protected final Map<ConnectionId, ConnectionState> brokerConnectionStates; 116 // The broker and wireformat info that was exchanged. 117 protected BrokerInfo brokerInfo; 118 protected final List<Command> dispatchQueue = new LinkedList<Command>(); 119 protected TaskRunner taskRunner; 120 protected final AtomicReference<Throwable> transportException = new AtomicReference<Throwable>(); 121 protected AtomicBoolean dispatchStopped = new AtomicBoolean(false); 122 private final Transport transport; 123 private MessageAuthorizationPolicy messageAuthorizationPolicy; 124 private WireFormatInfo wireFormatInfo; 125 // Used to do async dispatch.. this should perhaps be pushed down into the 126 // transport layer.. 127 private boolean inServiceException; 128 private final ConnectionStatistics statistics = new ConnectionStatistics(); 129 private boolean manageable; 130 private boolean slow; 131 private boolean markedCandidate; 132 private boolean blockedCandidate; 133 private boolean blocked; 134 private boolean connected; 135 private boolean active; 136 private boolean starting; 137 private boolean pendingStop; 138 private long timeStamp; 139 private final AtomicBoolean stopping = new AtomicBoolean(false); 140 private final CountDownLatch stopped = new CountDownLatch(1); 141 private final AtomicBoolean asyncException = new AtomicBoolean(false); 142 private final Map<ProducerId, ProducerBrokerExchange> producerExchanges = new HashMap<ProducerId, ProducerBrokerExchange>(); 143 private final Map<ConsumerId, ConsumerBrokerExchange> consumerExchanges = new HashMap<ConsumerId, ConsumerBrokerExchange>(); 144 private final CountDownLatch dispatchStoppedLatch = new CountDownLatch(1); 145 private ConnectionContext context; 146 private boolean networkConnection; 147 private boolean faultTolerantConnection; 148 private final AtomicInteger protocolVersion = new AtomicInteger(CommandTypes.PROTOCOL_VERSION); 149 private DemandForwardingBridge duplexBridge; 150 private final TaskRunnerFactory taskRunnerFactory; 151 private final TaskRunnerFactory stopTaskRunnerFactory; 152 private TransportConnectionStateRegister connectionStateRegister = new SingleTransportConnectionStateRegister(); 153 private final ReentrantReadWriteLock serviceLock = new ReentrantReadWriteLock(); 154 private String duplexNetworkConnectorId; 155 156 /** 157 * @param taskRunnerFactory - can be null if you want direct dispatch to the transport 158 * else commands are sent async. 159 * @param stopTaskRunnerFactory - can <b>not</b> be null, used for stopping this connection. 160 */ 161 public TransportConnection(TransportConnector connector, final Transport transport, Broker broker, 162 TaskRunnerFactory taskRunnerFactory, TaskRunnerFactory stopTaskRunnerFactory) { 163 this.connector = connector; 164 this.broker = broker; 165 RegionBroker rb = (RegionBroker) broker.getAdaptor(RegionBroker.class); 166 brokerConnectionStates = rb.getConnectionStates(); 167 if (connector != null) { 168 this.statistics.setParent(connector.getStatistics()); 169 this.messageAuthorizationPolicy = connector.getMessageAuthorizationPolicy(); 170 } 171 this.taskRunnerFactory = taskRunnerFactory; 172 this.stopTaskRunnerFactory = stopTaskRunnerFactory; 173 this.transport = transport; 174 final BrokerService brokerService = this.broker.getBrokerService(); 175 if( this.transport instanceof BrokerServiceAware ) { 176 ((BrokerServiceAware)this.transport).setBrokerService(brokerService); 177 } 178 this.transport.setTransportListener(new DefaultTransportListener() { 179 @Override 180 public void onCommand(Object o) { 181 serviceLock.readLock().lock(); 182 try { 183 if (!(o instanceof Command)) { 184 throw new RuntimeException("Protocol violation - Command corrupted: " + o.toString()); 185 } 186 Command command = (Command) o; 187 if (!brokerService.isStopping()) { 188 Response response = service(command); 189 if (response != null && !brokerService.isStopping()) { 190 dispatchSync(response); 191 } 192 } else { 193 throw new BrokerStoppedException("Broker " + brokerService + " is being stopped"); 194 } 195 } finally { 196 serviceLock.readLock().unlock(); 197 } 198 } 199 200 @Override 201 public void onException(IOException exception) { 202 serviceLock.readLock().lock(); 203 try { 204 serviceTransportException(exception); 205 } finally { 206 serviceLock.readLock().unlock(); 207 } 208 } 209 }); 210 connected = true; 211 } 212 213 /** 214 * Returns the number of messages to be dispatched to this connection 215 * 216 * @return size of dispatch queue 217 */ 218 @Override 219 public int getDispatchQueueSize() { 220 synchronized (dispatchQueue) { 221 return dispatchQueue.size(); 222 } 223 } 224 225 public void serviceTransportException(IOException e) { 226 BrokerService bService = connector.getBrokerService(); 227 if (bService.isShutdownOnSlaveFailure()) { 228 if (brokerInfo != null) { 229 if (brokerInfo.isSlaveBroker()) { 230 LOG.error("Slave has exception: {} shutting down master now.", e.getMessage(), e); 231 try { 232 doStop(); 233 bService.stop(); 234 } catch (Exception ex) { 235 LOG.warn("Failed to stop the master", ex); 236 } 237 } 238 } 239 } 240 if (!stopping.get() && !pendingStop) { 241 transportException.set(e); 242 e.printStackTrace(); 243 if (TRANSPORTLOG.isDebugEnabled()) { 244 TRANSPORTLOG.debug(this + " failed: " + e, e); 245 } else if (TRANSPORTLOG.isWarnEnabled() && !expected(e)) { 246 TRANSPORTLOG.warn(this + " failed: " + e); 247 } 248 stopAsync(e); 249 } 250 } 251 252 private boolean expected(IOException e) { 253 return isStomp() && ((e instanceof SocketException && e.getMessage().indexOf("reset") != -1) || e instanceof EOFException); 254 } 255 256 private boolean isStomp() { 257 URI uri = connector.getUri(); 258 return uri != null && uri.getScheme() != null && uri.getScheme().indexOf("stomp") != -1; 259 } 260 261 /** 262 * Calls the serviceException method in an async thread. Since handling a 263 * service exception closes a socket, we should not tie up broker threads 264 * since client sockets may hang or cause deadlocks. 265 */ 266 @Override 267 public void serviceExceptionAsync(final IOException e) { 268 if (asyncException.compareAndSet(false, true)) { 269 new Thread("Async Exception Handler") { 270 @Override 271 public void run() { 272 serviceException(e); 273 } 274 }.start(); 275 } 276 } 277 278 /** 279 * Closes a clients connection due to a detected error. Errors are ignored 280 * if: the client is closing or broker is closing. Otherwise, the connection 281 * error transmitted to the client before stopping it's transport. 282 */ 283 @Override 284 public void serviceException(Throwable e) { 285 // are we a transport exception such as not being able to dispatch 286 // synchronously to a transport 287 if (e instanceof IOException) { 288 serviceTransportException((IOException) e); 289 } else if (e.getClass() == BrokerStoppedException.class) { 290 // Handle the case where the broker is stopped 291 // But the client is still connected. 292 if (!stopping.get()) { 293 SERVICELOG.debug("Broker has been stopped. Notifying client and closing his connection."); 294 ConnectionError ce = new ConnectionError(); 295 ce.setException(e); 296 dispatchSync(ce); 297 // Record the error that caused the transport to stop 298 transportException.set(e); 299 // Wait a little bit to try to get the output buffer to flush 300 // the exception notification to the client. 301 try { 302 Thread.sleep(500); 303 } catch (InterruptedException ie) { 304 Thread.currentThread().interrupt(); 305 } 306 // Worst case is we just kill the connection before the 307 // notification gets to him. 308 stopAsync(); 309 } 310 } else if (!stopping.get() && !inServiceException) { 311 inServiceException = true; 312 try { 313 if (SERVICELOG.isDebugEnabled()) { 314 SERVICELOG.debug("Async error occurred: " + e, e); 315 } else { 316 SERVICELOG.warn("Async error occurred: " + e); 317 } 318 ConnectionError ce = new ConnectionError(); 319 ce.setException(e); 320 if (pendingStop) { 321 dispatchSync(ce); 322 } else { 323 dispatchAsync(ce); 324 } 325 } finally { 326 inServiceException = false; 327 } 328 } 329 } 330 331 @Override 332 public Response service(Command command) { 333 MDC.put("activemq.connector", connector.getUri().toString()); 334 Response response = null; 335 boolean responseRequired = command.isResponseRequired(); 336 int commandId = command.getCommandId(); 337 try { 338 if (!pendingStop) { 339 response = command.visit(this); 340 } else { 341 response = new ExceptionResponse(transportException.get()); 342 } 343 } catch (Throwable e) { 344 if (SERVICELOG.isDebugEnabled() && e.getClass() != BrokerStoppedException.class) { 345 SERVICELOG.debug("Error occured while processing " + (responseRequired ? "sync" : "async") 346 + " command: " + command + ", exception: " + e, e); 347 } 348 349 if (e instanceof SuppressReplyException || (e.getCause() instanceof SuppressReplyException)) { 350 LOG.info("Suppressing reply to: " + command + " on: " + e + ", cause: " + e.getCause()); 351 responseRequired = false; 352 } 353 354 if (responseRequired) { 355 if (e instanceof SecurityException || e.getCause() instanceof SecurityException) { 356 SERVICELOG.warn("Security Error occurred on connection to: {}, {}", 357 transport.getRemoteAddress(), e.getMessage()); 358 } 359 response = new ExceptionResponse(e); 360 } else { 361 serviceException(e); 362 } 363 } 364 if (responseRequired) { 365 if (response == null) { 366 response = new Response(); 367 } 368 response.setCorrelationId(commandId); 369 } 370 // The context may have been flagged so that the response is not 371 // sent. 372 if (context != null) { 373 if (context.isDontSendReponse()) { 374 context.setDontSendReponse(false); 375 response = null; 376 } 377 context = null; 378 } 379 MDC.remove("activemq.connector"); 380 return response; 381 } 382 383 @Override 384 public Response processKeepAlive(KeepAliveInfo info) throws Exception { 385 return null; 386 } 387 388 @Override 389 public Response processRemoveSubscription(RemoveSubscriptionInfo info) throws Exception { 390 broker.removeSubscription(lookupConnectionState(info.getConnectionId()).getContext(), info); 391 return null; 392 } 393 394 @Override 395 public Response processWireFormat(WireFormatInfo info) throws Exception { 396 wireFormatInfo = info; 397 protocolVersion.set(info.getVersion()); 398 return null; 399 } 400 401 @Override 402 public Response processShutdown(ShutdownInfo info) throws Exception { 403 stopAsync(); 404 return null; 405 } 406 407 @Override 408 public Response processFlush(FlushCommand command) throws Exception { 409 return null; 410 } 411 412 @Override 413 public Response processBeginTransaction(TransactionInfo info) throws Exception { 414 TransportConnectionState cs = lookupConnectionState(info.getConnectionId()); 415 context = null; 416 if (cs != null) { 417 context = cs.getContext(); 418 } 419 if (cs == null) { 420 throw new NullPointerException("Context is null"); 421 } 422 // Avoid replaying dup commands 423 if (cs.getTransactionState(info.getTransactionId()) == null) { 424 cs.addTransactionState(info.getTransactionId()); 425 broker.beginTransaction(context, info.getTransactionId()); 426 } 427 return null; 428 } 429 430 @Override 431 public int getActiveTransactionCount() { 432 int rc = 0; 433 for (TransportConnectionState cs : connectionStateRegister.listConnectionStates()) { 434 Collection<TransactionState> transactions = cs.getTransactionStates(); 435 for (TransactionState transaction : transactions) { 436 rc++; 437 } 438 } 439 return rc; 440 } 441 442 @Override 443 public Long getOldestActiveTransactionDuration() { 444 TransactionState oldestTX = null; 445 for (TransportConnectionState cs : connectionStateRegister.listConnectionStates()) { 446 Collection<TransactionState> transactions = cs.getTransactionStates(); 447 for (TransactionState transaction : transactions) { 448 if( oldestTX ==null || oldestTX.getCreatedAt() < transaction.getCreatedAt() ) { 449 oldestTX = transaction; 450 } 451 } 452 } 453 if( oldestTX == null ) { 454 return null; 455 } 456 return System.currentTimeMillis() - oldestTX.getCreatedAt(); 457 } 458 459 @Override 460 public Response processEndTransaction(TransactionInfo info) throws Exception { 461 // No need to do anything. This packet is just sent by the client 462 // make sure he is synced with the server as commit command could 463 // come from a different connection. 464 return null; 465 } 466 467 @Override 468 public Response processPrepareTransaction(TransactionInfo info) throws Exception { 469 TransportConnectionState cs = lookupConnectionState(info.getConnectionId()); 470 context = null; 471 if (cs != null) { 472 context = cs.getContext(); 473 } 474 if (cs == null) { 475 throw new NullPointerException("Context is null"); 476 } 477 TransactionState transactionState = cs.getTransactionState(info.getTransactionId()); 478 if (transactionState == null) { 479 throw new IllegalStateException("Cannot prepare a transaction that had not been started or previously returned XA_RDONLY: " 480 + info.getTransactionId()); 481 } 482 // Avoid dups. 483 if (!transactionState.isPrepared()) { 484 transactionState.setPrepared(true); 485 int result = broker.prepareTransaction(context, info.getTransactionId()); 486 transactionState.setPreparedResult(result); 487 if (result == XAResource.XA_RDONLY) { 488 // we are done, no further rollback or commit from TM 489 cs.removeTransactionState(info.getTransactionId()); 490 } 491 IntegerResponse response = new IntegerResponse(result); 492 return response; 493 } else { 494 IntegerResponse response = new IntegerResponse(transactionState.getPreparedResult()); 495 return response; 496 } 497 } 498 499 @Override 500 public Response processCommitTransactionOnePhase(TransactionInfo info) throws Exception { 501 TransportConnectionState cs = lookupConnectionState(info.getConnectionId()); 502 context = cs.getContext(); 503 cs.removeTransactionState(info.getTransactionId()); 504 broker.commitTransaction(context, info.getTransactionId(), true); 505 return null; 506 } 507 508 @Override 509 public Response processCommitTransactionTwoPhase(TransactionInfo info) throws Exception { 510 TransportConnectionState cs = lookupConnectionState(info.getConnectionId()); 511 context = cs.getContext(); 512 cs.removeTransactionState(info.getTransactionId()); 513 broker.commitTransaction(context, info.getTransactionId(), false); 514 return null; 515 } 516 517 @Override 518 public Response processRollbackTransaction(TransactionInfo info) throws Exception { 519 TransportConnectionState cs = lookupConnectionState(info.getConnectionId()); 520 context = cs.getContext(); 521 cs.removeTransactionState(info.getTransactionId()); 522 broker.rollbackTransaction(context, info.getTransactionId()); 523 return null; 524 } 525 526 @Override 527 public Response processForgetTransaction(TransactionInfo info) throws Exception { 528 TransportConnectionState cs = lookupConnectionState(info.getConnectionId()); 529 context = cs.getContext(); 530 broker.forgetTransaction(context, info.getTransactionId()); 531 return null; 532 } 533 534 @Override 535 public Response processRecoverTransactions(TransactionInfo info) throws Exception { 536 TransportConnectionState cs = lookupConnectionState(info.getConnectionId()); 537 context = cs.getContext(); 538 TransactionId[] preparedTransactions = broker.getPreparedTransactions(context); 539 return new DataArrayResponse(preparedTransactions); 540 } 541 542 @Override 543 public Response processMessage(Message messageSend) throws Exception { 544 ProducerId producerId = messageSend.getProducerId(); 545 ProducerBrokerExchange producerExchange = getProducerBrokerExchange(producerId); 546 if (producerExchange.canDispatch(messageSend)) { 547 broker.send(producerExchange, messageSend); 548 } 549 return null; 550 } 551 552 @Override 553 public Response processMessageAck(MessageAck ack) throws Exception { 554 ConsumerBrokerExchange consumerExchange = getConsumerBrokerExchange(ack.getConsumerId()); 555 if (consumerExchange != null) { 556 broker.acknowledge(consumerExchange, ack); 557 } else if (ack.isInTransaction()) { 558 LOG.warn("no matching consumer, ignoring ack {}", consumerExchange, ack); 559 } 560 return null; 561 } 562 563 @Override 564 public Response processMessagePull(MessagePull pull) throws Exception { 565 return broker.messagePull(lookupConnectionState(pull.getConsumerId()).getContext(), pull); 566 } 567 568 @Override 569 public Response processMessageDispatchNotification(MessageDispatchNotification notification) throws Exception { 570 broker.processDispatchNotification(notification); 571 return null; 572 } 573 574 @Override 575 public Response processAddDestination(DestinationInfo info) throws Exception { 576 TransportConnectionState cs = lookupConnectionState(info.getConnectionId()); 577 broker.addDestinationInfo(cs.getContext(), info); 578 if (info.getDestination().isTemporary()) { 579 cs.addTempDestination(info); 580 } 581 return null; 582 } 583 584 @Override 585 public Response processRemoveDestination(DestinationInfo info) throws Exception { 586 TransportConnectionState cs = lookupConnectionState(info.getConnectionId()); 587 broker.removeDestinationInfo(cs.getContext(), info); 588 if (info.getDestination().isTemporary()) { 589 cs.removeTempDestination(info.getDestination()); 590 } 591 return null; 592 } 593 594 @Override 595 public Response processAddProducer(ProducerInfo info) throws Exception { 596 SessionId sessionId = info.getProducerId().getParentId(); 597 ConnectionId connectionId = sessionId.getParentId(); 598 TransportConnectionState cs = lookupConnectionState(connectionId); 599 if (cs == null) { 600 throw new IllegalStateException("Cannot add a producer to a connection that had not been registered: " 601 + connectionId); 602 } 603 SessionState ss = cs.getSessionState(sessionId); 604 if (ss == null) { 605 throw new IllegalStateException("Cannot add a producer to a session that had not been registered: " 606 + sessionId); 607 } 608 // Avoid replaying dup commands 609 if (!ss.getProducerIds().contains(info.getProducerId())) { 610 ActiveMQDestination destination = info.getDestination(); 611 // Do not check for null here as it would cause the count of max producers to exclude 612 // anonymous producers. The isAdvisoryTopic method checks for null so it is safe to 613 // call it from here with a null Destination value. 614 if (!AdvisorySupport.isAdvisoryTopic(destination)) { 615 if (getProducerCount(connectionId) >= connector.getMaximumProducersAllowedPerConnection()){ 616 throw new IllegalStateException("Can't add producer on connection " + connectionId + ": at maximum limit: " + connector.getMaximumProducersAllowedPerConnection()); 617 } 618 } 619 broker.addProducer(cs.getContext(), info); 620 try { 621 ss.addProducer(info); 622 } catch (IllegalStateException e) { 623 broker.removeProducer(cs.getContext(), info); 624 } 625 626 } 627 return null; 628 } 629 630 @Override 631 public Response processRemoveProducer(ProducerId id) throws Exception { 632 SessionId sessionId = id.getParentId(); 633 ConnectionId connectionId = sessionId.getParentId(); 634 TransportConnectionState cs = lookupConnectionState(connectionId); 635 SessionState ss = cs.getSessionState(sessionId); 636 if (ss == null) { 637 throw new IllegalStateException("Cannot remove a producer from a session that had not been registered: " 638 + sessionId); 639 } 640 ProducerState ps = ss.removeProducer(id); 641 if (ps == null) { 642 throw new IllegalStateException("Cannot remove a producer that had not been registered: " + id); 643 } 644 removeProducerBrokerExchange(id); 645 broker.removeProducer(cs.getContext(), ps.getInfo()); 646 return null; 647 } 648 649 @Override 650 public Response processAddConsumer(ConsumerInfo info) throws Exception { 651 SessionId sessionId = info.getConsumerId().getParentId(); 652 ConnectionId connectionId = sessionId.getParentId(); 653 TransportConnectionState cs = lookupConnectionState(connectionId); 654 if (cs == null) { 655 throw new IllegalStateException("Cannot add a consumer to a connection that had not been registered: " 656 + connectionId); 657 } 658 SessionState ss = cs.getSessionState(sessionId); 659 if (ss == null) { 660 throw new IllegalStateException(broker.getBrokerName() 661 + " Cannot add a consumer to a session that had not been registered: " + sessionId); 662 } 663 // Avoid replaying dup commands 664 if (!ss.getConsumerIds().contains(info.getConsumerId())) { 665 ActiveMQDestination destination = info.getDestination(); 666 if (destination != null && !AdvisorySupport.isAdvisoryTopic(destination)) { 667 if (getConsumerCount(connectionId) >= connector.getMaximumConsumersAllowedPerConnection()){ 668 throw new IllegalStateException("Can't add consumer on connection " + connectionId + ": at maximum limit: " + connector.getMaximumConsumersAllowedPerConnection()); 669 } 670 } 671 672 broker.addConsumer(cs.getContext(), info); 673 try { 674 ss.addConsumer(info); 675 addConsumerBrokerExchange(info.getConsumerId()); 676 } catch (IllegalStateException e) { 677 broker.removeConsumer(cs.getContext(), info); 678 } 679 680 } 681 return null; 682 } 683 684 @Override 685 public Response processRemoveConsumer(ConsumerId id, long lastDeliveredSequenceId) throws Exception { 686 SessionId sessionId = id.getParentId(); 687 ConnectionId connectionId = sessionId.getParentId(); 688 TransportConnectionState cs = lookupConnectionState(connectionId); 689 if (cs == null) { 690 throw new IllegalStateException("Cannot remove a consumer from a connection that had not been registered: " 691 + connectionId); 692 } 693 SessionState ss = cs.getSessionState(sessionId); 694 if (ss == null) { 695 throw new IllegalStateException("Cannot remove a consumer from a session that had not been registered: " 696 + sessionId); 697 } 698 ConsumerState consumerState = ss.removeConsumer(id); 699 if (consumerState == null) { 700 throw new IllegalStateException("Cannot remove a consumer that had not been registered: " + id); 701 } 702 ConsumerInfo info = consumerState.getInfo(); 703 info.setLastDeliveredSequenceId(lastDeliveredSequenceId); 704 broker.removeConsumer(cs.getContext(), consumerState.getInfo()); 705 removeConsumerBrokerExchange(id); 706 return null; 707 } 708 709 @Override 710 public Response processAddSession(SessionInfo info) throws Exception { 711 ConnectionId connectionId = info.getSessionId().getParentId(); 712 TransportConnectionState cs = lookupConnectionState(connectionId); 713 // Avoid replaying dup commands 714 if (cs != null && !cs.getSessionIds().contains(info.getSessionId())) { 715 broker.addSession(cs.getContext(), info); 716 try { 717 cs.addSession(info); 718 } catch (IllegalStateException e) { 719 LOG.warn("Failed to add session: {}", info.getSessionId(), e); 720 broker.removeSession(cs.getContext(), info); 721 } 722 } 723 return null; 724 } 725 726 @Override 727 public Response processRemoveSession(SessionId id, long lastDeliveredSequenceId) throws Exception { 728 ConnectionId connectionId = id.getParentId(); 729 TransportConnectionState cs = lookupConnectionState(connectionId); 730 if (cs == null) { 731 throw new IllegalStateException("Cannot remove session from connection that had not been registered: " + connectionId); 732 } 733 SessionState session = cs.getSessionState(id); 734 if (session == null) { 735 throw new IllegalStateException("Cannot remove session that had not been registered: " + id); 736 } 737 // Don't let new consumers or producers get added while we are closing 738 // this down. 739 session.shutdown(); 740 // Cascade the connection stop to the consumers and producers. 741 for (ConsumerId consumerId : session.getConsumerIds()) { 742 try { 743 processRemoveConsumer(consumerId, lastDeliveredSequenceId); 744 } catch (Throwable e) { 745 LOG.warn("Failed to remove consumer: {}", consumerId, e); 746 } 747 } 748 for (ProducerId producerId : session.getProducerIds()) { 749 try { 750 processRemoveProducer(producerId); 751 } catch (Throwable e) { 752 LOG.warn("Failed to remove producer: {}", producerId, e); 753 } 754 } 755 cs.removeSession(id); 756 broker.removeSession(cs.getContext(), session.getInfo()); 757 return null; 758 } 759 760 @Override 761 public Response processAddConnection(ConnectionInfo info) throws Exception { 762 // Older clients should have been defaulting this field to true.. but 763 // they were not. 764 if (wireFormatInfo != null && wireFormatInfo.getVersion() <= 2) { 765 info.setClientMaster(true); 766 } 767 TransportConnectionState state; 768 // Make sure 2 concurrent connections by the same ID only generate 1 769 // TransportConnectionState object. 770 synchronized (brokerConnectionStates) { 771 state = (TransportConnectionState) brokerConnectionStates.get(info.getConnectionId()); 772 if (state == null) { 773 state = new TransportConnectionState(info, this); 774 brokerConnectionStates.put(info.getConnectionId(), state); 775 } 776 state.incrementReference(); 777 } 778 // If there are 2 concurrent connections for the same connection id, 779 // then last one in wins, we need to sync here 780 // to figure out the winner. 781 synchronized (state.getConnectionMutex()) { 782 if (state.getConnection() != this) { 783 LOG.debug("Killing previous stale connection: {}", state.getConnection().getRemoteAddress()); 784 state.getConnection().stop(); 785 LOG.debug("Connection {} taking over previous connection: {}", getRemoteAddress(), state.getConnection().getRemoteAddress()); 786 state.setConnection(this); 787 state.reset(info); 788 } 789 } 790 registerConnectionState(info.getConnectionId(), state); 791 LOG.debug("Setting up new connection id: {}, address: {}, info: {}", new Object[]{ info.getConnectionId(), getRemoteAddress(), info }); 792 this.faultTolerantConnection = info.isFaultTolerant(); 793 // Setup the context. 794 String clientId = info.getClientId(); 795 context = new ConnectionContext(); 796 context.setBroker(broker); 797 context.setClientId(clientId); 798 context.setClientMaster(info.isClientMaster()); 799 context.setConnection(this); 800 context.setConnectionId(info.getConnectionId()); 801 context.setConnector(connector); 802 context.setMessageAuthorizationPolicy(getMessageAuthorizationPolicy()); 803 context.setNetworkConnection(networkConnection); 804 context.setFaultTolerant(faultTolerantConnection); 805 context.setTransactions(new ConcurrentHashMap<TransactionId, Transaction>()); 806 context.setUserName(info.getUserName()); 807 context.setWireFormatInfo(wireFormatInfo); 808 context.setReconnect(info.isFailoverReconnect()); 809 this.manageable = info.isManageable(); 810 context.setConnectionState(state); 811 state.setContext(context); 812 state.setConnection(this); 813 if (info.getClientIp() == null) { 814 info.setClientIp(getRemoteAddress()); 815 } 816 817 try { 818 broker.addConnection(context, info); 819 } catch (Exception e) { 820 synchronized (brokerConnectionStates) { 821 brokerConnectionStates.remove(info.getConnectionId()); 822 } 823 unregisterConnectionState(info.getConnectionId()); 824 LOG.warn("Failed to add Connection {} due to {}", info.getConnectionId(), e); 825 if (e instanceof SecurityException) { 826 // close this down - in case the peer of this transport doesn't play nice 827 delayedStop(2000, "Failed with SecurityException: " + e.getLocalizedMessage(), e); 828 } 829 throw e; 830 } 831 if (info.isManageable()) { 832 // send ConnectionCommand 833 ConnectionControl command = this.connector.getConnectionControl(); 834 command.setFaultTolerant(broker.isFaultTolerantConfiguration()); 835 if (info.isFailoverReconnect()) { 836 command.setRebalanceConnection(false); 837 } 838 dispatchAsync(command); 839 } 840 return null; 841 } 842 843 @Override 844 public synchronized Response processRemoveConnection(ConnectionId id, long lastDeliveredSequenceId) 845 throws InterruptedException { 846 LOG.debug("remove connection id: {}", id); 847 TransportConnectionState cs = lookupConnectionState(id); 848 if (cs != null) { 849 // Don't allow things to be added to the connection state while we 850 // are shutting down. 851 cs.shutdown(); 852 // Cascade the connection stop to the sessions. 853 for (SessionId sessionId : cs.getSessionIds()) { 854 try { 855 processRemoveSession(sessionId, lastDeliveredSequenceId); 856 } catch (Throwable e) { 857 SERVICELOG.warn("Failed to remove session {}", sessionId, e); 858 } 859 } 860 // Cascade the connection stop to temp destinations. 861 for (Iterator<DestinationInfo> iter = cs.getTempDestinations().iterator(); iter.hasNext(); ) { 862 DestinationInfo di = iter.next(); 863 try { 864 broker.removeDestination(cs.getContext(), di.getDestination(), 0); 865 } catch (Throwable e) { 866 SERVICELOG.warn("Failed to remove tmp destination {}", di.getDestination(), e); 867 } 868 iter.remove(); 869 } 870 try { 871 broker.removeConnection(cs.getContext(), cs.getInfo(), transportException.get()); 872 } catch (Throwable e) { 873 SERVICELOG.warn("Failed to remove connection {}", cs.getInfo(), e); 874 } 875 TransportConnectionState state = unregisterConnectionState(id); 876 if (state != null) { 877 synchronized (brokerConnectionStates) { 878 // If we are the last reference, we should remove the state 879 // from the broker. 880 if (state.decrementReference() == 0) { 881 brokerConnectionStates.remove(id); 882 } 883 } 884 } 885 } 886 return null; 887 } 888 889 @Override 890 public Response processProducerAck(ProducerAck ack) throws Exception { 891 // A broker should not get ProducerAck messages. 892 return null; 893 } 894 895 @Override 896 public Connector getConnector() { 897 return connector; 898 } 899 900 @Override 901 public void dispatchSync(Command message) { 902 try { 903 processDispatch(message); 904 } catch (IOException e) { 905 serviceExceptionAsync(e); 906 } 907 } 908 909 @Override 910 public void dispatchAsync(Command message) { 911 if (!stopping.get()) { 912 if (taskRunner == null) { 913 dispatchSync(message); 914 } else { 915 synchronized (dispatchQueue) { 916 dispatchQueue.add(message); 917 } 918 try { 919 taskRunner.wakeup(); 920 } catch (InterruptedException e) { 921 Thread.currentThread().interrupt(); 922 } 923 } 924 } else { 925 if (message.isMessageDispatch()) { 926 MessageDispatch md = (MessageDispatch) message; 927 TransmitCallback sub = md.getTransmitCallback(); 928 broker.postProcessDispatch(md); 929 if (sub != null) { 930 sub.onFailure(); 931 } 932 } 933 } 934 } 935 936 protected void processDispatch(Command command) throws IOException { 937 MessageDispatch messageDispatch = (MessageDispatch) (command.isMessageDispatch() ? command : null); 938 try { 939 if (!stopping.get()) { 940 if (messageDispatch != null) { 941 try { 942 broker.preProcessDispatch(messageDispatch); 943 } catch (RuntimeException convertToIO) { 944 throw new IOException(convertToIO); 945 } 946 } 947 dispatch(command); 948 } 949 } catch (IOException e) { 950 if (messageDispatch != null) { 951 TransmitCallback sub = messageDispatch.getTransmitCallback(); 952 broker.postProcessDispatch(messageDispatch); 953 if (sub != null) { 954 sub.onFailure(); 955 } 956 messageDispatch = null; 957 throw e; 958 } 959 } finally { 960 if (messageDispatch != null) { 961 TransmitCallback sub = messageDispatch.getTransmitCallback(); 962 broker.postProcessDispatch(messageDispatch); 963 if (sub != null) { 964 sub.onSuccess(); 965 } 966 } 967 } 968 } 969 970 @Override 971 public boolean iterate() { 972 try { 973 if (pendingStop || stopping.get()) { 974 if (dispatchStopped.compareAndSet(false, true)) { 975 if (transportException.get() == null) { 976 try { 977 dispatch(new ShutdownInfo()); 978 } catch (Throwable ignore) { 979 } 980 } 981 dispatchStoppedLatch.countDown(); 982 } 983 return false; 984 } 985 if (!dispatchStopped.get()) { 986 Command command = null; 987 synchronized (dispatchQueue) { 988 if (dispatchQueue.isEmpty()) { 989 return false; 990 } 991 command = dispatchQueue.remove(0); 992 } 993 processDispatch(command); 994 return true; 995 } 996 return false; 997 } catch (IOException e) { 998 if (dispatchStopped.compareAndSet(false, true)) { 999 dispatchStoppedLatch.countDown(); 1000 } 1001 serviceExceptionAsync(e); 1002 return false; 1003 } 1004 } 1005 1006 /** 1007 * Returns the statistics for this connection 1008 */ 1009 @Override 1010 public ConnectionStatistics getStatistics() { 1011 return statistics; 1012 } 1013 1014 public MessageAuthorizationPolicy getMessageAuthorizationPolicy() { 1015 return messageAuthorizationPolicy; 1016 } 1017 1018 public void setMessageAuthorizationPolicy(MessageAuthorizationPolicy messageAuthorizationPolicy) { 1019 this.messageAuthorizationPolicy = messageAuthorizationPolicy; 1020 } 1021 1022 @Override 1023 public boolean isManageable() { 1024 return manageable; 1025 } 1026 1027 @Override 1028 public void start() throws Exception { 1029 try { 1030 synchronized (this) { 1031 starting = true; 1032 if (taskRunnerFactory != null) { 1033 taskRunner = taskRunnerFactory.createTaskRunner(this, "ActiveMQ Connection Dispatcher: " 1034 + getRemoteAddress()); 1035 } else { 1036 taskRunner = null; 1037 } 1038 transport.start(); 1039 active = true; 1040 BrokerInfo info = connector.getBrokerInfo().copy(); 1041 if (connector.isUpdateClusterClients()) { 1042 info.setPeerBrokerInfos(this.broker.getPeerBrokerInfos()); 1043 } else { 1044 info.setPeerBrokerInfos(null); 1045 } 1046 dispatchAsync(info); 1047 1048 connector.onStarted(this); 1049 } 1050 } catch (Exception e) { 1051 // Force clean up on an error starting up. 1052 pendingStop = true; 1053 throw e; 1054 } finally { 1055 // stop() can be called from within the above block, 1056 // but we want to be sure start() completes before 1057 // stop() runs, so queue the stop until right now: 1058 setStarting(false); 1059 if (isPendingStop()) { 1060 LOG.debug("Calling the delayed stop() after start() {}", this); 1061 stop(); 1062 } 1063 } 1064 } 1065 1066 @Override 1067 public void stop() throws Exception { 1068 // do not stop task the task runner factories (taskRunnerFactory, stopTaskRunnerFactory) 1069 // as their lifecycle is handled elsewhere 1070 1071 stopAsync(); 1072 while (!stopped.await(5, TimeUnit.SECONDS)) { 1073 LOG.info("The connection to '{}' is taking a long time to shutdown.", transport.getRemoteAddress()); 1074 } 1075 } 1076 1077 public void delayedStop(final int waitTime, final String reason, Throwable cause) { 1078 if (waitTime > 0) { 1079 synchronized (this) { 1080 pendingStop = true; 1081 transportException.set(cause); 1082 } 1083 try { 1084 stopTaskRunnerFactory.execute(new Runnable() { 1085 @Override 1086 public void run() { 1087 try { 1088 Thread.sleep(waitTime); 1089 stopAsync(); 1090 LOG.info("Stopping {} because {}", transport.getRemoteAddress(), reason); 1091 } catch (InterruptedException e) { 1092 } 1093 } 1094 }); 1095 } catch (Throwable t) { 1096 LOG.warn("Cannot create stopAsync. This exception will be ignored.", t); 1097 } 1098 } 1099 } 1100 1101 public void stopAsync(Throwable cause) { 1102 transportException.set(cause); 1103 stopAsync(); 1104 } 1105 1106 public void stopAsync() { 1107 // If we're in the middle of starting then go no further... for now. 1108 synchronized (this) { 1109 pendingStop = true; 1110 if (starting) { 1111 LOG.debug("stopAsync() called in the middle of start(). Delaying till start completes.."); 1112 return; 1113 } 1114 } 1115 if (stopping.compareAndSet(false, true)) { 1116 // Let all the connection contexts know we are shutting down 1117 // so that in progress operations can notice and unblock. 1118 List<TransportConnectionState> connectionStates = listConnectionStates(); 1119 for (TransportConnectionState cs : connectionStates) { 1120 ConnectionContext connectionContext = cs.getContext(); 1121 if (connectionContext != null) { 1122 connectionContext.getStopping().set(true); 1123 } 1124 } 1125 try { 1126 stopTaskRunnerFactory.execute(new Runnable() { 1127 @Override 1128 public void run() { 1129 serviceLock.writeLock().lock(); 1130 try { 1131 doStop(); 1132 } catch (Throwable e) { 1133 LOG.debug("Error occurred while shutting down a connection {}", this, e); 1134 } finally { 1135 stopped.countDown(); 1136 serviceLock.writeLock().unlock(); 1137 } 1138 } 1139 }); 1140 } catch (Throwable t) { 1141 LOG.warn("Cannot create async transport stopper thread. This exception is ignored. Not waiting for stop to complete", t); 1142 stopped.countDown(); 1143 } 1144 } 1145 } 1146 1147 @Override 1148 public String toString() { 1149 return "Transport Connection to: " + transport.getRemoteAddress(); 1150 } 1151 1152 protected void doStop() throws Exception { 1153 LOG.debug("Stopping connection: {}", transport.getRemoteAddress()); 1154 connector.onStopped(this); 1155 try { 1156 synchronized (this) { 1157 if (duplexBridge != null) { 1158 duplexBridge.stop(); 1159 } 1160 } 1161 } catch (Exception ignore) { 1162 LOG.trace("Exception caught stopping. This exception is ignored.", ignore); 1163 } 1164 try { 1165 transport.stop(); 1166 LOG.debug("Stopped transport: {}", transport.getRemoteAddress()); 1167 } catch (Exception e) { 1168 LOG.debug("Could not stop transport to {}. This exception is ignored.", transport.getRemoteAddress(), e); 1169 } 1170 if (taskRunner != null) { 1171 taskRunner.shutdown(1); 1172 taskRunner = null; 1173 } 1174 active = false; 1175 // Run the MessageDispatch callbacks so that message references get 1176 // cleaned up. 1177 synchronized (dispatchQueue) { 1178 for (Iterator<Command> iter = dispatchQueue.iterator(); iter.hasNext(); ) { 1179 Command command = iter.next(); 1180 if (command.isMessageDispatch()) { 1181 MessageDispatch md = (MessageDispatch) command; 1182 TransmitCallback sub = md.getTransmitCallback(); 1183 broker.postProcessDispatch(md); 1184 if (sub != null) { 1185 sub.onFailure(); 1186 } 1187 } 1188 } 1189 dispatchQueue.clear(); 1190 } 1191 // 1192 // Remove all logical connection associated with this connection 1193 // from the broker. 1194 if (!broker.isStopped()) { 1195 List<TransportConnectionState> connectionStates = listConnectionStates(); 1196 connectionStates = listConnectionStates(); 1197 for (TransportConnectionState cs : connectionStates) { 1198 cs.getContext().getStopping().set(true); 1199 try { 1200 LOG.debug("Cleaning up connection resources: {}", getRemoteAddress()); 1201 processRemoveConnection(cs.getInfo().getConnectionId(), RemoveInfo.LAST_DELIVERED_UNKNOWN); 1202 } catch (Throwable ignore) { 1203 LOG.debug("Exception caught removing connection {}. This exception is ignored.", cs.getInfo().getConnectionId(), ignore); 1204 } 1205 } 1206 } 1207 LOG.debug("Connection Stopped: {}", getRemoteAddress()); 1208 } 1209 1210 /** 1211 * @return Returns the blockedCandidate. 1212 */ 1213 public boolean isBlockedCandidate() { 1214 return blockedCandidate; 1215 } 1216 1217 /** 1218 * @param blockedCandidate The blockedCandidate to set. 1219 */ 1220 public void setBlockedCandidate(boolean blockedCandidate) { 1221 this.blockedCandidate = blockedCandidate; 1222 } 1223 1224 /** 1225 * @return Returns the markedCandidate. 1226 */ 1227 public boolean isMarkedCandidate() { 1228 return markedCandidate; 1229 } 1230 1231 /** 1232 * @param markedCandidate The markedCandidate to set. 1233 */ 1234 public void setMarkedCandidate(boolean markedCandidate) { 1235 this.markedCandidate = markedCandidate; 1236 if (!markedCandidate) { 1237 timeStamp = 0; 1238 blockedCandidate = false; 1239 } 1240 } 1241 1242 /** 1243 * @param slow The slow to set. 1244 */ 1245 public void setSlow(boolean slow) { 1246 this.slow = slow; 1247 } 1248 1249 /** 1250 * @return true if the Connection is slow 1251 */ 1252 @Override 1253 public boolean isSlow() { 1254 return slow; 1255 } 1256 1257 /** 1258 * @return true if the Connection is potentially blocked 1259 */ 1260 public boolean isMarkedBlockedCandidate() { 1261 return markedCandidate; 1262 } 1263 1264 /** 1265 * Mark the Connection, so we can deem if it's collectable on the next sweep 1266 */ 1267 public void doMark() { 1268 if (timeStamp == 0) { 1269 timeStamp = System.currentTimeMillis(); 1270 } 1271 } 1272 1273 /** 1274 * @return if after being marked, the Connection is still writing 1275 */ 1276 @Override 1277 public boolean isBlocked() { 1278 return blocked; 1279 } 1280 1281 /** 1282 * @return true if the Connection is connected 1283 */ 1284 @Override 1285 public boolean isConnected() { 1286 return connected; 1287 } 1288 1289 /** 1290 * @param blocked The blocked to set. 1291 */ 1292 public void setBlocked(boolean blocked) { 1293 this.blocked = blocked; 1294 } 1295 1296 /** 1297 * @param connected The connected to set. 1298 */ 1299 public void setConnected(boolean connected) { 1300 this.connected = connected; 1301 } 1302 1303 /** 1304 * @return true if the Connection is active 1305 */ 1306 @Override 1307 public boolean isActive() { 1308 return active; 1309 } 1310 1311 /** 1312 * @param active The active to set. 1313 */ 1314 public void setActive(boolean active) { 1315 this.active = active; 1316 } 1317 1318 /** 1319 * @return true if the Connection is starting 1320 */ 1321 public synchronized boolean isStarting() { 1322 return starting; 1323 } 1324 1325 @Override 1326 public synchronized boolean isNetworkConnection() { 1327 return networkConnection; 1328 } 1329 1330 @Override 1331 public boolean isFaultTolerantConnection() { 1332 return this.faultTolerantConnection; 1333 } 1334 1335 protected synchronized void setStarting(boolean starting) { 1336 this.starting = starting; 1337 } 1338 1339 /** 1340 * @return true if the Connection needs to stop 1341 */ 1342 public synchronized boolean isPendingStop() { 1343 return pendingStop; 1344 } 1345 1346 protected synchronized void setPendingStop(boolean pendingStop) { 1347 this.pendingStop = pendingStop; 1348 } 1349 1350 @Override 1351 public Response processBrokerInfo(BrokerInfo info) { 1352 if (info.isSlaveBroker()) { 1353 LOG.error(" Slave Brokers are no longer supported - slave trying to attach is: {}", info.getBrokerName()); 1354 } else if (info.isNetworkConnection() && info.isDuplexConnection()) { 1355 // so this TransportConnection is the rear end of a network bridge 1356 // We have been requested to create a two way pipe ... 1357 try { 1358 Properties properties = MarshallingSupport.stringToProperties(info.getNetworkProperties()); 1359 Map<String, String> props = createMap(properties); 1360 NetworkBridgeConfiguration config = new NetworkBridgeConfiguration(); 1361 IntrospectionSupport.setProperties(config, props, ""); 1362 config.setBrokerName(broker.getBrokerName()); 1363 1364 // check for existing duplex connection hanging about 1365 1366 // We first look if existing network connection already exists for the same broker Id and network connector name 1367 // It's possible in case of brief network fault to have this transport connector side of the connection always active 1368 // and the duplex network connector side wanting to open a new one 1369 // In this case, the old connection must be broken 1370 String duplexNetworkConnectorId = config.getName() + "@" + info.getBrokerId(); 1371 CopyOnWriteArrayList<TransportConnection> connections = this.connector.getConnections(); 1372 synchronized (connections) { 1373 for (Iterator<TransportConnection> iter = connections.iterator(); iter.hasNext(); ) { 1374 TransportConnection c = iter.next(); 1375 if ((c != this) && (duplexNetworkConnectorId.equals(c.getDuplexNetworkConnectorId()))) { 1376 LOG.warn("Stopping an existing active duplex connection [{}] for network connector ({}).", c, duplexNetworkConnectorId); 1377 c.stopAsync(); 1378 // better to wait for a bit rather than get connection id already in use and failure to start new bridge 1379 c.getStopped().await(1, TimeUnit.SECONDS); 1380 } 1381 } 1382 setDuplexNetworkConnectorId(duplexNetworkConnectorId); 1383 } 1384 Transport localTransport = NetworkBridgeFactory.createLocalTransport(broker); 1385 Transport remoteBridgeTransport = transport; 1386 if (! (remoteBridgeTransport instanceof ResponseCorrelator)) { 1387 // the vm transport case is already wrapped 1388 remoteBridgeTransport = new ResponseCorrelator(remoteBridgeTransport); 1389 } 1390 String duplexName = localTransport.toString(); 1391 if (duplexName.contains("#")) { 1392 duplexName = duplexName.substring(duplexName.lastIndexOf("#")); 1393 } 1394 MBeanNetworkListener listener = new MBeanNetworkListener(broker.getBrokerService(), config, broker.getBrokerService().createDuplexNetworkConnectorObjectName(duplexName)); 1395 listener.setCreatedByDuplex(true); 1396 duplexBridge = NetworkBridgeFactory.createBridge(config, localTransport, remoteBridgeTransport, listener); 1397 duplexBridge.setBrokerService(broker.getBrokerService()); 1398 // now turn duplex off this side 1399 info.setDuplexConnection(false); 1400 duplexBridge.setCreatedByDuplex(true); 1401 duplexBridge.duplexStart(this, brokerInfo, info); 1402 LOG.info("Started responder end of duplex bridge {}", duplexNetworkConnectorId); 1403 return null; 1404 } catch (TransportDisposedIOException e) { 1405 LOG.warn("Duplex bridge {} was stopped before it was correctly started.", duplexNetworkConnectorId); 1406 return null; 1407 } catch (Exception e) { 1408 LOG.error("Failed to create responder end of duplex network bridge {}", duplexNetworkConnectorId, e); 1409 return null; 1410 } 1411 } 1412 // We only expect to get one broker info command per connection 1413 if (this.brokerInfo != null) { 1414 LOG.warn("Unexpected extra broker info command received: {}", info); 1415 } 1416 this.brokerInfo = info; 1417 networkConnection = true; 1418 List<TransportConnectionState> connectionStates = listConnectionStates(); 1419 for (TransportConnectionState cs : connectionStates) { 1420 cs.getContext().setNetworkConnection(true); 1421 } 1422 return null; 1423 } 1424 1425 @SuppressWarnings({"unchecked", "rawtypes"}) 1426 private HashMap<String, String> createMap(Properties properties) { 1427 return new HashMap(properties); 1428 } 1429 1430 protected void dispatch(Command command) throws IOException { 1431 try { 1432 setMarkedCandidate(true); 1433 transport.oneway(command); 1434 } finally { 1435 setMarkedCandidate(false); 1436 } 1437 } 1438 1439 @Override 1440 public String getRemoteAddress() { 1441 return transport.getRemoteAddress(); 1442 } 1443 1444 public Transport getTransport() { 1445 return transport; 1446 } 1447 1448 @Override 1449 public String getConnectionId() { 1450 List<TransportConnectionState> connectionStates = listConnectionStates(); 1451 for (TransportConnectionState cs : connectionStates) { 1452 if (cs.getInfo().getClientId() != null) { 1453 return cs.getInfo().getClientId(); 1454 } 1455 return cs.getInfo().getConnectionId().toString(); 1456 } 1457 return null; 1458 } 1459 1460 @Override 1461 public void updateClient(ConnectionControl control) { 1462 if (isActive() && isBlocked() == false && isFaultTolerantConnection() && this.wireFormatInfo != null 1463 && this.wireFormatInfo.getVersion() >= 6) { 1464 dispatchAsync(control); 1465 } 1466 } 1467 1468 public ProducerBrokerExchange getProducerBrokerExchangeIfExists(ProducerInfo producerInfo){ 1469 ProducerBrokerExchange result = null; 1470 if (producerInfo != null && producerInfo.getProducerId() != null){ 1471 synchronized (producerExchanges){ 1472 result = producerExchanges.get(producerInfo.getProducerId()); 1473 } 1474 } 1475 return result; 1476 } 1477 1478 private ProducerBrokerExchange getProducerBrokerExchange(ProducerId id) throws IOException { 1479 ProducerBrokerExchange result = producerExchanges.get(id); 1480 if (result == null) { 1481 synchronized (producerExchanges) { 1482 result = new ProducerBrokerExchange(); 1483 TransportConnectionState state = lookupConnectionState(id); 1484 context = state.getContext(); 1485 result.setConnectionContext(context); 1486 if (context.isReconnect() || (context.isNetworkConnection() && connector.isAuditNetworkProducers())) { 1487 result.setLastStoredSequenceId(broker.getBrokerService().getPersistenceAdapter().getLastProducerSequenceId(id)); 1488 } 1489 SessionState ss = state.getSessionState(id.getParentId()); 1490 if (ss != null) { 1491 result.setProducerState(ss.getProducerState(id)); 1492 ProducerState producerState = ss.getProducerState(id); 1493 if (producerState != null && producerState.getInfo() != null) { 1494 ProducerInfo info = producerState.getInfo(); 1495 result.setMutable(info.getDestination() == null || info.getDestination().isComposite()); 1496 } 1497 } 1498 producerExchanges.put(id, result); 1499 } 1500 } else { 1501 context = result.getConnectionContext(); 1502 } 1503 return result; 1504 } 1505 1506 private void removeProducerBrokerExchange(ProducerId id) { 1507 synchronized (producerExchanges) { 1508 producerExchanges.remove(id); 1509 } 1510 } 1511 1512 private ConsumerBrokerExchange getConsumerBrokerExchange(ConsumerId id) { 1513 ConsumerBrokerExchange result = consumerExchanges.get(id); 1514 return result; 1515 } 1516 1517 private ConsumerBrokerExchange addConsumerBrokerExchange(ConsumerId id) { 1518 ConsumerBrokerExchange result = consumerExchanges.get(id); 1519 if (result == null) { 1520 synchronized (consumerExchanges) { 1521 result = new ConsumerBrokerExchange(); 1522 TransportConnectionState state = lookupConnectionState(id); 1523 context = state.getContext(); 1524 result.setConnectionContext(context); 1525 SessionState ss = state.getSessionState(id.getParentId()); 1526 if (ss != null) { 1527 ConsumerState cs = ss.getConsumerState(id); 1528 if (cs != null) { 1529 ConsumerInfo info = cs.getInfo(); 1530 if (info != null) { 1531 if (info.getDestination() != null && info.getDestination().isPattern()) { 1532 result.setWildcard(true); 1533 } 1534 } 1535 } 1536 } 1537 consumerExchanges.put(id, result); 1538 } 1539 } 1540 return result; 1541 } 1542 1543 private void removeConsumerBrokerExchange(ConsumerId id) { 1544 synchronized (consumerExchanges) { 1545 consumerExchanges.remove(id); 1546 } 1547 } 1548 1549 public int getProtocolVersion() { 1550 return protocolVersion.get(); 1551 } 1552 1553 @Override 1554 public Response processControlCommand(ControlCommand command) throws Exception { 1555 return null; 1556 } 1557 1558 @Override 1559 public Response processMessageDispatch(MessageDispatch dispatch) throws Exception { 1560 return null; 1561 } 1562 1563 @Override 1564 public Response processConnectionControl(ConnectionControl control) throws Exception { 1565 if (control != null) { 1566 faultTolerantConnection = control.isFaultTolerant(); 1567 } 1568 return null; 1569 } 1570 1571 @Override 1572 public Response processConnectionError(ConnectionError error) throws Exception { 1573 return null; 1574 } 1575 1576 @Override 1577 public Response processConsumerControl(ConsumerControl control) throws Exception { 1578 ConsumerBrokerExchange consumerExchange = getConsumerBrokerExchange(control.getConsumerId()); 1579 broker.processConsumerControl(consumerExchange, control); 1580 return null; 1581 } 1582 1583 protected synchronized TransportConnectionState registerConnectionState(ConnectionId connectionId, 1584 TransportConnectionState state) { 1585 TransportConnectionState cs = null; 1586 if (!connectionStateRegister.isEmpty() && !connectionStateRegister.doesHandleMultipleConnectionStates()) { 1587 // swap implementations 1588 TransportConnectionStateRegister newRegister = new MapTransportConnectionStateRegister(); 1589 newRegister.intialize(connectionStateRegister); 1590 connectionStateRegister = newRegister; 1591 } 1592 cs = connectionStateRegister.registerConnectionState(connectionId, state); 1593 return cs; 1594 } 1595 1596 protected synchronized TransportConnectionState unregisterConnectionState(ConnectionId connectionId) { 1597 return connectionStateRegister.unregisterConnectionState(connectionId); 1598 } 1599 1600 protected synchronized List<TransportConnectionState> listConnectionStates() { 1601 return connectionStateRegister.listConnectionStates(); 1602 } 1603 1604 protected synchronized TransportConnectionState lookupConnectionState(String connectionId) { 1605 return connectionStateRegister.lookupConnectionState(connectionId); 1606 } 1607 1608 protected synchronized TransportConnectionState lookupConnectionState(ConsumerId id) { 1609 return connectionStateRegister.lookupConnectionState(id); 1610 } 1611 1612 protected synchronized TransportConnectionState lookupConnectionState(ProducerId id) { 1613 return connectionStateRegister.lookupConnectionState(id); 1614 } 1615 1616 protected synchronized TransportConnectionState lookupConnectionState(SessionId id) { 1617 return connectionStateRegister.lookupConnectionState(id); 1618 } 1619 1620 // public only for testing 1621 public synchronized TransportConnectionState lookupConnectionState(ConnectionId connectionId) { 1622 return connectionStateRegister.lookupConnectionState(connectionId); 1623 } 1624 1625 protected synchronized void setDuplexNetworkConnectorId(String duplexNetworkConnectorId) { 1626 this.duplexNetworkConnectorId = duplexNetworkConnectorId; 1627 } 1628 1629 protected synchronized String getDuplexNetworkConnectorId() { 1630 return this.duplexNetworkConnectorId; 1631 } 1632 1633 public boolean isStopping() { 1634 return stopping.get(); 1635 } 1636 1637 protected CountDownLatch getStopped() { 1638 return stopped; 1639 } 1640 1641 private int getProducerCount(ConnectionId connectionId) { 1642 int result = 0; 1643 TransportConnectionState cs = lookupConnectionState(connectionId); 1644 if (cs != null) { 1645 for (SessionId sessionId : cs.getSessionIds()) { 1646 SessionState sessionState = cs.getSessionState(sessionId); 1647 if (sessionState != null) { 1648 result += sessionState.getProducerIds().size(); 1649 } 1650 } 1651 } 1652 return result; 1653 } 1654 1655 private int getConsumerCount(ConnectionId connectionId) { 1656 int result = 0; 1657 TransportConnectionState cs = lookupConnectionState(connectionId); 1658 if (cs != null) { 1659 for (SessionId sessionId : cs.getSessionIds()) { 1660 SessionState sessionState = cs.getSessionState(sessionId); 1661 if (sessionState != null) { 1662 result += sessionState.getConsumerIds().size(); 1663 } 1664 } 1665 } 1666 return result; 1667 } 1668 1669 public WireFormatInfo getRemoteWireFormatInfo() { 1670 return wireFormatInfo; 1671 } 1672}