/*
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package com.xpn.xwiki.tool.backup;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import org.apache.commons.io.IOUtils;
import org.hibernate.Session;
import org.hibernate.dialect.Dialect;
import org.hibernate.dialect.HSQLDialect;
import org.xwiki.component.manager.ComponentManager;
import org.xwiki.model.reference.DocumentReference;
import com.xpn.xwiki.XWikiContext;
import com.xpn.xwiki.XWikiException;
import com.xpn.xwiki.plugin.packaging.DocumentInfo;
import com.xpn.xwiki.plugin.packaging.Package;
import com.xpn.xwiki.plugin.packaging.PackageException;
import com.xpn.xwiki.store.XWikiCacheStore;
import com.xpn.xwiki.store.XWikiHibernateStore;
import com.xpn.xwiki.store.XWikiStoreInterface;
/**
* Import a set of XWiki documents into an existing database.
*
* @version $Id: 73e36f75133f519fcb9a0999f65d047591f1ce01 $
*/
public class Importer extends AbstractPackager
{
/**
* Default constructor.
*/
public Importer()
{
}
/**
* @param componentManager the component manager
*/
public Importer(ComponentManager componentManager)
{
super(componentManager);
}
/**
* Import documents defined in an XML file located in the passed document definition directory into a database
* defined by its passed name and by an Hibernate configuration file.
* <p>
* Note: I would have liked to call this method "import" but it's a reserved keyword... Strange that it's not
* allowed for method names though.
* </p>
*
* @param sourceDirectory the directory where the package.xml file is located and where the documents to import are
* located
* @param wikiId id of the wiki into which to import the documents (e.g. {@code xwiki})
* @param hibernateConfig the Hibernate config fill containing the database definition (JDBC driver, username and
* password, etc)
* @throws Exception if the import failed for any reason
*/
// TODO: Replace the Hibernate config file with a list of parameters required for the import
public void importDocuments(File sourceDirectory, String wikiId, File hibernateConfig) throws Exception
{
importDocuments(sourceDirectory, wikiId, hibernateConfig, null);
}
/**
* Import documents defined in an XML file located in the passed document definition directory into a database
* defined by its passed name and by an Hibernate configuration file.
* <p>
* Note: I would have liked to call this method "import" but it's a reserved keyword... Strange that it's not
* allowed for method names though.
* </p>
*
* @param sourceDirectory the directory where the package.xml file is located and where the documents to import are
* located
* @param wikiId id of the wiki into which to import the documents (e.g. {@code xwiki})
* @param hibernateConfig the Hibernate config fill containing the database definition (JDBC driver, username and
* password, etc)
* @param importUser optionally the user under which to perform the import (useful for example when importing pages
* that need to have Programming Rights and the page author is not the same as the importing user)
* @throws Exception if the import failed for any reason
*/
// TODO: Replace the Hibernate config file with a list of parameters required for the import
public void importDocuments(File sourceDirectory, String wikiId, File hibernateConfig, String importUser)
throws Exception
{
XWikiContext xcontext = createXWikiContext(wikiId, hibernateConfig);
Package pack = new Package();
pack.setWithVersions(false);
// TODO: The readFromDir method should not throw IOExceptions, only PackageException.
// See https://jira.xwiki.org/browse/XWIKI-458
try {
pack.readFromDir(sourceDirectory, xcontext);
} catch (IOException e) {
throw new PackageException(PackageException.ERROR_PACKAGE_UNKNOWN,
"Failed to import documents from [" + sourceDirectory + "]", e);
}
installWithUser(importUser, pack, xcontext);
// We MUST shutdown HSQLDB because otherwise the last transactions will not be flushed
// to disk and will be lost. In practice this means the last Document imported has a
// very high chance of not making it...
// TODO: Find a way to implement this generically for all databases and inside
// XWikiHibernateStore (cf https://jira.xwiki.org/browse/XWIKI-471).
shutdownHSQLDB(xcontext);
disposeXWikiContext(xcontext);
}
/**
* @param file the XAR file to import
* @param importUser optionally the user under which to perform the import (useful for example when importing pages
* that need to have Programming Rights and the page author is not the same as the importing user)
* @param context the XWiki context
* @return the number of imported documents
* @throws XWikiException failed to import the XAR file
* @throws IOException failed to parse the XAR file
*/
public int importXAR(File file, String importUser, XWikiContext context) throws XWikiException, IOException
{
Package pack = new Package();
pack.setWithVersions(false);
// Parse XAR
FileInputStream fis = new FileInputStream(file);
try {
pack.Import(fis, context);
} finally {
IOUtils.closeQuietly(fis);
}
// Import into the database
if (!pack.getFiles().isEmpty()) {
installWithUser(importUser, pack, context);
}
return pack.getFiles().size();
}
/**
* Install a Package as a backup pack or with the passed user (if any).
*
* @param importUser the user to import with or null if it should be imported as a backup pack
* @param pack the Package instance performing the import
* @param context the XWiki Context
* @throws XWikiException if the import failed for any reason
*/
private void installWithUser(String importUser, Package pack, XWikiContext context) throws XWikiException
{
// Set the current context user if an import user is specified (i.e. not null)
DocumentReference currentUserReference = context.getUserReference();
if (importUser != null) {
pack.setBackupPack(false);
// Set the current user in the context to the import user
context.setUserReference(new DocumentReference("xwiki", "XWiki", importUser));
}
try {
int code = pack.install(context);
if (code != DocumentInfo.INSTALL_OK) {
throw new XWikiException(XWikiException.MODULE_XWIKI_STORE, XWikiException.ERROR_XWIKI_UNKNOWN,
"Failed to import XAR with code [" + code + "]");
}
} finally {
// Restore context user as before
context.setUserReference(currentUserReference);
}
}
/**
* Shutdowns HSQLDB.
*
* @param context the XWiki Context object from which we can retrieve the Store implementation
* @throws XWikiException in case of shutdown error
*/
public void shutdownHSQLDB(XWikiContext context) throws XWikiException
{
XWikiStoreInterface store = context.getWiki().getStore();
if (XWikiCacheStore.class.isAssignableFrom(store.getClass())) {
store = ((XWikiCacheStore) store).getStore();
}
if (XWikiHibernateStore.class.isAssignableFrom(store.getClass())) {
XWikiHibernateStore hibernateStore = (XWikiHibernateStore) store;
// check that is HSQLDB
Dialect dialect = hibernateStore.getDialect();
if (!(dialect instanceof HSQLDialect)) {
return;
}
boolean bTransaction = true;
try {
hibernateStore.checkHibernate(context);
bTransaction = hibernateStore.beginTransaction(false, context);
Session session = hibernateStore.getSession(context);
session.connection().createStatement().execute("SHUTDOWN");
if (bTransaction) {
hibernateStore.endTransaction(context, false, false);
}
} catch (Exception e) {
e.printStackTrace();
throw new PackageException(PackageException.ERROR_PACKAGE_UNKNOWN, "Failed to shutdown database", e);
} finally {
try {
if (bTransaction) {
hibernateStore.endTransaction(context, false, false);
}
} catch (Exception e) {
// Ignore
}
}
}
}
}