1d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen/**
2d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * $RCSfile$
3d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * $Revision$
4d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * $Date$
5d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen *
6d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * Copyright 2003-2006 Jive Software.
7d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen *
8d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
9d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * you may not use this file except in compliance with the License.
10d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * You may obtain a copy of the License at
11d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen *
12d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen *     http://www.apache.org/licenses/LICENSE-2.0
13d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen *
14d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * Unless required by applicable law or agreed to in writing, software
15d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * distributed under the License is distributed on an "AS IS" BASIS,
16d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * See the License for the specific language governing permissions and
18d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * limitations under the License.
19d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen */
20d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenpackage org.jivesoftware.smackx.filetransfer;
21d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
22d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport org.jivesoftware.smack.PacketCollector;
23d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport org.jivesoftware.smack.SmackConfiguration;
24d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport org.jivesoftware.smack.Connection;
25d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport org.jivesoftware.smack.XMPPException;
26d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport org.jivesoftware.smack.filter.OrFilter;
27d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport org.jivesoftware.smack.filter.PacketFilter;
28d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport org.jivesoftware.smack.packet.Packet;
29d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport org.jivesoftware.smackx.packet.StreamInitiation;
30d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
31d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport java.io.InputStream;
32d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport java.io.OutputStream;
33d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport java.util.concurrent.*;
34d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport java.util.List;
35d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport java.util.ArrayList;
36d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
37d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
38d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen/**
39d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * The fault tolerant negotiator takes two stream negotiators, the primary and the secondary
40d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * negotiator. If the primary negotiator fails during the stream negotiaton process, the second
41d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * negotiator is used.
42d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen */
43d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenpublic class FaultTolerantNegotiator extends StreamNegotiator {
44d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
45d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    private StreamNegotiator primaryNegotiator;
46d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    private StreamNegotiator secondaryNegotiator;
47d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    private Connection connection;
48d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    private PacketFilter primaryFilter;
49d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    private PacketFilter secondaryFilter;
50d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
51d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    public FaultTolerantNegotiator(Connection connection, StreamNegotiator primary,
52d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            StreamNegotiator secondary) {
53d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        this.primaryNegotiator = primary;
54d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        this.secondaryNegotiator = secondary;
55d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        this.connection = connection;
56d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
57d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
58d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    public PacketFilter getInitiationPacketFilter(String from, String streamID) {
59d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        if (primaryFilter == null || secondaryFilter == null) {
60d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            primaryFilter = primaryNegotiator.getInitiationPacketFilter(from, streamID);
61d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            secondaryFilter = secondaryNegotiator.getInitiationPacketFilter(from, streamID);
62d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
63d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        return new OrFilter(primaryFilter, secondaryFilter);
64d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
65d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
66d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    InputStream negotiateIncomingStream(Packet streamInitiation) throws XMPPException {
67d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        throw new UnsupportedOperationException("Negotiation only handled by create incoming " +
68d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                "stream method.");
69d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
70d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
71d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    final Packet initiateIncomingStream(Connection connection, StreamInitiation initiation) {
72d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        throw new UnsupportedOperationException("Initiation handled by createIncomingStream " +
73d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                "method");
74d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
75d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
76d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    public InputStream createIncomingStream(StreamInitiation initiation) throws XMPPException {
77d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        PacketCollector collector = connection.createPacketCollector(
78d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                getInitiationPacketFilter(initiation.getFrom(), initiation.getSessionID()));
79d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
80d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        connection.sendPacket(super.createInitiationAccept(initiation, getNamespaces()));
81d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
82d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        ExecutorService threadPoolExecutor = Executors.newFixedThreadPool(2);
83d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        CompletionService<InputStream> service
84d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                = new ExecutorCompletionService<InputStream>(threadPoolExecutor);
85d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        List<Future<InputStream>> futures = new ArrayList<Future<InputStream>>();
86d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        InputStream stream = null;
87d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        XMPPException exception = null;
88d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        try {
89d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            futures.add(service.submit(new NegotiatorService(collector)));
90d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            futures.add(service.submit(new NegotiatorService(collector)));
91d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
92d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            int i = 0;
93d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            while (stream == null && i < futures.size()) {
94d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                Future<InputStream> future;
95d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                try {
96d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                    i++;
97d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                    future = service.poll(10, TimeUnit.SECONDS);
98d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                }
99d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                catch (InterruptedException e) {
100d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                    continue;
101d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                }
102d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
103d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                if (future == null) {
104d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                    continue;
105d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                }
106d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
107d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                try {
108d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                    stream = future.get();
109d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                }
110d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                catch (InterruptedException e) {
111d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                    /* Do Nothing */
112d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                }
113d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                catch (ExecutionException e) {
114d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                    exception = new XMPPException(e.getCause());
115d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                }
116d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            }
117d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
118d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        finally {
119d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            for (Future<InputStream> future : futures) {
120d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                future.cancel(true);
121d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            }
122d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            collector.cancel();
123d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            threadPoolExecutor.shutdownNow();
124d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
125d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        if (stream == null) {
126d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            if (exception != null) {
127d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                throw exception;
128d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            }
129d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            else {
130d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                throw new XMPPException("File transfer negotiation failed.");
131d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            }
132d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
133d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
134d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        return stream;
135d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
136d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
137d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    private StreamNegotiator determineNegotiator(Packet streamInitiation) {
138d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        return primaryFilter.accept(streamInitiation) ? primaryNegotiator : secondaryNegotiator;
139d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
140d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
141d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    public OutputStream createOutgoingStream(String streamID, String initiator, String target)
142d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            throws XMPPException {
143d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        OutputStream stream;
144d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        try {
145d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            stream = primaryNegotiator.createOutgoingStream(streamID, initiator, target);
146d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
147d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        catch (XMPPException ex) {
148d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            stream = secondaryNegotiator.createOutgoingStream(streamID, initiator, target);
149d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
150d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
151d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        return stream;
152d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
153d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
154d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    public String[] getNamespaces() {
155d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        String[] primary = primaryNegotiator.getNamespaces();
156d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        String[] secondary = secondaryNegotiator.getNamespaces();
157d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
158d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        String[] namespaces = new String[primary.length + secondary.length];
159d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        System.arraycopy(primary, 0, namespaces, 0, primary.length);
160d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        System.arraycopy(secondary, 0, namespaces, primary.length, secondary.length);
161d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
162d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        return namespaces;
163d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
164d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
165d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    public void cleanup() {
166d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
167d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
168d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    private class NegotiatorService implements Callable<InputStream> {
169d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
170d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        private PacketCollector collector;
171d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
172d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        NegotiatorService(PacketCollector collector) {
173d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            this.collector = collector;
174d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
175d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
176d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        public InputStream call() throws Exception {
177d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            Packet streamInitiation = collector.nextResult(
178d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                    SmackConfiguration.getPacketReplyTimeout() * 2);
179d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            if (streamInitiation == null) {
180d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                throw new XMPPException("No response from remote client");
181d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            }
182d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            StreamNegotiator negotiator = determineNegotiator(streamInitiation);
183d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            return negotiator.negotiateIncomingStream(streamInitiation);
184d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
185d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
186d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen}
187