| /******************************************************************************* |
| * Copyright (c) 2011 Eike Stepper (Berlin, Germany) and others. |
| * |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * Eike Stepper - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.emf.ecp.cdo.internal.core; |
| |
| import java.io.File; |
| import java.io.FileNotFoundException; |
| import java.io.PrintStream; |
| import java.util.Collection; |
| import java.util.Set; |
| |
| import org.eclipse.emf.cdo.common.branch.CDOBranch; |
| import org.eclipse.emf.cdo.common.branch.CDOBranchPoint; |
| import org.eclipse.emf.cdo.eresource.CDOResource; |
| import org.eclipse.emf.cdo.net4j.CDONet4jSession; |
| import org.eclipse.emf.cdo.net4j.CDONet4jSessionConfiguration; |
| import org.eclipse.emf.cdo.server.db.CDODBUtil; |
| import org.eclipse.emf.cdo.server.db.IDBStore; |
| import org.eclipse.emf.cdo.server.db.mapping.IMappingStrategy; |
| import org.eclipse.emf.cdo.session.CDOSessionConfigurationFactory; |
| import org.eclipse.emf.cdo.util.CDOUtil; |
| import org.eclipse.emf.cdo.workspace.CDOWorkspaceBase; |
| import org.eclipse.emf.cdo.workspace.CDOWorkspaceConfiguration; |
| import org.eclipse.emf.cdo.workspace.CDOWorkspaceUtil; |
| import org.eclipse.emf.common.notify.Notifier; |
| import org.eclipse.emf.common.util.EList; |
| import org.eclipse.emf.ecore.EObject; |
| import org.eclipse.emf.ecp.core.util.ECPContainer; |
| import org.eclipse.emf.ecp.core.util.ECPProperties; |
| import org.eclipse.emf.ecp.spi.core.DefaultProvider; |
| import org.eclipse.emf.ecp.spi.core.InternalProject; |
| import org.eclipse.emf.ecp.spi.core.InternalRepository; |
| import org.eclipse.emf.ecp.spi.core.util.InternalChildrenList; |
| import org.eclipse.net4j.db.DBUtil; |
| import org.eclipse.net4j.db.IDBAdapter; |
| import org.eclipse.net4j.db.IDBConnectionProvider; |
| import org.eclipse.net4j.db.h2.H2Adapter; |
| import org.eclipse.net4j.util.UUIDGenerator; |
| import org.eclipse.net4j.util.io.IOUtil; |
| import org.h2.jdbcx.JdbcDataSource; |
| |
| /** |
| * @author Eike Stepper |
| */ |
| public class CDOProvider extends DefaultProvider { |
| /** |
| * The unique provider name. |
| */ |
| public static final String NAME = "org.eclipse.emf.ecp.cdo.provider"; //$NON-NLS-1$ |
| |
| /** |
| * The key for the connector type ECP project property. |
| */ |
| public static final String PROP_CONNECTOR_TYPE = "connectorType"; //$NON-NLS-1$ |
| |
| /** |
| * The key for the connector description ECP project property. |
| */ |
| public static final String PROP_CONNECTOR_DESCRIPTION = "connectorDescription"; //$NON-NLS-1$ |
| |
| /** |
| * The key for the repository name ECP project property. |
| */ |
| public static final String PROP_REPOSITORY_NAME = "repositoryName"; //$NON-NLS-1$ |
| |
| /** |
| * The key for the branch path ECP project property. |
| */ |
| public static final String PROP_BRANCH_PATH = "branchPath"; //$NON-NLS-1$ |
| |
| /** |
| * The key for the time stamp ECP project property. |
| */ |
| public static final String PROP_TIME_STAMP = "timeStamp"; //$NON-NLS-1$ |
| |
| /** |
| * The key for the workspace ID ECP project property. |
| */ |
| public static final String PROP_WORKSPACE_ID = "workspaceID"; //$NON-NLS-1$ |
| |
| /** |
| * Contains the singleton instance of this provider. |
| */ |
| private static CDOProvider instance; |
| |
| /** |
| * Default constructor. |
| */ |
| @SuppressWarnings("deprecation") |
| public CDOProvider() { |
| super(NAME); |
| instance = this; |
| CDOUtil.setLegacyModeDefault(true); |
| } |
| |
| /** |
| * Get the CDO Provider singleton. |
| * |
| * @return the singleton instance or null |
| * @deprecated use ECPUtil.getECPProviderRegistry().getProvider(CDOProvider.NAME) instead |
| */ |
| @Deprecated |
| public static CDOProvider getInstance() { |
| // TODO: what if instance is still null because constructor was never called? |
| return instance; |
| } |
| |
| @Override |
| protected void doDispose() { |
| instance = null; |
| super.doDispose(); |
| } |
| |
| @Override |
| public <T> T getAdapter(Object adaptable, Class<T> adapterType) { |
| final T adapter = ECPProjectAdapterFactory.adapt(adaptable, adapterType); |
| if (adapter != null) { |
| return adapter; |
| } |
| |
| return super.getAdapter(adaptable, adapterType); |
| } |
| |
| @Override |
| public boolean isSlow(Object parent) { |
| if (parent instanceof CDOBranchWrapper) { |
| return true; |
| } |
| |
| if (parent instanceof EObject) { |
| return true; |
| } |
| |
| return super.isSlow(parent); |
| } |
| |
| @Override |
| public void fillChildren(ECPContainer context, Object parent, InternalChildrenList childrenList) { |
| if (parent instanceof InternalProject) { |
| final InternalProject project = (InternalProject) parent; |
| final CDOProjectData projectData = getProjectData(project); |
| childrenList.addChildren(projectData.getRootResource().getContents()); |
| } else if (parent instanceof InternalRepository) { |
| final InternalRepository repository = (InternalRepository) parent; |
| final CDOBranchWrapper wrapper = new CDOBranchWrapper(repository, CDOBranch.MAIN_BRANCH_NAME); |
| childrenList.addChild(wrapper); |
| } else if (parent instanceof CDOBranchWrapper) { |
| final CDOBranchWrapper parentWrapper = (CDOBranchWrapper) parent; |
| final InternalRepository repository = parentWrapper.getRepository(); |
| final String branchPath = parentWrapper.getBranchPath(); |
| |
| CDONet4jSession session = null; |
| |
| try { |
| final CDORepositoryData repositoryData = getRepositoryData(repository); |
| final CDONet4jSessionConfiguration sessionConfiguration = repositoryData.createSessionConfiguration(); |
| session = sessionConfiguration.openNet4jSession(); |
| |
| final CDOBranch branch = session.getBranchManager().getBranch(branchPath); |
| for (final CDOBranch child : branch.getBranches()) { |
| final CDOBranchWrapper wrapper = new CDOBranchWrapper(repository, child.getPathName()); |
| childrenList.addChild(wrapper); |
| } |
| } finally { |
| IOUtil.close(session); |
| } |
| } else { |
| super.fillChildren(context, parent, childrenList); |
| } |
| } |
| |
| /** {@inheritDoc} */ |
| @Override |
| @SuppressWarnings("unchecked") |
| public EList<Object> getElements(InternalProject project) { |
| final CDOProjectData data = (CDOProjectData) project.getProviderSpecificData(); |
| return (EList<Object>) (EList<?>) data.getTransaction().getResourceSet().getResources(); |
| // TODO: implement CDOProvider.addRootElement(project, rootElement) |
| // throw new UnsupportedOperationException(); |
| // return new BasicEList<Object>(); |
| } |
| |
| @Override |
| public void handleLifecycle(ECPContainer context, LifecycleEvent event) { |
| super.handleLifecycle(context, event); |
| |
| if (context instanceof InternalProject) { |
| final InternalProject project = (InternalProject) context; |
| switch (event) { |
| case CREATE: |
| createProject(project); |
| break; |
| |
| case INIT: |
| break; |
| |
| case DISPOSE: |
| disposeProject(project); |
| break; |
| |
| case REMOVE: |
| removeProject(project); |
| break; |
| |
| default: |
| break; |
| } |
| } |
| } |
| |
| /** |
| * Creates a provider specific project for the given internal project. |
| * |
| * @param project the internal project |
| */ |
| protected void createProject(InternalProject project) { |
| final String workspaceID = UUIDGenerator.DEFAULT.generate(); |
| project.getProperties().addProperty(PROP_WORKSPACE_ID, workspaceID); |
| |
| final CDOProjectData projectData = getProjectData(project); |
| projectData.checkoutWorkspace(); |
| final File folder = getProjectFolder(project); |
| PrintStream stream = null; |
| |
| try { |
| stream = new PrintStream(new File(folder, "ecp.properties")); //$NON-NLS-1$ |
| stream.println("project.name = " + project.getName()); //$NON-NLS-1$ |
| |
| } catch (final FileNotFoundException ex) { |
| Activator.log(ex); |
| throw new IllegalStateException("Retrieving project folder failed!", ex); //$NON-NLS-1$ |
| } finally { |
| IOUtil.close(stream); |
| } |
| } |
| |
| /** |
| * {@inheritDoc} |
| * |
| */ |
| @Override |
| public ECPContainer getModelContext(Object element) { |
| if (element instanceof CDOResource) { |
| final Set<InternalProject> openProjects = getOpenProjects(); |
| for (final InternalProject project : openProjects) { |
| final CDOProjectData projectData = (CDOProjectData) project.getProviderSpecificData(); |
| if (projectData.getRootResource().getContents().contains(element)) { |
| return project; |
| } |
| } |
| } |
| return super.getModelContext(element); |
| } |
| |
| /** |
| * Create and store a {@link CDOWorkspaceConfiguration} for the given internal project. |
| * |
| * @param project the internal project. |
| * @return the {@link CDOWorkspaceConfiguration} |
| */ |
| protected CDOWorkspaceConfiguration createWorkspaceConfiguration(InternalProject project) { |
| final File folder = getProjectFolder(project); |
| folder.mkdirs(); |
| |
| final IDBStore localStore = createLocalStore(project, folder); |
| final CDOWorkspaceBase base = createWorkspaceBase(project, folder); |
| final CDOSessionConfigurationFactory remote = getRepositoryData(project.getRepository()); |
| |
| final ECPProperties properties = project.getProperties(); |
| final String branchPath = properties.getValue(PROP_BRANCH_PATH); |
| final String timeStamp = properties.getValue(PROP_TIME_STAMP); |
| |
| final CDOWorkspaceConfiguration config = CDOWorkspaceUtil.createWorkspaceConfiguration(); |
| config.setLocalRepositoryName(folder.getName()); // workspaceID |
| config.setStore(localStore); |
| config.setBase(base); |
| config.setRemote(remote); |
| config.setBranchPath(branchPath == null ? CDOBranch.MAIN_BRANCH_NAME : branchPath); |
| config.setTimeStamp(timeStamp == null ? CDOBranchPoint.UNSPECIFIED_DATE : Long.parseLong(timeStamp)); |
| |
| return config; |
| } |
| |
| /** |
| * Create and store a {@link CDOWorkspaceBase}. |
| * |
| * @param project the internal project. |
| * @param folder the folder to put the {@link CDOWorkspaceBase} into |
| * @return the {@link CDOWorkspaceBase} |
| */ |
| protected CDOWorkspaceBase createWorkspaceBase(InternalProject project, File folder) { |
| final File base = new File(folder, "base"); //$NON-NLS-1$ |
| base.mkdirs(); |
| |
| return CDOWorkspaceUtil.createFolderWorkspaceBase(base); |
| } |
| |
| /** |
| * Create a local {@link IDBStore}. |
| * |
| * @param project the internal project |
| * @param folder the folder to store the data in |
| * @return the {@link IDBStore} |
| */ |
| protected IDBStore createLocalStore(InternalProject project, File folder) { |
| final File local = new File(folder, "local"); //$NON-NLS-1$ |
| local.mkdirs(); |
| |
| final JdbcDataSource dataSource = new JdbcDataSource(); |
| dataSource.setURL("jdbc:h2:" + new File(local, "local").getAbsolutePath()); //$NON-NLS-1$ //$NON-NLS-2$ |
| |
| final IMappingStrategy mappingStrategy = CDODBUtil.createHorizontalMappingStrategy(false, false); |
| final IDBAdapter dbAdapter = new H2Adapter(); |
| final IDBConnectionProvider dbConnectionProvider = DBUtil.createConnectionProvider(dataSource); |
| |
| return CDODBUtil.createStore(mappingStrategy, dbAdapter, dbConnectionProvider); |
| } |
| |
| /** |
| * Dispose the given project and its data. |
| * |
| * @param project the internal project |
| */ |
| protected void disposeProject(InternalProject project) { |
| final CDOProjectData data = (CDOProjectData) project.getProviderSpecificData(); |
| data.dispose(); |
| } |
| |
| /** |
| * Remove the internal project and its configuration data. |
| * |
| * @param project the internal project |
| */ |
| protected void removeProject(InternalProject project) { |
| final File folder = getProjectFolder(project); |
| if (folder.exists()) { |
| if (!folder.delete()) { |
| folder.deleteOnExit(); |
| } |
| } |
| } |
| |
| /** |
| * Retrieve {@link CDORepositoryData} for a given {@link InternalRepository}. |
| * |
| * @param repository the internal repositorz |
| * @return the {@link CDORepositoryData} |
| */ |
| public static CDORepositoryData getRepositoryData(InternalRepository repository) { |
| synchronized (repository) { |
| CDORepositoryData data = (CDORepositoryData) repository.getProviderSpecificData(); |
| if (data == null) { |
| data = new CDORepositoryData(repository); |
| repository.setProviderSpecificData(data); |
| } |
| |
| return data; |
| } |
| } |
| |
| /** |
| * Get {@link CDOProjectData} for the given internal project. |
| * |
| * @param project the internal project |
| * @return the {@link CDOProjectData} |
| */ |
| public static CDOProjectData getProjectData(InternalProject project) { |
| synchronized (project) { |
| CDOProjectData data = (CDOProjectData) project.getProviderSpecificData(); |
| if (data == null) { |
| data = new CDOProjectData(project); |
| project.setProviderSpecificData(data); |
| } |
| |
| return data; |
| } |
| } |
| |
| /** |
| * Get the folder for the configuration data of the internal project. |
| * |
| * @param project the internal project. |
| * @return the {@link File} |
| */ |
| public static File getProjectFolder(InternalProject project) { |
| final String workspaceID = project.getProperties().getValue(PROP_WORKSPACE_ID); |
| return new File(Activator.getInstance().getStateLocation().toFile(), workspaceID); |
| } |
| |
| /** {@inheritDoc} */ |
| @Override |
| public void cloneProject(final InternalProject projectToClone, InternalProject targetProject) { |
| throw new UnsupportedOperationException(); |
| } |
| |
| /** {@inheritDoc} */ |
| @Override |
| public Notifier getRoot(InternalProject project) { |
| final CDOProjectData data = (CDOProjectData) project.getProviderSpecificData(); |
| if (data != null) { |
| return data.getRootResource(); |
| } |
| return null; |
| } |
| |
| /** |
| * {@inheritDoc} |
| * |
| * @see org.eclipse.emf.ecp.spi.core.InternalProvider#isThreadSafe() |
| */ |
| @Override |
| public boolean isThreadSafe() { |
| return true; |
| } |
| |
| /** |
| * {@inheritDoc} |
| * |
| * @see org.eclipse.emf.ecp.spi.core.DefaultProvider#doDelete(org.eclipse.emf.ecp.spi.core.InternalProject, |
| * java.util.Collection) |
| */ |
| @Override |
| public void doDelete(InternalProject project, Collection<Object> objects) { |
| final CDOResource cdoResource = getProjectData(project).getRootResource(); |
| cdoResource.getContents().removeAll(objects); |
| |
| } |
| } |