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.servicemix.ftp;
018
019 import java.io.IOException;
020 import java.io.OutputStream;
021 import java.net.URI;
022 import java.util.StringTokenizer;
023
024 import javax.jbi.JBIException;
025 import javax.jbi.management.DeploymentException;
026 import javax.jbi.messaging.MessageExchange;
027 import javax.jbi.messaging.NormalizedMessage;
028 import javax.jbi.servicedesc.ServiceEndpoint;
029
030 import org.apache.commons.net.ftp.FTPClient;
031 import org.apache.servicemix.common.endpoints.ProviderEndpoint;
032 import org.apache.servicemix.components.util.DefaultFileMarshaler;
033 import org.apache.servicemix.components.util.FileMarshaler;
034
035 /**
036 * An FTP endpoint
037 *
038 * @version $Revision: $
039 * @org.apache.xbean.XBean element="sender"
040 */
041 public class FtpSenderEndpoint extends ProviderEndpoint implements FtpEndpointType {
042
043 private FTPClientPool clientPool;
044 private FileMarshaler marshaler = new DefaultFileMarshaler();
045 private String uniqueFileName = "ServiceMix";
046 private boolean overwrite;
047 private URI uri;
048 @Deprecated
049 private String uploadPrefix;
050 @Deprecated
051 private String uploadSuffix;
052 private boolean checkDuplicates = true;
053 private boolean autoCreateDirectory = true;
054
055 public FtpSenderEndpoint() {
056 }
057
058 public FtpSenderEndpoint(FtpComponent component, ServiceEndpoint endpoint) {
059 super(component, endpoint);
060 }
061
062 public void validate() throws DeploymentException {
063 super.validate();
064 if (uri == null && (getClientPool() == null || getClientPool().getHost() == null)) {
065 throw new DeploymentException("Property uri or clientPool.host must be configured");
066 }
067 if (uri != null && getClientPool() != null && getClientPool().getHost() != null) {
068 throw new DeploymentException("Properties uri and clientPool.host can not be configured at the same time");
069 }
070 }
071
072 /**
073 * Configures the endpoint from a URI
074 */
075 public void setUri(URI uri) {
076 this.uri = uri;
077 }
078
079 public boolean isCheckDuplicates() {
080 return checkDuplicates;
081 }
082
083 public void setCheckDuplicates(boolean checkDuplicates) {
084 this.checkDuplicates = checkDuplicates;
085 }
086
087 public void start() throws Exception {
088 super.start();
089 if (clientPool == null) {
090 clientPool = createClientPool();
091 }
092 if (uri != null) {
093 clientPool.setHost(uri.getHost());
094 clientPool.setPort(uri.getPort());
095 if (uri.getUserInfo() != null) {
096 String[] infos = uri.getUserInfo().split(":");
097 clientPool.setUsername(infos[0]);
098 if (infos.length > 1) {
099 clientPool.setPassword(infos[1]);
100 }
101 }
102 }
103
104 // borrow client from pool
105 FTPClient ftp = borrowClient();
106 String folderName = "";
107 try {
108 StringTokenizer strTok = null;
109 if (isAutoCreateDirectory() && !ftp.changeWorkingDirectory(getWorkingPath())) {
110 // it seems the folder isn't there, so create it
111 strTok = new StringTokenizer(getWorkingPath(), "/");
112
113 while (strTok.hasMoreTokens()) {
114 folderName += '/';
115 folderName += strTok.nextToken();
116 if (!ftp.changeWorkingDirectory(folderName)) {
117 if (ftp.makeDirectory(folderName)) {
118 // the folder now exists
119 } else {
120 // unable to create the folder
121 throw new IOException("The defined folder " + getWorkingPath() + " doesn't exist on the server and it can't be created automatically.");
122 }
123 }
124 }
125 }
126 } finally {
127 // give back the client
128 returnClient(ftp);
129 }
130 }
131
132 private String getWorkingPath() {
133 return (uri != null && uri.getPath() != null) ? uri.getPath() : ".";
134 }
135
136 // Properties
137 //-------------------------------------------------------------------------
138 public FTPClientPool getClientPool() {
139 return clientPool;
140 }
141
142 public void setClientPool(FTPClientPool clientPool) {
143 this.clientPool = clientPool;
144 }
145
146 public FileMarshaler getMarshaler() {
147 return marshaler;
148 }
149
150 public void setMarshaler(FileMarshaler marshaler) {
151 this.marshaler = marshaler;
152 }
153
154 public String getUniqueFileName() {
155 return uniqueFileName;
156 }
157
158 /**
159 * Sets the name used to make a unique name if no file name is available on the message.
160 *
161 * @param uniqueFileName the new value of the unique name to use for generating unique names
162 */
163 public void setUniqueFileName(String uniqueFileName) {
164 this.uniqueFileName = uniqueFileName;
165 }
166
167 public boolean isOverwrite() {
168 return overwrite;
169 }
170
171 public void setOverwrite(boolean overwrite) {
172 this.overwrite = overwrite;
173 }
174
175 @Deprecated
176 public String getUploadPrefix() {
177 return uploadPrefix;
178 }
179
180 /**
181 * Set the file name prefix used during upload. The prefix will be automatically removed as soon as the upload has completed.
182 * This allows other processes to discern completed files from files that are being uploaded.
183 *
184 * @param uploadPrefix
185 */
186 @Deprecated
187 public void setUploadPrefix(String uploadPrefix) {
188 this.uploadPrefix = uploadPrefix;
189 }
190
191 @Deprecated
192 public String getUploadSuffix() {
193 return uploadSuffix;
194 }
195
196 /**
197 * Set the file name suffix used during upload. The suffix will be automatically removed as soon as the upload has completed.
198 * This allows other processes to discern completed files from files that are being uploaded.
199 *
200 * @param uploadSuffix
201 */
202 @Deprecated
203 public void setUploadSuffix(String uploadSuffix) {
204 this.uploadSuffix = uploadSuffix;
205 }
206
207 /**
208 * Specifies if the endpoint should create the target directory, if it does
209 * not already exist. If you set this to <code>false</code> and the
210 * directory does not exist, the endpoint will not do anything. Default
211 * value is <code>true</code>.
212 *
213 * @param autoCreateDirectory a boolean specifying if the endpoint creates
214 * directories.
215 */
216 public void setAutoCreateDirectory(boolean autoCreateDirectory) {
217 this.autoCreateDirectory = autoCreateDirectory;
218 }
219
220 public boolean isAutoCreateDirectory() {
221 return autoCreateDirectory;
222 }
223
224 // Implementation methods
225 //-------------------------------------------------------------------------
226
227 protected void processInOnly(MessageExchange exchange, NormalizedMessage message) throws Exception {
228 FTPClient client = null;
229 OutputStream out = null;
230 String name = null;
231 String uploadName = null;
232 try {
233 client = borrowClient();
234 // Change to the directory specified by the URI path if any
235 if (uri != null && uri.getPath() != null) {
236 if (!client.changeWorkingDirectory(uri.getPath())) {
237 logger.warn("Unable to change ftp directory to '" + uri.getPath() + "'");
238 }
239 }
240
241 name = marshaler.getOutputName(exchange, message);
242 if (name == null) {
243 if (uniqueFileName != null) {
244 out = client.storeUniqueFileStream(uniqueFileName);
245 } else {
246 out = client.storeUniqueFileStream();
247 }
248 } else {
249 if (checkDuplicates && client.listFiles(name).length > 0) {
250 if (overwrite) {
251 client.deleteFile(name);
252 } else {
253 throw new IOException("Can not send " + name
254 + " : file already exists and overwrite has not been enabled");
255 }
256 }
257 uploadName = marshaler.getTempOutputName(exchange, message) != null ? marshaler.getTempOutputName(exchange, message) : name;
258 out = client.storeFileStream(uploadName);
259 }
260 if (out == null) {
261 throw new IOException("No output stream available for output name: " + uploadName + ". Maybe the file already exists?");
262 }
263 marshaler.writeMessage(exchange, message, out, uploadName);
264 } finally {
265 if (out != null) {
266 try {
267 out.close();
268 client.completePendingCommand();
269 if (name != null && !name.equals(uploadName) && !client.rename(uploadName, name)) {
270 throw new IOException("File " + uploadName + " could not be renamed to " + name);
271 }
272 } catch (IOException e) {
273 logger.error("Caught exception while closing stream on error: " + e, e);
274 }
275 }
276 returnClient(client);
277 }
278 }
279
280 @Deprecated
281 protected String getUploadName(String name) {
282 String result = uploadPrefix == null ? name : uploadPrefix + name;
283 result = uploadSuffix == null ? result : result + uploadSuffix;
284 return result;
285 }
286
287 protected FTPClientPool createClientPool() throws Exception {
288 FTPClientPool pool = new FTPClientPool();
289 pool.afterPropertiesSet();
290 return pool;
291 }
292
293 protected FTPClient borrowClient() throws JBIException {
294 try {
295 return (FTPClient) getClientPool().borrowClient();
296 } catch (Exception e) {
297 throw new JBIException(e);
298 }
299 }
300
301 protected void returnClient(FTPClient client) {
302 if (client != null) {
303 try {
304 getClientPool().returnClient(client);
305 } catch (Exception e) {
306 logger.error("Failed to return client to pool: " + e, e);
307 }
308 }
309 }
310 }