1. Project Clover database Tue Dec 20 2016 21:24:09 CET
  2. Package com.xpn.xwiki.store.migration.hibernate

File HibernateDataMigrationManager.java

 

Coverage histogram

../../../../../../img/srcFileCovDistChart6.png
69% of files have more coverage

Code metrics

26
98
16
1
371
260
37
0.38
6.12
16
2.31

Classes

Class Line # Actions
HibernateDataMigrationManager 63 98 0% 37 57
0.592857159.3%
 

Contributing tests

No tests hitting this source file were found.

Source view

1    /*
2    * See the NOTICE file distributed with this work for additional
3    * information regarding copyright ownership.
4    *
5    * This is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU Lesser General Public License as
7    * published by the Free Software Foundation; either version 2.1 of
8    * the License, or (at your option) any later version.
9    *
10    * This software is distributed in the hope that it will be useful,
11    * but WITHOUT ANY WARRANTY; without even the implied warranty of
12    * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13    * Lesser General Public License for more details.
14    *
15    * You should have received a copy of the GNU Lesser General Public
16    * License along with this software; if not, write to the Free
17    * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
18    * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
19    */
20   
21    package com.xpn.xwiki.store.migration.hibernate;
22   
23    import java.io.IOException;
24    import java.util.Collection;
25    import java.util.List;
26   
27    import javax.inject.Named;
28    import javax.inject.Singleton;
29   
30    import org.hibernate.HibernateException;
31    import org.hibernate.Session;
32    import org.hibernate.Transaction;
33    import org.hibernate.criterion.Projections;
34    import org.xwiki.component.annotation.Component;
35    import org.xwiki.component.manager.ComponentLookupException;
36   
37    import com.xpn.xwiki.XWikiContext;
38    import com.xpn.xwiki.XWikiException;
39    import com.xpn.xwiki.doc.XWikiDocument;
40    import com.xpn.xwiki.store.XWikiHibernateBaseStore;
41    import com.xpn.xwiki.store.XWikiHibernateBaseStore.HibernateCallback;
42    import com.xpn.xwiki.store.XWikiStoreInterface;
43    import com.xpn.xwiki.store.migration.AbstractDataMigrationManager;
44    import com.xpn.xwiki.store.migration.DataMigration;
45    import com.xpn.xwiki.store.migration.DataMigrationException;
46    import com.xpn.xwiki.store.migration.XWikiDBVersion;
47   
48    import liquibase.Liquibase;
49    import liquibase.database.Database;
50    import liquibase.database.DatabaseFactory;
51    import liquibase.database.jvm.JdbcConnection;
52    import liquibase.exception.LiquibaseException;
53   
54    /**
55    * Migration manager for hibernate store.
56    *
57    * @version $Id: b08828c82df63b147db0248648343f8f0d2084cf $
58    * @since 3.4M1
59    */
60    @Component
61    @Named("hibernate")
62    @Singleton
 
63    public class HibernateDataMigrationManager extends AbstractDataMigrationManager
64    {
65    /**
66    * Name of the liquibase resource used to include additional change logs XMLs.
67    * If it exists, this resource should contains at least one valid liquibase XML definition.
68    */
69    private static final String LIQUIBASE_RESOURCE = "liquibase-xwiki/";
70   
71    /**
72    * @return store system for execute store-specific actions.
73    * @throws DataMigrationException if the store could not be reached
74    */
 
75  280 toggle public XWikiHibernateBaseStore getStore() throws DataMigrationException
76    {
77  280 try {
78  280 return (XWikiHibernateBaseStore) this.componentManager.getInstance(XWikiStoreInterface.class, "hibernate");
79    } catch (ComponentLookupException e) {
80  0 throw new DataMigrationException(String.format("Unable to reach the store for database %s",
81    getXWikiContext().getWikiId()), e);
82    }
83    }
84   
 
85  64 toggle @Override
86    public XWikiDBVersion getDBVersionFromDatabase() throws DataMigrationException
87    {
88  64 XWikiDBVersion ver = getDBVersionFromConfig();
89  64 if (ver != null) {
90  0 return ver;
91    }
92   
93  64 final XWikiContext context = getXWikiContext();
94  64 final XWikiHibernateBaseStore store = getStore();
95   
96    // Try retrieving a version from the database
97  64 ver = store.failSafeExecuteRead(context,
98    new HibernateCallback<XWikiDBVersion>()
99    {
 
100  64 toggle @Override
101    public XWikiDBVersion doInHibernate(Session session) throws HibernateException
102    {
103    // Retrieve the version from the database
104  64 return (XWikiDBVersion) session.createCriteria(XWikiDBVersion.class).uniqueResult();
105    }
106    });
107   
108    // if it fails, return version 0 if there is some documents in the database, else null (empty db?)
109  64 if (ver == null) {
110  38 ver = store.failSafeExecuteRead(getXWikiContext(),
111    new HibernateCallback<XWikiDBVersion>()
112    {
 
113  38 toggle @Override
114    public XWikiDBVersion doInHibernate(Session session) throws HibernateException
115    {
116  0 if (((Number) session.createCriteria(XWikiDocument.class)
117    .setProjection(Projections.rowCount())
118    .uniqueResult()).longValue() > 0)
119    {
120  0 return new XWikiDBVersion(0);
121    }
122  0 return null;
123    }
124    });
125    }
126   
127  64 return ver;
128    }
129   
 
130  38 toggle @Override
131    protected void initializeEmptyDB() throws DataMigrationException
132    {
133  38 final XWikiContext context = getXWikiContext();
134  38 final XWikiHibernateBaseStore store = getStore();
135   
136  38 final Session originalSession = store.getSession(context);
137  38 final Transaction originalTransaction = store.getTransaction(context);
138  38 store.setSession(null, context);
139  38 store.setTransaction(null, context);
140   
141  38 try {
142  38 updateSchema(null);
143  38 setDBVersion(getLatestVersion());
144    } finally {
145  38 store.setSession(originalSession, context);
146  38 store.setTransaction(originalTransaction, context);
147    }
148    }
149   
 
150  38 toggle @Override
151    protected void setDBVersionToDatabase(final XWikiDBVersion version) throws DataMigrationException
152    {
153  38 final XWikiContext context = getXWikiContext();
154  38 final XWikiHibernateBaseStore store = getStore();
155  38 final boolean bTransaction = store.getTransaction(context) == null;
156   
157  38 try {
158  38 getStore().executeWrite(context, bTransaction, new HibernateCallback<Object>()
159    {
 
160  38 toggle @Override
161    public Object doInHibernate(Session session) throws HibernateException
162    {
163  38 session.createQuery("delete from " + XWikiDBVersion.class.getName()).executeUpdate();
164  38 session.save(version);
165  38 return null;
166    }
167    });
168    } catch (Exception e) {
169  0 throw new DataMigrationException(String.format("Unable to store new data version %d into database %s",
170    version.getVersion(), context.getWikiId()), e);
171    }
172    }
173   
 
174  70 toggle @Override
175    protected void updateSchema(Collection<XWikiMigration> migrations) throws DataMigrationException
176    {
177  70 try {
178  70 liquibaseUpdate(migrations, true);
179  70 hibernateShemaUpdate();
180  70 liquibaseUpdate(migrations, false);
181    } catch (Exception e) {
182  0 throw new DataMigrationException(String.format("Unable to update schema of wiki [%s]",
183    getXWikiContext().getWikiId()), e);
184    }
185    }
186   
187    /**
188    * Run hibernate schema updates
189    *
190    * @throws DataMigrationException if the store is not accessible
191    */
 
192  70 toggle private void hibernateShemaUpdate() throws DataMigrationException
193    {
194  70 if (this.logger.isInfoEnabled()) {
195  70 this.logger.info("Checking Hibernate mapping and updating schema if needed for wiki [{}]",
196    getXWikiContext().getWikiId());
197    }
198  70 getStore().updateSchema(getXWikiContext(), true);
199    }
200   
201    /**
202    * Get agregated liquibase change logs from a set of migration.
203    *
204    * @param migrations the set of migration to visit
205    * @param preHibernate if true, get pre-hibernate schema update changelogs.
206    * @return retrieved change logs
207    * @throws DataMigrationException if an issue occurs in a migrator during retrieval of a change log
208    * @since 4.3
209    */
 
210  140 toggle private String getLiquibaseChangeLogs(Collection<XWikiMigration> migrations, boolean preHibernate)
211    throws DataMigrationException
212    {
213  140 StringBuilder changeLogs = new StringBuilder(10000);
214  140 if (migrations != null) {
215  64 for (XWikiMigration migration : migrations) {
216  0 if (migration.dataMigration instanceof HibernateDataMigration) {
217  0 String changeLog;
218  0 if (preHibernate) {
219  0 changeLog =
220    ((HibernateDataMigration) migration.dataMigration).getPreHibernateLiquibaseChangeLog();
221    } else {
222  0 changeLog = ((HibernateDataMigration) migration.dataMigration).getLiquibaseChangeLog();
223    }
224  0 if (changeLog != null) {
225  0 changeLogs.append(changeLog);
226    }
227    }
228    }
229    }
230   
231  140 if (!preHibernate) {
232    // Add liquibase changes from resources if any
233  70 try {
234  70 if (getClass().getClassLoader().getResources(LIQUIBASE_RESOURCE).hasMoreElements()) {
235  0 changeLogs.append("<includeAll path=\"" + LIQUIBASE_RESOURCE + "\"/>");
236    }
237    } catch (IOException ignored) {
238    // ignored
239    }
240    }
241   
242  140 return changeLogs.toString();
243    }
244   
245    /**
246    * Run liquibase for a given set of change logs
247    *
248    * @param migrations the set of migration to visit
249    * @param preHibernate if true, use pre-hibernate schema update changelogs.
250    * @throws XWikiException
251    * @throws DataMigrationException
252    * @since 4.3
253    */
 
254  140 toggle private void liquibaseUpdate(Collection<XWikiMigration> migrations, boolean preHibernate) throws XWikiException,
255    DataMigrationException
256    {
257  140 String liquibaseChangeLogs = getLiquibaseChangeLogs(migrations, preHibernate);
258  140 if (liquibaseChangeLogs == null || liquibaseChangeLogs.length() == 0) {
259  140 return;
260    }
261   
262  0 final String database = getXWikiContext().getWikiId();
263   
264  0 if (this.logger.isInfoEnabled()) {
265  0 if (preHibernate) {
266  0 this.logger.info("Running early schema updates (using liquibase) for database [{}]", database);
267    } else {
268  0 this.logger.info("Running additional schema updates (using liquibase) for database [{}]", database);
269    }
270    }
271   
272  0 final StringBuilder changeLogs = new StringBuilder(10000);
273  0 changeLogs.append(getLiquibaseChangeLogHeader());
274  0 changeLogs.append(liquibaseChangeLogs);
275  0 changeLogs.append(getLiquibaseChangeLogFooter());
276   
277  0 final XWikiHibernateBaseStore store = getStore();
278   
279  0 store.executeRead(getXWikiContext(), new HibernateCallback<Object>()
280    {
 
281  0 toggle @Override
282    @SuppressWarnings("unchecked")
283    public Object doInHibernate(Session session) throws XWikiException
284    {
285   
286  0 Liquibase lb;
287  0 try {
288  0 Database lbDatabase =
289    DatabaseFactory.getInstance().findCorrectDatabaseImplementation(
290    new JdbcConnection(session.connection()));
291   
292    // Precise the schema name to liquibase, since it does not usually determine it
293    // properly (See XWIKI-8813).
294  0 lbDatabase.setDefaultSchemaName(store.getSchemaFromWikiName(getXWikiContext()));
295   
296  0 lb = new Liquibase(MigrationResourceAccessor.CHANGELOG_NAME,
297    new MigrationResourceAccessor(changeLogs.toString()),
298    lbDatabase);
299    } catch (LiquibaseException e) {
300  0 throw new XWikiException(XWikiException.MODULE_XWIKI_STORE,
301    XWikiException.ERROR_XWIKI_STORE_MIGRATION,
302    String.format("Unable to launch liquibase for database %s, schema update failed.",
303    database), e);
304    }
305   
306  0 try {
307  0 lb.update(null);
308    } catch (LiquibaseException e) {
309  0 throw new XWikiException(XWikiException.MODULE_XWIKI_STORE,
310    XWikiException.ERROR_XWIKI_STORE_MIGRATION,
311    String.format("Unable to update schema of database %s.",
312    database), e);
313    }
314   
315  0 return null;
316    }
317    });
318    }
319   
320    /**
321    * @return the liquibase XML change log top level element opening with the XML declaration
322    * @since 4.0M1
323    */
 
324  0 toggle private String getLiquibaseChangeLogHeader()
325    {
326  0 return "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
327    + "<databaseChangeLog\n"
328    + " xmlns=\"http://www.liquibase.org/xml/ns/dbchangelog\"\n"
329    + " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n"
330    + " xsi:schemaLocation=\"http://www.liquibase.org/xml/ns/dbchangelog "
331    + "http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-2.0.xsd\">";
332    }
333   
334    /**
335    * @return the liquibase change log top level element close tag
336    * @since 4.0M1
337    */
 
338  0 toggle private String getLiquibaseChangeLogFooter()
339    {
340  0 return "</databaseChangeLog>";
341    }
342   
 
343  32 toggle @Override
344    protected void startMigrations() throws DataMigrationException
345    {
346  32 XWikiContext context = getXWikiContext();
347  32 XWikiHibernateBaseStore store = getStore();
348   
349  32 Session originalSession = store.getSession(context);
350  32 Transaction originalTransaction = store.getTransaction(context);
351  32 store.setSession(null, context);
352  32 store.setTransaction(null, context);
353   
354  32 try {
355  32 super.startMigrations();
356    } finally {
357  32 store.setSession(originalSession, context);
358  32 store.setTransaction(originalTransaction, context);
359    }
360    }
361   
 
362  87 toggle @Override
363    protected List<? extends DataMigration> getAllMigrations() throws DataMigrationException
364    {
365  87 try {
366  87 return this.componentManager.getInstanceList(HibernateDataMigration.class);
367    } catch (ComponentLookupException e) {
368  0 throw new DataMigrationException("Unable to retrieve the list of hibernate data migrations", e);
369    }
370    }
371    }