Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
  /*
   * JBoss, Home of Professional Open Source
   * Copyright 2010, Red Hat Inc., and individual contributors as indicated
   * by the @authors tag. See the copyright.txt in the distribution for a
   * full listing of individual contributors.
   *
   * This is free software; you can redistribute it and/or modify it
   * under the terms of the GNU Lesser General Public License as
   * published by the Free Software Foundation; either version 2.1 of
  * the License, or (at your option) any later version.
  *
  * This software is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  * Lesser General Public License for more details.
  *
  * You should have received a copy of the GNU Lesser General Public
  * License along with this software; if not, write to the Free
  * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
  * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
  */
 package org.jboss.as.ejb3.timerservice.persistence.filestore;
 
 import static org.jboss.as.ejb3.EjbLogger.ROOT_LOGGER;
 import static org.jboss.as.ejb3.EjbMessages.MESSAGES;
 
 import java.io.File;
 import java.util.List;
 import java.util.Map;
 
 
File based persistent timer store.

TODO: this is fairly hackey at the moment, it should be registered as an XA resource to support proper XA semantics

Author(s):
Stuart Douglas
 
 
     public static final ServiceName SERVICE_NAME = ..append("ejb3""timerService""fileTimerPersistence");
 
     private final boolean createIfNotExists;
     private MarshallerFactory factory;
     private final InjectedValue<ModuleLoadermoduleLoader = new InjectedValue<ModuleLoader>();
     private final InjectedValue<PathManagerpathManager = new InjectedValue<PathManager>();
     private final String path;
     private final String pathRelativeTo;
     private File baseDir;
     private PathManager.Callback.Handle callbackHandle;


    
map of timed object id : timer id : timer
 
     private final ConcurrentMap<StringMap<StringTimerEntity>> timers = new ConcurrentHashMap<StringMap<StringTimerEntity>>();
     private final ConcurrentMap<StringLocklocks = new ConcurrentHashMap<StringLock>();
     private final ConcurrentMap<StringStringdirectories = new ConcurrentHashMap<StringString>();
 
     public FileTimerPersistence(final boolean createIfNotExistsfinal String pathfinal String pathRelativeTo) {
         this. = createIfNotExists;
        this. = path;
        this. = pathRelativeTo;
    }
    @Override
    public synchronized void start(final StartContext context) {
        final RiverMarshallerFactory factory = new RiverMarshallerFactory();
        final MarshallingConfiguration configuration = new MarshallingConfiguration();
        configuration.setClassResolver(ModularClassResolver.getInstance(.getValue()));
        this. = configuration;
        this. = factory;
        if ( != null) {
             = .getValue().registerCallback(, PathManager.ReloadServerCallback.create(), ....);
        }
        if (!.exists()) {
            if () {
                if (!.mkdirs()) {
                    throw .failToCreateTimerFileStoreDir();
                }
            } else {
                throw .timerFileStoreDirNotExist();
            }
        }
        if (!.isDirectory()) {
            throw .invalidTimerFileStoreDir();
        }
    }
    @Override
    public void stop(final StopContext context) {
        .clear();
        .clear();
        .clear();
        if ( != null) {
            .remove();
        }
         = null;
         = null;
    }
    @Override
        return this;
    }
    @Override
    public void addTimer(final TimerEntity timerEntity) {
        persistTimer(timerEntitytrue);
    }
    @Override
    public void persistTimer(final TimerEntity timerEntity) {
        persistTimer(timerEntityfalse);
    }
    private void persistTimer(final TimerEntity timerEntityboolean newTimer) {
        final Lock lock = getLock(timerEntity.getTimedObjectId());
        try {
            final int status = .getValue().getStatus();
            if (status == . || status == . ||
                    status == .) {
                //no need to persist anyway
                return;
            }
            lock.lock();
            if (status == . ||
                    status == . || isBeforeCompletion()
                    || status == .) {
                Map<StringTimerEntitymap = getTimers(timerEntity.getTimedObjectId());
                if (timerEntity.getTimerState() == . ||
                        timerEntity.getTimerState() == .) {
                    map.remove(timerEntity.getId());
                    writeFile(timerEntity);
                } else if (newTimer || map.containsKey(timerEntity.getId())) {
                    //if it is not a new timer and is not in the map then it has
                    //been removed by another thread.
                    map.put(timerEntity.getId(), timerEntity);
                    writeFile(timerEntity);
                }
            } else {
                final String key = timerTransactionKey(timerEntity);
                Object existing = .getValue().getResource(key);
                //check is there is already a persist sync for this timer
                if (existing == null) {
                    .getValue().registerInterposedSynchronization(new PersistTransactionSynchronization(lockkeynewTimer));
                }
                //update the most recent version of the timer to be persisted
                .getValue().putResource(keytimerEntity);
            }
        } catch (SystemException e) {
            throw new RuntimeException(e);
        } finally {
            lock.unlock();
        }
    }
    private String timerTransactionKey(final TimerEntity timerEntity) {
        return "org.jboss.as.ejb3.timerTransactionKey." + timerEntity.getId();
    }
    @Override
    public void timerUndeployed(final String timedObjectId) {
        final Lock lock = getLock(timedObjectId);
        try {
            lock.lock();
            .remove(timedObjectId);
            .remove(timedObjectId);
            .remove(timedObjectId);
        } finally {
            lock.unlock();
        }
    }
    private boolean isBeforeCompletion() {
        final CurrentSynchronizationCallback.CallbackType type = CurrentSynchronizationCallback.get();
        if (type != null) {
            return type == ..;
        }
        return false;
    }
    @Override
    public TimerEntity loadTimer(final String idfinal String timedObjectId) {
        final Lock lock = getLock(timedObjectId);
        try {
            lock.lock();
            final Map<StringTimerEntitytimers = getTimers(timedObjectId);
            final TimerEntity timer = timers.get(id);
            if (timer == null) {
                return null;
            }
            return mostRecentEntityVersion(timer);
        } finally {
            lock.unlock();
        }
    }
    @Override
    public List<TimerEntityloadActiveTimers(final String timedObjectIdObject primaryKey) {
        final Lock lock = getLock(timedObjectId);
        try {
            lock.lock();
            final Map<StringTimerEntitytimers = getTimers(timedObjectId);
            final List<TimerEntityentities = new ArrayList<TimerEntity>();
            for (Map.Entry<StringTimerEntityentry : timers.entrySet()) {
                if (primaryKey == null || primaryKey.equals(entry.getValue().getPrimaryKey())) {
                    entities.add(mostRecentEntityVersion(entry.getValue()));
                }
            }
            return entities;
        } finally {
            lock.unlock();
        }
    }
    @Override
    public List<TimerEntityloadActiveTimers(final String timedObjectId) {
        return loadActiveTimers(timedObjectIdnull);
    }

    
Returns either the loaded entity or the most recent version of the entity that has been persisted in this transaction.
    private TimerEntity mostRecentEntityVersion(final TimerEntity timerEntity) {
        try {
            final int status = .getValue().getStatus();
            if (status == . ||
                    status == .) {
                return timerEntity;
            }
            final String key = timerTransactionKey(timerEntity);
            TimerEntity existing = (TimerEntity.getValue().getResource(key);
            return existing != null ? existing : timerEntity;
        } catch (SystemException e) {
            throw new RuntimeException(e);
        }
    }
    private Lock getLock(final String timedObjectId) {
        Lock lock = .get(timedObjectId);
        if (lock == null) {
            final Lock addedLock = new ReentrantLock();
            lock = .putIfAbsent(timedObjectIdaddedLock);
            if (lock == null) {
                lock = addedLock;
            }
        }
        return lock;
    }

    
Gets the timer map, loading from the persistent store if necessary. Should be called under lock

Parameters:
timedObjectId The timed object id
Returns:
The timers for the object
    private Map<StringTimerEntitygetTimers(final String timedObjectId) {
        Map<StringTimerEntitymap = .get(timedObjectId);
        if (map == null) {
            map = loadTimersFromFile(timedObjectId);
            .put(timedObjectIdmap);
        }
        return map;
    }
    private Map<StringTimerEntityloadTimersFromFile(final String timedObjectId) {
        final Map<StringTimerEntitytimers = new HashMap<StringTimerEntity>();
        try {
            final File file = new File(getDirectory(timedObjectId));
            if (!file.exists()) {
                //no timers exist yet
                return timers;
            } else if (!file.isDirectory()) {
                .failToRestoreTimers(file);
                return timers;
            }
            Unmarshaller unmarshaller = .createUnmarshaller();
            for (File timerFile : file.listFiles()) {
                FileInputStream in = null;
                try {
                    in = new FileInputStream(timerFile);
                    unmarshaller.start(new InputStreamByteInput(in));
                    final TimerEntity entity = unmarshaller.readObject(TimerEntity.class);
                    timers.put(entity.getId(), entity);
                    unmarshaller.finish();
                } catch (Exception e) {
                    .failToRestoreTimersFromFile(timerFilee);
                } finally {
                    if (in != null) {
                        try {
                            in.close();
                        } catch (IOException e) {
                            .failToCloseFile(e);
                        }
                    }
                }
            }
        } catch (Exception e) {
            .failToRestoreTimersForObjectId(timedObjectIde);
        }
        return timers;
    }
    private File fileName(String timedObjectIdString timerId) {
        return new File(getDirectory(timedObjectId) + . + timerId.replace(."-"));
    }

    
Gets the directory for a given timed object, making sure it exists.

Parameters:
timedObjectId The timed object
Returns:
The directory
    private String getDirectory(String timedObjectId) {
        String dirName = .get(timedObjectId);
        if (dirName == null) {
            dirName = .getAbsolutePath() + . + timedObjectId.replace(."-");
            File file = new File(dirName);
            if (!file.exists()) {
                if (!file.mkdirs()) {
                    .failToCreateDirectoryForPersistTimers(file);
                }
            }
            .put(timedObjectIddirName);
        }
        return dirName;
    }
    private void writeFile(TimerEntity entity) {
        final File file = fileName(entity.getTimedObjectId(), entity.getId());
        //if the timer is expired or cancelled delete the file
        if (entity.getTimerState() == . ||
                entity.getTimerState() == .) {
            if (file.exists()) {
                file.delete();
            }
            return;
        }
        FileOutputStream fileOutputStream = null;
        try {
            fileOutputStream = new FileOutputStream(filefalse);
            final Marshaller marshaller = .createMarshaller();
            marshaller.start(new OutputStreamByteOutput(fileOutputStream));
            marshaller.writeObject(entity);
            marshaller.finish();
            fileOutputStream.flush();
            fileOutputStream.getFD().sync();
        } catch (FileNotFoundException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            if (fileOutputStream != null) {
                try {
                    fileOutputStream.close();
                } catch (IOException e) {
                    .failToCloseFile(e);
                }
            }
        }
    }
    private final class PersistTransactionSynchronization implements Synchronization {
        private final String transactionKey;
        private final Lock lock;
        private final boolean newTimer;
        private volatile TimerEntity timer;
        public PersistTransactionSynchronization(final Lock lockfinal String transactionKeyfinal boolean newTimer) {
            this. = lock;
            this. = transactionKey;
            this. = newTimer;
        }
        @Override
        public void beforeCompletion() {
            //get the latest version of the entity
            if ( == null) {
                return;
            }
        }
        @Override
        public void afterCompletion(final int status) {
            if ( == null) {
                return;
            }
            try {
                .lock();
                if (status == .) {
                    final Map<StringTimerEntitymap = getTimers(.getTimedObjectId());
                    if (.getTimerState() == . ||
                            .getTimerState() == .) {
                        map.remove(.getId());
                    } else {
                        if ( || map.containsKey(.getId())) {
                            //if an existing timer is not in the map it has been cancelled by another thread
                            map.put(.getId(), );
                        }
                    }
                    writeFile();
                }
            } finally {
                .unlock();
            }
        }
    }
        return ;
    }
        return ;
    }
        return ;
    }
        return ;
    }
New to GrepCode? Check out our FAQ X