001/**
002 *
003 * Copyright 2003-2006 Jive Software.
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * 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.jivesoftware.smackx.filetransfer;
018
019import java.io.InputStream;
020import java.io.OutputStream;
021
022import org.jivesoftware.smack.SmackException;
023import org.jivesoftware.smack.XMPPConnection;
024import org.jivesoftware.smack.XMPPException;
025import org.jivesoftware.smack.XMPPException.XMPPErrorException;
026import org.jivesoftware.smack.packet.IQ;
027import org.jivesoftware.smack.packet.Stanza;
028import org.jivesoftware.smackx.bytestreams.ibb.packet.Open;
029import org.jivesoftware.smackx.bytestreams.socks5.packet.Bytestream;
030import org.jivesoftware.smackx.si.packet.StreamInitiation;
031
032
033/**
034 * The fault tolerant negotiator takes two stream negotiators, the primary and the secondary
035 * negotiator. If the primary negotiator fails during the stream negotiaton process, the second
036 * negotiator is used.
037 */
038public class FaultTolerantNegotiator extends StreamNegotiator {
039
040    private final StreamNegotiator primaryNegotiator;
041    private final StreamNegotiator secondaryNegotiator;
042    private final XMPPConnection connection;
043
044    public FaultTolerantNegotiator(XMPPConnection connection, StreamNegotiator primary,
045            StreamNegotiator secondary) {
046        this.primaryNegotiator = primary;
047        this.secondaryNegotiator = secondary;
048        this.connection = connection;
049    }
050
051    @Override
052    public void newStreamInitiation(String from, String streamID) {
053        primaryNegotiator.newStreamInitiation(from, streamID);
054        secondaryNegotiator.newStreamInitiation(from, streamID);
055    }
056
057    InputStream negotiateIncomingStream(Stanza streamInitiation) {
058        throw new UnsupportedOperationException("Negotiation only handled by create incoming " +
059                "stream method.");
060    }
061
062    public InputStream createIncomingStream(final StreamInitiation initiation) throws SmackException, XMPPErrorException {
063        // This could be either an xep47 ibb 'open' iq or an xep65 streamhost iq
064        IQ initationSet = initiateIncomingStream(connection, initiation);
065
066        StreamNegotiator streamNegotiator = determineNegotiator(initationSet);
067        try {
068            return streamNegotiator.negotiateIncomingStream(initationSet);
069        }
070        catch (InterruptedException e) {
071            // TODO remove this try/catch once merged into 4.2's master branch
072            throw new IllegalStateException(e);
073        }
074    }
075
076    private StreamNegotiator determineNegotiator(Stanza streamInitiation) {
077        if (streamInitiation instanceof Bytestream) {
078            return primaryNegotiator;
079        } else if (streamInitiation instanceof Open){
080            return secondaryNegotiator;
081        } else {
082            throw new IllegalStateException("Unknown stream initation type");
083        }
084    }
085
086    public OutputStream createOutgoingStream(String streamID, String initiator, String target)
087                    throws SmackException, XMPPException {
088        OutputStream stream;
089        try {
090            stream = primaryNegotiator.createOutgoingStream(streamID, initiator, target);
091        }
092        catch (Exception ex) {
093            stream = secondaryNegotiator.createOutgoingStream(streamID, initiator, target);
094        }
095
096        return stream;
097    }
098
099    public String[] getNamespaces() {
100        String[] primary = primaryNegotiator.getNamespaces();
101        String[] secondary = secondaryNegotiator.getNamespaces();
102
103        String[] namespaces = new String[primary.length + secondary.length];
104        System.arraycopy(primary, 0, namespaces, 0, primary.length);
105        System.arraycopy(secondary, 0, namespaces, primary.length, secondary.length);
106
107        return namespaces;
108    }
109
110}