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