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