1 package com.atlassian.activeobjects.backup;
2
3 import com.atlassian.activeobjects.ao.PrefixedSchemaConfiguration;
4 import com.atlassian.activeobjects.internal.DatabaseProviderFactory;
5 import com.atlassian.activeobjects.internal.Prefix;
6 import com.atlassian.activeobjects.internal.SimplePrefix;
7 import com.atlassian.activeobjects.osgi.ActiveObjectsServiceFactory;
8 import com.atlassian.activeobjects.spi.Backup;
9 import com.atlassian.activeobjects.spi.BackupProgressMonitor;
10 import com.atlassian.activeobjects.spi.ImportExportException;
11 import com.atlassian.activeobjects.spi.RestoreProgressMonitor;
12 import com.atlassian.activeobjects.spi.TenantAwareDataSourceProvider;
13 import com.atlassian.dbexporter.BatchMode;
14 import com.atlassian.dbexporter.CleanupMode;
15 import com.atlassian.dbexporter.ConnectionProvider;
16 import com.atlassian.dbexporter.DatabaseInformation;
17 import com.atlassian.dbexporter.DbExporter;
18 import com.atlassian.dbexporter.DbImporter;
19 import com.atlassian.dbexporter.EntityNameProcessor;
20 import com.atlassian.dbexporter.ImportExportConfiguration;
21 import com.atlassian.dbexporter.ImportExportErrorService;
22 import com.atlassian.dbexporter.exporter.ConnectionProviderInformationReader;
23 import com.atlassian.dbexporter.exporter.DataExporter;
24 import com.atlassian.dbexporter.exporter.DatabaseInformationExporter;
25 import com.atlassian.dbexporter.exporter.ExportConfiguration;
26 import com.atlassian.dbexporter.exporter.TableDefinitionExporter;
27 import com.atlassian.dbexporter.importer.DataImporter;
28 import com.atlassian.dbexporter.importer.DatabaseInformationImporter;
29 import com.atlassian.dbexporter.importer.ImportConfiguration;
30 import com.atlassian.dbexporter.importer.SqlServerAroundTableImporter;
31 import com.atlassian.dbexporter.importer.TableDefinitionImporter;
32 import com.atlassian.dbexporter.node.NodeStreamReader;
33 import com.atlassian.dbexporter.node.NodeStreamWriter;
34 import com.atlassian.dbexporter.node.stax.StaxStreamReader;
35 import com.atlassian.dbexporter.node.stax.StaxStreamWriter;
36 import com.atlassian.dbexporter.progress.ProgressMonitor;
37 import com.atlassian.tenancy.api.Tenant;
38 import com.atlassian.tenancy.api.TenantContext;
39 import com.google.common.base.Supplier;
40 import com.google.common.base.Suppliers;
41 import net.java.ao.DatabaseProvider;
42 import net.java.ao.SchemaConfiguration;
43 import net.java.ao.schema.NameConverters;
44
45 import java.io.Closeable;
46 import java.io.IOException;
47 import java.io.InputStream;
48 import java.io.InputStreamReader;
49 import java.io.OutputStream;
50 import java.io.OutputStreamWriter;
51 import java.nio.charset.Charset;
52
53 import static com.atlassian.activeobjects.ao.ConverterUtils.toUpperCase;
54 import static com.google.common.base.Preconditions.checkNotNull;
55
56 public final class ActiveObjectsBackup implements Backup {
57 public static final Prefix PREFIX = new SimplePrefix("AO");
58
59 private static final Charset CHARSET = Charset.forName("UTF-8");
60 private static final String NAMESPACE = "http://www.atlassian.com/ao";
61
62 private final Supplier<DatabaseProvider> databaseProviderSupplier;
63 private final NameConverters nameConverters;
64 private final ImportExportErrorService errorService;
65 private final ActiveObjectsServiceFactory aoServiceFactory;
66
67 public ActiveObjectsBackup(final DatabaseProviderFactory databaseProviderFactory, final TenantAwareDataSourceProvider tenantAwareDataSourceProvider, final TenantContext tenantContext, NameConverters converters, ImportExportErrorService errorService, final ActiveObjectsServiceFactory aoServiceFactory) {
68 this(new Supplier<DatabaseProvider>() {
69 @Override
70 public DatabaseProvider get() {
71 final Tenant tenant = tenantContext.getCurrentTenant();
72 return checkNotNull(databaseProviderFactory).getDatabaseProvider(tenantAwareDataSourceProvider.getDataSource(tenant), tenantAwareDataSourceProvider.getDatabaseType(tenant), tenantAwareDataSourceProvider.getSchema(tenant));
73 }
74 }, converters, errorService, aoServiceFactory);
75 }
76
77 ActiveObjectsBackup(DatabaseProvider databaseProvider, NameConverters converters, ImportExportErrorService errorService, final ActiveObjectsServiceFactory aoServiceFactory) {
78 this(Suppliers.ofInstance(checkNotNull(databaseProvider)), converters, errorService, aoServiceFactory);
79 }
80
81 private ActiveObjectsBackup(Supplier<DatabaseProvider> databaseProviderSupplier, NameConverters converters, ImportExportErrorService errorService, final ActiveObjectsServiceFactory aoServiceFactory) {
82 this.aoServiceFactory = checkNotNull(aoServiceFactory);
83 this.databaseProviderSupplier = checkNotNull(databaseProviderSupplier);
84 this.nameConverters = checkNotNull(converters);
85 this.errorService = checkNotNull(errorService);
86 }
87
88
89
90
91
92
93
94
95
96 public void save(OutputStream stream, BackupProgressMonitor monitor) {
97 final DatabaseProvider provider = databaseProviderSupplier.get();
98 final DatabaseProviderConnectionProvider connectionProvider = getConnectionProvider(provider);
99 final ExportConfiguration configuration = new ActiveObjectsExportConfiguration(connectionProvider, getProgressMonitor(monitor));
100
101 final DbExporter dbExporter = new DbExporter(
102 new DatabaseInformationExporter(new ConnectionProviderInformationReader(errorService, connectionProvider)),
103 new TableDefinitionExporter(new ActiveObjectsTableReader(errorService, nameConverters, provider, schemaConfiguration())),
104 new DataExporter(errorService, provider.getSchema()));
105
106
107 NodeStreamWriter streamWriter = null;
108 try {
109 streamWriter = new StaxStreamWriter(errorService, new OutputStreamWriter(stream, CHARSET), CHARSET, NAMESPACE);
110 dbExporter.exportData(streamWriter, configuration);
111 streamWriter.flush();
112 } finally {
113 closeCloseable(streamWriter);
114 }
115 }
116
117 public static SchemaConfiguration schemaConfiguration() {
118 return new PrefixedSchemaConfiguration(PREFIX);
119 }
120
121
122
123
124
125
126
127
128
129 public void restore(InputStream stream, RestoreProgressMonitor monitor) {
130 final DatabaseProvider provider = databaseProviderSupplier.get();
131 final DatabaseProviderConnectionProvider connectionProvider = getConnectionProvider(provider);
132
133 final DatabaseInformation databaseInformation = getDatabaseInformation(connectionProvider);
134
135 final ImportConfiguration configuration = new ActiveObjectsImportConfiguration(connectionProvider, getProgressMonitor(monitor), databaseInformation);
136
137 final DbImporter dbImporter = new DbImporter(errorService,
138 new DatabaseInformationImporter(errorService),
139 new TableDefinitionImporter(errorService, new ActiveObjectsTableCreator(errorService, provider, nameConverters), new ActiveObjectsDatabaseCleaner(provider, nameConverters, schemaConfiguration(), errorService, aoServiceFactory)),
140 new DataImporter(errorService,
141 provider.getSchema(),
142 new SqlServerAroundTableImporter(errorService, provider.getSchema()),
143 new PostgresSequencesAroundImporter(errorService, provider),
144 new OracleSequencesAroundImporter(errorService, provider, nameConverters),
145 new ForeignKeyAroundImporter(new ActiveObjectsForeignKeyCreator(errorService, nameConverters, provider))
146 ));
147
148 NodeStreamReader streamReader = null;
149 try {
150 streamReader = new StaxStreamReader(errorService, new InputStreamReader(stream, CHARSET));
151 dbImporter.importData(streamReader, configuration);
152 } finally {
153 closeCloseable(streamReader);
154 }
155 }
156
157 @Override
158 public void clear() {
159 final DatabaseProvider provider = databaseProviderSupplier.get();
160 new ActiveObjectsDatabaseCleaner(provider, nameConverters, schemaConfiguration(), errorService, aoServiceFactory).cleanup(CleanupMode.CLEAN);
161 }
162
163 private DatabaseInformation getDatabaseInformation(DatabaseProviderConnectionProvider connectionProvider) {
164 return new DatabaseInformation(new ConnectionProviderInformationReader(errorService, connectionProvider).get());
165 }
166
167 private static DatabaseProviderConnectionProvider getConnectionProvider(DatabaseProvider provider) {
168 return new DatabaseProviderConnectionProvider(provider);
169 }
170
171 private ProgressMonitor getProgressMonitor(BackupProgressMonitor backupProgressMonitor) {
172 return new ActiveObjectsBackupProgressMonitor(backupProgressMonitor);
173 }
174
175 private ProgressMonitor getProgressMonitor(RestoreProgressMonitor restoreProgressMonitor) {
176 return new ActiveObjectsRestoreProgressMonitor(restoreProgressMonitor);
177 }
178
179 private static void closeCloseable(Closeable streamWriter) {
180 if (streamWriter != null) {
181 try {
182 streamWriter.close();
183 } catch (IOException e) {
184
185 }
186 }
187 }
188
189 private static abstract class ActiveObjectsImportExportConfiguration implements ImportExportConfiguration {
190 private final ConnectionProvider connectionProvider;
191 private final ProgressMonitor progressMonitor;
192 private final EntityNameProcessor entityNameProcessor;
193
194 ActiveObjectsImportExportConfiguration(ConnectionProvider connectionProvider, ProgressMonitor progressMonitor) {
195 this.connectionProvider = checkNotNull(connectionProvider);
196 this.progressMonitor = checkNotNull(progressMonitor);
197 this.entityNameProcessor = new UpperCaseEntityNameProcessor();
198 }
199
200 @Override
201 public final ConnectionProvider getConnectionProvider() {
202 return connectionProvider;
203 }
204
205 @Override
206 public final ProgressMonitor getProgressMonitor() {
207 return progressMonitor;
208 }
209
210 @Override
211 public final EntityNameProcessor getEntityNameProcessor() {
212 return entityNameProcessor;
213 }
214 }
215
216 private static final class ActiveObjectsExportConfiguration extends ActiveObjectsImportExportConfiguration implements ExportConfiguration {
217 public ActiveObjectsExportConfiguration(ConnectionProvider connectionProvider, ProgressMonitor progressMonitor) {
218 super(connectionProvider, progressMonitor);
219 }
220 }
221
222 private static final class ActiveObjectsImportConfiguration extends ActiveObjectsImportExportConfiguration implements ImportConfiguration {
223 private final DatabaseInformation databaseInformation;
224
225 ActiveObjectsImportConfiguration(ConnectionProvider connectionProvider, ProgressMonitor progressMonitor, DatabaseInformation databaseInformation) {
226 super(connectionProvider, progressMonitor);
227 this.databaseInformation = checkNotNull(databaseInformation);
228 }
229
230 @Override
231 public DatabaseInformation getDatabaseInformation() {
232 return databaseInformation;
233 }
234
235 @Override
236 public CleanupMode getCleanupMode() {
237 return CleanupMode.CLEAN;
238 }
239
240 @Override
241 public BatchMode getBatchMode() {
242 return BatchMode.ON;
243 }
244 }
245
246 public static final class UpperCaseEntityNameProcessor implements EntityNameProcessor {
247 @Override
248 public String tableName(String tableName) {
249 return toUpperCase(tableName);
250 }
251
252 @Override
253 public String columnName(String columnName) {
254 return toUpperCase(columnName);
255 }
256 }
257 }