All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.fabric3.fabric.container.builder.ChannelConnectorImpl Maven / Gradle / Ivy

The newest version!
/*
 * Fabric3
 * Copyright (c) 2009-2015 Metaform Systems
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.fabric3.fabric.container.builder;

import java.net.URI;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Supplier;

import org.fabric3.api.host.Fabric3Exception;
import org.fabric3.api.model.type.contract.DataType;
import org.fabric3.fabric.container.channel.ChannelConnectionImpl;
import org.fabric3.fabric.container.channel.ChannelManager;
import org.fabric3.fabric.container.channel.EventStreamImpl;
import org.fabric3.spi.container.builder.DirectConnectionFactory;
import org.fabric3.spi.container.builder.SourceConnectionAttacher;
import org.fabric3.spi.container.builder.TargetConnectionAttacher;
import org.fabric3.spi.container.channel.Channel;
import org.fabric3.spi.container.channel.ChannelConnection;
import org.fabric3.spi.container.channel.EventStream;
import org.fabric3.spi.container.channel.EventStreamHandler;
import org.fabric3.spi.container.channel.TransformerHandlerFactory;
import org.fabric3.spi.model.physical.ChannelSide;
import org.fabric3.spi.model.physical.PhysicalChannelConnection;
import org.fabric3.spi.model.physical.PhysicalConnectionSource;
import org.fabric3.spi.model.physical.PhysicalConnectionTarget;
import org.oasisopen.sca.annotation.Reference;

/**
 * Default ChannelConnector implementation.
 *
 * This implementation caches connections for reuse. For example, if two bound consumers are connected to the same channel, two connections will be generated
 * for each consumer: one from the binding to the channel and one from the channel to the target component. In this case, only one connection should be engaged
 * from the binding to the channel (otherwise duplicate messages will be received by the components). For the second component, the cached channel will be
 * returned instead of creating an additional one.
 *
 * Channels are cached based on their source id/target id pair. Bindings will generally only engage one connection from the transport to the channel.
 * Connections from channels to components will generally always be engaged (i.e. their target ids will be unique) since a component will need to be injected
 * with the connection proxy.
 */
public class ChannelConnectorImpl implements ChannelConnector {
    private Map, DirectConnectionFactory> connectionFactories = new HashMap<>();

    private Map cachedConnections = new HashMap<>();  // connection cache

    @Reference
    protected ChannelManager channelManager;

    @Reference(required = false)
    protected Map, SourceConnectionAttacher> sourceAttachers = new HashMap<>();

    @Reference(required = false)
    protected Map, TargetConnectionAttacher> targetAttachers = new HashMap<>();

    @Reference
    protected TransformerHandlerFactory transformerHandlerFactory;

    @Reference(required = false)
    public void setConnectionFactories(List factories) {
        this.connectionFactories.clear();
        factories.forEach(factory -> factory.getTypes().forEach(type -> connectionFactories.put(type, factory)));
    }

    @SuppressWarnings({"unchecked"})
    public ChannelConnection connect(PhysicalChannelConnection physicalConnection) {
        PhysicalConnectionSource source = physicalConnection.getSource();
        PhysicalConnectionTarget target = physicalConnection.getTarget();
        Key key = new Key(source.getSourceId(), target.getTargetId());
        Holder holder = cachedConnections.get(key);
        if (holder != null) {
            // connection is cached; don't engage a second one
            holder.count.incrementAndGet();
            return holder.connection;
        }
        SourceConnectionAttacher sourceAttacher = sourceAttachers.get(source.getClass());
        if (sourceAttacher == null) {
            throw new Fabric3Exception("Attacher not found for type: " + source.getClass().getName());
        }
        TargetConnectionAttacher targetAttacher = targetAttachers.get(target.getClass());
        if (targetAttacher == null) {
            throw new Fabric3Exception("Attacher not found for type: " + target.getClass().getName());
        }

        ChannelConnection connection = createConnection(physicalConnection);

        sourceAttacher.attach(source, target, connection);
        targetAttacher.attach(source, target, connection);
        cachedConnections.put(key, new Holder(connection));
        return connection;
    }

    @SuppressWarnings({"unchecked"})
    public void disconnect(PhysicalChannelConnection physicalConnection) {
        PhysicalConnectionSource source = physicalConnection.getSource();
        PhysicalConnectionTarget target = physicalConnection.getTarget();
        Key key = new Key(source.getSourceId(), target.getTargetId());
        Holder holder = cachedConnections.get(key);
        if (holder == null) {
            return;
        }
        if (holder.count.decrementAndGet() == 0) {
            cachedConnections.remove(key);
        } else {
            return;
        }
        SourceConnectionAttacher sourceAttacher = sourceAttachers.get(source.getClass());
        if (sourceAttacher == null) {
            throw new Fabric3Exception("Attacher not found for type: " + source.getClass().getName());
        }
        TargetConnectionAttacher targetAttacher = targetAttachers.get(target.getClass());
        if (targetAttacher == null) {
            throw new Fabric3Exception("Attacher not found for type: " + target.getClass().getName());
        }
        sourceAttacher.detach(source, target);
        targetAttacher.detach(source, target);
    }

    /**
     * Creates the connection.
     *
     * @param physicalConnection the connection
     * @return the connection
     * @ if there is an error creating the connection
     */
    private ChannelConnection createConnection(PhysicalChannelConnection physicalConnection) {
        PhysicalConnectionSource source = physicalConnection.getSource();
        PhysicalConnectionTarget target = physicalConnection.getTarget();
        if (source.isDirectConnection() || target.isDirectConnection()) {
            // handle direct connection
            int sequence = source.getSequence();
            String topic = physicalConnection.getTopic();
            URI channelUri = physicalConnection.getChannelUri();

            Supplier supplier;
            if (physicalConnection.isBound()) {
                // get the direct connection from the binding
                Class type = source.isDirectConnection() ? source.getServiceInterface() : target.getServiceInterface();
                DirectConnectionFactory factory = connectionFactories.get(type);
                if (factory == null) {
                    throw new Fabric3Exception("Factory type not found: " + type.getName());
                }
                URI attachUri = physicalConnection.getAttachUri();
                // Return a Supplier of a Supplier to lazily initialize the connection. This is so the source attachment can be done before this call to
                // getConnection. Otherwise, DirectConnectionFactory.getConnection() will be called before the source is attached and channel resources
                // can be created
                supplier = () -> factory.getConnection(channelUri, attachUri, type, physicalConnection.getTopic()).get();
            } else {
                // get the direct connection to the local channel
                Channel channel = channelManager.getChannel(channelUri, ChannelSide.COLLOCATED);
                if (channel == null) {
                    throw new Fabric3Exception("Channel not found: " + channelUri);
                }
                supplier = () -> channel.getDirectConnection(topic);
            }

            return new ChannelConnectionImpl(supplier, topic, sequence);
        } else {
            // connect using an event stream
            ClassLoader loader = physicalConnection.getTarget().getClassLoader();
            Class eventType = physicalConnection.getEventType();
            EventStream stream = new EventStreamImpl(eventType);
            addTypeTransformer(physicalConnection, stream, loader);
            String topic = physicalConnection.getTopic();
            int sequence = source.getSequence();
            return new ChannelConnectionImpl(stream, topic, sequence);
        }
    }

    private void addTypeTransformer(PhysicalChannelConnection connection, EventStream stream, ClassLoader loader) {
        if (transformerHandlerFactory == null) {
            return;  // bootstrap
        }
        List sourceTypes = connection.getSource().getDataTypes();
        List targetTypes = connection.getTarget().getDataTypes();
        if (sourceTypes.isEmpty() || targetTypes.isEmpty()) {
            return;
        }
        if (sourceTypes.size() > 1 || targetTypes.size() > 1) {
            // for now, only support one data type
            throw new Fabric3Exception("Multi-type events are not supported");
        }
        DataType sourceType = sourceTypes.get(0);
        DataType targetType = targetTypes.get(0);
        if (sourceType.equals(targetType)) {
            return;
        }
        List> eventTypes = Collections.singletonList(stream.getEventType());
        EventStreamHandler handler = transformerHandlerFactory.createHandler(sourceType, targetType, eventTypes, loader);
        stream.addHandler(handler);
    }

    private class Key {
        String sourceId;
        String targetId;

        public Key(String sourceId, String targetId) {
            this.sourceId = sourceId;
            this.targetId = targetId;
        }

        public boolean equals(Object o) {
            if (this == o) {
                return true;
            }
            if (o == null || getClass() != o.getClass()) {
                return false;
            }

            Key key = (Key) o;

            return sourceId.equals(key.sourceId) && targetId.equals(key.targetId);
        }

        public int hashCode() {
            int result = sourceId.hashCode();
            result = 31 * result + targetId.hashCode();
            return result;
        }
    }

    private class Holder {
        AtomicInteger count = new AtomicInteger(1);
        ChannelConnection connection;

        public Holder(ChannelConnection connection) {
            this.connection = connection;
        }
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy