001/* 002 * Copyright (c) 2006-2011 Nuxeo SA (http://nuxeo.com/) and others. 003 * 004 * All rights reserved. This program and the accompanying materials 005 * are made available under the terms of the Eclipse Public License v1.0 006 * which accompanies this distribution, and is available at 007 * http://www.eclipse.org/legal/epl-v10.html 008 * 009 * Contributors: 010 * bstefanescu 011 */ 012package org.nuxeo.runtime.model.persistence.fs; 013 014import java.io.ByteArrayInputStream; 015import java.io.File; 016import java.io.IOException; 017import java.util.ArrayList; 018import java.util.List; 019 020import javax.xml.parsers.DocumentBuilder; 021import javax.xml.parsers.DocumentBuilderFactory; 022import javax.xml.parsers.ParserConfigurationException; 023 024import org.apache.commons.logging.Log; 025import org.apache.commons.logging.LogFactory; 026import org.nuxeo.common.Environment; 027import org.nuxeo.common.utils.FileUtils; 028import org.nuxeo.common.xmap.DOMSerializer; 029import org.nuxeo.runtime.model.persistence.Contribution; 030import org.nuxeo.runtime.model.persistence.ContributionStorage; 031import org.w3c.dom.DOMException; 032import org.w3c.dom.Document; 033import org.w3c.dom.Element; 034import org.w3c.dom.Node; 035import org.xml.sax.SAXException; 036 037/** 038 * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a> 039 */ 040public class FileSystemStorage implements ContributionStorage { 041 042 public static final Log log = LogFactory.getLog(FileSystemStorage.class); 043 044 protected static final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); 045 046 protected final File root; 047 048 public FileSystemStorage() { 049 root = new File(Environment.getDefault().getData(), "contribs"); 050 root.mkdirs(); 051 } 052 053 public static synchronized String safeRead(File file) { 054 try { 055 return FileUtils.readFile(file); 056 } catch (IOException e) { 057 throw new RuntimeException(e); 058 } 059 } 060 061 public static synchronized void safeWrite(File file, String content) { 062 try { 063 FileUtils.writeFile(file, content); 064 } catch (IOException e) { 065 throw new RuntimeException(e); 066 } 067 } 068 069 public static synchronized boolean safeCreate(File file, String content) { 070 if (file.isFile()) { 071 return false; 072 } 073 try { 074 FileUtils.writeFile(file, content); 075 } catch (IOException e) { 076 throw new RuntimeException(e); 077 } 078 return true; 079 } 080 081 public static synchronized boolean safeRemove(File file) { 082 return file.delete(); 083 } 084 085 public static void loadMetadata(Contribution contrib) { 086 try { 087 DocumentBuilder docBuilder = factory.newDocumentBuilder(); 088 Document doc = docBuilder.parse(new ByteArrayInputStream(contrib.getContent().getBytes())); 089 Element root = doc.getDocumentElement(); 090 contrib.setDisabled(Boolean.parseBoolean(root.getAttribute("disabled"))); 091 Node node = root.getFirstChild(); 092 while (node != null) { 093 if (node.getNodeType() == Node.ELEMENT_NODE && "documentation".equals(node.getNodeName())) { 094 break; 095 } 096 node = node.getNextSibling(); 097 } 098 if (node != null) { 099 node = node.getFirstChild(); 100 StringBuilder buf = new StringBuilder(); 101 while (node != null) { 102 if (node.getNodeType() == Node.TEXT_NODE) { 103 buf.append(node.getNodeValue()); 104 } 105 node = node.getNextSibling(); 106 } 107 contrib.setDescription(buf.toString().trim()); 108 } else { 109 contrib.setDescription(""); 110 } 111 } catch (ParserConfigurationException | SAXException | IOException | DOMException e) { 112 log.error("Failed to read contribution metadata", e); 113 } 114 } 115 116 @Override 117 public Contribution addContribution(Contribution contribution) { 118 File file = new File(root, contribution.getName() + ".xml"); 119 String content = contribution.getContent(); 120 if (safeCreate(file, content)) { 121 return new ContributionFile(contribution.getName(), file); 122 } 123 return null; 124 } 125 126 @Override 127 public Contribution getContribution(String name) { 128 File file = new File(root, name + ".xml"); 129 if (file.isFile()) { 130 return new ContributionFile(name, file); 131 } 132 return null; 133 } 134 135 @Override 136 public List<Contribution> getContributions() { 137 List<Contribution> result = new ArrayList<Contribution>(); 138 File[] files = root.listFiles(); 139 if (files == null) { 140 return result; 141 } 142 for (File file : files) { 143 String name = file.getName(); 144 if (name.endsWith(".xml")) { 145 name = name.substring(0, name.length() - 4); 146 result.add(new ContributionFile(name, file)); 147 } 148 } 149 return result; 150 } 151 152 @Override 153 public boolean removeContribution(Contribution contrib) { 154 return safeRemove(new File(root, contrib.getName() + ".xml")); 155 } 156 157 @Override 158 public Contribution updateContribution(Contribution contribution) { 159 File file = new File(root, contribution.getName() + ".xml"); 160 String content = safeRead(file); 161 DocumentBuilder docBuilder; 162 try { 163 docBuilder = factory.newDocumentBuilder(); 164 } catch (ParserConfigurationException e) { 165 throw new RuntimeException(e); 166 } 167 Document doc; 168 try { 169 doc = docBuilder.parse(new ByteArrayInputStream(content.getBytes())); 170 } catch (SAXException | IOException e) { 171 throw new RuntimeException(e); 172 } 173 Element root = doc.getDocumentElement(); 174 if (contribution.isDisabled()) { 175 root.setAttribute("disabled", "true"); 176 } else { 177 root.removeAttribute("disabled"); 178 } 179 Node node = root.getFirstChild(); 180 while (node != null) { 181 if (node.getNodeType() == Node.ELEMENT_NODE && "documentation".equals(node.getNodeName())) { 182 break; 183 } 184 node = node.getNextSibling(); 185 } 186 String description = contribution.getDescription(); 187 if (description == null) { 188 description = ""; 189 } 190 if (node != null) { 191 root.removeChild(node); 192 } 193 Element el = doc.createElement("documentation"); 194 el.appendChild(doc.createTextNode(description)); 195 root.appendChild(el); 196 197 try { 198 safeWrite(file, DOMSerializer.toString(doc)); 199 } catch (IOException e) { 200 throw new RuntimeException(e); 201 } 202 return getContribution(contribution.getName()); 203 } 204 205}