001 /*
002 * Sonar, open source software quality management tool.
003 * Copyright (C) 2008-2011 SonarSource
004 * mailto:contact AT sonarsource DOT com
005 *
006 * Sonar is free software; you can redistribute it and/or
007 * modify it under the terms of the GNU Lesser General Public
008 * License as published by the Free Software Foundation; either
009 * version 3 of the License, or (at your option) any later version.
010 *
011 * Sonar is distributed in the hope that it will be useful,
012 * but WITHOUT ANY WARRANTY; without even the implied warranty of
013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014 * Lesser General Public License for more details.
015 *
016 * You should have received a copy of the GNU Lesser General Public
017 * License along with Sonar; if not, write to the Free Software
018 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
019 */
020 package org.sonar.server.configuration;
021
022 import com.thoughtworks.xstream.XStream;
023 import com.thoughtworks.xstream.converters.basic.DateConverter;
024 import com.thoughtworks.xstream.core.util.QuickWriter;
025 import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
026 import com.thoughtworks.xstream.io.xml.PrettyPrintWriter;
027 import com.thoughtworks.xstream.io.xml.XppDriver;
028 import org.apache.commons.io.IOUtils;
029 import org.apache.commons.lang.CharEncoding;
030 import org.apache.commons.lang.StringUtils;
031 import org.sonar.api.database.DatabaseSession;
032 import org.sonar.jpa.entity.SchemaMigration;
033
034 import java.io.IOException;
035 import java.io.InputStream;
036 import java.io.Writer;
037 import java.util.ArrayList;
038 import java.util.Collection;
039 import java.util.Date;
040 import java.util.List;
041
042 public class Backup {
043
044 private List<Backupable> backupables;
045 private DatabaseSession session;
046
047 protected static final String DATE_FORMAT = "yyyy-MM-dd";
048
049 protected Backup() {
050 backupables = new ArrayList<Backupable>();
051 }
052
053 public Backup(DatabaseSession session) {
054 this();
055 this.session = session;
056
057 backupables.add(new MetricsBackup(session));
058 backupables.add(new PropertiesBackup(session));
059 // Note that order is important, because profile can have reference to rule
060 backupables.add(new RulesBackup(session));
061 backupables.add(new ProfilesBackup(session));
062 }
063
064 /**
065 * For unit tests
066 */
067 Backup(List<Backupable> backupables) {
068 this();
069 this.backupables = backupables;
070 }
071
072 /*
073 * Export methods
074 */
075
076 public String exportXml() {
077 try {
078 startDb();
079 SonarConfig sonarConfig = new SonarConfig(getVersion(), getCurrentDate());
080 return exportXml(sonarConfig);
081 } finally {
082 stopDb();
083 }
084 }
085
086 protected String exportXml(SonarConfig sonarConfig) {
087 for (Backupable backupable : backupables) {
088 backupable.exportXml(sonarConfig);
089 }
090 String xml = getXmlFromSonarConfig(sonarConfig);
091 return addXmlHeader(xml);
092 }
093
094 protected String getXmlFromSonarConfig(SonarConfig sonarConfig) {
095 XStream xStream = getConfiguredXstream();
096 return xStream.toXML(sonarConfig);
097 }
098
099 private String addXmlHeader(String xml) {
100 return "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n".concat(xml);
101 }
102
103 /*
104 * Import methods
105 */
106 public void importXml(String xml) {
107 try {
108 startDb();
109 doImportXml(xml);
110 } finally {
111 stopDb();
112 }
113 }
114
115 void doImportXml(String xml) {
116 SonarConfig sonarConfig = getSonarConfigFromXml(xml);
117 importBackupablesXml(sonarConfig);
118 }
119
120 protected void importBackupablesXml(SonarConfig sonarConfig) {
121 for (Backupable backupable : backupables) {
122 backupable.importXml(sonarConfig);
123 }
124 }
125
126 protected SonarConfig getSonarConfigFromXml(String xml) {
127 try {
128 XStream xStream = getConfiguredXstream();
129 // Backward compatibility with old levels
130 xml = xml.replace("<level><![CDATA[ERROR]]></level>", "<level><![CDATA[MAJOR]]></level>");
131 xml = xml.replace("<level><![CDATA[WARNING]]></level>", "<level><![CDATA[INFO]]></level>");
132 InputStream inputStream = IOUtils.toInputStream(xml, CharEncoding.UTF_8);
133
134 return (SonarConfig) xStream.fromXML(inputStream);
135 } catch (IOException e) {
136 throw new RuntimeException("Can't read xml", e);
137 }
138 }
139
140 /*
141 * Utils methods
142 */
143 protected int getVersion() {
144 return SchemaMigration.LAST_VERSION;
145 }
146
147 protected Date getCurrentDate() {
148 return new Date();
149 }
150
151 private XStream getConfiguredXstream() {
152 XStream xStream = new XStream(
153 new XppDriver() {
154 @Override
155 public HierarchicalStreamWriter createWriter(Writer out) {
156 return new PrettyPrintWriter(out) {
157 @Override
158 protected void writeText(QuickWriter writer, String text) {
159 writer.write("<![CDATA[");
160 /*
161 * See http://jira.codehaus.org/browse/SONAR-1605 According to XML specification (
162 * http://www.w3.org/TR/REC-xml/#sec-cdata-sect ) CData section may contain everything except of sequence ']]>' so we will
163 * split all occurrences of this sequence into two CDATA first one would contain ']]' and second '>'
164 */
165 text = StringUtils.replace(text, "]]>", "]]]]><![CDATA[>");
166 writer.write(text);
167 writer.write("]]>");
168 }
169 };
170 }
171 });
172
173 xStream.processAnnotations(SonarConfig.class);
174 xStream.addDefaultImplementation(ArrayList.class, Collection.class);
175 xStream.registerConverter(new DateConverter(DATE_FORMAT, new String[] {}));
176
177 for (Backupable backupable : backupables) {
178 backupable.configure(xStream);
179 }
180 return xStream;
181 }
182
183 private void startDb() {
184 session.start();
185 }
186
187 private void stopDb() {
188 session.stop();
189 }
190
191 }