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