All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.fabric3.binding.file.runtime.receiver.FileSystemReceiver Maven / Gradle / Ivy

The newest version!
/*
 * Fabric3
 * Copyright (c) 2009-2015 Metaform Systems
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * Portions originally based on Apache Tuscany 2007
 * licensed under the Apache 2.0 license.
 */
package org.fabric3.binding.file.runtime.receiver;

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
import java.nio.channels.OverlappingFileLockException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;

import org.fabric3.api.binding.file.InvalidDataException;
import org.fabric3.api.binding.file.ServiceAdapter;
import org.fabric3.api.binding.file.annotation.Strategy;
import org.fabric3.api.host.util.IOHelper;
import org.fabric3.spi.container.invocation.Message;
import org.fabric3.spi.container.invocation.MessageCache;
import org.fabric3.spi.container.invocation.WorkContext;
import org.fabric3.spi.container.invocation.WorkContextCache;
import org.fabric3.spi.container.wire.Interceptor;

/**
 * Periodically scans a directory for new files. When a new file is detected, the service bound to the directory is invoked with expected data types associated
 * with each file. After an invocation completes, the detected file is either archived or deleted according to the configured {@link Strategy} value. If an
 * error is encountered, the file will be moved to the configured error location.
 * 

* This receiver is non-transactional but supports clustered locking through the use of file locks placed in the <location>/locks directory. */ @SuppressWarnings({"ResultOfMethodCallIgnored"}) public class FileSystemReceiver implements Runnable { private File location; private File lockDirectory; private File errorDirectory; private File archiveDirectory; private Strategy strategy; private Pattern filePattern; private long delay; private Interceptor interceptor; private ScheduledExecutorService executorService; private ServiceAdapter adapter; private ReceiverMonitor monitor; private Map cache = new ConcurrentHashMap<>(); private ScheduledFuture future; public FileSystemReceiver(ReceiverConfiguration configuration) { this.location = configuration.getLocation(); this.strategy = configuration.getStrategy(); this.errorDirectory = configuration.getErrorLocation(); this.archiveDirectory = configuration.getArchiveLocation(); this.filePattern = configuration.getFilePattern(); this.interceptor = configuration.getInterceptor(); this.monitor = configuration.getMonitor(); this.lockDirectory = configuration.getLockDirectory(); this.adapter = configuration.getAdapter(); this.delay = configuration.getDelay(); } public void start() { executorService = Executors.newSingleThreadScheduledExecutor(); future = executorService.scheduleWithFixedDelay(this, delay, delay, TimeUnit.MILLISECONDS); createDirectories(); } public void stop() { if (future != null) { future.cancel(true); } if (executorService != null) { executorService.shutdownNow(); } } public synchronized void run() { List files = new ArrayList<>(); if (!location.isDirectory()) { // there is no drop directory, return without processing return; } File[] pathFiles = location.listFiles(); for (File file : pathFiles) { // add non-ignorable files if (!ignore(file)) { files.add(file); } } if (files.isEmpty()) { // there are no files to process return; } try { processFiles(files); } catch (RuntimeException e) { monitor.error(e); } catch (Error e) { monitor.error(e); throw e; } } void createDirectories() { lockDirectory.mkdirs(); errorDirectory.mkdirs(); if (archiveDirectory != null) { // archive directory is optional if the strategy is delete archiveDirectory.mkdirs(); } } private synchronized void processFiles(List files) { for (File file : files) { String name = file.getName(); FileEntry cached = cache.get(name); if (cached == null) { // the file is new, cache it and wait for next run in case it is in the process of being updated cached = new FileEntry(file); cache.put(name, cached); } else { if (!cached.isChanged()) { // file has finished being updated, process it processFile(file); } } } } private boolean ignore(File file) { String name = file.getName(); return name.startsWith(".") || file.isDirectory() || !filePattern.matcher(name).matches(); } @SuppressWarnings({"ResultOfMethodCallIgnored"}) private void processFile(File file) { String name = file.getName(); // remove file from the cache as it will either be processed by this runtime or skipped cache.remove(name); // attempt to lock the file FileChannel lockChannel; FileLock fileLock; File lockFile = new File(lockDirectory, file.getName() + ".f3"); try { // Always attempt to lock since a lock file could have been created by this or another VM before it crashed. In this case, // the lock file is orphaned and another VM must continue processing. lockChannel = new RandomAccessFile(lockFile, "rw").getChannel(); fileLock = lockChannel.tryLock(); if (fileLock == null) { // file lock is held bu another VM. ignore return; } } catch (OverlappingFileLockException e) { // already being processed by this VM, ignore return; } catch (IOException e) { // error acquiring the lock, skip processing monitor.error(e); return; } try { Object[] payload; try { payload = adapter.beforeInvoke(file); } catch (InvalidDataException e) { monitor.error(e); // invalid file, return and send it the the error directory handleError(file, e); return; } WorkContext workContext = WorkContextCache.getAndResetThreadWorkContext(); Message message = MessageCache.getAndResetMessage(); try { message.setWorkContext(workContext); Message response = dispatch(payload, message); afterInvoke(file, payload); if (response.isFault()) { // the service threw an exception. this is interpreted as a bad file. Move the file to the error location Exception error = (Exception) response.getBody(); handleError(file, error); monitor.error("Error processing file: " + file.getName(), error); } else { if (Strategy.ARCHIVE == strategy) { archiveFile(file); } else { deleteFile(file); } } } catch (RuntimeException e) { // an unexpected runtime error, try and close the resources and retry afterInvoke(file, payload); throw e; } finally { message.reset(); workContext.reset(); } } finally { releaseLock(lockFile, fileLock, lockChannel); } } private Message dispatch(Object[] payload, Message message) { message.setBody(payload); return interceptor.invoke(message); } private void afterInvoke(File file, Object[] payload) { try { adapter.afterInvoke(file, payload); } catch (IOException e) { monitor.error(e); } } private void archiveFile(File file) { try { adapter.archive(file, archiveDirectory); } catch (IOException e) { monitor.error(e); } } private void deleteFile(File file) { try { adapter.delete(file); } catch (IOException e) { monitor.error(e); } } private void handleError(File file, Exception e) { try { adapter.error(file, errorDirectory, e); } catch (IOException ex) { monitor.error(ex); } } private void releaseLock(File lockFile, FileLock lock, FileChannel lockChannel) { if (lock != null) { try { lock.release(); IOHelper.closeQuietly(lockChannel); if (lockFile.exists()) { lockFile.delete(); } } catch (IOException e) { // ignore } } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy