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.IOException; 020import java.net.URI; 021import java.net.URISyntaxException; 022import java.util.ArrayList; 023import java.util.LinkedList; 024import java.util.List; 025import java.util.StringTokenizer; 026import java.util.concurrent.CopyOnWriteArrayList; 027import java.util.regex.Pattern; 028 029import javax.management.ObjectName; 030 031import org.apache.activemq.broker.jmx.ManagedTransportConnector; 032import org.apache.activemq.broker.jmx.ManagementContext; 033import org.apache.activemq.broker.region.ConnectorStatistics; 034import org.apache.activemq.command.BrokerInfo; 035import org.apache.activemq.command.ConnectionControl; 036import org.apache.activemq.security.MessageAuthorizationPolicy; 037import org.apache.activemq.thread.TaskRunnerFactory; 038import org.apache.activemq.transport.Transport; 039import org.apache.activemq.transport.TransportAcceptListener; 040import org.apache.activemq.transport.TransportFactorySupport; 041import org.apache.activemq.transport.TransportServer; 042import org.apache.activemq.transport.discovery.DiscoveryAgent; 043import org.apache.activemq.transport.discovery.DiscoveryAgentFactory; 044import org.apache.activemq.util.ServiceStopper; 045import org.apache.activemq.util.ServiceSupport; 046import org.slf4j.Logger; 047import org.slf4j.LoggerFactory; 048 049/** 050 * @org.apache.xbean.XBean 051 */ 052public class TransportConnector implements Connector, BrokerServiceAware { 053 054 final Logger LOG = LoggerFactory.getLogger(TransportConnector.class); 055 056 protected final CopyOnWriteArrayList<TransportConnection> connections = new CopyOnWriteArrayList<TransportConnection>(); 057 protected TransportStatusDetector statusDector; 058 private BrokerService brokerService; 059 private TransportServer server; 060 private URI uri; 061 private BrokerInfo brokerInfo = new BrokerInfo(); 062 private TaskRunnerFactory taskRunnerFactory; 063 private MessageAuthorizationPolicy messageAuthorizationPolicy; 064 private DiscoveryAgent discoveryAgent; 065 private final ConnectorStatistics statistics = new ConnectorStatistics(); 066 private URI discoveryUri; 067 private String name; 068 private boolean disableAsyncDispatch; 069 private boolean enableStatusMonitor = false; 070 private Broker broker; 071 private boolean updateClusterClients = false; 072 private boolean rebalanceClusterClients; 073 private boolean updateClusterClientsOnRemove = false; 074 private String updateClusterFilter; 075 private boolean auditNetworkProducers = false; 076 private int maximumProducersAllowedPerConnection = Integer.MAX_VALUE; 077 private int maximumConsumersAllowedPerConnection = Integer.MAX_VALUE; 078 private PublishedAddressPolicy publishedAddressPolicy = new PublishedAddressPolicy(); 079 private boolean allowLinkStealing = false; 080 private boolean warnOnRemoteClose = false; 081 082 LinkedList<String> peerBrokers = new LinkedList<String>(); 083 084 public TransportConnector() { 085 } 086 087 public TransportConnector(TransportServer server) { 088 this(); 089 setServer(server); 090 if (server != null && server.getConnectURI() != null) { 091 URI uri = server.getConnectURI(); 092 if (uri != null && uri.getScheme().equals("vm")) { 093 setEnableStatusMonitor(false); 094 } 095 } 096 } 097 098 /** 099 * @return Returns the connections. 100 */ 101 public CopyOnWriteArrayList<TransportConnection> getConnections() { 102 return connections; 103 } 104 105 /** 106 * Factory method to create a JMX managed version of this transport 107 * connector 108 */ 109 public ManagedTransportConnector asManagedConnector(ManagementContext context, ObjectName connectorName) throws IOException, URISyntaxException { 110 ManagedTransportConnector rc = new ManagedTransportConnector(context, connectorName, getServer()); 111 rc.setBrokerInfo(getBrokerInfo()); 112 rc.setDisableAsyncDispatch(isDisableAsyncDispatch()); 113 rc.setDiscoveryAgent(getDiscoveryAgent()); 114 rc.setDiscoveryUri(getDiscoveryUri()); 115 rc.setEnableStatusMonitor(isEnableStatusMonitor()); 116 rc.setMessageAuthorizationPolicy(getMessageAuthorizationPolicy()); 117 rc.setName(getName()); 118 rc.setTaskRunnerFactory(getTaskRunnerFactory()); 119 rc.setUri(getUri()); 120 rc.setBrokerService(brokerService); 121 rc.setUpdateClusterClients(isUpdateClusterClients()); 122 rc.setRebalanceClusterClients(isRebalanceClusterClients()); 123 rc.setUpdateClusterFilter(getUpdateClusterFilter()); 124 rc.setUpdateClusterClientsOnRemove(isUpdateClusterClientsOnRemove()); 125 rc.setAuditNetworkProducers(isAuditNetworkProducers()); 126 rc.setMaximumConsumersAllowedPerConnection(getMaximumConsumersAllowedPerConnection()); 127 rc.setMaximumProducersAllowedPerConnection(getMaximumProducersAllowedPerConnection()); 128 rc.setPublishedAddressPolicy(getPublishedAddressPolicy()); 129 rc.setAllowLinkStealing(allowLinkStealing); 130 rc.setWarnOnRemoteClose(isWarnOnRemoteClose()); 131 return rc; 132 } 133 134 @Override 135 public BrokerInfo getBrokerInfo() { 136 return brokerInfo; 137 } 138 139 public void setBrokerInfo(BrokerInfo brokerInfo) { 140 this.brokerInfo = brokerInfo; 141 } 142 143 public TransportServer getServer() throws IOException, URISyntaxException { 144 if (server == null) { 145 setServer(createTransportServer()); 146 } 147 return server; 148 } 149 150 public void setServer(TransportServer server) { 151 this.server = server; 152 } 153 154 public URI getUri() { 155 if (uri == null) { 156 try { 157 uri = getConnectUri(); 158 } catch (Throwable e) { 159 } 160 } 161 return uri; 162 } 163 164 /** 165 * Sets the server transport URI to use if there is not a 166 * {@link TransportServer} configured via the 167 * {@link #setServer(TransportServer)} method. This value is used to lazy 168 * create a {@link TransportServer} instance 169 * 170 * @param uri 171 */ 172 public void setUri(URI uri) { 173 this.uri = uri; 174 } 175 176 public TaskRunnerFactory getTaskRunnerFactory() { 177 return taskRunnerFactory; 178 } 179 180 public void setTaskRunnerFactory(TaskRunnerFactory taskRunnerFactory) { 181 this.taskRunnerFactory = taskRunnerFactory; 182 } 183 184 /** 185 * @return the statistics for this connector 186 */ 187 @Override 188 public ConnectorStatistics getStatistics() { 189 return statistics; 190 } 191 192 public MessageAuthorizationPolicy getMessageAuthorizationPolicy() { 193 return messageAuthorizationPolicy; 194 } 195 196 /** 197 * Sets the policy used to decide if the current connection is authorized to 198 * consume a given message 199 */ 200 public void setMessageAuthorizationPolicy(MessageAuthorizationPolicy messageAuthorizationPolicy) { 201 this.messageAuthorizationPolicy = messageAuthorizationPolicy; 202 } 203 204 @Override 205 public void start() throws Exception { 206 broker = brokerService.getBroker(); 207 brokerInfo.setBrokerName(broker.getBrokerName()); 208 brokerInfo.setBrokerId(broker.getBrokerId()); 209 brokerInfo.setPeerBrokerInfos(broker.getPeerBrokerInfos()); 210 brokerInfo.setFaultTolerantConfiguration(broker.isFaultTolerantConfiguration()); 211 brokerInfo.setBrokerURL(broker.getBrokerService().getDefaultSocketURIString()); 212 getServer().setAcceptListener(new TransportAcceptListener() { 213 @Override 214 public void onAccept(final Transport transport) { 215 final String remoteHost = transport.getRemoteAddress(); 216 try { 217 brokerService.getTaskRunnerFactory().execute(new Runnable() { 218 @Override 219 public void run() { 220 try { 221 if (!brokerService.isStopping()) { 222 Connection connection = createConnection(transport); 223 connection.start(); 224 } else { 225 throw new BrokerStoppedException("Broker " + brokerService + " is being stopped"); 226 } 227 } catch (Exception e) { 228 ServiceSupport.dispose(transport); 229 onAcceptError(e, remoteHost); 230 } 231 } 232 }); 233 } catch (Exception e) { 234 ServiceSupport.dispose(transport); 235 onAcceptError(e, remoteHost); 236 } 237 } 238 239 @Override 240 public void onAcceptError(Exception error) { 241 onAcceptError(error, null); 242 } 243 244 private void onAcceptError(Exception error, String remoteHost) { 245 if (brokerService != null && brokerService.isStopping()) { 246 LOG.info("Could not accept connection during shutdown {} : {} ({})", (remoteHost == null ? "" : "from " + remoteHost), error.getLocalizedMessage(), getRootCause(error).getMessage()); 247 } else { 248 LOG.warn("Could not accept connection {}: {} ({})", (remoteHost == null ? "" : "from " + remoteHost), error.getMessage(), getRootCause(error).getMessage()); 249 LOG.debug("Reason: " + error.getMessage(), error); 250 } 251 } 252 }); 253 getServer().setBrokerInfo(brokerInfo); 254 getServer().start(); 255 256 DiscoveryAgent da = getDiscoveryAgent(); 257 if (da != null) { 258 da.registerService(getPublishableConnectString()); 259 da.start(); 260 } 261 if (enableStatusMonitor) { 262 this.statusDector = new TransportStatusDetector(this); 263 this.statusDector.start(); 264 } 265 266 LOG.info("Connector {} started", getName()); 267 } 268 269 static Throwable getRootCause(final Throwable throwable) { 270 final List<Throwable> list = getThrowableList(throwable); 271 return list.isEmpty() ? null : list.get(list.size() - 1); 272 } 273 274 static List<Throwable> getThrowableList(Throwable throwable) { 275 final List<Throwable> list = new ArrayList<>(); 276 while (throwable != null && !list.contains(throwable)) { 277 list.add(throwable); 278 throwable = throwable.getCause(); 279 } 280 return list; 281 } 282 283 public String getPublishableConnectString() throws Exception { 284 String publishableConnectString = publishedAddressPolicy.getPublishableConnectString(this); 285 LOG.debug("Publishing: {} for broker transport URI: {}", publishableConnectString, getConnectUri()); 286 return publishableConnectString; 287 } 288 289 public URI getPublishableConnectURI() throws Exception { 290 return publishedAddressPolicy.getPublishableConnectURI(this); 291 } 292 293 @Override 294 public void stop() throws Exception { 295 ServiceStopper ss = new ServiceStopper(); 296 if (discoveryAgent != null) { 297 ss.stop(discoveryAgent); 298 } 299 if (server != null) { 300 ss.stop(server); 301 } 302 if (this.statusDector != null) { 303 this.statusDector.stop(); 304 } 305 306 for (TransportConnection connection : connections) { 307 ss.stop(connection); 308 } 309 server = null; 310 ss.throwFirstException(); 311 LOG.info("Connector {} stopped", getName()); 312 } 313 314 // Implementation methods 315 // ------------------------------------------------------------------------- 316 protected Connection createConnection(Transport transport) throws IOException { 317 // prefer to use task runner from broker service as stop task runner, as we can then 318 // tie it to the lifecycle of the broker service 319 TransportConnection answer = new TransportConnection(this, transport, broker, disableAsyncDispatch ? null 320 : taskRunnerFactory, brokerService.getTaskRunnerFactory()); 321 boolean statEnabled = this.getStatistics().isEnabled(); 322 answer.getStatistics().setEnabled(statEnabled); 323 answer.setMessageAuthorizationPolicy(messageAuthorizationPolicy); 324 return answer; 325 } 326 327 protected TransportServer createTransportServer() throws IOException, URISyntaxException { 328 if (uri == null) { 329 throw new IllegalArgumentException("You must specify either a server or uri property"); 330 } 331 if (brokerService == null) { 332 throw new IllegalArgumentException( 333 "You must specify the brokerService property. Maybe this connector should be added to a broker?"); 334 } 335 return TransportFactorySupport.bind(brokerService, uri); 336 } 337 338 public DiscoveryAgent getDiscoveryAgent() throws IOException { 339 if (discoveryAgent == null) { 340 discoveryAgent = createDiscoveryAgent(); 341 } 342 return discoveryAgent; 343 } 344 345 protected DiscoveryAgent createDiscoveryAgent() throws IOException { 346 if (discoveryUri != null) { 347 DiscoveryAgent agent = DiscoveryAgentFactory.createDiscoveryAgent(discoveryUri); 348 349 if (agent != null && agent instanceof BrokerServiceAware) { 350 ((BrokerServiceAware) agent).setBrokerService(brokerService); 351 } 352 353 return agent; 354 } 355 return null; 356 } 357 358 public void setDiscoveryAgent(DiscoveryAgent discoveryAgent) { 359 this.discoveryAgent = discoveryAgent; 360 } 361 362 public URI getDiscoveryUri() { 363 return discoveryUri; 364 } 365 366 public void setDiscoveryUri(URI discoveryUri) { 367 this.discoveryUri = discoveryUri; 368 } 369 370 public URI getConnectUri() throws IOException, URISyntaxException { 371 if (server != null) { 372 return server.getConnectURI(); 373 } else { 374 return uri; 375 } 376 } 377 378 public void onStarted(TransportConnection connection) { 379 connections.add(connection); 380 } 381 382 public void onStopped(TransportConnection connection) { 383 connections.remove(connection); 384 } 385 386 public String getName() { 387 if (name == null) { 388 uri = getUri(); 389 if (uri != null) { 390 name = uri.toString(); 391 } 392 } 393 return name; 394 } 395 396 public void setName(String name) { 397 this.name = name; 398 } 399 400 @Override 401 public String toString() { 402 String rc = getName(); 403 if (rc == null) { 404 rc = super.toString(); 405 } 406 return rc; 407 } 408 409 protected ConnectionControl getConnectionControl() { 410 boolean rebalance = isRebalanceClusterClients(); 411 String connectedBrokers = ""; 412 String separator = ""; 413 414 if (isUpdateClusterClients()) { 415 synchronized (peerBrokers) { 416 for (String uri : getPeerBrokers()) { 417 connectedBrokers += separator + uri; 418 separator = ","; 419 } 420 421 if (rebalance) { 422 String shuffle = peerBrokers.removeFirst(); 423 peerBrokers.addLast(shuffle); 424 } 425 } 426 } 427 ConnectionControl control = new ConnectionControl(); 428 control.setConnectedBrokers(connectedBrokers); 429 control.setRebalanceConnection(rebalance); 430 return control; 431 } 432 433 public void addPeerBroker(BrokerInfo info) { 434 if (isMatchesClusterFilter(info.getBrokerName())) { 435 synchronized (peerBrokers) { 436 getPeerBrokers().addLast(info.getBrokerURL()); 437 } 438 } 439 } 440 441 public void removePeerBroker(BrokerInfo info) { 442 synchronized (peerBrokers) { 443 getPeerBrokers().remove(info.getBrokerURL()); 444 } 445 } 446 447 public LinkedList<String> getPeerBrokers() { 448 synchronized (peerBrokers) { 449 if (peerBrokers.isEmpty()) { 450 peerBrokers.add(brokerService.getDefaultSocketURIString()); 451 } 452 return peerBrokers; 453 } 454 } 455 456 @Override 457 public void updateClientClusterInfo() { 458 if (isRebalanceClusterClients() || isUpdateClusterClients()) { 459 ConnectionControl control = getConnectionControl(); 460 for (Connection c : this.connections) { 461 c.updateClient(control); 462 if (isRebalanceClusterClients()) { 463 control = getConnectionControl(); 464 } 465 } 466 } 467 } 468 469 private boolean isMatchesClusterFilter(String brokerName) { 470 boolean result = true; 471 String filter = getUpdateClusterFilter(); 472 if (filter != null) { 473 filter = filter.trim(); 474 if (filter.length() > 0) { 475 result = false; 476 StringTokenizer tokenizer = new StringTokenizer(filter, ","); 477 while (!result && tokenizer.hasMoreTokens()) { 478 String token = tokenizer.nextToken(); 479 result = isMatchesClusterFilter(brokerName, token); 480 } 481 } 482 } 483 484 return result; 485 } 486 487 private boolean isMatchesClusterFilter(String brokerName, String match) { 488 boolean result = false; 489 if (brokerName != null && match != null && brokerName.length() > 0 && match.length() > 0) { 490 result = Pattern.matches(match, brokerName); 491 } 492 return result; 493 } 494 495 public boolean isDisableAsyncDispatch() { 496 return disableAsyncDispatch; 497 } 498 499 public void setDisableAsyncDispatch(boolean disableAsyncDispatch) { 500 this.disableAsyncDispatch = disableAsyncDispatch; 501 } 502 503 /** 504 * @return the enableStatusMonitor 505 */ 506 public boolean isEnableStatusMonitor() { 507 return enableStatusMonitor; 508 } 509 510 /** 511 * @param enableStatusMonitor 512 * the enableStatusMonitor to set 513 */ 514 public void setEnableStatusMonitor(boolean enableStatusMonitor) { 515 this.enableStatusMonitor = enableStatusMonitor; 516 } 517 518 /** 519 * This is called by the BrokerService right before it starts the transport. 520 */ 521 @Override 522 public void setBrokerService(BrokerService brokerService) { 523 this.brokerService = brokerService; 524 } 525 526 public Broker getBroker() { 527 return broker; 528 } 529 530 public BrokerService getBrokerService() { 531 return brokerService; 532 } 533 534 /** 535 * @return the updateClusterClients 536 */ 537 @Override 538 public boolean isUpdateClusterClients() { 539 return this.updateClusterClients; 540 } 541 542 /** 543 * @param updateClusterClients 544 * the updateClusterClients to set 545 */ 546 public void setUpdateClusterClients(boolean updateClusterClients) { 547 this.updateClusterClients = updateClusterClients; 548 } 549 550 /** 551 * @return the rebalanceClusterClients 552 */ 553 @Override 554 public boolean isRebalanceClusterClients() { 555 return this.rebalanceClusterClients; 556 } 557 558 /** 559 * @param rebalanceClusterClients 560 * the rebalanceClusterClients to set 561 */ 562 public void setRebalanceClusterClients(boolean rebalanceClusterClients) { 563 this.rebalanceClusterClients = rebalanceClusterClients; 564 } 565 566 /** 567 * @return the updateClusterClientsOnRemove 568 */ 569 @Override 570 public boolean isUpdateClusterClientsOnRemove() { 571 return this.updateClusterClientsOnRemove; 572 } 573 574 /** 575 * @param updateClusterClientsOnRemove the updateClusterClientsOnRemove to set 576 */ 577 public void setUpdateClusterClientsOnRemove(boolean updateClusterClientsOnRemove) { 578 this.updateClusterClientsOnRemove = updateClusterClientsOnRemove; 579 } 580 581 /** 582 * @return the updateClusterFilter 583 */ 584 @Override 585 public String getUpdateClusterFilter() { 586 return this.updateClusterFilter; 587 } 588 589 /** 590 * @param updateClusterFilter 591 * the updateClusterFilter to set 592 */ 593 public void setUpdateClusterFilter(String updateClusterFilter) { 594 this.updateClusterFilter = updateClusterFilter; 595 } 596 597 @Override 598 public int connectionCount() { 599 return connections.size(); 600 } 601 602 @Override 603 public boolean isAllowLinkStealing() { 604 return server.isAllowLinkStealing(); 605 } 606 607 public void setAllowLinkStealing(boolean allowLinkStealing) { 608 this.allowLinkStealing = allowLinkStealing; 609 } 610 611 public boolean isAuditNetworkProducers() { 612 return auditNetworkProducers; 613 } 614 615 /** 616 * Enable a producer audit on network connections, Traps the case of a missing send reply and resend. 617 * Note: does not work with conduit=false, networked composite destinations or networked virtual topics 618 * @param auditNetworkProducers 619 */ 620 public void setAuditNetworkProducers(boolean auditNetworkProducers) { 621 this.auditNetworkProducers = auditNetworkProducers; 622 } 623 624 public int getMaximumProducersAllowedPerConnection() { 625 return maximumProducersAllowedPerConnection; 626 } 627 628 public void setMaximumProducersAllowedPerConnection(int maximumProducersAllowedPerConnection) { 629 this.maximumProducersAllowedPerConnection = maximumProducersAllowedPerConnection; 630 } 631 632 public int getMaximumConsumersAllowedPerConnection() { 633 return maximumConsumersAllowedPerConnection; 634 } 635 636 public void setMaximumConsumersAllowedPerConnection(int maximumConsumersAllowedPerConnection) { 637 this.maximumConsumersAllowedPerConnection = maximumConsumersAllowedPerConnection; 638 } 639 640 /** 641 * Gets the currently configured policy for creating the published connection address of this 642 * TransportConnector. 643 * 644 * @return the publishedAddressPolicy 645 */ 646 public PublishedAddressPolicy getPublishedAddressPolicy() { 647 return publishedAddressPolicy; 648 } 649 650 /** 651 * Sets the configured policy for creating the published connection address of this 652 * TransportConnector. 653 * 654 * @return the publishedAddressPolicy 655 */ 656 public void setPublishedAddressPolicy(PublishedAddressPolicy publishedAddressPolicy) { 657 this.publishedAddressPolicy = publishedAddressPolicy; 658 } 659 660 public boolean isWarnOnRemoteClose() { 661 return warnOnRemoteClose; 662 } 663 664 public void setWarnOnRemoteClose(boolean warnOnRemoteClose) { 665 this.warnOnRemoteClose = warnOnRemoteClose; 666 } 667}