/******************************************************************************* * Copyright (c) 2010- Association for Decentralized Information Management in * Industry THTH ry. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * VTT Technical Research Centre of Finland - initial API and implementation *******************************************************************************/ package org.simantics.databoard.accessor.file; import java.io.File; import java.io.IOException; import java.lang.ref.ReferenceQueue; import java.lang.ref.WeakReference; import java.util.HashMap; import java.util.Map; import org.simantics.databoard.Bindings; import org.simantics.databoard.Datatypes; import org.simantics.databoard.accessor.binary.BinaryObject; import org.simantics.databoard.accessor.error.AccessorConstructionException; import org.simantics.databoard.accessor.error.AccessorException; import org.simantics.databoard.accessor.impl.AccessorParams; import org.simantics.databoard.binding.Binding; import org.simantics.databoard.binding.error.BindingConstructionException; import org.simantics.databoard.binding.error.BindingException; import org.simantics.databoard.util.binary.BinaryFile; /** * File library handles sharing of file accessors and automatic closing of files. *

* On call of getFile or createFile the file library opens a file and returns a * file accessor. In concecutive runs the same accessor instance is returned, * unless the file was garbage collected. * * The shared accessor instances are not concurrent-use-safe. * *

* The file handles are actually closed by two ways: * a) File accessor are garbage collected and the the user calls {@link FileLibrary#expunge()} * b) The user invokes {@link FileLibrary#close()} in the FileLibrary. * * Files are flushed before closed. * * @author Toni Kalajainen */ public class FileLibrary { /** Files */ Map files = new HashMap(); /** Queue */ ReferenceQueue queue = new ReferenceQueue(); /** Acessor params */ AccessorParams params; /** * Create new file library */ public FileLibrary() { params = AccessorParams.DEFAULT; } /** * Create new file library */ public FileLibrary(AccessorParams params) { this.params = params; } /** * Get existing open file accessor. * * @param file * @return file or null * @throws IOException * @throws AccessorConstructionException */ public FileVariantAccessor getExistingFile(File file) throws AccessorConstructionException { file = file.getAbsoluteFile(); Entry ref = files.get(file); FileVariantAccessor accessor = ref==null ? null : ref.get(); if (ref!=null && !ref.file.isOpen()) { files.remove(file); return null; } expunge(); return accessor; } /** * Open file or get an existing file accessor. * The caller must not close the file, it is closed upon garbage collection * or when FileLibrary is closed. * * @param file * @return an accessor to the contents of a file * @throws IOException * @throws AccessorConstructionException */ public FileVariantAccessor getFile(File file) throws AccessorConstructionException { file = file.getAbsoluteFile(); Entry ref = files.get(file); FileVariantAccessor accessor = ref==null ? null : ref.get(); expunge(); if (ref!=null && !ref.file.isOpen()) { files.remove(file); ref = null; } // Open file if (accessor == null) { BinaryFile bf = ref!=null ? ref.file : null; if (bf==null) { try { bf = new BinaryFile(file); } catch (IOException e1) { throw new AccessorConstructionException(e1); } } accessor = (FileVariantAccessor) BinaryObject.createAccessor(bf, Datatypes.VARIANT, params); Entry e = new Entry(bf, accessor); files.put(file, e); } return accessor; } /** * Create a new file and put it in the library. * If the file exists it is overwritten. * * @param file * @return accessor to contents of a file * @throws AccessorConstructionException */ public FileVariantAccessor createFile(File file) throws AccessorConstructionException { file = file.getAbsoluteFile(); Entry ref = files.get(file); FileVariantAccessor accessor = ref==null ? null : ref.get(); expunge(); if (ref!=null && !ref.file.isOpen()) { files.remove(file); ref = null; } // Create a new file if (accessor == null) { BinaryFile bf = ref!=null ? ref.file : null; if (bf==null) { try { file.createNewFile(); bf = new BinaryFile(file); } catch (IOException e1) { throw new AccessorConstructionException(e1); } } accessor = (FileVariantAccessor) BinaryObject.createAccessor(bf, Datatypes.VARIANT, params); Binding vb; try { vb = Bindings.getBinding(void.class); Object vv = vb.createDefault(); accessor.setContentValue(vb, vv); } catch (BindingConstructionException e1) { throw new AccessorConstructionException(e1); } catch (AccessorException e) { throw new AccessorConstructionException(e); } catch (BindingException e) { throw new AccessorConstructionException(e); } Entry e = new Entry(bf, accessor); files.put(file, e); } return accessor; } public boolean deleteFile(File file) throws AccessorException { file = file.getAbsoluteFile(); expunge(); Entry ref = files.remove(file); if (ref!=null) { FileVariantAccessor accessor = ref.get(); if (accessor!=null) { accessor.close(); accessor = null; } else { try { ref.file.close(); } catch (IOException e) { throw new AccessorException(e); } } } expunge(); if (!file.exists()) { return true; } boolean ok = file.delete(); return ok; } /** * Close unused file accessors. */ public void expunge() { Entry e; while ( (e = (Entry) queue.poll()) != null) { // System.out.println("expunging "+e.file.file()); files.remove(e.file.file()); try { e.file.close(); } catch (IOException e1) { e1.printStackTrace(); } } } /** * Close and free all files, this invalidates all existing FileAccessors. * close() doesn't invalidate FileLibrary. * */ public void close() { for (Entry e : files.values()) { try { // System.out.println("closing "+e.file.file()); e.file.flush(); e.file.close(); } catch (IOException e1) { e1.printStackTrace(); } } files.clear(); } class Entry extends WeakReference { BinaryFile file; public Entry(BinaryFile file, FileVariantAccessor accessor) { super(accessor, FileLibrary.this.queue); this.file = file; } } }