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    public 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}