Package org.apache.hedwig.server.handlers

Source Code of org.apache.hedwig.server.handlers.SubscribeHandler

/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements.  See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership.  The ASF licenses this file
* to you 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.apache.hedwig.server.handlers;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelFuture;
import org.jboss.netty.channel.ChannelFutureListener;

import com.google.protobuf.ByteString;

import org.apache.bookkeeper.util.MathUtils;
import org.apache.bookkeeper.util.ReflectionUtils;
import org.apache.hedwig.client.data.TopicSubscriber;
import org.apache.hedwig.exceptions.PubSubException;
import org.apache.hedwig.exceptions.PubSubException.ServerNotResponsibleForTopicException;
import org.apache.hedwig.filter.PipelineFilter;
import org.apache.hedwig.filter.ServerMessageFilter;
import org.apache.hedwig.protocol.PubSubProtocol.MessageSeqId;
import org.apache.hedwig.protocol.PubSubProtocol.OperationType;
import org.apache.hedwig.protocol.PubSubProtocol.PubSubRequest;
import org.apache.hedwig.protocol.PubSubProtocol.ResponseBody;
import org.apache.hedwig.protocol.PubSubProtocol.SubscribeRequest;
import org.apache.hedwig.protocol.PubSubProtocol.SubscribeResponse;
import org.apache.hedwig.protocol.PubSubProtocol.SubscriptionData;
import org.apache.hedwig.protoextensions.PubSubResponseUtils;
import org.apache.hedwig.protoextensions.SubscriptionStateUtils;
import org.apache.hedwig.server.common.ServerConfiguration;
import org.apache.hedwig.server.delivery.ChannelEndPoint;
import org.apache.hedwig.server.delivery.DeliveryManager;
import org.apache.hedwig.server.netty.ServerStats;
import org.apache.hedwig.server.netty.ServerStats.OpStats;
import org.apache.hedwig.server.netty.UmbrellaHandler;
import org.apache.hedwig.server.persistence.PersistenceManager;
import org.apache.hedwig.server.subscriptions.SubscriptionManager;
import org.apache.hedwig.server.subscriptions.AllToAllTopologyFilter;
import org.apache.hedwig.server.topics.TopicManager;
import org.apache.hedwig.util.Callback;
import static org.apache.hedwig.util.VarArgs.va;

public class SubscribeHandler extends BaseHandler {
    static Logger logger = LoggerFactory.getLogger(SubscribeHandler.class);

    private final DeliveryManager deliveryMgr;
    private final PersistenceManager persistenceMgr;
    private final SubscriptionManager subMgr;
    private final SubscriptionChannelManager subChannelMgr;

    // op stats
    private final OpStats subStats;

    public SubscribeHandler(ServerConfiguration cfg, TopicManager topicMgr,
                            DeliveryManager deliveryManager,
                            PersistenceManager persistenceMgr,
                            SubscriptionManager subMgr,
                            SubscriptionChannelManager subChannelMgr) {
        super(topicMgr, cfg);
        this.deliveryMgr = deliveryManager;
        this.persistenceMgr = persistenceMgr;
        this.subMgr = subMgr;
        this.subChannelMgr = subChannelMgr;
        subStats = ServerStats.getInstance().getOpStats(OperationType.SUBSCRIBE);
    }

    @Override
    public void handleRequestAtOwner(final PubSubRequest request, final Channel channel) {

        if (!request.hasSubscribeRequest()) {
            UmbrellaHandler.sendErrorResponseToMalformedRequest(channel, request.getTxnId(),
                    "Missing subscribe request data");
            subStats.incrementFailedOps();
            return;
        }

        final ByteString topic = request.getTopic();

        MessageSeqId seqId;
        try {
            seqId = persistenceMgr.getCurrentSeqIdForTopic(topic);
        } catch (ServerNotResponsibleForTopicException e) {
            channel.write(PubSubResponseUtils.getResponseForException(e, request.getTxnId())).addListener(
                ChannelFutureListener.CLOSE);
            logger.error("Error getting current seq id for topic " + topic.toStringUtf8()
                       + " when processing subscribe request (txnid:" + request.getTxnId() + ") :", e);
            subStats.incrementFailedOps();
            ServerStats.getInstance().incrementRequestsRedirect();
            return;
        }

        final SubscribeRequest subRequest = request.getSubscribeRequest();
        final ByteString subscriberId = subRequest.getSubscriberId();

        MessageSeqId lastSeqIdPublished = MessageSeqId.newBuilder(seqId).setLocalComponent(seqId.getLocalComponent()).build();

        final long requestTime = MathUtils.now();
        subMgr.serveSubscribeRequest(topic, subRequest, lastSeqIdPublished, new Callback<SubscriptionData>() {

            @Override
            public void operationFailed(Object ctx, PubSubException exception) {
                channel.write(PubSubResponseUtils.getResponseForException(exception, request.getTxnId())).addListener(
                    ChannelFutureListener.CLOSE);
                logger.error("Error serving subscribe request (" + request.getTxnId() + ") for (topic: "
                           + topic.toStringUtf8() + " , subscriber: " + subscriberId.toStringUtf8() + ")", exception);
                subStats.incrementFailedOps();
            }

            @Override
            public void operationFinished(Object ctx, final SubscriptionData subData) {

                TopicSubscriber topicSub = new TopicSubscriber(topic, subscriberId);
                synchronized (channel) {
                    if (!channel.isConnected()) {
                        // channel got disconnected while we were processing the
                        // subscribe request,
                        // nothing much we can do in this case
                        subStats.incrementFailedOps();
                        return;
                    }
                }
                // initialize the message filter
                PipelineFilter filter = new PipelineFilter();
                try {
                    // the filter pipeline should be
                    // 1) AllToAllTopologyFilter to filter cross-region messages
                    filter.addLast(new AllToAllTopologyFilter());
                    // 2) User-Customized MessageFilter
                    if (subData.hasPreferences() &&
                        subData.getPreferences().hasMessageFilter()) {
                        String messageFilterName = subData.getPreferences().getMessageFilter();
                        filter.addLast(ReflectionUtils.newInstance(messageFilterName, ServerMessageFilter.class));
                    }
                    // initialize the filter
                    filter.initialize(cfg.getConf());
                    filter.setSubscriptionPreferences(topic, subscriberId,
                                                      subData.getPreferences());
                } catch (RuntimeException re) {
                    String errMsg = "RuntimeException caught when instantiating message filter for (topic:"
                                  + topic.toStringUtf8() + ", subscriber:" + subscriberId.toStringUtf8() + ")."
                                  + "It might be introduced by programming error in message filter.";
                    logger.error(errMsg, re);
                    PubSubException pse = new PubSubException.InvalidMessageFilterException(errMsg, re);
                    subStats.incrementFailedOps();
                    // we should not close the subscription channel, just response error
                    // client decide to close it or not.
                    channel.write(PubSubResponseUtils.getResponseForException(pse, request.getTxnId()));
                    return;
                } catch (Throwable t) {
                    String errMsg = "Failed to instantiate message filter for (topic:" + topic.toStringUtf8()
                                  + ", subscriber:" + subscriberId.toStringUtf8() + ").";
                    logger.error(errMsg, t);
                    PubSubException pse = new PubSubException.InvalidMessageFilterException(errMsg, t);
                    subStats.incrementFailedOps();
                    channel.write(PubSubResponseUtils.getResponseForException(pse, request.getTxnId()))
                    .addListener(ChannelFutureListener.CLOSE);
                    return;
                }
                boolean forceAttach = false;
                if (subRequest.hasForceAttach()) {
                    forceAttach = subRequest.getForceAttach();
                }
                // Try to store the subscription channel for the topic subscriber
                Channel oldChannel = subChannelMgr.put(topicSub, channel, forceAttach);
                if (null != oldChannel) {
                    PubSubException pse = new PubSubException.TopicBusyException(
                        "Subscriber " + subscriberId.toStringUtf8() + " for topic " + topic.toStringUtf8()
                        + " is already being served on a different channel " + oldChannel + ".");
                    subStats.incrementFailedOps();
                    channel.write(PubSubResponseUtils.getResponseForException(pse, request.getTxnId()))
                    .addListener(ChannelFutureListener.CLOSE);
                    return;
                }

                // want to start 1 ahead of the consume ptr
                MessageSeqId lastConsumedSeqId = subData.getState().getMsgId();
                MessageSeqId seqIdToStartFrom = MessageSeqId.newBuilder(lastConsumedSeqId).setLocalComponent(
                                                    lastConsumedSeqId.getLocalComponent() + 1).build();
                deliveryMgr.startServingSubscription(topic, subscriberId,
                        subData.getPreferences(), seqIdToStartFrom, new ChannelEndPoint(channel), filter,
                        new Callback<Void>() {
                            @Override
                            public void operationFinished(Object ctx, Void result) {
                                // First write success and then tell the delivery manager,
                                // otherwise the first message might go out before the response
                                // to the subscribe
                                SubscribeResponse.Builder subRespBuilder = SubscribeResponse.newBuilder()
                                    .setPreferences(subData.getPreferences());
                                ResponseBody respBody = ResponseBody.newBuilder()
                                    .setSubscribeResponse(subRespBuilder).build();
                                channel.write(PubSubResponseUtils.getSuccessResponse(request.getTxnId(), respBody));
                                logger.info("Subscribe request (" + request.getTxnId() + ") for (topic:"
                                            + topic.toStringUtf8() + ", subscriber:" + subscriberId.toStringUtf8()
                                            + ") from channel " + channel.getRemoteAddress()
                                            + " succeed - its subscription data is "
                                            + SubscriptionStateUtils.toString(subData));
                                subStats.updateLatency(MathUtils.now() - requestTime);
                            }
                            @Override
                            public void operationFailed(Object ctx, PubSubException exception) {
                                // would not happened
                            }
                        }, null);
            }
        }, null);

    }

}
TOP

Related Classes of org.apache.hedwig.server.handlers.SubscribeHandler

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.