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.components.util.DefaultFileMarshaler;
036    import org.apache.servicemix.components.util.FileMarshaler;
037    import org.apache.servicemix.locks.LockManager;
038    import org.apache.servicemix.locks.impl.SimpleLockManager;
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: 10252 $
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                            lock.unlock();
210                        }
211                    } else {
212                        if (logger.isDebugEnabled()) {
213                            logger.debug("Unable to acquire lock on " + aFile);
214                        }
215                    }
216                }
217            });
218        }
219    
220        protected void processFileAndDelete(File aFile) {
221            try {
222                if (logger.isDebugEnabled()) {
223                    logger.debug("Processing file " + aFile);
224                }
225                if (aFile.exists()) {
226                    processFile(aFile);
227                    if (isDeleteFile()) {
228                        if (!aFile.delete()) {
229                            throw new IOException("Could not delete file " + aFile);
230                        }
231                    }
232                }
233            } catch (Exception e) {
234                logger.error("Failed to process file: " + aFile + ". Reason: " + e, e);
235            }
236        }
237    
238        protected void processFile(File aFile) throws Exception {
239            String name = aFile.getCanonicalPath();
240            InputStream in = new BufferedInputStream(new FileInputStream(aFile));
241            InOnly exchange = getExchangeFactory().createInOnlyExchange();
242            configureExchangeTarget(exchange);
243            NormalizedMessage message = exchange.createMessage();
244            exchange.setInMessage(message);
245            marshaler.readMessage(exchange, message, in, name);
246            sendSync(exchange);
247            in.close();
248        }
249    
250        public String getLocationURI() {
251            return file.toURI().toString();
252        }
253    
254        public void process(MessageExchange exchange) throws Exception {
255            // Do nothing. In our case, this method should never be called
256            // as we only send synchronous InOnly exchange
257        }
258    }