001: /*
002: (c) Copyright 2003, 2004, 2005, 2006, 2007, 2008 Hewlett-Packard Development Company, LP
003: [See end of file]
004: $Id: FileGraph.java,v 1.33 2008/01/02 12:05:18 andy_seaborne Exp $
005: */
006:
007: package com.hp.hpl.jena.graph.impl;
008:
009: import com.hp.hpl.jena.util.FileUtils;
010: import com.hp.hpl.jena.graph.TransactionHandler;
011: import com.hp.hpl.jena.mem.GraphMem;
012: import com.hp.hpl.jena.mem.faster.GraphMemFaster;
013: import com.hp.hpl.jena.rdf.model.Model;
014: import com.hp.hpl.jena.rdf.model.impl.ModelCom;
015: import com.hp.hpl.jena.shared.*;
016:
017: import java.io.*;
018:
019: /**
020: A FileGraph is a memory-based graph that is optionally read in from a file
021: when it is created, and is written back when it is closed. It supports
022: (weak) transactions by using checkpoint files.
023:
024: @author hedgehog
025: */
026: public class FileGraph extends GraphMemFaster {
027: public interface NotifyOnClose {
028: void notifyClosed(File f);
029:
030: static final NotifyOnClose ignore = new NotifyOnClose() {
031: public void notifyClosed(File f) {
032: }
033: };
034: }
035:
036: /**
037: See FileGraph( f, create, strict, Reifier.ReificationStyle ).
038: */
039: public FileGraph(File f, boolean create, boolean strict) {
040: this (NotifyOnClose.ignore, f, create, strict,
041: ReificationStyle.Minimal);
042: }
043:
044: /**
045: The File-name of this graph, used to name it in the filing system
046: */
047: public final File name;
048:
049: /**
050: A model used to wrap the graph for the IO operations (since these are not
051: yet available at the graph level).
052: */
053: protected final Model model;
054:
055: /**
056: The language used to read and write the graph, guessed from the filename's
057: suffix.
058: */
059: public final String lang;
060:
061: protected final NotifyOnClose notify;
062:
063: /**
064: Construct a new FileGraph who's name is given by the specified File,
065: If create is true, this is a new file, and any existing file will be destroyed;
066: if create is false, this is an existing file, and its current contents will
067: be loaded. The language code for the file is guessed from its suffix.
068:
069: @param f the File naming the associated file-system file
070: @param create true to create a new one, false to read an existing one
071: @param strict true to throw exceptions for create: existing, open: not found
072: @param style the reification style for the graph
073: */
074: public FileGraph(NotifyOnClose notify, File f, boolean create,
075: boolean strict, ReificationStyle style) {
076: this (notify, f, FileUtils.guessLang(f.toString()), create,
077: strict, style);
078: }
079:
080: /**
081: Construct a new FileGraph who's name is given by the specified File,
082: If create is true, this is a new file, and any existing file will be destroyed;
083: if create is false, this is an existing file, and its current contents will
084: be loaded. The language code for the file is supplied.
085:
086: @param f the File naming the associated file-system file
087: @param lang the language string for the file
088: @param create true to create a new one, false to read an existing one
089: @param strict true to throw exceptions for create: existing, open: not found
090: @param style the reification style for the graph
091: */
092: public FileGraph(NotifyOnClose notify, File f, String lang,
093: boolean create, boolean strict, ReificationStyle style) {
094: super (style);
095: this .name = f;
096: this .notify = notify;
097: this .model = new ModelCom(this );
098: this .lang = lang;
099: if (create) {
100: if (f.exists() && strict)
101: throw new AlreadyExistsException(f.toString());
102: } else
103: readModel(this .model, strict);
104: }
105:
106: protected void readModel(Model m, boolean strict) {
107: readModelFrom(m, strict, name);
108: }
109:
110: protected void readModelFrom(Model m, boolean strict, File name) {
111: FileInputStream in = null;
112: try {
113: in = new FileInputStream(name);
114: model.read(in, "", this .lang);
115: } catch (FileNotFoundException f) {
116: if (strict)
117: throw new DoesNotExistException(name.toString());
118: } finally {
119: if (in != null)
120: try {
121: in.close();
122: } catch (IOException ignore) {
123: }
124: }
125: }
126:
127: /**
128: As for FileGraph(File,boolean), except the name is given as a String.
129: */
130: public FileGraph(String s, boolean create) {
131: this (new File(s), create, true);
132: }
133:
134: public static FileGraph create() {
135: return new FileGraph(FileUtils.tempFileName(
136: "anonymousFileGraph", ".rdf"), true, true);
137: }
138:
139: /**
140: Answer true iff the filename string given is plausibly the name of a
141: graph, ie, may have RDF content. We appeal to FileUtils - if it can
142: guess an RDF language name, we deliver true, otherwise false.
143:
144: @param name the leaf component of a filename
145: @return true if it is likely to be an RDF file
146: */
147: public static boolean isPlausibleGraphName(String name) {
148: return FileUtils.guessLang(name, null) != null;
149: }
150:
151: /**
152: Write out and then close this FileGraph.
153: */
154: public void close() {
155: saveContents(name);
156: super .close();
157: if (count == 0)
158: notify.notifyClosed(name);
159: }
160:
161: /**
162: Delete the backing file. Primarily intended for test cleanup.
163: */
164: public void delete() {
165: name.delete();
166: }
167:
168: /**
169: The graph is written out to the
170: named file in the language guessed from the suffix, and then the
171: parent close is invoked. The write-out goes to an intermediate file
172: first, which is then renamed to the correct name, to try and ensure
173: that the output is either done completely or not at all.
174: */
175: protected void saveContents(File targetName) {
176: try {
177: File intermediate = new File(targetName.getPath() + ".new");
178: FileOutputStream out = new FileOutputStream(intermediate);
179: model.write(out, lang);
180: out.close();
181: updateFrom(targetName, intermediate);
182: } catch (Exception e) {
183: throw new JenaException(e);
184: }
185: }
186:
187: /**
188: The file intermediate has the new file contents. We want to move
189: them to the current file. renameTo doesn't have a powerful enough
190: semantics, so we anticipate failure and attempt to bypass it ...
191: <p>
192: If the rename works, that's fine. If it fails, we delete the old file if it
193: exists, and try again.
194: */
195: protected void updateFrom(File targetName, File intermediate) {
196: if (intermediate.renameTo(targetName) == false) {
197: if (targetName.exists())
198: mustDelete(targetName);
199: mustRename(intermediate, targetName);
200: }
201: }
202:
203: protected void mustDelete(File f) {
204: if (f.delete() == false)
205: throw new JenaException("could not delete " + f);
206: }
207:
208: protected void mustRename(File from, File to) {
209: if (from.renameTo(to) == false)
210: throw new JenaException("could not rename " + from + " to "
211: + to);
212: }
213:
214: public TransactionHandler getTransactionHandler() {
215: if (th == null)
216: th = new FileGraphTransactionHandler(this );
217: return th;
218: }
219:
220: protected TransactionHandler th;
221: }
222:
223: /*
224: (c) Copyright 2003, 2004, 2005, 2006, 2007, 2008 Hewlett-Packard Development Company, LP
225: All rights reserved.
226:
227: Redistribution and use in source and binary forms, with or without
228: modification, are permitted provided that the following conditions
229: are met:
230:
231: 1. Redistributions of source code must retain the above copyright
232: notice, this list of conditions and the following disclaimer.
233:
234: 2. Redistributions in binary form must reproduce the above copyright
235: notice, this list of conditions and the following disclaimer in the
236: documentation and/or other materials provided with the distribution.
237:
238: 3. The name of the author may not be used to endorse or promote products
239: derived from this software without specific prior written permission.
240:
241: THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
242: IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
243: OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
244: IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
245: INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
246: NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
247: DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
248: THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
249: (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
250: THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
251: */
|