1 16 package org.outerj.daisy.books.store.impl; 17 18 import org.outerj.daisy.books.store.*; 19 import org.outerj.daisy.xmlutil.LocalSAXParserFactory; 20 import org.apache.xmlbeans.XmlOptions; 21 import org.apache.xmlbeans.XmlObject; 22 import org.outerx.daisy.x10Bookstoremeta.ResourcePropertiesDocument; 23 24 import java.io.*; 25 import java.net.URI ; 26 import java.util.Date ; 27 import java.util.Collection ; 28 import java.util.List ; 29 import java.util.ArrayList ; 30 31 36 public class CommonBookInstance implements BookInstance { 37 private final File bookInstanceDirectory; 38 private final AclResource aclResource; 39 private final PublicationsInfoResource publicationsInfoResource; 40 private final MetaDataResource metaDataResource; 41 private final CommonBookStore owner; 42 43 private static final int BUFFER_SIZE = 32768; 44 static final String ACL_FILENAME = "acl.xml"; 45 static final String PUBLICATIONS_INFO_FILENAME = "publications_info.xml"; 46 static final String METADATA_FILENAME = "metadata.xml"; 47 static final String [] META_FILES = {ACL_FILENAME, PUBLICATIONS_INFO_FILENAME, METADATA_FILENAME}; 48 static final String LOCK_FILE_NAME = "lock"; 49 50 protected CommonBookInstance(File bookInstanceDirectory, CommonBookStore owner) { 51 this.bookInstanceDirectory = bookInstanceDirectory; 52 this.owner = owner; 53 this.aclResource = new AclResource(new File(bookInstanceDirectory, ACL_FILENAME)); 54 this.publicationsInfoResource = new PublicationsInfoResource(new File(bookInstanceDirectory, PUBLICATIONS_INFO_FILENAME)); 55 this.metaDataResource = new MetaDataResource(new File(bookInstanceDirectory, METADATA_FILENAME)); 56 } 57 58 File getDirectory() { 59 return bookInstanceDirectory; 60 } 61 62 public String getName() { 63 return bookInstanceDirectory.getName(); 64 } 65 66 public InputStream getResource(String path) { 67 File file = getFile(path); 68 69 FileInputStream fis; 70 try { 71 fis = new FileInputStream(file); 72 } catch (FileNotFoundException e) { 73 throw new BookResourceNotFoundException("Resource not found: " + path); 74 } 75 return new BufferedInputStream(fis); 76 } 77 78 public ResourcePropertiesDocument getResourceProperties(String path) { 79 String metaFileName = path + ".meta.xml"; 80 File metaFile = getFile(metaFileName); 81 if (metaFile.exists()) { 82 try { 83 XmlOptions xmlOptions = new XmlOptions().setLoadUseXMLReader(LocalSAXParserFactory.newXmlReader()); 84 ResourcePropertiesDocument propertiesDocument = ResourcePropertiesDocument.Factory.parse(metaFile, xmlOptions); 85 return propertiesDocument; 86 } catch (Exception e) { 87 throw new RuntimeException ("Error reading resource properties.", e); 88 } 89 } 90 return null; 91 } 92 93 public void storeResourceProperties(String path, ResourcePropertiesDocument resourcePropertiesDocument) { 94 XmlOptions xmlOptions = new XmlOptions(); 95 xmlOptions.setSavePrettyPrint(); 96 xmlOptions.setUseDefaultNamespace(); 97 storeResource(path + ".meta.xml", resourcePropertiesDocument.newInputStream(xmlOptions)); 98 } 99 100 public void storeResource(String path, InputStream is) { 101 BookStoreException exception = null; 102 try { 103 File file = getFileForStorage(path); 104 FileOutputStream fos = null; 105 try { 106 try { 107 fos = new FileOutputStream(file); 108 BufferedOutputStream bos = new BufferedOutputStream(fos); 109 byte[] buffer = new byte[BUFFER_SIZE]; 110 int read; 111 while ((read = is.read(buffer)) != -1) { 112 bos.write(buffer, 0, read); 113 } 114 bos.flush(); 115 fos.getFD().sync(); 116 } finally { 117 if (fos != null) 118 fos.close(); 119 } 120 } catch (IOException e) { 121 exception = new BookStoreException("Error storing resource in book instance.", e); 122 throw exception; 123 } 124 } finally { 125 try { 126 is.close(); 127 } catch (IOException e) { 128 if (exception != null) { 129 throw new BookStoreException("Error closing input stream, but also got an earlier exception.", exception); 131 } else { 132 throw new BookStoreException("Error closing input stream.", e); 133 } 134 } 135 } 136 } 137 138 public OutputStream getResourceOutputStream(String path) throws IOException { 139 File file = getFileForStorage(path); 140 return new BufferedOutputStream(new FileOutputStream(file)); 141 } 142 143 public boolean rename(String path, String newName) { 144 if (path == null) 145 throw new IllegalArgumentException ("path argument cannot be null"); 146 if (newName == null) 147 throw new IllegalArgumentException ("newName argument cannot be null"); 148 if (newName.indexOf(System.getProperty("file.separator")) != -1) 149 throw new IllegalArgumentException ("New name may not contain a slash."); 150 151 File file = new File(bookInstanceDirectory, path); 152 if (!BookUtil.isWithin(bookInstanceDirectory, file)) 153 throw new BookStoreException("It is not allowed to access a file outside of the book instance directory."); 154 155 return file.renameTo(new File(file.getParent(), newName)); 156 } 157 158 private File getFileForStorage(String path) { 159 File file = new File(bookInstanceDirectory, path); 160 if (!BookUtil.isWithin(bookInstanceDirectory, file)) 161 throw new BookStoreException("It is not allowed to write a file outside of the book instance directory."); 162 163 File parentDir = file.getParentFile(); 164 if (!parentDir.exists()) 165 parentDir.mkdirs(); 166 167 return file; 168 } 169 170 public boolean exists(String path) { 171 return getFile(path).exists(); 172 } 173 174 public long getLastModified(String path) { 175 return getFile(path).lastModified(); 176 } 177 178 public long getContentLength(String path) { 179 return getFile(path).length(); 180 } 181 182 private File getFile(String path) { 183 File file = new File(bookInstanceDirectory, path); 184 if (!BookUtil.isWithin(bookInstanceDirectory, file)) 185 throw new BookStoreException("It is not allowed to access a file outside of the book instance directory."); 186 return file; 187 } 188 189 boolean isLocked() { 190 File lockFile = new File(getDirectory(), CommonBookInstance.LOCK_FILE_NAME); 191 return lockFile.exists(); 192 } 193 194 public void lock() { 195 throw new BookStoreException("This method should never be called."); 196 } 197 198 public void unlock() { 199 throw new BookStoreException("This method should never be called."); 200 } 201 202 public boolean canManage() { 203 throw new BookStoreException("This method should never be called."); 204 } 205 206 public BookAcl getAcl() { 207 return aclResource.get(); 208 } 209 210 public void setAcl(BookAcl bookAcl) { 211 aclResource.store(bookAcl); 212 } 213 214 public PublicationsInfo getPublicationsInfo() { 215 return publicationsInfoResource.get(); 216 } 217 218 public void addPublication(PublicationInfo publicationInfo) { 219 PublicationInfo[] infos = getPublicationsInfo().getInfos(); 220 PublicationInfo[] newInfos = new PublicationInfo[infos.length + 1]; 221 System.arraycopy(infos, 0, newInfos, 0, infos.length); 222 newInfos[newInfos.length - 1] = publicationInfo; 223 publicationsInfoResource.store(new PublicationsInfo(newInfos)); 224 } 225 226 public void setPublications(PublicationsInfo publicationsInfo) { 227 publicationsInfoResource.store(publicationsInfo); 228 } 229 230 public BookInstanceMetaData getMetaData() { 231 return (BookInstanceMetaData)metaDataResource.get().clone(); 232 } 233 234 public void setMetaData(BookInstanceMetaData metaData) { 235 metaDataResource.store((BookInstanceMetaData)metaData.clone()); 236 } 237 238 public URI getResourceURI(String path) { 239 return getFile(path).toURI(); 240 } 241 242 public String [] getDescendantPaths(String path) { 243 File file = getFile(path); 244 if (!file.isDirectory()) { 245 throw new RuntimeException ("path is not a directory: " + path); 246 } 247 248 List result = new ArrayList (); 249 collectPaths(file.listFiles(), result, bookInstanceDirectory.getAbsolutePath()); 250 return (String [])result.toArray(new String [result.size()]); 251 } 252 253 private void collectPaths(File[] files, Collection result, String refPath) { 254 for (int i = 0; i < files.length; i++) { 255 if (files[i].isDirectory()) { 256 collectPaths(files[i].listFiles(), result, refPath); 257 } else { 258 String path = files[i].getAbsolutePath(); 259 if (!path.startsWith(refPath)) 260 throw new RuntimeException ("Assertion error: path does not start with refPath"); 261 result.add(path.substring(refPath.length())); 262 } 263 } 264 } 265 266 269 void initialize(String label, BookAcl initialAcl, long creator) { 270 BookInstanceMetaData metaData = new BookInstanceMetaData(label, new Date (), creator); 271 try { 273 new File(bookInstanceDirectory, LOCK_FILE_NAME).createNewFile(); 274 } catch (IOException e) { 275 throw new BookStoreException("Error locking newly created book instance.", e); 276 } 277 metaDataResource.store(metaData); 278 publicationsInfoResource.store(new PublicationsInfo(new PublicationInfo[0])); 279 aclResource.store(initialAcl); 280 } 281 282 private static XmlOptions getMetaXmlOptions() { 283 XmlOptions xmlOptions = new XmlOptions(); 284 xmlOptions.setSavePrettyPrint(); 285 xmlOptions.setUseDefaultNamespace(); 286 return xmlOptions; 287 } 288 289 abstract class RefreshableResource { 290 private final File file; 291 private long lastModifed; 292 private Object object; 293 private final String what; 294 295 public RefreshableResource(File file, String what) { 296 this.file = file; 297 this.what = what; 298 } 299 300 protected Object getObject() { 301 if (object == null || (lastModifed != file.lastModified())) 302 loadObject(); 303 return object; 304 } 305 306 protected void loadObject() { 307 synchronized(file) { 308 long lastModified = file.lastModified(); 309 Object object; 310 try { 311 object = create(new BufferedInputStream(new FileInputStream(file))); 312 } catch (FileNotFoundException e) { 313 owner.notifyBookInstanceDeleted(CommonBookInstance.this); 314 throw new NonExistingBookInstanceException(getName()); 315 } catch (Exception e) { 316 throw new BookStoreException("Error loading book " + what, e); 317 } 318 this.object = object; 319 this.lastModifed = lastModified; 320 } 321 } 322 323 protected abstract Object create(InputStream is) throws Exception ; 324 325 protected void store(Object object, XmlObject xmlObject) { 326 synchronized(file) { 327 try { 328 OutputStream os = null; 329 try { 330 os = new BufferedOutputStream(new FileOutputStream(file)); 331 xmlObject.save(os, getMetaXmlOptions()); 332 } finally { 333 if (os != null) 334 os.close(); 335 } 336 } catch (Exception e) { 337 throw new BookStoreException("Error storing book ACL.", e); 338 } 339 this.object = object; 340 this.lastModifed = file.lastModified(); 341 } 342 } 343 } 344 345 class AclResource extends RefreshableResource { 346 public AclResource(File file) { 347 super(file, "ACL"); 348 } 349 350 protected Object create(InputStream is) throws Exception { 351 return BookAclBuilder.build(is); 352 } 353 354 public BookAcl get() { 355 return (BookAcl)getObject(); 356 } 357 358 public void store(BookAcl bookAcl) { 359 store(bookAcl, bookAcl.getXml()); 360 } 361 } 362 363 class PublicationsInfoResource extends RefreshableResource { 364 public PublicationsInfoResource(File file) { 365 super(file, "publications info"); 366 } 367 368 protected Object create(InputStream is) throws Exception { 369 return PublicationsInfoBuilder.build(is); 370 } 371 372 PublicationsInfo get() { 373 return (PublicationsInfo)getObject(); 374 } 375 376 void store(PublicationsInfo publicationsInfo) { 377 store(publicationsInfo, publicationsInfo.getXml()); 378 } 379 } 380 381 class MetaDataResource extends RefreshableResource { 382 public MetaDataResource(File file) { 383 super(file, "meta data"); 384 } 385 386 protected Object create(InputStream is) throws Exception { 387 return BookInstanceMetaDataBuilder.build(is); 388 } 389 390 public BookInstanceMetaData get() { 391 return (BookInstanceMetaData)getObject(); 392 } 393 394 public void store(BookInstanceMetaData metaData) { 395 store(metaData, metaData.getXml()); 396 } 397 } 398 } 399 | Popular Tags |