1 package com.atlassian.config.xml;
2
3 import com.atlassian.config.AbstractConfigurationPersister;
4 import com.atlassian.config.ConfigurationException;
5 import com.atlassian.core.util.Dom4jUtil;
6 import org.dom4j.Document;
7 import org.dom4j.DocumentException;
8 import org.dom4j.DocumentHelper;
9 import org.dom4j.Element;
10 import org.dom4j.io.SAXReader;
11 import org.slf4j.Logger;
12 import org.slf4j.LoggerFactory;
13 import org.xml.sax.SAXException;
14
15 import javax.xml.XMLConstants;
16 import java.io.File;
17 import java.io.IOException;
18 import java.io.InputStream;
19 import java.net.MalformedURLException;
20 import java.util.List;
21 import java.util.Map;
22
23 public abstract class AbstractDom4jXmlConfigurationPersister extends AbstractConfigurationPersister
24 {
25
26 @Deprecated
27 public static final org.apache.log4j.Logger log = org.apache.log4j.Logger.getLogger(AbstractDom4jXmlConfigurationPersister.class);
28 private static final Logger privateLog = LoggerFactory.getLogger(AbstractDom4jXmlConfigurationPersister.class);
29 private Document document;
30 protected boolean useCData = false;
31
32 public AbstractDom4jXmlConfigurationPersister()
33 {
34
35 clearDocument();
36
37
38 addConfigMapping(String.class, Dom4jXmlStringConfigElement.class);
39 addConfigMapping(Map.class, Dom4jXmlMapConfigElement.class);
40 addConfigMapping(Map.Entry.class, Dom4jXmlMapEntryConfigElement.class);
41 addConfigMapping(List.class, Dom4jXmlListConfigElement.class);
42 }
43
44 public Document loadDocument(File xmlFile) throws DocumentException, MalformedURLException
45 {
46 SAXReader xmlReader = newSecureSaxReader();
47 document = xmlReader.read(xmlFile);
48 return document;
49 }
50
51 public Object load(InputStream istream) throws ConfigurationException
52 {
53 try
54 {
55 return loadDocument(istream);
56 }
57 catch (DocumentException e)
58 {
59 throw new ConfigurationException("Failed to load Xml doc: " + e.getMessage(), e);
60 }
61 }
62
63 public Document loadDocument(InputStream istream) throws DocumentException
64 {
65 SAXReader xmlReader = newSecureSaxReader();
66 document = xmlReader.read(istream);
67 return document;
68 }
69
70 public void save(String configPath, String configFile) throws ConfigurationException
71 {
72 saveDocument(configPath, configFile);
73 }
74
75 public void saveDocument(String configPath, String configFile) throws ConfigurationException
76 {
77 try
78 {
79 saveDocumentAtomically(getDocument(), configPath, configFile);
80 }
81 catch (IOException e)
82 {
83 throw new ConfigurationException("Couldn't save " + configFile + " to " + configPath + " directory.", e);
84 }
85 }
86
87 private void saveDocumentAtomically(Document document, String configPath, String configFile) throws IOException
88 {
89 File tempFile = File.createTempFile(configFile, "tmp", new File(configPath));
90 File saveFile = new File(configPath, configFile);
91
92 try
93 {
94 Dom4jUtil.saveDocumentTo(document, configPath, tempFile.getName());
95
96
97
98 if (!tempFile.renameTo(saveFile))
99 {
100 privateLog.warn("Unable to move " + tempFile.getCanonicalPath() + " to " + saveFile.getCanonicalPath() + ". Falling back to non-atomic overwrite.");
101 Dom4jUtil.saveDocumentTo(document, configPath, configFile);
102 }
103 }
104 finally
105 {
106 tempFile.delete();
107 }
108 }
109
110 public Document getDocument()
111 {
112 return document;
113 }
114
115 public Object getRootContext()
116 {
117 return document.getRootElement();
118 }
119
120 public Element getElement(String path)
121 {
122 return DocumentHelper.makeElement(document, path);
123 }
124
125 public void clear()
126 {
127 clearDocument();
128 }
129
130 private void clearDocument()
131 {
132 document = null;
133 document = DocumentHelper.createDocument();
134 document.addElement(getRootName());
135 }
136
137 public abstract String getRootName();
138
139 public boolean isUseCData()
140 {
141 return useCData;
142 }
143
144 public void setUseCData(boolean useCData)
145 {
146 this.useCData = useCData;
147 }
148
149 private SAXReader newSecureSaxReader()
150 {
151 SAXReader saxReader = new SAXReader();
152 setFeature(saxReader, "http://xml.org/sax/features/external-parameter-entities", false);
153 setFeature(saxReader, "http://xml.org/sax/features/external-general-entities", false);
154 setFeature(saxReader, "http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
155 setFeature(saxReader, XMLConstants.FEATURE_SECURE_PROCESSING, true);
156 return saxReader;
157 }
158
159 private static void setFeature(final SAXReader xmlReader, final String name, final boolean value)
160 {
161 try
162 {
163 xmlReader.setFeature(name, value);
164 }
165 catch (SAXException e)
166 {
167 privateLog.warn("Unable to set " + name + " to " + value);
168 }
169 }
170 }