blob: 752d08b2114a3a44a066e1b10de4488a7f347e3c [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2011-2012 EclipseSource Muenchen GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Eugen Neufeld - initial API and implementation
*
*******************************************************************************/
package org.eclipse.emf.ecp.emfstore.core.internal;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.common.notify.Notifier;
import org.eclipse.emf.common.notify.impl.AdapterImpl;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.emf.ecp.core.ECPProject;
import org.eclipse.emf.ecp.core.ECPRepository;
import org.eclipse.emf.ecp.core.util.ECPContainer;
import org.eclipse.emf.ecp.core.util.ECPModelContextProvider;
import org.eclipse.emf.ecp.core.util.ECPUtil;
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.emf.edit.domain.EditingDomain;
import org.eclipse.emf.edit.provider.ItemPropertyDescriptor;
import org.eclipse.emf.emfstore.client.ESLocalProject;
import org.eclipse.emf.emfstore.client.ESRemoteProject;
import org.eclipse.emf.emfstore.client.ESServer;
import org.eclipse.emf.emfstore.client.ESWorkspace;
import org.eclipse.emf.emfstore.client.ESWorkspaceProvider;
import org.eclipse.emf.emfstore.internal.client.model.Configuration;
import org.eclipse.emf.emfstore.internal.client.model.ESWorkspaceProviderImpl;
import org.eclipse.emf.emfstore.internal.client.model.ProjectSpace;
import org.eclipse.emf.emfstore.internal.client.model.ServerInfo;
import org.eclipse.emf.emfstore.internal.client.model.impl.ProjectSpaceImpl;
import org.eclipse.emf.emfstore.internal.client.model.impl.api.ESLocalProjectImpl;
import org.eclipse.emf.emfstore.internal.client.model.impl.api.ESWorkspaceImpl;
import org.eclipse.emf.emfstore.internal.client.model.util.EMFStoreClientUtil;
import org.eclipse.emf.emfstore.internal.client.model.util.EMFStoreCommand;
import org.eclipse.emf.emfstore.internal.client.observers.OperationObserver;
import org.eclipse.emf.emfstore.internal.common.model.IdEObjectCollection;
import org.eclipse.emf.emfstore.internal.common.model.Project;
import org.eclipse.emf.emfstore.internal.common.model.impl.ProjectImpl;
import org.eclipse.emf.emfstore.internal.common.model.util.IdEObjectCollectionChangeObserver;
import org.eclipse.emf.emfstore.internal.server.model.versioning.operations.AbstractOperation;
import org.eclipse.emf.emfstore.server.exceptions.ESException;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.preferences.IPreferencesService;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
/**
* This is the EMFStore Provider for ECP.
*
* @author Eugen Neufeld
*/
public final class EMFStoreProvider extends DefaultProvider {
/**
* This is the name of the EMFStore Provider.
*/
public static final String NAME = "org.eclipse.emf.ecp.emfstore.provider";
/**
* EMFStore Provider Singleton.
*/
public static EMFStoreProvider INSTANCE;
/**
* Property constant for Repository URL.
*/
public static final String PROP_REPOSITORY_URL = "repositoryUrl";
/**
* Property constant for Repository Port.
*/
public static final String PROP_PORT = "port";
/**
* Property constant for Repository Certificate.
*/
public static final String PROP_CERTIFICATE = "certificate";
/**
* Property constant for ProjectSpaceID.
*/
public static final String PROP_PROJECTSPACEID = "projectSpaceID";
/**
* Property constant for ServerInfoID.
*/
public static final String PROP_SERVERINFOID = "serverInfoID";
private AdapterImpl adapter;
public EMFStoreProvider() {
super(NAME);
configureEMFStore();
INSTANCE = this;
}
/**
*
*/
private void configureEMFStore() {
Configuration.getClientBehavior().setAutoSave(false);
}
/** {@inheritDoc} */
@Override
public EditingDomain createEditingDomain(final InternalProject project) {
EditingDomain domain = ((ESWorkspaceImpl) ESWorkspaceProvider.INSTANCE.getWorkspace()).toInternalAPI()
.getEditingDomain();
return domain;
}
/** {@inheritDoc} */
@Override
public void fillChildren(ECPContainer context, Object parent, InternalChildrenList childrenList) {
if (parent instanceof InternalProject) {
ESLocalProject projectSpace = getProjectSpace((InternalProject) parent);
if (projectSpace != null) {
childrenList.addChildren(projectSpace.getModelElements());
}
} else if (parent instanceof InternalRepository) {
ESServer serverInfo = getServerInfo((InternalRepository) parent);
if (serverInfo.getLastUsersession() != null && serverInfo.getLastUsersession().isLoggedIn()) {
try {
List<ESRemoteProject> projectInfos = serverInfo.getRemoteProjects(serverInfo.getLastUsersession());
for (ESRemoteProject projectInfo : projectInfos) {
childrenList.addChild(new EMFStoreProjectWrapper((InternalRepository) parent, projectInfo));
}
} catch (ESException e) {
Activator.log(e);
}
}
}
super.fillChildren(context, parent, childrenList);
}
@Override
public boolean hasCreateProjectWithoutRepositorySupport() {
return true;
}
/** {@inheritDoc} */
public EList<? extends Object> getElements(InternalProject project) {
return getProjectSpace(project).getModelElements();
}
/** {@inheritDoc} */
@Override
public void handleLifecycle(ECPContainer context, LifecycleEvent event) {
switch (event) {
case INIT:
handleInit(context);
break;
case DISPOSE:
handelDispose(context);
break;
case CREATE:
handleCreate(context);
break;
case REMOVE:
handleRemove(context);
break;
default:
break;
}
String providerClass = getClass().getSimpleName();
String contextClass = context.getClass().getSimpleName();
Activator.log(IStatus.INFO, providerClass + " received " + event + " for " + contextClass + " " + context);
}
/**
* Called to handle the remove operation on an {@link ECPContainer}.
*
* @param context the {@link ECPContainer} to remove
*/
private void handleRemove(ECPContainer context) {
if (context instanceof InternalProject) {
InternalProject project = (InternalProject) context;
ESLocalProject ps = (ESLocalProject) project.getProviderSpecificData();
if (ps != null) {
try {
ps.delete(new NullProgressMonitor());
} catch (ESException ex) {
Activator.log(ex);
} catch (IOException ex) {
Activator.log(ex);
}
project.setProviderSpecificData(null);
}
}
}
/**
* @param context
*/
private void handleCreate(final ECPContainer context) {
if (context instanceof InternalRepository) {
ESServer serverInfo = getServerInfo((InternalRepository) context);
// TODO autologin?
// if (serverInfo.getLastUsersession() != null && !serverInfo.getLastUsersession().isLoggedIn()) {
// try {
// serverInfo.login(serverInfo.getLastUsersession().getUsername(), serverInfo.getLastUsersession()
// .getPassword());
// } catch (AccessControlException ex) {
// Activator.log(ex);
// } catch (ESException ex) {
// Activator.log(ex);
// }
// }
} else if (context instanceof InternalProject) {
getProjectSpace((InternalProject) context);
}
}
/**
* @param context
*/
private void handelDispose(ECPContainer context) {
if (context instanceof InternalProject) {
ESLocalProject projectSpace = getProjectSpace((InternalProject) context);
if (projectSpace != null) {
((ESLocalProjectImpl) projectSpace).toInternalAPI().getProject().eAdapters().remove(adapter);
}
}
}
private boolean isAutosave() {
IPreferencesService service = Platform.getPreferencesService();
return service.getBoolean("org.eclipse.emf.ecp", "AUTOSAVE", false, null);
}
/**
* @param context
*/
private void handleInit(final ECPContainer context) {
if (context instanceof InternalProject) {
ESLocalProject localProject = getProjectSpace((InternalProject) context, true);
if (localProject == null) {
return;
}
ProjectSpace projectSpace = ((ESLocalProjectImpl) localProject).toInternalAPI();
if (isAutosave()) {
// TODO EMFStore how to listen to operations?
projectSpace.getOperationManager().addOperationObserver(new OperationObserver() {
public void operationUndone(AbstractOperation operation) {
doSave((InternalProject) context);
}
public void operationExecuted(AbstractOperation operation) {
doSave((InternalProject) context);
}
});
}
// TODO EMFStore how to add IdEObjectCollection Observer?
projectSpace.getProject().addIdEObjectCollectionChangeObserver(new IdEObjectCollectionChangeObserver() {
// 2
public void notify(Notification notification, IdEObjectCollection collection, EObject modelElement) {
if (modelElement instanceof ProjectImpl) {
ProjectSpaceImpl projectSpace = (ProjectSpaceImpl) modelElement.eContainer();
ECPProject ecpProject = getProject(projectSpace.toAPI());
((InternalProject) context).notifyObjectsChanged(
(Collection) Collections.singleton(ecpProject), true);
} else {
((InternalProject) context).notifyObjectsChanged(
(Collection) Collections.singleton(modelElement), true);
}
}
// 3
public void modelElementRemoved(IdEObjectCollection collection, EObject modelElement) {
if (modelElement.eContainer() == null) {
((InternalProject) context).notifyObjectsChanged(Arrays.asList(context, modelElement), true);
}
}
// 1
public void modelElementAdded(IdEObjectCollection collection, EObject modelElement) {
if (Project.class.isInstance(modelElement.eContainer())) {
((InternalProject) context).notifyObjectsChanged(Arrays.asList(modelElement, context), true);
}
((InternalProject) context).notifyObjectsChanged((Collection) Collections.singleton(modelElement),
true);
}
public void collectionDeleted(IdEObjectCollection collection) {
// project delete
((InternalProject) context).notifyObjectsChanged((Collection) Collections.singleton(context), true);
}
});
}
}
/** {@inheritDoc} */
@Override
public Iterator<EObject> getLinkElements(InternalProject project, EObject modelElement, EReference eReference) {
Collection<EObject> result = new HashSet<EObject>();
// TODO EMFStore does it work with ESLocalProject?
ProjectSpace projectSpace = ((ESLocalProjectImpl) getProjectSpace(project)).toInternalAPI();
ItemPropertyDescriptor.collectReachableObjectsOfType(new HashSet<EObject>(), result, projectSpace.getProject(),
eReference.getEType());
return result.iterator();
}
/**
* @param info
* @param ecpProperties
* @return
*/
private boolean isSameServerInfo(ESServer info, String url, int port, String certificate) {
return info.getURL().equalsIgnoreCase(url) && info.getPort() == port
&& info.getCertificateAlias().equalsIgnoreCase(certificate);
}
/** {@inheritDoc} */
@Override
public void doSave(InternalProject project) {
getProjectSpace(project).save();
}
/** {@inheritDoc} */
@Override
public boolean isDirty(InternalProject project) {
return getProjectSpace(project).hasUnsavedChanges();
}
/** {@inheritDoc} */
public void delete(InternalProject project, final Collection<Object> objects) {
final ProjectSpace projectSpace = ((ESLocalProjectImpl) getProjectSpace(project)).toInternalAPI();
// TODO EMFStore how to delete eObject?
new EMFStoreCommand() {
@Override
protected void doRun() {
for (Object object : objects) {
if (EObject.class.isInstance(object)) {
projectSpace.getProject().deleteModelElement((EObject) object);
}
}
}
}.run(false);
}
/** {@inheritDoc} */
public void cloneProject(final InternalProject projectToClone, InternalProject targetProject) {
// TODO EMFStore how to clone local project?
ProjectSpace toClone = ((ESLocalProjectImpl) getProjectSpace(projectToClone)).toInternalAPI();
ProjectSpace target = ((ESLocalProjectImpl) getProjectSpace(targetProject)).toInternalAPI();
target.setProject(EcoreUtil.copy(toClone.getProject()));
}
/** {@inheritDoc} */
@Override
public boolean modelExists(InternalProject project) {
return getProjectSpace(project, false) != null;
}
/** {@inheritDoc} */
public Notifier getRoot(InternalProject project) {
// TODO EMFStore other way to get root of localproject?
return ((ESLocalProjectImpl) getProjectSpace(project)).toInternalAPI().getProject();
}
@Override
public boolean contains(InternalProject project, Object object) {
if (!EObject.class.isInstance(object)) {
return false;
}
ESLocalProject projectSpace = getProjectSpace(project);
return projectSpace.contains((EObject) object);
}
@Override
public ECPContainer getModelContext(Object element) {
if (element instanceof ECPContainer) {
return (ECPContainer) element;
}
if (element instanceof ECPModelContextProvider) {
return ((ECPModelContextProvider) element).getModelContext(element);
}
if (element instanceof EObject) {
EObject eObject = (EObject) element;
ProjectSpace ps = null;
try {
ps = ESWorkspaceProviderImpl.getProjectSpace(eObject);
} catch (IllegalArgumentException iae) {
return null;
}
if (ps != null) {
ECPContainer context = getModelContextFromAdapter(ps.getProject());
if (context != null) {
return context;
}
}
element = eObject.eResource();
}
if (element instanceof Resource) {
Resource resource = (Resource) element;
ECPContainer context = getModelContextFromAdapter(resource);
if (context != null) {
return context;
}
element = resource.getResourceSet();
}
if (element instanceof ResourceSet) {
ResourceSet resourceSet = (ResourceSet) element;
ECPContainer context = getModelContextFromAdapter(resourceSet);
if (context != null) {
return context;
}
}
return null;
}
/**
* This retrieves the {@link ProjectSpace} from an {@link InternalProject}.
* First it checks whether the {@link InternalProject} has a ProjectSpaceID attached.
* If an ID is attached, a ProjectSpace is searched with this ID.
* If no ID is attached or now ProjectSpace was found a LocalProject is created.
*
* @param internalProject the project to get the ProjectSpace for
* @return the corresponding ProjectSpace
*/
public ESLocalProject getProjectSpace(InternalProject internalProject) {
return getProjectSpace(internalProject, false);
}
private ESLocalProject getProjectSpace(InternalProject internalProject, boolean createNewIfNeeded) {
ESLocalProject projectSpace = (ESLocalProject) internalProject.getProviderSpecificData();
if (projectSpace == null) {
boolean found = false;
List<ESLocalProject> localProjects = ESWorkspaceProvider.INSTANCE.getWorkspace().getLocalProjects();
for (ESLocalProject localProject : localProjects) {
String projectSpaceID = internalProject.getProperties().getValue(EMFStoreProvider.PROP_PROJECTSPACEID);
if (localProject.getLocalProjectId().getId().equals(projectSpaceID)) {
found = true;
projectSpace = localProject;
break;
}
}
if (!found && createNewIfNeeded) {
projectSpace = ESWorkspaceProvider.INSTANCE.getWorkspace()
.createLocalProject(internalProject.getName());
internalProject.getProperties().addProperty(EMFStoreProvider.PROP_PROJECTSPACEID,
projectSpace.getLocalProjectId().getId());
}
internalProject.setProviderSpecificData(projectSpace);
}
return projectSpace;
}
/**
* This retrieves the {@link ServerInfo} from an {@link InternalRepository}.
* First it checks whether the {@link InternalRepository} has a ServerInfoID attached.
* If an ID is attached, a ServerInfo is searched with this ID.
* If no ID is attached or now ServerInfo was found a default ServerInfo is created.
*
* @param internalRepository the repository to get the ServerInfo for
* @return the corresponding ServerInfo
*/
public ESServer getServerInfo(InternalRepository internalRepository) {
ESServer serverInfo = (ESServer) internalRepository.getProviderSpecificData();
if (serverInfo == null) {
ESWorkspace workspace = ESWorkspaceProvider.INSTANCE.getWorkspace();
boolean foundExisting = false;
for (ESServer info : workspace.getServers()) {
if (internalRepository.getProperties().hasProperties()
&& isSameServerInfo(info,
internalRepository.getProperties().getValue(EMFStoreProvider.PROP_REPOSITORY_URL),
Integer.parseInt(internalRepository.getProperties().getValue(EMFStoreProvider.PROP_PORT)),
internalRepository.getProperties().getValue(EMFStoreProvider.PROP_CERTIFICATE))) {
serverInfo = info;
foundExisting = true;
break;
}
}
if (!foundExisting && internalRepository.getProperties().hasProperties()) {
serverInfo = EMFStoreClientUtil.createServerInfo(
internalRepository.getProperties().getValue(EMFStoreProvider.PROP_REPOSITORY_URL),
Integer.parseInt(internalRepository.getProperties().getValue(EMFStoreProvider.PROP_PORT)),
internalRepository.getProperties().getValue(EMFStoreProvider.PROP_CERTIFICATE)).toAPI();
workspace.addServer(serverInfo);
} else if (!foundExisting && !internalRepository.getProperties().hasProperties()) {
serverInfo = EMFStoreClientUtil.giveServerInfo("localhost", 8080).toAPI();
}
internalRepository.setProviderSpecificData(serverInfo);
}
return serverInfo;
}
/**
* This gets the ECPProject based on a ProjectSpace.
*
* @param projectSpace the {@link ProjectSpace} to get the {@link ECPProject} for
* @return the {@link ECPProject} corresponding to this ProjectSpace or null if none found
*/
public ECPProject getProject(ESLocalProject projectSpace) {
for (InternalProject project : getOpenProjects()) {
ESLocalProject localProjectSpace = (ESLocalProject) project.getProviderSpecificData();
if (localProjectSpace.equals(projectSpace)) {
return project;
}
}
return null;
}
/**
* This gets the ECPRepository based on a ServerInfo.
*
* @param serverInfo the {@link ServerInfo} to get the {@link ECPRepository} for
* @return the {@link ECPRepository} corresponding to this ServerInfo or null if none found
*/
public ECPRepository getRepository(ServerInfo serverInfo) {
if (serverInfo != null) {
for (ECPRepository internalRepository : ECPUtil.getECPRepositoryManager().getRepositories()) {
if (internalRepository.getProvider().equals(this)) {
if (serverInfo.equals(((InternalRepository) internalRepository).getProviderSpecificData())) {
return internalRepository;
}
}
}
}
return null;
}
}