/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You 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. */ package org.apache.ignite.spi.checkpoint.sharedfs; import java.io.File; import java.io.IOException; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import org.apache.ignite.IgniteCheckedException; import org.apache.ignite.IgniteLogger; import org.apache.ignite.internal.util.typedef.internal.S; import org.apache.ignite.internal.util.typedef.internal.U; import org.apache.ignite.marshaller.Marshaller; import org.apache.ignite.spi.IgniteSpiThread; import org.apache.ignite.spi.checkpoint.CheckpointListener; /** * Implementation of {@link org.apache.ignite.spi.IgniteSpiThread} that takes care about outdated files. * Every checkpoint has expiration date after which it makes no sense to * keep it. This class periodically compares files last access time with given * expiration time. * <p> * If this file was not accessed then it is deleted. If file access time is * different from modification date new expiration date is set. */ class SharedFsTimeoutTask extends IgniteSpiThread { /** Map of files to their access and expiration date. */ private Map<File, SharedFsTimeData> files = new HashMap<>(); /** Messages logger. */ private IgniteLogger log; /** Messages marshaller. */ private Marshaller marshaller; /** */ private final Object mux = new Object(); /** Timeout listener. */ private CheckpointListener lsnr; /** * Creates new instance of task that looks after files. * * @param igniteInstanceName Ignite instance name. * @param marshaller Messages marshaller. * @param log Messages logger. */ SharedFsTimeoutTask(String igniteInstanceName, Marshaller marshaller, IgniteLogger log) { super(igniteInstanceName, "grid-sharedfs-timeout-worker", log); assert marshaller != null; assert log != null; this.marshaller = marshaller; this.log = log.getLogger(getClass()); } /** {@inheritDoc} */ @Override public void body() throws InterruptedException { long nextTime = 0; Collection<String> rmvKeys = new HashSet<>(); while (!isInterrupted()) { rmvKeys.clear(); synchronized (mux) { // nextTime is 0 only on first iteration. if (nextTime != 0) { long delay; if (nextTime == -1) delay = 5000; else { assert nextTime > 0; delay = nextTime - U.currentTimeMillis(); } while (delay > 0) { mux.wait(delay); delay = nextTime - U.currentTimeMillis(); } } Map<File, SharedFsTimeData> snapshot = new HashMap<>(files); long now = U.currentTimeMillis(); nextTime = -1; // Check files one by one and physically remove // if (now - last modification date) > expiration time for (Map.Entry<File, SharedFsTimeData> entry : snapshot.entrySet()) { File file = entry.getKey(); SharedFsTimeData timeData = entry.getValue(); try { if (timeData.getLastAccessTime() != file.lastModified()) timeData.setExpireTime(SharedFsUtils.read(file, marshaller, log).getExpireTime()); } catch (IgniteCheckedException e) { U.error(log, "Failed to marshal/unmarshal in checkpoint file: " + file.getAbsolutePath(), e); continue; } catch (IOException e) { if (!file.exists()) { files.remove(file); rmvKeys.add(timeData.getKey()); } else U.error(log, "Failed to read checkpoint file: " + file.getAbsolutePath(), e); continue; } if (timeData.getExpireTime() > 0) if (timeData.getExpireTime() <= now) if (!file.delete() && file.exists()) U.error(log, "Failed to delete check point file by timeout: " + file.getAbsolutePath()); else { files.remove(file); rmvKeys.add(timeData.getKey()); if (log.isDebugEnabled()) log.debug("File was deleted by timeout: " + file.getAbsolutePath()); } else if (timeData.getExpireTime() < nextTime || nextTime == -1) nextTime = timeData.getExpireTime(); } } CheckpointListener lsnr = this.lsnr; if (lsnr != null) for (String key : rmvKeys) lsnr.onCheckpointRemoved(key); } synchronized (mux) { files.clear(); } } /** * Adds file to a list of files this task should look after. * * @param file File being watched. * @param timeData File expiration and access information. */ void add(File file, SharedFsTimeData timeData) { assert file != null; assert timeData != null; synchronized (mux) { files.put(file, timeData); mux.notifyAll(); } } /** * Adds list of files this task should looks after. * * @param newFiles List of files. */ void add(Map<File, SharedFsTimeData> newFiles) { assert newFiles != null; synchronized (mux) { files.putAll(newFiles); mux.notifyAll(); } } /** * Stops watching file. * * @param file File that task should not look after anymore. */ void remove(File file) { assert file != null; synchronized (mux) { files.remove(file); } } /** * Stops watching file list. * * @param delFiles List of files this task should not look after anymore. */ void remove(Iterable<File> delFiles) { assert delFiles != null; synchronized (mux) { for (File file : delFiles) files.remove(file); } } /** * Sets listener. * * @param lsnr Listener. */ void setCheckpointListener(CheckpointListener lsnr) { this.lsnr = lsnr; } /** {@inheritDoc} */ @Override public String toString() { return S.toString(SharedFsTimeoutTask.class, this); } }