EMMA Coverage Report (generated Tue Apr 17 08:51:20 BST 2007)
[all classes][org.jtoolkit.essence.data.impl]

COVERAGE SUMMARY FOR SOURCE FILE [FileSetBackingStore.java]

nameclass, %method, %block, %line, %
FileSetBackingStore.java100% (1/1)86%  (6/7)36%  (292/802)46%  (62.6/135)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class FileSetBackingStore100% (1/1)86%  (6/7)36%  (292/802)46%  (62.6/135)
clear (): void 0%   (0/1)0%   (0/104)0%   (0/16)
FileSetBackingStore (String, ClusterComponentBuilder$ClusterCollectionData, S... 100% (1/1)23%  (87/371)30%  (17.5/58)
notifyUpdate (Collection): void 100% (1/1)44%  (50/113)58%  (12.7/22)
close (): void 100% (1/1)64%  (23/36)82%  (6.6/8)
flush (String): boolean 100% (1/1)73%  (117/160)83%  (20.7/25)
persistChanges (Collection): void 100% (1/1)79%  (11/14)80%  (4/5)
<static initializer> 100% (1/1)100% (4/4)100% (1/1)

1package org.jtoolkit.essence.data.impl;
2 
3/*
4   Copyright 2006 Peter Lawrey
5 
6   Licensed under the Apache License, Version 2.0 (the "License");
7   you may not use this file except in compliance with the License.
8   You may obtain a copy of the License at
9 
10       http://www.apache.org/licenses/LICENSE-2.0
11 
12   Unless required by applicable law or agreed to in writing, software
13   distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
14   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   See the License for the specific language governing permissions and
16   limitations under the License.
17*/
18 
19import org.apache.commons.logging.Log;
20import static org.apache.commons.logging.LogFactory.getLog;
21import org.jetbrains.annotations.NotNull;
22import static org.jtoolkit.essence.app.pojo.DatableUtils.readObjects;
23import static org.jtoolkit.essence.app.pojo.DatableUtils.writeObjects;
24import org.jtoolkit.essence.concurrency.Concurrency;
25import org.jtoolkit.essence.concurrency.ThreadSafe;
26import org.jtoolkit.essence.data.ClusterComponentBuilder;
27import org.jtoolkit.essence.data.Store;
28import org.jtoolkit.essence.utils.FilenameMap;
29import org.jtoolkit.essence.utils.IOUtils;
30 
31import java.io.*;
32import java.util.Collection;
33import java.util.LinkedHashSet;
34import java.util.Set;
35import java.util.concurrent.Future;
36import java.util.concurrent.ScheduledExecutorService;
37import java.util.concurrent.TimeUnit;
38 
39/**
40 * @author Peter Lawrey
41 */
42@ThreadSafe(Concurrency.CONCURRENT_READ_WRITE)
43public class FileSetBackingStore<K, V> extends MemoryBackingStore<K, V> implements Flushable {
44    private static final Log LOG = getLog(FileSetBackingStore.class);
45    private static final long SEMI_SYNC_PERIOD = 25; // ms
46    private static final long ASYNC_PERIOD = 500; // ms
47    private static final String JUST_META_DATA1 = "Just MetaData 1";
48 
49    private final FilenameMap<K> filenameMap = new FilenameMap<K>();
50    private final boolean syncWrite;
51    private final Set<String> dirty = new LinkedHashSet<String>();
52    private final Future updateTask;
53    private final Object diskLock = new Object();
54 
55    private final String dirname;
56    private final Flusher flusher;
57 
58    public FileSetBackingStore(String name, ClusterComponentBuilder.ClusterCollectionData cd, ScheduledExecutorService executor) throws IOException {
59        super(name, cd);
60        dirname = cd.location;
61 
62        File dir = new File(dirname);
63        dir.mkdirs();
64        // create lock file.
65        File[] files = dir.listFiles();
66        if (files == null) {
67            if (LOG.isInfoEnabled()) LOG.info(name + ": Directory " + dirname + " does not exist, creating.");
68        } else {
69            if (LOG.isDebugEnabled()) LOG.debug(name + ": Loading " + files.length + " files from " + dirname);
70            for (File file : files) {
71//                if (LOCK_FILE.equals(file.getName())) continue;
72                String filename = file.getName();
73                if (filename.endsWith(".new")) {
74                    new File(filename).delete();
75                    continue;
76                }
77                DataInputStream in = null;
78                try {
79                    in = new DataInputStream(IOUtils.getInputStream(file.toString()));
80                    try {
81                        String fileFormat = in.readUTF();
82                        if (!fileFormat.equals(JUST_META_DATA1))
83                            throw new UnsupportedEncodingException("Unable to read file " + file + ", format not supported " + fileFormat);
84                    } catch (IOException ignored) {
85                        throw new UnsupportedEncodingException("Unable to read file " + file + ", unknown format not supported");
86                    }
87                    Object[] metaDatas = readObjects(in, 0, Integer.MAX_VALUE);
88                    for (Object metaDataObj : metaDatas) {
89                        MetaData<K, V> metaData = (MetaData<K, V>) metaDataObj;
90                        K key = metaData.getKey();
91                        V value = metaData.getValue();
92                        this.metaData.put(key, metaData);
93                        if (value != null) {
94                            V prev = backing.put(key, value);
95                            if (prev != null) {
96                                if (!prev.equals(value))
97                                    LOG.warn(name + ": previous entry for key " + key + " detected. was " + prev + " now " + value);
98                                dirty.add(filenameMap.acquireFilenameForKey(key));
99                            }
100                        }
101                        filenameMap.put(filename, key);
102                    }
103                } catch (EOFException eof) {
104                    LOG.warn(name + ": File was corrupt. " + file + ' ' + eof);
105                } catch (FileNotFoundException fnfe) {
106                    LOG.warn(name + ": File disappeared while reading file " + file + ' ' + fnfe);
107                } finally {
108                    IOUtils.close(in);
109                }
110            }
111            if (LOG.isDebugEnabled() && files.length > 0) LOG.debug(name + ": ... Found " + backing.size() + " entries.");
112        }
113        Store.PersistMode persistMode = cd.persistMode;
114        flusher = new Flusher(dirty, this);
115        boolean syncWrite = false;
116        if (persistMode == Store.PersistMode.SEMI_SYNC || persistMode == Store.PersistMode.ANY) {
117            updateTask = executor.scheduleAtFixedRate(flusher, SEMI_SYNC_PERIOD, SEMI_SYNC_PERIOD, TimeUnit.MILLISECONDS);
118            filenameMap.setTargetCount(1000);
119        } else if (persistMode == Store.PersistMode.ASYNC) {
120            updateTask = executor.scheduleAtFixedRate(flusher, ASYNC_PERIOD, ASYNC_PERIOD, TimeUnit.MILLISECONDS);
121            filenameMap.setTargetCount(5000);
122        } else {
123            updateTask = null;
124            syncWrite = true;
125            filenameMap.setTargetCount(50);
126        }
127        this.syncWrite = syncWrite;
128    }
129 
130    protected void persistChanges(Collection<MetaData<K, V>> changes) {
131        super.persistChanges(changes);
132        if (metaData.isEmpty())
133            clear();
134        else
135            notifyUpdate(changes);
136    }
137 
138    private void clear() {
139        filenameMap.clear();
140        File dir = new File(dirname);
141        File[] files;
142        synchronized (diskLock) {
143            files = dir.listFiles();
144            if (files == null) return;
145            if (syncWrite) {
146                for (File f : files)
147                    if (!f.delete())
148                        LOG.warn(getName() + ": Unable to remove " + f);
149            }
150        }
151        if (!syncWrite) {
152            synchronized (dirty) {
153                for (File f : files)
154                    dirty.add(f.getName());
155            }
156        }
157    }
158 
159    public void close() {
160        boolean closed = isClosed();
161        super.close();
162        if (updateTask != null) updateTask.cancel(false);
163        if (LOG.isDebugEnabled() && !closed)
164            LOG.debug(getName() + ": Closing.");
165        if (canWrite()) {
166            flusher.run();
167        }
168    }
169 
170    private void notifyUpdate(@NotNull Collection<MetaData<K, V>> changes) {
171        if (closed)
172            LOG.error(getName() + ": Update after closed!");
173        if (!canWrite())
174            return;
175        Set<String> filesToFlush = new LinkedHashSet<String>();
176        for (MetaData<K, V> dataKey : changes) {
177            if (dataKey == null) {
178                LOG.warn(getName() + ": Illegal data key null ignored in notifyUpdate");
179                continue;
180            }
181            String filenameToFlush = filenameMap.acquireFilenameForKey(dataKey.getKey());
182            filesToFlush.add(filenameToFlush);
183        }
184        if (syncWrite) {
185            for (String filenameToFlush : filesToFlush) {
186                if (!flush(filenameToFlush))
187                    synchronized (dirty) {
188                        dirty.add(filenameToFlush);
189                    }
190            }
191        } else {
192            synchronized (dirty) {
193                dirty.addAll(filesToFlush);
194            }
195        }
196    }
197 
198    public boolean flush(String filename) {
199        if (LOG.isDebugEnabled()) LOG.debug(Thread.currentThread().getName() + ": Flushing " + dirname + '/' + filename);
200        DataOutputStream out = null;
201        try {
202            synchronized (diskLock) {
203                File parentFile = new File(dirname);
204                File file = new File(parentFile, filename);
205                IOUtils.deleteIfTmp(file);
206 
207                File file2 = IOUtils.getNewFile(dirname + '/' + filename);
208 
209                Collection<K> ks = filenameMap.getKeysForFilename(filename);
210                MetaData[] metaDatas = null;
211                if (ks != null) {
212                    ks.removeAll(getRetiredKeys(ks));
213                    metaDatas = getSnapshot((K[]) ks.toArray());
214                }
215 
216                if (metaDatas == null || metaDatas.length == 0) {
217                    filenameMap.removeFilename(filename);
218                    return !file.exists() || file.delete();
219                }
220 
221                out = new DataOutputStream(IOUtils.getOutputStream(file2));
222                out.writeUTF(JUST_META_DATA1);
223                // take a synchronised copy of this.
224                writeObjects(out, (Object[]) metaDatas);
225                IOUtils.close(out);
226 
227                return IOUtils.rename(file, file2);
228            }
229        } catch (IOException e) {
230            LOG.error(Thread.currentThread().getName() + ": Unable to save " + dirname + " cannot create.", e);
231            return false;
232        } finally {
233            IOUtils.close(out);
234        }
235    }
236}

[all classes][org.jtoolkit.essence.data.impl]
EMMA 2.0.5312 (C) Vladimir Roubtsov