es.udc.pfc.xmpp.component.AbstractXMPPComponent.java Source code

Java tutorial

Introduction

Here is the source code for es.udc.pfc.xmpp.component.AbstractXMPPComponent.java

Source

/**
 * Copyright 2012 Jos Martnez
 * 
 * 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 es.udc.pfc.xmpp.component;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;

import java.util.Map;
import java.util.logging.Logger;

import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.Channels;

import com.google.common.base.Strings;
import com.google.common.collect.Maps;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.SettableFuture;

import es.udc.pfc.xmpp.stanza.IQ;
import es.udc.pfc.xmpp.stanza.JID;
import es.udc.pfc.xmpp.stanza.Message;
import es.udc.pfc.xmpp.stanza.Presence;
import es.udc.pfc.xmpp.stanza.Stanza;

/**
 * Basic implementation of an XMPP component.
 */
public abstract class AbstractXMPPComponent implements XMPPComponent {

    protected static final Logger log = Logger.getLogger(XMPPComponent.class.getName());

    private final Map<String, SettableFuture<IQ>> futureHandlers;

    private Channel channel;
    private JID componentID;
    private JID serverID;

    protected AbstractXMPPComponent() {
        futureHandlers = Maps.newHashMap();
    }

    protected abstract void handleMessage(Message message);

    protected abstract void handlePresence(Presence presence);

    protected abstract ListenableFuture<IQ> handleIQ(IQ iq);

    @Override
    public final void init(final Channel channel, final JID serverID, final JID componentID) {
        this.channel = checkNotNull(channel);
        this.componentID = checkNotNull(componentID);
        this.serverID = checkNotNull(serverID);
    }

    @Override
    public final JID getServerJID() {
        return serverID;
    }

    @Override
    public final JID getJID() {
        return componentID;
    }

    @Override
    public final void receivedMessage(final Message message) {
        checkNotNull(message);
        log.finest("Received message: " + message.toString());
        handleMessage(message);
    }

    @Override
    public final void receivedPresence(final Presence presence) {
        checkNotNull(presence);
        log.finest("Received presence: " + presence.toString());
        handlePresence(presence);
    }

    @Override
    public final void receivedIQ(final IQ iq) {
        checkNotNull(iq);
        log.finest("Received iq: " + iq.toString());
        if (iq.isRequest()) {
            Futures.addCallback(handleIQ(iq), new FutureCallback<IQ>() {

                @Override
                public void onSuccess(IQ result) {
                    send(result);
                }

                @Override
                public void onFailure(Throwable t) {
                    // TODO: send an error
                }
            });
        } else if (iq.isResponse()) {
            final SettableFuture<IQ> future = futureHandlers.remove(iq.getId());
            if (future == null) {
                log.warning("No handler for ID " + iq.getId());
                return;
            }

            if (iq.getType() == IQ.Type.result) {
                future.set(iq);
            } else if (iq.getType() == IQ.Type.error) {
                future.setException(new Exception("Error IQ: " + iq.toString()));
            }
        } else {
            log.warning("IQ not request or response");
        }
    }

    /**
     * Send a Stanza to the server.
     * 
     * @param stanza the Stanza to be sent
     */
    public final void send(final Stanza stanza) {
        checkNotNull(stanza);
        if (channel == null || !channel.isConnected()) {
            log.warning("Disconnected, can't send stanza: " + stanza.toString());
            return;
        }

        log.finest("Sending stanza: " + stanza.toString());
        Channels.write(channel, stanza);
    }

    /**
     * Send an IQ request, handling the response using a Future.
     * 
     * @param iq the IQ request to be sent
     * @return a Future 
     */
    public final ListenableFuture<IQ> sendIQ(final IQ iq) {
        checkNotNull(iq);
        checkArgument(iq.isRequest() && !Strings.isNullOrEmpty(iq.getId()));

        if (futureHandlers.containsKey(iq.getId())) {
            log.warning("ID " + iq.getId() + " already being handled.");
            return futureHandlers.get(iq.getId());
        }

        final SettableFuture<IQ> future = SettableFuture.create();
        futureHandlers.put(iq.getId(), future);
        send(iq);
        return future;
    }

    @Override
    public void connected() {
        log.fine("Connected");
    }

    @Override
    public void willDisconnect() {
        log.fine("Will disconnect");
    }

    @Override
    public void disconnected() {
        log.fine("Disconnected");
    }

}