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