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 if (!client.changeWorkingDirectory(uri.getPath())) {
181 logger.warn("Unable to change ftp directory to '" + uri.getPath() + "'");
182 }
183 }
184
185 name = marshaler.getOutputName(exchange, message);
186 if (name == null) {
187 if (uniqueFileName != null) {
188 out = client.storeUniqueFileStream(uniqueFileName);
189 } else {
190 out = client.storeUniqueFileStream();
191 }
192 } else {
193 if (checkDuplicates && client.listFiles(name).length > 0) {
194 if (overwrite) {
195 client.deleteFile(name);
196 } else {
197 throw new IOException("Can not send " + name
198 + " : file already exists and overwrite has not been enabled");
199 }
200 }
201 uploadName = getUploadName(name);
202 out = client.storeFileStream(uploadName);
203 }
204 if (out == null) {
205 throw new IOException("No output stream available for output name: " + uploadName + ". Maybe the file already exists?");
206 }
207 marshaler.writeMessage(exchange, message, out, uploadName);
208 } finally {
209 if (out != null) {
210 try {
211 out.close();
212 client.completePendingCommand();
213 if (name != null && !name.equals(uploadName) && !client.rename(uploadName, name)) {
214 throw new IOException("File " + uploadName + " could not be renamed to " + name);
215 }
216 } catch (IOException e) {
217 logger.error("Caught exception while closing stream on error: " + e, e);
218 }
219 }
220 returnClient(client);
221 }
222 }
223
224 protected String getUploadName(String name) {
225 String result = uploadPrefix == null ? name : uploadPrefix + name;
226 result = uploadSuffix == null ? result : result + uploadSuffix;
227 return result;
228 }
229
230 protected FTPClientPool createClientPool() throws Exception {
231 FTPClientPool pool = new FTPClientPool();
232 pool.afterPropertiesSet();
233 return pool;
234 }
235
236 protected FTPClient borrowClient() throws JBIException {
237 try {
238 return (FTPClient) getClientPool().borrowClient();
239 } catch (Exception e) {
240 throw new JBIException(e);
241 }
242 }
243
244 protected void returnClient(FTPClient client) {
245 if (client != null) {
246 try {
247 getClientPool().returnClient(client);
248 } catch (Exception e) {
249 logger.error("Failed to return client to pool: " + e, e);
250 }
251 }
252 }
253 }