/* * Copyright (C) 2009 eXo Platform SAS. * * 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 org.exoplatform.services.jcr.ext.backup; import org.exoplatform.container.xml.InitParams; import org.exoplatform.container.xml.PropertiesParam; import org.exoplatform.services.jcr.config.QueryHandlerParams; import org.exoplatform.services.jcr.config.RepositoryConfigurationException; import org.exoplatform.services.jcr.config.RepositoryEntry; import org.exoplatform.services.jcr.config.ValueStorageEntry; import org.exoplatform.services.jcr.config.WorkspaceEntry; import org.exoplatform.services.jcr.core.ManageableRepository; import org.exoplatform.services.jcr.core.WorkspaceContainerFacade; import org.exoplatform.services.jcr.ext.BaseStandaloneTest; import org.exoplatform.services.jcr.ext.app.SessionProviderService; import org.exoplatform.services.jcr.ext.backup.impl.BackupManagerImpl; import org.exoplatform.services.jcr.ext.backup.impl.JobRepositoryRestore; import org.exoplatform.services.jcr.ext.backup.impl.JobWorkspaceRestore; import org.exoplatform.services.jcr.ext.backup.server.HTTPBackupAgent; import org.exoplatform.services.jcr.ext.backup.server.HTTPBackupAgentTest; import org.exoplatform.services.jcr.ext.backup.server.bean.response.DetailedInfo; import org.exoplatform.services.jcr.ext.backup.server.bean.response.ShortInfo; import org.exoplatform.services.jcr.ext.common.SessionProvider; import org.exoplatform.services.jcr.impl.clean.rdbms.DBCleanService; import org.exoplatform.services.jcr.impl.core.SessionImpl; import org.exoplatform.services.jcr.impl.core.SessionRegistry; import org.exoplatform.services.jcr.impl.core.query.SystemSearchManager; import org.exoplatform.services.jcr.impl.storage.jdbc.JDBCDataContainerConfig.DatabaseStructureType; import org.exoplatform.services.jcr.impl.storage.value.fs.FileValueStorage; import org.exoplatform.services.jcr.impl.util.io.DirectoryHelper; import org.exoplatform.services.jcr.util.TesterConfigurationHelper; import org.exoplatform.services.rest.ContainerResponseWriter; import org.exoplatform.services.rest.RequestHandler; import org.exoplatform.services.rest.impl.ContainerResponse; import org.exoplatform.services.rest.impl.InputHeadersMap; import org.exoplatform.services.rest.impl.MultivaluedMapImpl; import org.exoplatform.services.rest.tools.ByteArrayContainerResponseWriter; import org.exoplatform.services.security.ConversationState; import org.exoplatform.services.security.Identity; import org.exoplatform.ws.frameworks.json.JsonHandler; import org.exoplatform.ws.frameworks.json.JsonParser; import org.exoplatform.ws.frameworks.json.impl.BeanBuilder; import org.exoplatform.ws.frameworks.json.impl.JsonDefaultHandler; import org.exoplatform.ws.frameworks.json.impl.JsonGeneratorImpl; import org.exoplatform.ws.frameworks.json.impl.JsonParserImpl; import org.exoplatform.ws.frameworks.json.value.JsonValue; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileInputStream; import java.io.InputStream; import java.net.URI; import java.util.ArrayList; import java.util.Calendar; import java.util.Date; import java.util.List; import javax.jcr.ItemExistsException; import javax.jcr.LoginException; import javax.jcr.NoSuchWorkspaceException; import javax.jcr.Node; import javax.jcr.PathNotFoundException; import javax.jcr.RepositoryException; import javax.jcr.Session; import javax.jcr.ValueFormatException; import javax.jcr.lock.LockException; import javax.jcr.nodetype.ConstraintViolationException; import javax.jcr.version.VersionException; import javax.ws.rs.core.MultivaluedMap; /** * Created by The eXo Platform SAS Author : Peter Nedonosko peter.nedonosko@exoplatform.com.ua * 04.02.2008 * * @author <a href="mailto:peter.nedonosko@exoplatform.com.ua">Peter Nedonosko</a> * @version $Id: AbstractBackupTestCase.java 760 2008-02-07 15:08:07Z pnedonosko $ */ public abstract class AbstractBackupTestCase extends BaseStandaloneTest { protected TesterConfigurationHelper helper = TesterConfigurationHelper.getInstance(); protected File blob; protected ExtendedBackupManager backup; protected File backupDir; protected RequestHandler handler; /** * {@inheritDoc} */ public void setUp() throws Exception { super.setUp();// this backup = getBackupManager(); forceStopAutoStopperThreads(); blob = createBLOBTempFile(300); backupDir = new File("target/temp/backup/" + System.currentTimeMillis()); backupDir.mkdirs(); handler = (RequestHandler)container.getComponentInstanceOfType(RequestHandler.class); SessionProviderService sessionProviderService = (SessionProviderService)container.getComponentInstanceOfType(SessionProviderService.class); assertNotNull(sessionProviderService); sessionProviderService.setSessionProvider(null, new SessionProvider(new ConversationState(new Identity("root")))); } private void forceStopAutoStopperThreads() { ((BackupManagerImpl)backup).stop(); } /** * {@inheritDoc} */ protected void tearDown() throws Exception { super.tearDown(); blob.delete(); } protected abstract ExtendedBackupManager getBackupManager(); protected ExtendedBackupManager getJCRBackupManager() { if (backup == null) { InitParams initParams = new InitParams(); PropertiesParam pps = new PropertiesParam(); pps.setProperty(BackupManagerImpl.FULL_BACKUP_TYPE, "org.exoplatform.services.jcr.ext.backup.impl.fs.FullBackupJob"); pps.setProperty(BackupManagerImpl.INCREMENTAL_BACKUP_TYPE, "org.exoplatform.services.jcr.ext.backup.impl.fs.IncrementalBackupJob"); pps.setProperty(BackupManagerImpl.BACKUP_DIR, "target/backup"); pps.setProperty(BackupManagerImpl.DEFAULT_INCREMENTAL_JOB_PERIOD, "3600"); initParams.put(BackupManagerImpl.BACKUP_PROPERTIES, pps); BackupManagerImpl backup = new BackupManagerImpl(initParams, repositoryService); backup.start(); return backup; } return backup; } protected ExtendedBackupManager getRDBMSBackupManager() { if (backup == null) { InitParams initParams = new InitParams(); PropertiesParam pps = new PropertiesParam(); pps.setProperty(BackupManagerImpl.FULL_BACKUP_TYPE, "org.exoplatform.services.jcr.ext.backup.impl.rdbms.FullBackupJob"); pps.setProperty(BackupManagerImpl.INCREMENTAL_BACKUP_TYPE, "org.exoplatform.services.jcr.ext.backup.impl.fs.IncrementalBackupJob"); pps.setProperty(BackupManagerImpl.BACKUP_DIR, "target/backup"); pps.setProperty(BackupManagerImpl.DEFAULT_INCREMENTAL_JOB_PERIOD, "3600"); initParams.put(BackupManagerImpl.BACKUP_PROPERTIES, pps); BackupManagerImpl backup = new BackupManagerImpl(initParams, repositoryService); backup.start(); return backup; } return backup; } protected void addContent(Node node, int startIndex, int stopIndex, long sleepTime) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, ItemExistsException, PathNotFoundException, RepositoryException, InterruptedException { for (int i = startIndex; i <= stopIndex; i++) { node.addNode("node_" + i).setProperty("exo:data", "property-" + i); Thread.sleep(sleepTime); if (i % 10 == 0) node.save(); // log here via listener } node.save(); } protected void waitTime(Date time) throws InterruptedException { while (Calendar.getInstance().getTime().before(time)) { Thread.yield(); Thread.sleep(50); } Thread.sleep(250); } protected void removeWorkspaceFully(String repositoryName, String workspaceName) throws Exception { // get current workspace configuration WorkspaceEntry wEntry = null;; for (WorkspaceEntry entry : repositoryService.getRepository(repositoryName).getConfiguration() .getWorkspaceEntries()) { if (entry.getName().equals(workspaceName)) { wEntry = entry; break; } } if (wEntry == null) { throw new WorkspaceRestoreException("Workspace " + workspaceName + " did not found in current repository " + repositoryName + " configuration"); } boolean isSystem = repositoryService.getRepository(repositoryName).getConfiguration().getSystemWorkspaceName() .equals(wEntry.getName()); // remove workspace forceCloseSession(repositoryName, wEntry.getName()); repositoryService.getRepository(repositoryName).removeWorkspace(wEntry.getName()); // clean db DBCleanService.cleanWorkspaceData(wEntry); // clean value storage if (wEntry.getContainer().getValueStorages() != null) { for (ValueStorageEntry valueStorage : wEntry.getContainer().getValueStorages()) { DirectoryHelper.removeDirectory(new File(valueStorage.getParameterValue(FileValueStorage.PATH))); } } // clean index if (wEntry.getQueryHandler() != null) { DirectoryHelper.removeDirectory(new File(wEntry.getQueryHandler().getParameterValue( QueryHandlerParams.PARAM_INDEX_DIR, null))); if (isSystem) { DirectoryHelper.removeDirectory(new File(wEntry.getQueryHandler().getParameterValue( QueryHandlerParams.PARAM_INDEX_DIR, null) + "_" + SystemSearchManager.INDEX_DIR_SUFFIX)); } } } protected void removeWorkspaceFullySingleDB(String repositoryName, String workspaceName) throws Exception { // get current workspace configuration WorkspaceEntry wEntry = null;; for (WorkspaceEntry entry : repositoryService.getRepository(repositoryName).getConfiguration() .getWorkspaceEntries()) { if (entry.getName().equals(workspaceName)) { wEntry = entry; break; } } if (wEntry == null) { throw new WorkspaceRestoreException("Workspace " + workspaceName + " did not found in current repository " + repositoryName + " configuration"); } boolean isSystem = repositoryService.getRepository(repositoryName).getConfiguration().getSystemWorkspaceName() .equals(wEntry.getName()); //close all session forceCloseSession(repositoryName, wEntry.getName()); repositoryService.getRepository(repositoryName).removeWorkspace(wEntry.getName()); DBCleanService.cleanWorkspaceData(wEntry); if (wEntry.getContainer().getValueStorages() != null) { for (ValueStorageEntry valueStorage : wEntry.getContainer().getValueStorages()) { DirectoryHelper.removeDirectory(new File(valueStorage.getParameterValue(FileValueStorage.PATH))); } } if (wEntry.getQueryHandler() != null) { DirectoryHelper.removeDirectory(new File(wEntry.getQueryHandler().getParameterValue( QueryHandlerParams.PARAM_INDEX_DIR, null))); if (isSystem) { DirectoryHelper.removeDirectory(new File(wEntry.getQueryHandler().getParameterValue( QueryHandlerParams.PARAM_INDEX_DIR, null) + "_" + SystemSearchManager.INDEX_DIR_SUFFIX)); } } } protected void removeRepositoryFully(String repositoryName) throws Exception { // get current repository configuration RepositoryEntry repositoryEntry = repositoryService.getConfig().getRepositoryConfiguration(repositoryName); if (repositoryEntry == null) { throw new RepositoryRestoreExeption("Current repository configuration " + repositoryName + " did not found"); } //Create local copy of WorkspaceEntry for all workspaces ArrayList<WorkspaceEntry> workspaceList = new ArrayList<WorkspaceEntry>(); workspaceList.addAll(repositoryEntry.getWorkspaceEntries()); //close all session for (WorkspaceEntry wEntry : workspaceList) { forceCloseSession(repositoryEntry.getName(), wEntry.getName()); } String systemWorkspaceName = repositoryService.getRepository(repositoryName).getConfiguration().getSystemWorkspaceName(); //remove repository repositoryService.removeRepository(repositoryEntry.getName()); // clean data for (WorkspaceEntry wEntry : workspaceList) { DBCleanService.cleanWorkspaceData(wEntry); if (wEntry.getContainer().getValueStorages() != null) { for (ValueStorageEntry valueStorage : wEntry.getContainer().getValueStorages()) { DirectoryHelper.removeDirectory(new File(valueStorage.getParameterValue(FileValueStorage.PATH))); } } boolean isSystem = systemWorkspaceName.equals(wEntry.getName()); if (wEntry.getQueryHandler() != null) { DirectoryHelper.removeDirectory(new File(wEntry.getQueryHandler().getParameterValue( QueryHandlerParams.PARAM_INDEX_DIR, null))); if (isSystem) { DirectoryHelper.removeDirectory(new File(wEntry.getQueryHandler().getParameterValue( QueryHandlerParams.PARAM_INDEX_DIR, null) + "_" + SystemSearchManager.INDEX_DIR_SUFFIX)); } } } } /** * forceCloseSession. Close sessions on specific workspace. * * @param repositoryName * repository name * @param workspaceName * workspace name * @return int return the how many sessions was closed * @throws RepositoryConfigurationException * will be generate RepositoryConfigurationException * @throws RepositoryException * will be generate RepositoryException */ private int forceCloseSession(String repositoryName, String workspaceName) throws RepositoryException, RepositoryConfigurationException { ManageableRepository mr = repositoryService.getRepository(repositoryName); WorkspaceContainerFacade wc = mr.getWorkspaceContainer(workspaceName); SessionRegistry sessionRegistry = (SessionRegistry) wc.getComponent(SessionRegistry.class); return sessionRegistry.closeSessions(workspaceName); } public void waitEndOfBackup(BackupChain bch) throws Exception { while (bch.getFullBackupState() != BackupChain.FINISHED) { Thread.yield(); Thread.sleep(50); } } public void waitEndOfBackup(RepositoryBackupChain bch) throws Exception { while (bch.getState() != RepositoryBackupChain.FINISHED && bch.getState() != RepositoryBackupChain.FULL_BACKUP_FINISHED_INCREMENTAL_BACKUP_WORKING) { Thread.yield(); Thread.sleep(50); } } public void waitEndOfRestore(String repositoryName) throws Exception { while (backup.getLastRepositoryRestore(repositoryName).getStateRestore() != JobRepositoryRestore.REPOSITORY_RESTORE_SUCCESSFUL && backup.getLastRepositoryRestore(repositoryName).getStateRestore() != JobRepositoryRestore.REPOSITORY_RESTORE_FAIL) { Thread.sleep(50); } } public void waitEndOfRestore(String repositoryName, String workspaceName) throws Exception { while (backup.getLastRestore(repositoryName, workspaceName).getStateRestore() != JobWorkspaceRestore.RESTORE_SUCCESSFUL && backup.getLastRestore(repositoryName, workspaceName).getStateRestore() != JobWorkspaceRestore.RESTORE_FAIL) { Thread.sleep(50); } } public void addIncrementalConent(ManageableRepository repository, String wsName) throws Exception { SessionImpl session = (SessionImpl)repository.login(credentials, wsName); Node rootNode = session.getRootNode().addNode("testIncremental"); // add some changes which will be logged in incremental log rootNode.addNode("node1").setProperty("prop1", "value1"); rootNode.addNode("node2").setProperty("prop2", new FileInputStream(blob)); rootNode.addNode("node3").addMixin("mix:lockable"); session.save(); } public void addConent(ManageableRepository repository, String wsName) throws Exception { SessionImpl session = (SessionImpl)repository.login(credentials, wsName); Node rootNode = session.getRootNode().addNode("test"); // add some changes which will be logged in incremental log rootNode.addNode("node1").setProperty("prop1", "value1"); rootNode.addNode("node2").setProperty("prop2", new FileInputStream(blob)); rootNode.addNode("node3").addMixin("mix:lockable"); session.save(); } public void checkConent(ManageableRepository repository, String wsName) throws Exception { SessionImpl session = (SessionImpl)repository.login(credentials, wsName); Node rootNode = session.getRootNode().getNode("test"); assertEquals(rootNode.getNode("node1").getProperty("prop1").getString(), "value1"); InputStream in = rootNode.getNode("node2").getProperty("prop2").getStream(); try { compareStream(new FileInputStream(blob), in); } finally { in.close(); } } public void checkIncrementalConent(ManageableRepository repository, String wsName) throws Exception { SessionImpl session = (SessionImpl)repository.login(credentials, wsName); Node rootNode = session.getRootNode().getNode("testIncremental"); assertEquals(rootNode.getNode("node1").getProperty("prop1").getString(), "value1"); InputStream in = rootNode.getNode("node2").getProperty("prop2").getStream(); try { compareStream(new FileInputStream(blob), in); } finally { in.close(); } } protected RepoInfo createRepositoryAndGetSession() throws Exception { ManageableRepository repository = helper.createRepository(container, DatabaseStructureType.MULTI, null); WorkspaceEntry wsEntry = helper.createWorkspaceEntry(DatabaseStructureType.MULTI, null); helper.addWorkspace(repository, wsEntry); RepoInfo rInfo = new RepoInfo(); rInfo.rName = repository.getConfiguration().getName(); rInfo.wsName = wsEntry.getName(); rInfo.sysWsName = repository.getConfiguration().getSystemWorkspaceName(); rInfo.session = repositoryService.getRepository(rInfo.rName).login(credentials, rInfo.wsName); return rInfo; } /** * Class for tests purpose only. To have ability to access to {@link ContainerResponseWriter}. */ protected class TesterContainerResponce extends ContainerResponse { public ByteArrayContainerResponseWriter responseWriter; public TesterContainerResponce(ByteArrayContainerResponseWriter responseWriter) { super(responseWriter); this.responseWriter = responseWriter; } } /** * Aggregate info about newly created repository. */ protected class RepoInfo { public String rName; public String wsName; public String sysWsName; public Session session; } protected boolean isRepositoryExists(String rName) { return isWorkspaceExists(rName, null); } protected boolean isWorkspaceExists(String rName, String wsName) { ManageableRepository repository = null; try { repository = repositoryService.getRepository(rName); } catch (RepositoryException e) { return false; } catch (RepositoryConfigurationException e) { return false; } try { repository.login(credentials, wsName); } catch (LoginException e) { return false; } catch (NoSuchWorkspaceException e) { return false; } catch (RepositoryException e) { return false; } return true; } /** * Will be created the Object from JSON binary data. * * @param cl * Class * @param data * binary data (JSON) * @return Object * @throws Exception * will be generated Exception */ protected Object getObject(Class cl, byte[] data) throws Exception { JsonHandler jsonHandler = new JsonDefaultHandler(); JsonParser jsonParser = new JsonParserImpl(); InputStream inputStream = new ByteArrayInputStream(data); jsonParser.parse(inputStream, jsonHandler); JsonValue jsonValue = jsonHandler.getJsonObject(); return new BeanBuilder().createObject(cl, jsonValue); } protected void waitWorkspaceRestore(String repoName, String wsName) throws Exception { while (true) { TesterContainerResponce cres = makeGetRequest(new URI(HTTPBackupAgentTest.HTTP_BACKUP_AGENT_PATH + HTTPBackupAgent.Constants.OperationType.CURRENT_RESTORE_INFO_ON_WS + "/" + repoName + "/" + wsName)); assertEquals(200, cres.getStatus()); DetailedInfo info = (DetailedInfo)getObject(DetailedInfo.class, cres.responseWriter.getBody()); if (info.getState().intValue() == JobWorkspaceRestore.RESTORE_SUCCESSFUL || info.getState().intValue() == JobWorkspaceRestore.RESTORE_FAIL) { break; } Thread.sleep(500); } } protected void waitRepositoryRestore(String repoName) throws Exception { while (true) { TesterContainerResponce cres = makeGetRequest(new URI(HTTPBackupAgentTest.HTTP_BACKUP_AGENT_PATH + HTTPBackupAgent.Constants.OperationType.CURRENT_RESTORE_INFO_ON_REPOSITORY + "/" + repoName)); assertEquals(200, cres.getStatus()); DetailedInfo info = (DetailedInfo)getObject(DetailedInfo.class, cres.responseWriter.getBody()); if (info.getState().intValue() == JobRepositoryRestore.REPOSITORY_RESTORE_SUCCESSFUL || info.getState().intValue() == JobRepositoryRestore.REPOSITORY_RESTORE_FAIL) { break; } Thread.sleep(500); } } protected ShortInfo getBackupInfo(List<ShortInfo> list, String rName) { for (ShortInfo info : list) { if (info.getRepositoryName().equals(rName)) { return info; } } return null; } protected BackupChain backupWorkspace(RepoInfo rInfo) throws Exception { BackupConfig config = new BackupConfig(); config.setRepository(rInfo.rName); config.setWorkspace(rInfo.wsName); config.setBackupType(BackupManager.FULL_BACKUP_ONLY); config.setBackupDir(backupDir); BackupChain bch = backup.startBackup(config); waitEndOfBackup(bch); return bch; } protected RepositoryBackupChain backupRepository(RepoInfo rInfo) throws Exception { RepositoryBackupConfig config = new RepositoryBackupConfig(); config.setRepository(rInfo.rName); config.setBackupType(BackupManager.FULL_BACKUP_ONLY); config.setBackupDir(backupDir); RepositoryBackupChain bch = backup.startBackup(config); waitEndOfBackup(bch); return bch; } protected TesterContainerResponce makeGetRequest(URI uri) throws Exception { MultivaluedMap<String, String> headers = new MultivaluedMapImpl(); ContainerRequestUserRole creq = new ContainerRequestUserRole("GET", uri, new URI(""), null, new InputHeadersMap(headers)); ByteArrayContainerResponseWriter responseWriter = new ByteArrayContainerResponseWriter(); TesterContainerResponce cres = new TesterContainerResponce(responseWriter); handler.handleRequest(creq, cres); return cres; } protected TesterContainerResponce makePostRequest(URI uri, Object object) throws Exception { JsonGeneratorImpl generatorImpl = new JsonGeneratorImpl(); JsonValue json = generatorImpl.createJsonObject(object); MultivaluedMap<String, String> headers = new MultivaluedMapImpl(); headers.putSingle("Content-Type", "application/json; charset=UTF-8"); ContainerRequestUserRole creq = new ContainerRequestUserRole("POST", uri, new URI(""), new ByteArrayInputStream(json.toString().getBytes( "UTF-8")), new InputHeadersMap(headers)); ByteArrayContainerResponseWriter responseWriter = new ByteArrayContainerResponseWriter(); TesterContainerResponce cres = new TesterContainerResponce(responseWriter); handler.handleRequest(creq, cres); return cres; } }