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.truezip;
018
019 import java.io.BufferedInputStream;
020 import java.io.FileFilter;
021 import java.io.IOException;
022 import java.io.InputStream;
023 import java.util.concurrent.locks.Lock;
024
025 import javax.jbi.management.DeploymentException;
026 import javax.jbi.messaging.InOnly;
027 import javax.jbi.messaging.MessageExchange;
028 import javax.jbi.messaging.NormalizedMessage;
029 import javax.jbi.servicedesc.ServiceEndpoint;
030 import javax.xml.namespace.QName;
031
032 import org.apache.servicemix.common.DefaultComponent;
033 import org.apache.servicemix.common.ServiceUnit;
034 import org.apache.servicemix.common.endpoints.PollingEndpoint;
035 import org.apache.servicemix.common.locks.LockManager;
036 import org.apache.servicemix.common.locks.impl.SimpleLockManager;
037 import org.apache.servicemix.components.util.DefaultFileMarshaler;
038 import org.apache.servicemix.components.util.FileMarshaler;
039
040 import de.schlichtherle.io.File;
041 import de.schlichtherle.io.FileInputStream;
042
043 /**
044 * A polling endpoint which looks for a file or files in a directory and sends
045 * the files into the JBI bus as messages, deleting the files by default when
046 * they are processed.
047 *
048 * @org.apache.xbean.XBean element="poller"
049 *
050 * @version $Revision: 69300 $
051 */
052 public class TrueZipPollerEndpoint extends PollingEndpoint implements TrueZipEndpointType {
053
054 private File file;
055
056 private FileFilter filter;
057
058 private boolean deleteFile = true;
059
060 private boolean recursive = true;
061
062 private boolean autoCreateDirectory = true;
063
064 private FileMarshaler marshaler = new DefaultFileMarshaler();
065
066 private LockManager lockManager;
067
068 public TrueZipPollerEndpoint() {
069 }
070
071 public TrueZipPollerEndpoint(ServiceUnit serviceUnit, QName service, String endpoint) {
072 super(serviceUnit, service, endpoint);
073 }
074
075 public TrueZipPollerEndpoint(DefaultComponent component, ServiceEndpoint endpoint) {
076 super(component, endpoint);
077 }
078
079 public void poll() throws Exception {
080 pollFileOrDirectory(file);
081 }
082
083 public void validate() throws DeploymentException {
084 super.validate();
085 if (file == null) {
086 throw new DeploymentException("You must specify a file property");
087 }
088 if (isAutoCreateDirectory() && !file.exists()) {
089 file.mkdirs();
090 }
091 if (lockManager == null) {
092 lockManager = createLockManager();
093 }
094 }
095
096 protected LockManager createLockManager() {
097 return new SimpleLockManager();
098 }
099
100 // Properties
101 // -------------------------------------------------------------------------
102 public java.io.File getFile() {
103 return file;
104 }
105
106 /**
107 * Sets the file to poll, which can be a directory or a file.
108 *
109 * @param file
110 */
111 public void setFile(java.io.File file) {
112 this.file = new File(file);
113 }
114
115 /**
116 * @return the lockManager
117 */
118 public LockManager getLockManager() {
119 return lockManager;
120 }
121
122 /**
123 * @param lockManager
124 * the lockManager to set
125 */
126 public void setLockManager(LockManager lockManager) {
127 this.lockManager = lockManager;
128 }
129
130 public FileFilter getFilter() {
131 return filter;
132 }
133
134 /**
135 * Sets the optional filter to choose which files to process
136 */
137 public void setFilter(FileFilter filter) {
138 this.filter = filter;
139 }
140
141 /**
142 * Returns whether or not we should delete the file when its processed
143 */
144 public boolean isDeleteFile() {
145 return deleteFile;
146 }
147
148 public void setDeleteFile(boolean deleteFile) {
149 this.deleteFile = deleteFile;
150 }
151
152 public boolean isRecursive() {
153 return recursive;
154 }
155
156 public void setRecursive(boolean recursive) {
157 this.recursive = recursive;
158 }
159
160 public boolean isAutoCreateDirectory() {
161 return autoCreateDirectory;
162 }
163
164 public void setAutoCreateDirectory(boolean autoCreateDirectory) {
165 this.autoCreateDirectory = autoCreateDirectory;
166 }
167
168 public FileMarshaler getMarshaler() {
169 return marshaler;
170 }
171
172 public void setMarshaler(FileMarshaler marshaler) {
173 this.marshaler = marshaler;
174 }
175
176 // Implementation methods
177 // -------------------------------------------------------------------------
178
179 protected void pollFileOrDirectory(File fileOrDirectory) {
180 pollFileOrDirectory(fileOrDirectory, true);
181 }
182
183 protected void pollFileOrDirectory(File fileOrDirectory, boolean processDir) {
184 if (!fileOrDirectory.isDirectory()) {
185 pollFile(fileOrDirectory); // process the file
186 } else if (processDir) {
187 logger.debug("Polling directory " + fileOrDirectory);
188 File[] files = (File[]) fileOrDirectory.listFiles(getFilter());
189 for (int i = 0; i < files.length; i++) {
190 pollFileOrDirectory(files[i], isRecursive()); // self-recursion
191 }
192 } else {
193 logger.debug("Skipping directory " + fileOrDirectory);
194 }
195 }
196
197 protected void pollFile(final File aFile) {
198 if (logger.isDebugEnabled()) {
199 logger.debug("Scheduling file " + aFile + " for processing");
200 }
201 getExecutor().execute(new Runnable() {
202 public void run() {
203 String uri = file.toURI().relativize(aFile.toURI()).toString();
204 Lock lock = lockManager.getLock(uri);
205 if (lock.tryLock()) {
206 try {
207 processFileAndDelete(aFile);
208 } finally {
209 try {
210 lock.unlock();
211 } catch (Exception ex) {
212 // can't release the lock
213 logger.error(ex);
214 }
215 lockManager.removeLock(uri);
216 }
217 } else {
218 if (logger.isDebugEnabled()) {
219 logger.debug("Unable to acquire lock on " + aFile);
220 }
221 lockManager.removeLock(uri);
222 }
223 }
224 });
225 }
226
227 protected void processFileAndDelete(File aFile) {
228 try {
229 if (logger.isDebugEnabled()) {
230 logger.debug("Processing file " + aFile);
231 }
232 if (aFile.exists()) {
233 processFile(aFile);
234 if (isDeleteFile() && !aFile.delete()) {
235 throw new IOException("Could not delete file " + aFile);
236 }
237 }
238 } catch (Exception e) {
239 logger.error("Failed to process file: " + aFile + ". Reason: " + e, e);
240 }
241 }
242
243 protected void processFile(File aFile) throws Exception {
244 String name = aFile.getCanonicalPath();
245 InputStream in = new BufferedInputStream(new FileInputStream(aFile));
246 InOnly exchange = getExchangeFactory().createInOnlyExchange();
247 configureExchangeTarget(exchange);
248 NormalizedMessage message = exchange.createMessage();
249 exchange.setInMessage(message);
250 marshaler.readMessage(exchange, message, in, name);
251 sendSync(exchange);
252 in.close();
253 }
254
255 public String getLocationURI() {
256 return file.toURI().toString();
257 }
258
259 public void process(MessageExchange exchange) throws Exception {
260 // Do nothing. In our case, this method should never be called
261 // as we only send synchronous InOnly exchange
262 }
263 }