| /* |
| * Copyright (c) 2007-2013, 2015, 2016, 2019, 2020 Eike Stepper (Loehne, Germany) 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: |
| * Eike Stepper - initial API and implementation |
| * Simon McDuff - bug 233490 |
| */ |
| package org.eclipse.emf.cdo.internal.server; |
| |
| import org.eclipse.emf.cdo.common.CDOCommonView; |
| import org.eclipse.emf.cdo.common.branch.CDOBranch; |
| import org.eclipse.emf.cdo.common.branch.CDOBranchPoint; |
| import org.eclipse.emf.cdo.common.id.CDOID; |
| import org.eclipse.emf.cdo.common.id.CDOIDUtil; |
| import org.eclipse.emf.cdo.common.protocol.CDOProtocolConstants.UnitOpcode; |
| import org.eclipse.emf.cdo.common.revision.CDORevision; |
| import org.eclipse.emf.cdo.common.revision.CDORevisionHandler; |
| import org.eclipse.emf.cdo.common.revision.CDORevisionManager; |
| import org.eclipse.emf.cdo.common.revision.delta.CDORevisionDelta; |
| import org.eclipse.emf.cdo.server.IUnit; |
| import org.eclipse.emf.cdo.server.IUnitManager; |
| import org.eclipse.emf.cdo.spi.common.branch.CDOBranchUtil; |
| import org.eclipse.emf.cdo.spi.common.branch.InternalCDOBranchManager; |
| import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision; |
| import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevisionManager; |
| import org.eclipse.emf.cdo.spi.server.InternalRepository; |
| import org.eclipse.emf.cdo.spi.server.InternalSession; |
| import org.eclipse.emf.cdo.spi.server.InternalView; |
| |
| import org.eclipse.net4j.util.AdapterUtil; |
| import org.eclipse.net4j.util.ObjectUtil; |
| import org.eclipse.net4j.util.lifecycle.Lifecycle; |
| import org.eclipse.net4j.util.om.monitor.OMMonitor; |
| import org.eclipse.net4j.util.options.IOptionsContainer; |
| import org.eclipse.net4j.util.registry.HashMapRegistry; |
| import org.eclipse.net4j.util.registry.IRegistry; |
| |
| import java.text.MessageFormat; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Set; |
| import java.util.concurrent.ExecutorService; |
| |
| /** |
| * @author Eike Stepper |
| */ |
| public class View extends Lifecycle implements InternalView, CDOCommonView.Options |
| { |
| private InternalSession session; |
| |
| private final int viewID; |
| |
| /** |
| * Needed here so we can compute the hashCode even after session becomes null due to deactivation! |
| */ |
| private final int sessionID; |
| |
| private CDOBranchPoint branchPoint; |
| |
| private CDOBranchPoint normalizedBranchPoint; |
| |
| private String durableLockingID; |
| |
| private final InternalRepository repository; |
| |
| private final Set<CDOID> changeSubscriptionIDs = new HashSet<>(); |
| |
| private final Set<CDOID> openUnitRoots = new HashSet<>(); |
| |
| private boolean lockNotificationsEnabled; |
| |
| private boolean closed; |
| |
| private final IRegistry<String, Object> properties = new HashMapRegistry<String, Object>() |
| { |
| @Override |
| public void setAutoCommit(boolean autoCommit) |
| { |
| throw new UnsupportedOperationException(); |
| } |
| }; |
| |
| /** |
| * @since 2.0 |
| */ |
| public View(InternalSession session, int viewID, CDOBranchPoint branchPoint) |
| { |
| this.session = session; |
| this.viewID = viewID; |
| sessionID = session.getSessionID(); |
| |
| repository = session.getManager().getRepository(); |
| setBranchPoint(branchPoint); |
| } |
| |
| @Override |
| public ExecutorService getExecutorService() |
| { |
| return session.getExecutorService(); |
| } |
| |
| @Override |
| public InternalSession getSession() |
| { |
| return session; |
| } |
| |
| @Override |
| public int getSessionID() |
| { |
| return session.getSessionID(); |
| } |
| |
| @Override |
| public int getViewID() |
| { |
| return viewID; |
| } |
| |
| @Override |
| public CDOBranch getBranch() |
| { |
| return branchPoint.getBranch(); |
| } |
| |
| @Override |
| public long getTimeStamp() |
| { |
| return branchPoint.getTimeStamp(); |
| } |
| |
| @Override |
| public boolean isReadOnly() |
| { |
| return true; |
| } |
| |
| @Override |
| public boolean isHistorical() |
| { |
| return branchPoint.getTimeStamp() != CDOBranchPoint.UNSPECIFIED_DATE; |
| } |
| |
| @Override |
| public boolean isDurableView() |
| { |
| return durableLockingID != null; |
| } |
| |
| @Override |
| public String getDurableLockingID() |
| { |
| return durableLockingID; |
| } |
| |
| /** |
| * @since 2.0 |
| */ |
| @Override |
| public InternalRepository getRepository() |
| { |
| return repository; |
| } |
| |
| @Override |
| public InternalCDORevision getRevision(CDOID id) |
| { |
| CDORevisionManager revisionManager = repository.getRevisionManager(); |
| return (InternalCDORevision)revisionManager.getRevision(id, normalizedBranchPoint, CDORevision.UNCHUNKED, CDORevision.DEPTH_NONE, true); |
| } |
| |
| private List<CDORevision> getRevisions(List<CDOID> ids) |
| { |
| InternalCDORevisionManager revisionManager = repository.getRevisionManager(); |
| return revisionManager.getRevisions(ids, normalizedBranchPoint, CDORevision.UNCHUNKED, CDORevision.DEPTH_NONE, true); |
| } |
| |
| @Override |
| public void changeTarget(CDOBranchPoint branchPoint, List<CDOID> invalidObjects, List<CDORevisionDelta> allChangedObjects, List<CDOID> allDetachedObjects) |
| { |
| List<CDORevision> oldRevisions = getRevisions(invalidObjects); |
| setBranchPoint(branchPoint); |
| List<CDORevision> newRevisions = getRevisions(invalidObjects); |
| |
| Iterator<CDORevision> it = newRevisions.iterator(); |
| for (CDORevision oldRevision : oldRevisions) |
| { |
| CDORevision newRevision = it.next(); |
| if (newRevision == null) |
| { |
| allDetachedObjects.add(oldRevision.getID()); |
| } |
| else if (newRevision != oldRevision) |
| { |
| // Fix for Bug 369646: ensure that revisions are fully loaded |
| repository.ensureChunks((InternalCDORevision)newRevision, CDORevision.UNCHUNKED); |
| repository.ensureChunks((InternalCDORevision)oldRevision, CDORevision.UNCHUNKED); |
| |
| CDORevisionDelta delta = newRevision.compare(oldRevision); |
| allChangedObjects.add(delta); |
| } |
| } |
| } |
| |
| @Override |
| public void setBranchPoint(CDOBranchPoint branchPoint) |
| { |
| checkOpen(); |
| validateTimeStamp(branchPoint.getTimeStamp()); |
| |
| InternalCDOBranchManager branchManager = getSession().getManager().getRepository().getBranchManager(); |
| this.branchPoint = CDOBranchUtil.adjustBranchPoint(branchPoint, branchManager); |
| normalizedBranchPoint = CDOBranchUtil.normalizeBranchPoint(this.branchPoint); |
| } |
| |
| protected void validateTimeStamp(long timeStamp) throws IllegalArgumentException |
| { |
| if (timeStamp != UNSPECIFIED_DATE) |
| { |
| repository.validateTimeStamp(timeStamp); |
| } |
| } |
| |
| @Override |
| public void setDurableLockingID(String durableLockingID) |
| { |
| this.durableLockingID = durableLockingID; |
| } |
| |
| @Override |
| public boolean openUnit(CDOID rootID, UnitOpcode opcode, CDORevisionHandler revisionHandler, OMMonitor monitor) |
| { |
| IUnitManager unitManager = repository.getUnitManager(); |
| IUnit unit = unitManager.getUnit(rootID); |
| |
| switch (opcode) |
| { |
| case CREATE: |
| case CREATE_AND_OPEN: |
| if (unit != null) |
| { |
| return false; |
| } |
| |
| unit = unitManager.createUnit(rootID, this, revisionHandler, monitor); |
| break; |
| |
| case OPEN: |
| case OPEN_DEMAND_CREATE: |
| if (unit == null) |
| { |
| if (opcode.isCreate()) |
| { |
| unit = unitManager.createUnit(rootID, this, revisionHandler, monitor); |
| break; |
| } |
| |
| return false; |
| } |
| |
| unit.open(this, revisionHandler, monitor); |
| break; |
| } |
| |
| if (opcode.isOpen()) |
| { |
| openUnitRoots.add(rootID); |
| } |
| |
| return true; |
| } |
| |
| @Override |
| public void closeUnit(CDOID rootID) |
| { |
| openUnitRoots.remove(rootID); |
| } |
| |
| @Override |
| public boolean isInOpenUnit(CDOID id) |
| { |
| if (openUnitRoots.isEmpty()) |
| { |
| return false; |
| } |
| |
| if (openUnitRoots.contains(id)) |
| { |
| return true; |
| } |
| |
| InternalCDORevision revision = getRevision(id); |
| if (revision != null) |
| { |
| CDOID parentID = revision.getResourceID(); |
| if (parentID == id) |
| { |
| // This must be the root resource; break the recursion. |
| return false; |
| } |
| |
| if (CDOIDUtil.isNull(parentID)) |
| { |
| parentID = (CDOID)revision.getContainerID(); |
| } |
| |
| return isInOpenUnit(parentID); |
| } |
| |
| return false; |
| } |
| |
| /** |
| * @since 2.0 |
| */ |
| @Override |
| public synchronized void subscribe(CDOID id) |
| { |
| checkOpen(); |
| changeSubscriptionIDs.add(id); |
| } |
| |
| /** |
| * @since 2.0 |
| */ |
| @Override |
| public synchronized void unsubscribe(CDOID id) |
| { |
| checkOpen(); |
| changeSubscriptionIDs.remove(id); |
| } |
| |
| /** |
| * @since 2.0 |
| */ |
| @Override |
| public synchronized boolean hasSubscription(CDOID id) |
| { |
| if (isClosed()) |
| { |
| return false; |
| } |
| |
| if (changeSubscriptionIDs.contains(id)) |
| { |
| return true; |
| } |
| |
| return false; |
| } |
| |
| /** |
| * @since 2.0 |
| */ |
| @Override |
| public synchronized void clearChangeSubscription() |
| { |
| checkOpen(); |
| changeSubscriptionIDs.clear(); |
| } |
| |
| @Override |
| @SuppressWarnings({ "unchecked", "rawtypes" }) |
| public Object getAdapter(Class adapter) |
| { |
| return AdapterUtil.adapt(this, adapter, false); |
| } |
| |
| @Override |
| public int hashCode() |
| { |
| return ObjectUtil.hashCode(sessionID, viewID); |
| } |
| |
| @Override |
| public String toString() |
| { |
| int sessionID = session == null ? 0 : session.getSessionID(); |
| return MessageFormat.format("{0}[{1}:{2}]", getClassName(), sessionID, viewID); //$NON-NLS-1$ |
| } |
| |
| protected String getClassName() |
| { |
| return "View"; //$NON-NLS-1$ |
| } |
| |
| /** |
| * @since 2.0 |
| */ |
| @Override |
| public void close() |
| { |
| deactivate(); |
| } |
| |
| @Override |
| protected void doDeactivate() throws Exception |
| { |
| if (!isClosed()) |
| { |
| session.viewClosed(this); |
| } |
| |
| super.doDeactivate(); |
| } |
| |
| /** |
| * @since 2.0 |
| */ |
| @Override |
| public void doClose() |
| { |
| clearChangeSubscription(); |
| openUnitRoots.clear(); |
| closed = true; |
| } |
| |
| /** |
| * @since 2.0 |
| */ |
| @Override |
| public boolean isClosed() |
| { |
| return closed; |
| } |
| |
| protected void checkOpen() |
| { |
| if (isClosed()) |
| { |
| throw new IllegalStateException("View closed"); //$NON-NLS-1$ |
| } |
| } |
| |
| @Override |
| public IOptionsContainer getContainer() |
| { |
| return this; |
| } |
| |
| @Override |
| public Options options() |
| { |
| return this; |
| } |
| |
| @Override |
| public final IRegistry<String, Object> properties() |
| { |
| return properties; |
| } |
| |
| @Override |
| public boolean isLockNotificationEnabled() |
| { |
| return lockNotificationsEnabled; |
| } |
| |
| @Override |
| public void setLockNotificationEnabled(boolean enable) |
| { |
| lockNotificationsEnabled = enable; |
| } |
| } |