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