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