/*
* Copyright (c) 2011-2013 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 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
*/
package org.eclipse.emf.cdo.tests.offline;
import org.eclipse.emf.cdo.CDOObject;
import org.eclipse.emf.cdo.common.CDOCommonRepository.IDGenerationLocation;
import org.eclipse.emf.cdo.common.CDOCommonSession.Options.PassiveUpdateMode;
import org.eclipse.emf.cdo.common.commit.CDOCommitInfo;
import org.eclipse.emf.cdo.common.commit.CDOCommitInfoHandler;
import org.eclipse.emf.cdo.common.id.CDOID;
import org.eclipse.emf.cdo.common.lob.CDOBlob;
import org.eclipse.emf.cdo.common.lob.CDOClob;
import org.eclipse.emf.cdo.common.revision.delta.CDOListFeatureDelta;
import org.eclipse.emf.cdo.common.revision.delta.CDORevisionDelta;
import org.eclipse.emf.cdo.eresource.CDOResource;
import org.eclipse.emf.cdo.internal.server.syncing.OfflineClone;
import org.eclipse.emf.cdo.session.CDOSession;
import org.eclipse.emf.cdo.session.CDOSessionInvalidationEvent;
import org.eclipse.emf.cdo.spi.server.InternalRepository;
import org.eclipse.emf.cdo.spi.server.InternalSynchronizableRepository;
import org.eclipse.emf.cdo.tests.AbstractSyncingTest;
import org.eclipse.emf.cdo.tests.bundle.OM;
import org.eclipse.emf.cdo.tests.model1.Company;
import org.eclipse.emf.cdo.tests.model3.File;
import org.eclipse.emf.cdo.tests.model3.Image;
import org.eclipse.emf.cdo.tests.util.TestListener;
import org.eclipse.emf.cdo.transaction.CDOTransaction;
import org.eclipse.emf.cdo.view.CDOViewInvalidationEvent;
import org.eclipse.net4j.util.event.IEvent;
import org.eclipse.net4j.util.io.IOUtil;
import org.eclipse.net4j.util.om.monitor.NotifyingMonitor.ProgressEvent;
import org.eclipse.emf.spi.cdo.DefaultCDOMerger;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
/**
* @author Eike Stepper
*/
public class OfflineTest extends AbstractSyncingTest
{
public void testMasterCommits_ArrivalInClone() throws Exception
{
CDOSession session = openSession("master");
CDOTransaction transaction = session.openTransaction();
CDOResource resource = transaction.createResource(getResourcePath("/my/resource"));
Company company = getModel1Factory().createCompany();
company.setName("Test");
// 2 * Root resource + folder + resource + company
int expectedRevisions = 2 + 1 + 1 + 1;
resource.getContents().add(company);
long timeStamp = transaction.commit().getTimeStamp();
checkRevisions(getRepository(), timeStamp, expectedRevisions);
for (int i = 0; i < 10; i++)
{
company.setName("Test" + i);
timeStamp = transaction.commit().getTimeStamp();
expectedRevisions += 1; // Changed company
checkRevisions(getRepository(), timeStamp, expectedRevisions);
}
for (int i = 0; i < 10; i++)
{
company.getCategories().add(getModel1Factory().createCategory());
timeStamp = transaction.commit().getTimeStamp();
expectedRevisions += 2; // Changed company + new category
checkRevisions(getRepository(), timeStamp, expectedRevisions);
}
for (int i = 0; i < 10; i++)
{
company.getCategories().remove(0);
timeStamp = transaction.commit().getTimeStamp();
expectedRevisions += 2; // Changed company + detached category
checkRevisions(getRepository(), timeStamp, expectedRevisions);
}
session.close();
}
protected void masterCommits_NotificationsFromClone() throws Exception
{
CDOSession masterSession = openSession("master");
CDOTransaction transaction = masterSession.openTransaction();
CDOResource resource = transaction.createResource(getResourcePath("/my/resource"));
TestListener listener = new TestListener();
CDOSession cloneSession = openSession();
cloneSession.addListener(listener);
Company company = getModel1Factory().createCompany();
company.setName("Test");
resource.getContents().add(company);
long timeStamp = transaction.commit().getTimeStamp();
assertEquals(true, cloneSession.waitForUpdate(timeStamp, DEFAULT_TIMEOUT));
checkEvent(listener, 1, 3, 1, 0);
for (int i = 0; i < 10; i++)
{
company.setName("Test" + i);
timeStamp = transaction.commit().getTimeStamp();
assertEquals(true, cloneSession.waitForUpdate(timeStamp, DEFAULT_TIMEOUT));
checkEvent(listener, 0, 0, 1, 0);
}
for (int i = 0; i < 10; i++)
{
company.getCategories().add(getModel1Factory().createCategory());
timeStamp = transaction.commit().getTimeStamp();
assertEquals(true, cloneSession.waitForUpdate(timeStamp, DEFAULT_TIMEOUT));
checkEvent(listener, 0, 1, 1, 0);
}
for (int i = 0; i < 10; i++)
{
company.getCategories().remove(0);
timeStamp = transaction.commit().getTimeStamp();
assertEquals(true, cloneSession.waitForUpdate(timeStamp, DEFAULT_TIMEOUT));
checkEvent(listener, 0, 0, 1, 1);
}
cloneSession.close();
masterSession.close();
}
public void testClientCommits() throws Exception
{
InternalRepository clone = getRepository();
InternalRepository master = getRepository("master");
TestListener listener = new TestListener();
CDOSession masterSession = openSession(master.getName());
masterSession.addListener(listener);
Company company = getModel1Factory().createCompany();
company.setName("Test");
CDOSession cloneSession = openSession();
waitForOnline(cloneSession.getRepositoryInfo());
CDOTransaction transaction = cloneSession.openTransaction();
CDOResource resource = transaction.createResource(getResourcePath("/my/resource"));
resource.getContents().add(company);
transaction.commit();
IEvent[] events = listener.getEvents();
assertEquals(1, events.length);
checkRevision(company, master, "master");
checkRevision(company, clone, "clone");
}
public void testDisconnectAndSyncAddition() throws Exception
{
TestListener listener = new TestListener();
InternalRepository clone = getRepository();
waitForOnline(clone);
{
getOfflineConfig().stopMasterTransport();
waitForOffline(clone);
CDOSession masterSession = openSession("master");
CDOTransaction masterTransaction = masterSession.openTransaction();
CDOResource masterResource = masterTransaction.createResource(getResourcePath("/master/resource"));
masterResource.getContents().add(getModel1Factory().createCompany());
masterTransaction.commit();
masterResource.getContents().add(getModel1Factory().createCompany());
masterTransaction.commit();
masterTransaction.close();
masterSession.addListener(listener);
getOfflineConfig().startMasterTransport();
waitForOnline(clone);
}
Company company = getModel1Factory().createCompany();
company.setName("Test");
CDOSession session = openSession();
CDOTransaction transaction = session.openTransaction();
CDOResource resource = transaction.createResource(getResourcePath("/my/resource"));
resource.getContents().add(company);
transaction.commit();
sleep(1000);
IEvent[] events = listener.getEvents();
assertEquals(1, events.length);
}
public void testDisconnectAndSyncChange() throws Exception
{
InternalRepository clone = getRepository();
waitForOnline(clone);
{
getOfflineConfig().stopMasterTransport();
waitForOffline(clone);
CDOSession masterSession = openSession("master");
CDOTransaction masterTransaction = masterSession.openTransaction();
CDOResource masterResource = masterTransaction.createResource(getResourcePath("/master/resource"));
Company comp = getModel1Factory().createCompany();
masterResource.getContents().add(comp);
masterTransaction.commit();
comp.setName("MODIFICATION");
masterTransaction.commit();
masterTransaction.close();
getOfflineConfig().startMasterTransport();
waitForOnline(clone);
}
CDOSession session = openSession();
CDOTransaction transaction = session.openTransaction();
CDOResource resource = transaction.getResource(getResourcePath("/master/resource"));
Company company = (Company)resource.getContents().get(0);
assertEquals("MODIFICATION", company.getName());
}
public void testDisconnectAndSyncRemoval() throws Exception
{
InternalRepository clone = getRepository();
waitForOnline(clone);
{
getOfflineConfig().stopMasterTransport();
waitForOffline(clone);
CDOSession masterSession = openSession("master");
CDOTransaction masterTransaction = masterSession.openTransaction();
CDOResource masterResource = masterTransaction.createResource(getResourcePath("/master/resource"));
Company comp = getModel1Factory().createCompany();
masterResource.getContents().add(comp);
masterTransaction.commit();
masterResource.getContents().remove(comp);
masterTransaction.commit();
masterTransaction.close();
getOfflineConfig().startMasterTransport();
waitForOnline(clone);
}
CDOSession session = openSession();
CDOTransaction transaction = session.openTransaction();
CDOResource resource = transaction.getResource(getResourcePath("/master/resource"));
assertEquals(0, resource.getContents().size());
}
public void testDisconnectAndCommit() throws Exception
{
OfflineClone clone = (OfflineClone)getRepository();
waitForOnline(clone);
getOfflineConfig().stopMasterTransport();
waitForOffline(clone);
Company company = getModel1Factory().createCompany();
company.setName("Test");
CDOSession session = openSession();
CDOTransaction transaction = session.openTransaction();
CDOResource resource = transaction.createResource(getResourcePath("/my/resource"));
resource.getContents().add(company);
CDOCommitInfo commitInfo = transaction.commit();
assertEquals(true, commitInfo.getBranch().isLocal());
assertEquals(true, transaction.getBranch().isLocal());
}
public void testDisconnectAndCommitAndMerge() throws Exception
{
OfflineClone clone = (OfflineClone)getRepository();
waitForOnline(clone);
CDOSession session = openSession();
CDOTransaction transaction = session.openTransaction();
CDOResource resource = transaction.createResource(getResourcePath("/my/resource"));
resource.getContents().add(getModel1Factory().createCompany());
transaction.commit();
getOfflineConfig().stopMasterTransport();
waitForOffline(clone);
resource.getContents().add(getModel1Factory().createCompany());
CDOCommitInfo commitInfo = transaction.commit();
getOfflineConfig().startMasterTransport();
waitForOnline(clone);
DefaultCDOMerger.PerFeature.ManyValued merger = new DefaultCDOMerger.PerFeature.ManyValued();
transaction.setBranch(session.getBranchManager().getMainBranch());
transaction.merge(commitInfo, merger);
assertEquals(1, transaction.getNewObjects().size());
CDOObject offlineCompany = transaction.getNewObjects().values().iterator().next();
if (getRepositoryConfig().getIDGenerationLocation() != IDGenerationLocation.CLIENT)
{
assertEquals(CDOID.Type.TEMP_OBJECT, offlineCompany.cdoID().getType());
}
commitInfo = transaction.commit();
assertEquals(CDOID.Type.OBJECT, offlineCompany.cdoID().getType());
}
/**
* @since 4.0
*/
public void _testDisconnectAndCommitAndMergeWithNewPackages() throws Exception
{
OfflineClone clone = (OfflineClone)getRepository();
waitForOnline(clone);
getOfflineConfig().stopMasterTransport();
waitForOffline(clone);
Company company = getModel1Factory().createCompany();
company.setName("Test");
CDOSession session = openSession();
CDOTransaction transaction = session.openTransaction();
CDOResource resource = transaction.createResource(getResourcePath("/my/resource"));
resource.getContents().add(company);
CDOCommitInfo commitInfo = transaction.commit();
getOfflineConfig().startMasterTransport();
waitForOnline(clone);
transaction.setBranch(session.getBranchManager().getMainBranch());
transaction.merge(commitInfo, new DefaultCDOMerger.PerFeature.ManyValued());
transaction.commit();
}
public void testManyCommitInfos_Initial() throws Exception
{
OfflineClone clone = (OfflineClone)getRepository();
waitForOnline(clone);
getOfflineConfig().stopMasterTransport();
waitForOffline(clone);
CDOSession masterSession = openSession("master");
CDOTransaction transaction = masterSession.openTransaction();
CDOResource resource = transaction.createResource(getResourcePath("/my/resource"));
for (int i = 0; i < 10; i++)
{
Company company = getModel1Factory().createCompany();
company.setName("Company" + i);
resource.getContents().add(company);
}
transaction.setCommitComment("Creation");
long timeStamp = transaction.commit().getTimeStamp();
msg(timeStamp);
for (int k = 0; k < 10; k++)
{
sleep(SLEEP_MILLIS);
for (int i = 0; i < 10; i++)
{
Company company = (Company)resource.getContents().get(i);
company.setName("Company" + i + "_" + transaction.getBranch().getID() + "_" + k);
}
transaction.setCommitComment("Modification");
timeStamp = transaction.commit().getTimeStamp();
msg(timeStamp);
}
masterSession.close();
getOfflineConfig().startMasterTransport();
waitForOnline(clone);
final List<CDOCommitInfo> result = new ArrayList<CDOCommitInfo>();
CDOSession session = openSession();
session.getCommitInfoManager().getCommitInfos(null, 0L, 0L, new CDOCommitInfoHandler()
{
public void handleCommitInfo(CDOCommitInfo commitInfo)
{
result.add(commitInfo);
commitInfo.getNewPackageUnits();
}
});
for (CDOCommitInfo commitInfo : result)
{
System.out.println("-----> " + commitInfo);
}
assertEquals(12, result.size());
}
/**
* See bug 364548.
*/
public void testEmptyCommit() throws Exception
{
InternalRepository master = getRepository("master");
TestListener listener = new TestListener();
CDOSession masterSession = openSession(master.getName());
masterSession.addListener(listener);
CDOSession cloneSession = openSession();
waitForOnline(cloneSession.getRepositoryInfo());
CDOTransaction transaction = cloneSession.openTransaction();
transaction.commit();
}
public void _testDisconnectAndSyncBLOB() throws Exception
{
TestListener listener = new TestListener();
InternalRepository clone = getRepository();
waitForOnline(clone);
{
getOfflineConfig().stopMasterTransport();
waitForOffline(clone);
CDOSession masterSession = openSession("master");
CDOTransaction masterTransaction = masterSession.openTransaction();
CDOResource masterResource = masterTransaction.createResource(getResourcePath("/master/resource"));
InputStream inputStream = null;
try
{
inputStream = OM.BUNDLE.getInputStream("uml2/Ecore.uml");
CDOBlob blob = new CDOBlob(inputStream);
Image image = getModel3Factory().createImage();
image.setWidth(320);
image.setHeight(200);
image.setData(blob);
masterResource.getContents().add(image);
}
finally
{
IOUtil.close(inputStream);
}
masterTransaction.commit();
masterTransaction.close();
masterSession.addListener(listener);
getOfflineConfig().startMasterTransport();
waitForOnline(clone);
}
CDOSession session = openSession();
CDOTransaction transaction = session.openTransaction();
CDOResource resource = transaction.getResource(getResourcePath("/master/resource"));
Image image = (Image)resource.getContents().get(0);
InputStream fromDisk = null;
try
{
fromDisk = OM.BUNDLE.getInputStream("uml2/Ecore.uml");
IOUtil.equals(fromDisk, image.getData().getContents());
}
finally
{
IOUtil.close(fromDisk);
}
}
public void _testDisconnectAndSyncCLOB() throws Exception
{
TestListener listener = new TestListener();
InternalRepository clone = getRepository();
waitForOnline(clone);
{
getOfflineConfig().stopMasterTransport();
waitForOffline(clone);
CDOSession masterSession = openSession("master");
CDOTransaction masterTransaction = masterSession.openTransaction();
CDOResource masterResource = masterTransaction.createResource(getResourcePath("/master/resource"));
InputStream inputStream = null;
try
{
inputStream = OM.BUNDLE.getInputStream("uml2/Ecore.uml");
CDOClob clob = new CDOClob(new InputStreamReader(inputStream));
File file = getModel3Factory().createFile();
file.setName("Ecore.uml");
file.setData(clob);
masterResource.getContents().add(file);
}
finally
{
IOUtil.close(inputStream);
}
try
{
inputStream = OM.BUNDLE.getInputStream("uml2/Ecore.uml");
CDOClob clob = new CDOClob(new InputStreamReader(inputStream));
File file = getModel3Factory().createFile();
file.setName("plugin.properties");
file.setData(clob);
masterResource.getContents().add(file);
}
finally
{
IOUtil.close(inputStream);
}
masterTransaction.commit();
masterTransaction.close();
masterSession.addListener(listener);
getOfflineConfig().startMasterTransport();
waitForOnline(clone);
}
CDOSession session = openSession();
CDOTransaction transaction = session.openTransaction();
CDOResource resource = transaction.getResource(getResourcePath("/master/resource"));
File file = (File)resource.getContents().get(0);
InputStream fromDisk = null;
try
{
fromDisk = OM.BUNDLE.getInputStream("uml2/Ecore.uml");
IOUtil.equals(new InputStreamReader(fromDisk), file.getData().getContents());
}
finally
{
IOUtil.close(fromDisk);
}
}
public void _testMasterCommits_NotificationsFromClone() throws Exception
{
masterCommits_NotificationsFromClone();
}
/**
* @since 4.0
*/
public void _testNotification() throws Exception
{
InternalRepository clone = getRepository();
waitForOnline(clone);
CDOSession session = openSession();
CDOTransaction transaction = session.openTransaction();
CDOResource resource = transaction.createResource(getResourcePath("/my/resource"));
resource.getContents().add(getModel1Factory().createCompany());
transaction.setCommitComment("resource with one company created on clone");
transaction.commit();
getOfflineConfig().stopMasterTransport();
waitForOffline(clone);
TestListener sessionListener = new TestListener();
session.addListener(sessionListener);
TestListener transactionListener = new TestListener();
transaction.addListener(transactionListener);
{
CDOSession masterSession = openSession("master");
CDOTransaction masterTransaction = masterSession.openTransaction();
CDOResource masterResource = masterTransaction.getResource(getResourcePath("/my/resource"));
masterResource.getContents().add(getModel1Factory().createCompany());
masterTransaction.setCommitComment("one company added on master");
masterTransaction.commit();
masterResource.getContents().add(getModel1Factory().createCompany());
masterTransaction.setCommitComment("one company added on master");
masterTransaction.commit();
masterTransaction.close();
}
getOfflineConfig().startMasterTransport();
waitForOnline(clone);
sleep(1000);
IEvent[] sessionEvents = sessionListener.getEvents();
assertEquals(4, sessionEvents.length); // 3x repo state change + 1x invalidation
int count = 0;
for (IEvent sessionEvent : sessionEvents)
{
if (sessionEvent instanceof CDOSessionInvalidationEvent)
{
CDOSessionInvalidationEvent sessionInvalidationEvent = (CDOSessionInvalidationEvent)sessionEvent;
assertEquals(2, sessionInvalidationEvent.getNewObjects().size());
assertEquals(1, sessionInvalidationEvent.getChangedObjects().size());
assertEquals(0, sessionInvalidationEvent.getDetachedObjects().size());
++count;
}
}
assertEquals(1, count);
IEvent[] transactionEvents = transactionListener.getEvents();
assertEquals(2, transactionEvents.length); // 1x invalidation + 1x adapters notified
CDOViewInvalidationEvent viewInvalidationEvent = (CDOViewInvalidationEvent)transactionEvents[0];
assertEquals(1, viewInvalidationEvent.getDirtyObjects().size());
assertEquals(1, viewInvalidationEvent.getRevisionDeltas().size());
assertEquals(0, viewInvalidationEvent.getDetachedObjects().size());
CDORevisionDelta delta = viewInvalidationEvent.getRevisionDeltas().get(resource);
assertEquals(null, delta);
assertEquals(true, viewInvalidationEvent.getRevisionDeltas().containsKey(resource));
}
/**
* @since 4.0
*/
public void testNotificationAllDeltas() throws Exception
{
InternalRepository clone = getRepository();
waitForOnline(clone);
CDOSession session = openSession(); // Session2 [repo1]
session.options().setPassiveUpdateMode(PassiveUpdateMode.ADDITIONS);
CDOTransaction transaction = session.openTransaction();
CDOResource resource = transaction.createResource(getResourcePath("/my/resource"));
resource.getContents().add(getModel1Factory().createCompany());
transaction.setCommitComment("resource with one company created on clone");
transaction.commit();
TestListener sessionListener = new TestListener();
session.addListener(sessionListener);
getOfflineConfig().stopMasterTransport();
waitForOffline(clone);
TestListener transactionListener = new TestListener();
transaction.addListener(transactionListener);
{
CDOSession masterSession = openSession("master"); // Session3 [master]
CDOTransaction masterTransaction = masterSession.openTransaction();
CDOResource masterResource = masterTransaction.getResource(getResourcePath("/my/resource"));
masterResource.getContents().add(getModel1Factory().createCompany());
masterTransaction.setCommitComment("one company added on master");
masterTransaction.commit();
masterResource.getContents().add(getModel1Factory().createCompany());
masterTransaction.setCommitComment("one company added on master");
masterTransaction.commit();
masterTransaction.close();
}
getOfflineConfig().startMasterTransport();
waitForOnline(clone);
sleep(1000);
IEvent[] sessionEvents = sessionListener.getEvents();
assertEquals(4, sessionEvents.length); // 3x repo state change + 1x invalidation
int count = 0;
for (IEvent sessionEvent : sessionEvents)
{
if (sessionEvent instanceof CDOSessionInvalidationEvent)
{
CDOSessionInvalidationEvent sessionInvalidationEvent = (CDOSessionInvalidationEvent)sessionEvent;
assertEquals(2, sessionInvalidationEvent.getNewObjects().size());
assertEquals(1, sessionInvalidationEvent.getChangedObjects().size());
assertEquals(0, sessionInvalidationEvent.getDetachedObjects().size());
++count;
}
}
assertEquals(1, count);
IEvent[] transactionEvents = transactionListener.getEvents();
assertEquals(2, transactionEvents.length); // 1x invalidation + 1x adapters notified
CDOViewInvalidationEvent viewInvalidationEvent = (CDOViewInvalidationEvent)transactionEvents[0];
assertEquals(1, viewInvalidationEvent.getDirtyObjects().size());
assertEquals(1, viewInvalidationEvent.getRevisionDeltas().size());
assertEquals(0, viewInvalidationEvent.getDetachedObjects().size());
CDORevisionDelta delta = viewInvalidationEvent.getRevisionDeltas().get(resource);
assertEquals(1, delta.getFeatureDeltas().size());
CDOListFeatureDelta listDelta = (CDOListFeatureDelta)delta.getFeatureDeltas().get(0);
assertEquals(2, listDelta.getListChanges().size());
}
/**
* @since 4.0
*/
public void testSyncProgressEvents() throws Exception
{
InternalSynchronizableRepository clone = getRepository();
waitForOnline(clone);
CDOSession session = openSession();
CDOTransaction transaction = session.openTransaction();
CDOResource resource = transaction.createResource(getResourcePath("/my/resource"));
resource.getContents().add(getModel1Factory().createCompany());
transaction.setCommitComment("resource with one company created on clone");
transaction.commit();
getOfflineConfig().stopMasterTransport();
waitForOffline(clone);
{
CDOSession masterSession = openSession("master");
CDOTransaction masterTransaction = masterSession.openTransaction();
CDOResource masterResource = masterTransaction.getResource(getResourcePath("/my/resource"));
for (int i = 0; i < 100; i++)
{
masterResource.getContents().add(getModel1Factory().createCompany());
masterTransaction.setCommitComment("one company added on master");
masterTransaction.commit();
}
masterTransaction.close();
}
final int[] workPercent = { 0 };
TestListener listener = new TestListener()
{
@Override
public void notifyEvent(IEvent event)
{
super.notifyEvent(event);
if (event instanceof ProgressEvent)
{
ProgressEvent e = (ProgressEvent)event;
workPercent[0] = (int)e.getWorkPercent();
msg(e.getTask() + ": " + workPercent[0] + " percent");
}
}
};
clone.getSynchronizer().addListener(listener);
getOfflineConfig().startMasterTransport();
waitForOnline(clone);
assertEquals(100, workPercent[0]);
}
}