001 /** 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. 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 */ 017 package org.apache.camel.component.file.remote; 018 019 import org.apache.camel.Exchange; 020 import org.apache.camel.ServicePoolAware; 021 import org.apache.camel.component.file.GenericFileOperationFailedException; 022 import org.apache.camel.component.file.GenericFileProducer; 023 import org.apache.camel.util.ExchangeHelper; 024 import org.apache.camel.util.ObjectHelper; 025 026 /** 027 * Generic remote file producer for all the FTP variations. 028 */ 029 public class RemoteFileProducer<T> extends GenericFileProducer<T> implements ServicePoolAware { 030 031 private boolean loggedIn; 032 033 protected RemoteFileProducer(RemoteFileEndpoint<T> endpoint, RemoteFileOperations<T> operations) { 034 super(endpoint, operations); 035 } 036 037 @Override 038 public String getFileSeparator() { 039 return "/"; 040 } 041 042 @Override 043 public String normalizePath(String name) { 044 return name; 045 } 046 047 public void process(Exchange exchange) throws Exception { 048 Exchange remoteExchange = getEndpoint().createExchange(exchange); 049 String target = createFileName(exchange); 050 processExchange(remoteExchange, target); 051 ExchangeHelper.copyResults(exchange, remoteExchange); 052 } 053 054 protected RemoteFileOperations getOperations() { 055 return (RemoteFileOperations) operations; 056 } 057 058 @Override 059 @SuppressWarnings("unchecked") 060 public RemoteFileEndpoint<T> getEndpoint() { 061 return (RemoteFileEndpoint<T>) super.getEndpoint(); 062 } 063 064 /** 065 * The file could not be written. We need to disconnect from the remote server. 066 */ 067 public void handleFailedWrite(Exchange exchange, Exception exception) throws Exception { 068 loggedIn = false; 069 if (isStopping() || isStopped()) { 070 // if we are stopping then ignore any exception during a poll 071 log.debug("Exception occurred during stopping: " + exception.getMessage()); 072 } else { 073 log.warn("Writing file failed with: " + exception.getMessage()); 074 try { 075 disconnect(); 076 } catch (Exception e) { 077 // ignore exception 078 log.debug("Ignored exception during disconnect: " + e.getMessage()); 079 } 080 // rethrow the original exception*/ 081 throw exception; 082 } 083 } 084 085 public void disconnect() throws GenericFileOperationFailedException { 086 loggedIn = false; 087 if (getOperations().isConnected()) { 088 if (log.isDebugEnabled()) { 089 log.debug("Disconnecting from: " + getEndpoint()); 090 } 091 getOperations().disconnect(); 092 } 093 } 094 095 @Override 096 public void preWriteCheck() throws Exception { 097 // before writing send a noop to see if the connection is alive and works 098 boolean noop = false; 099 if (loggedIn) { 100 try { 101 noop = getOperations().sendNoop(); 102 } catch (Exception e) { 103 // ignore as we will try to recover connection 104 noop = false; 105 } 106 } 107 108 if (log.isTraceEnabled()) { 109 log.trace("preWriteCheck send noop success: " + noop); 110 } 111 112 // if not alive then reconnect 113 if (!noop) { 114 try { 115 if (getEndpoint().getMaximumReconnectAttempts() > 0) { 116 // only use recoverable if we are allowed any re-connect attempts 117 recoverableConnectIfNecessary(); 118 } else { 119 connectIfNecessary(); 120 } 121 } catch (Exception e) { 122 loggedIn = false; 123 124 // must be logged in to be able to upload the file 125 throw e; 126 } 127 } 128 } 129 130 @Override 131 public void postWriteCheck() { 132 try { 133 if (getEndpoint().isDisconnect()) { 134 if (log.isTraceEnabled()) { 135 log.trace("postWriteCheck disconnect from: " + getEndpoint()); 136 } 137 disconnect(); 138 } 139 } catch (GenericFileOperationFailedException e) { 140 // ignore just log a warning 141 log.warn("Exception occurred during disconnecting from: " + getEndpoint() + " " + e.getMessage()); 142 } 143 } 144 145 @Override 146 protected void doStart() throws Exception { 147 log.debug("Starting"); 148 // do not connect when component starts, just wait until we process as we will 149 // connect at that time if needed 150 super.doStart(); 151 } 152 153 @Override 154 protected void doStop() throws Exception { 155 try { 156 disconnect(); 157 } catch (Exception e) { 158 log.debug("Exception occurred during disconnecting from: " + getEndpoint() + " " + e.getMessage()); 159 } 160 super.doStop(); 161 } 162 163 protected void recoverableConnectIfNecessary() throws Exception { 164 try { 165 connectIfNecessary(); 166 } catch (Exception e) { 167 loggedIn = false; 168 169 // are we interrupted 170 InterruptedException ie = ObjectHelper.getException(InterruptedException.class, e); 171 if (ie != null) { 172 if (log.isDebugEnabled()) { 173 log.debug("Interrupted during connect to: " + getEndpoint(), ie); 174 } 175 throw ie; 176 } 177 178 if (log.isDebugEnabled()) { 179 log.debug("Could not connect to: " + getEndpoint() + ". Will try to recover.", e); 180 } 181 } 182 183 // recover by re-creating operations which should most likely be able to recover 184 if (!loggedIn) { 185 if (log.isDebugEnabled()) { 186 log.debug("Trying to recover connection to: " + getEndpoint() + " with a fresh client."); 187 } 188 setOperations(getEndpoint().createRemoteFileOperations()); 189 connectIfNecessary(); 190 } 191 } 192 193 protected void connectIfNecessary() throws GenericFileOperationFailedException { 194 if (!loggedIn) { 195 if (log.isDebugEnabled()) { 196 log.debug("Not already connected/logged in. Connecting to: " + getEndpoint()); 197 } 198 RemoteFileConfiguration config = getEndpoint().getConfiguration(); 199 loggedIn = getOperations().connect(config); 200 if (!loggedIn) { 201 return; 202 } 203 log.info("Connected and logged in to: " + getEndpoint()); 204 } 205 } 206 207 public boolean isSingleton() { 208 // this producer is stateful because the remote file operations is not thread safe 209 return false; 210 } 211 212 }