View Javadoc
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       * Saves the backup to an output stream.
90       *
91       * @param stream  the stream to write the backup to
92       * @param monitor the progress monitor for the current backup
93       * @throws ImportExportException or one of its sub-types if any error happens during the backup.
94       *                               {@link java.sql.SQLException SQL exceptions} will be wrapped in {@link ImportExportException}.
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      * Restores the backup coming from the given input stream.
123      *
124      * @param stream  the stream of data previously backed up by the plugin.
125      * @param monitor the progress monitor for the current restore
126      * @throws ImportExportException or one of its sub-types if any error happens during the backup.
127      *                               {@link java.sql.SQLException SQL exceptions} will be wrapped in {@link ImportExportException}.
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                 // ignore
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 }