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 }