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