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