/* * Copyright (c) 2010-2012, 2014, 2016 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.server; 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.lob.CDOLobHandler; import org.eclipse.emf.cdo.common.lob.CDOLobUtil; import org.eclipse.emf.cdo.common.model.CDOClassifierRef; import org.eclipse.emf.cdo.common.model.CDOModelUtil; import org.eclipse.emf.cdo.common.model.CDOPackageUnit; import org.eclipse.emf.cdo.common.model.CDOPackageUnit.Type; import org.eclipse.emf.cdo.common.model.EMFUtil; import org.eclipse.emf.cdo.common.model.EMFUtil.ExtResourceSet; import org.eclipse.emf.cdo.common.revision.CDOList; import org.eclipse.emf.cdo.common.revision.CDORevision; import org.eclipse.emf.cdo.common.revision.CDORevisionFactory; import org.eclipse.emf.cdo.common.revision.CDORevisionHandler; import org.eclipse.emf.cdo.common.revision.CDORevisionUtil; import org.eclipse.emf.cdo.server.IStoreAccessor.Raw2; import org.eclipse.emf.cdo.spi.common.branch.InternalCDOBranch; import org.eclipse.emf.cdo.spi.common.branch.InternalCDOBranchManager; import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageInfo; import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageRegistry; import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageRegistry.PackageLoader; import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageUnit; import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision; import org.eclipse.emf.cdo.spi.server.InternalRepository; import org.eclipse.net4j.util.HexUtil; import org.eclipse.net4j.util.WrappedException; import org.eclipse.net4j.util.io.AsyncOutputStream; import org.eclipse.net4j.util.io.AsyncWriter; import org.eclipse.net4j.util.io.IOUtil; import org.eclipse.net4j.util.lifecycle.LifecycleUtil; import org.eclipse.net4j.util.om.monitor.Monitor; import org.eclipse.net4j.util.om.monitor.OMMonitor; import org.eclipse.emf.ecore.EClass; import org.eclipse.emf.ecore.EPackage; import org.eclipse.emf.ecore.EStructuralFeature; import org.xml.sax.Attributes; import org.xml.sax.SAXException; import org.xml.sax.helpers.DefaultHandler; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.Reader; import java.io.Writer; import java.math.BigDecimal; import java.math.BigInteger; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; /** * Imports the complete contents of a {@link IRepository repository} from the output created by a * {@link CDOServerExporter exporter} into a new repository. * <p> * Subtypes specifiy the actual exchange format. * * @author Eike Stepper * @since 4.0 * @apiviz.has {@link CDOServerImporter.Handler} */ public abstract class CDOServerImporter { private InternalRepository repository; public CDOServerImporter(IRepository repository) { this.repository = (InternalRepository)repository; init(); } private void init() { LifecycleUtil.checkInactive(repository); repository.setSkipInitialization(true); repository.getStore().setDropAllDataOnActivate(true); LifecycleUtil.activate(repository); } protected final InternalRepository getRepository() { return repository; } public void importRepository(InputStream in) throws Exception { try { FlushHandler handler = new FlushHandler(); importAll(in, handler); handler.flush(); } finally { StoreThreadLocal.release(); repository = null; } } protected abstract void importAll(InputStream in, Handler handler) throws Exception; /** * Persists the data that has been read by a {@link CDOServerImporter importer} into a new {@link IRepository * repository}. * * @author Eike Stepper */ public static interface Handler extends CDORevisionHandler, CDOLobHandler { public void handleRepository(String name, String uuid, CDOID root, long created, long committed); public InternalCDOPackageUnit handlePackageUnit(String id, Type type, long time, String data); public InternalCDOPackageInfo handlePackageInfo(String packageURI); public InternalCDOPackageRegistry handleModels(); public InternalCDOBranch handleBranch(int id, String name, long time, int parentID); public void handleCommitInfo(long time, long previous, int branch, String user, String comment); public void flush(); } /** * Persists the data that has been read by a {@link CDOServerImporter importer} into a new {@link IRepository * repository}. * * @author Eike Stepper * @since 4.6 */ public static interface Handler2 extends Handler { public void handleCommitInfo(long time, long previous, int branch, String user, String comment, int mergeSourceBranchID, long mergeSourceTime); } /** * @author Eike Stepper */ private final class FlushHandler implements Handler2 { private OMMonitor monitor = new Monitor(); private IStoreAccessor.Raw accessor; private Map<String, String> models = new HashMap<String, String>(); private LinkedList<InternalCDOPackageUnit> packageUnits = new LinkedList<InternalCDOPackageUnit>(); private List<InternalCDOPackageInfo> packageInfos; private InternalCDOPackageRegistry packageRegistry = getRepository().getPackageRegistry(false); public FlushHandler() { } public void handleRepository(String name, String uuid, CDOID root, long created, long committed) { repository.getStore().setCreationTime(created); repository.getStore().setLastCommitTime(committed); InternalCDOBranchManager branchManager = repository.getBranchManager(); repository.initMainBranch(branchManager, created); LifecycleUtil.activate(branchManager); repository.initSystemPackages(true); repository.setRootResourceID(root); // InternalSession session = repository.getSessionManager().openSession(null); // StoreThreadLocal.setSession(session); accessor = (IStoreAccessor.Raw)repository.getStore().getWriter(null); StoreThreadLocal.setAccessor(accessor); } public InternalCDOPackageUnit handlePackageUnit(String id, Type type, long time, String data) { collectPackageInfos(); InternalCDOPackageUnit packageUnit = packageRegistry.createPackageUnit(); packageUnit.setOriginalType(type); packageUnit.setTimeStamp(time); models.put(id, data); packageUnits.add(packageUnit); packageInfos = new ArrayList<InternalCDOPackageInfo>(); return packageUnit; } public InternalCDOPackageInfo handlePackageInfo(String packageURI) { InternalCDOPackageInfo packageInfo = (InternalCDOPackageInfo)CDOModelUtil.createPackageInfo(); packageInfo.setPackageURI(packageURI); packageInfos.add(packageInfo); return packageInfo; } public InternalCDOPackageRegistry handleModels() { collectPackageInfos(); InternalCDOPackageUnit[] array = packageUnits.toArray(new InternalCDOPackageUnit[packageUnits.size()]); packageUnits = null; final ExtResourceSet resourceSet = EMFUtil.createExtResourceSet(packageRegistry, false, false); PackageLoader loader = new PackageLoader() { public EPackage[] loadPackages(CDOPackageUnit packageUnit) { String id = packageUnit.getID(); String data = models.get(id); EPackage ePackage = EMFUtil.createEPackage(id, data.getBytes(), false, resourceSet, true); return EMFUtil.getAllPackages(ePackage); } }; packageRegistry.putPackageUnits(array, CDOPackageUnit.State.PROXY); for (InternalCDOPackageUnit packageUnit : array) { packageUnit.load(loader, false); } // Before we resolve, we configure the resourceSet to start delegating, which means // it will consult the packageRegistry for packages it didn't just load -- such as the Ecore // package resourceSet.setDelegating(true); EMFUtil.safeResolveAll(resourceSet); accessor.rawStore(array, monitor); return packageRegistry; } public InternalCDOBranch handleBranch(int id, String name, long time, int parentID) { InternalCDOBranchManager branchManager = repository.getBranchManager(); if (id == CDOBranch.MAIN_BRANCH_ID) { return branchManager.getMainBranch(); } InternalCDOBranch parent = branchManager.getBranch(parentID); return branchManager.createBranch(id, name, parent, time); } public boolean handleRevision(CDORevision revision) { accessor.rawStore((InternalCDORevision)revision, monitor); return true; } public OutputStream handleBlob(final byte[] id, final long size) throws IOException { return new AsyncOutputStream() { @Override protected void asyncWrite(InputStream in) throws IOException { accessor.rawStore(id, size, in); } }; } public Writer handleClob(final byte[] id, final long size) throws IOException { return new AsyncWriter() { @Override protected void asyncWrite(Reader in) throws IOException { accessor.rawStore(id, size, in); } }; } public void handleCommitInfo(long time, long previous, int branchID, String user, String comment) { handleCommitInfo(time, previous, branchID, user, comment, 0, CDOBranchPoint.UNSPECIFIED_DATE); } public void handleCommitInfo(long time, long previous, int branchID, String user, String comment, int mergeSourceBranchID, long mergeSourceTime) { CDOBranch branch = repository.getBranchManager().getBranch(branchID); if (mergeSourceBranchID != 0 && accessor instanceof Raw2) { CDOBranch mergeSourceBranch = repository.getBranchManager().getBranch(mergeSourceBranchID); CDOBranchPoint mergeSource = mergeSourceBranch.getPoint(mergeSourceTime); Raw2 raw2 = (Raw2)accessor; raw2.rawStore(branch, time, previous, user, comment, mergeSource, monitor); } else { accessor.rawStore(branch, time, previous, user, comment, monitor); } } public void flush() { accessor.rawCommit(1.0, monitor); } private void collectPackageInfos() { if (packageInfos != null) { InternalCDOPackageUnit packageUnit = packageUnits.getLast(); packageUnit.setPackageInfos(packageInfos.toArray(new InternalCDOPackageInfo[packageInfos.size()])); packageInfos = null; } } } /** * An {@link CDOServerImporter importer} that reads and interprets XML output created by an * {@link CDOServerExporter.XML XML exporter}. * * @author Eike Stepper */ public static class XML extends CDOServerImporter implements CDOServerExporter.XMLConstants { public XML(IRepository repository) { super(repository); } @Override protected void importAll(InputStream in, final Handler handler) throws Exception { DefaultHandler xmlHandler = new XMLHandler(handler); SAXParserFactory factory = SAXParserFactory.newInstance(); SAXParser saxParser = factory.newSAXParser(); saxParser.parse(in, xmlHandler); } /** * @author Eike Stepper */ private static final class XMLHandler extends DefaultHandler { private Handler handler; private InternalCDOPackageRegistry packageRegistry; private InternalCDOBranch branch; private InternalCDORevision revision; private Character blobChar; private OutputStream blob; private Writer clob; private XMLHandler(Handler handler) { this.handler = handler; } @Override public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { if (REPOSITORY.equals(qName)) { String name = attributes.getValue(REPOSITORY_NAME); String uuid = attributes.getValue(REPOSITORY_UUID); CDOID root = id(attributes.getValue(REPOSITORY_ROOT)); long created = Long.parseLong(attributes.getValue(REPOSITORY_CREATED)); long committed = Long.parseLong(attributes.getValue(REPOSITORY_COMMITTED)); handler.handleRepository(name, uuid, root, created, committed); } else if (PACKAGE_UNIT.equals(qName)) { String id = attributes.getValue(PACKAGE_UNIT_ID); Type type = CDOPackageUnit.Type.valueOf(attributes.getValue(PACKAGE_UNIT_TYPE)); long time = Long.parseLong(attributes.getValue(PACKAGE_UNIT_TIME)); String data = attributes.getValue(PACKAGE_UNIT_DATA); handler.handlePackageUnit(id, type, time, data); } else if (PACKAGE_INFO.equals(qName)) { String packageURI = attributes.getValue(PACKAGE_INFO_URI); handler.handlePackageInfo(packageURI); } else if (BRANCH.equals(qName)) { int id = Integer.parseInt(attributes.getValue(BRANCH_ID)); String name = attributes.getValue(BRANCH_NAME); long time = Long.parseLong(attributes.getValue(BRANCH_TIME)); String parent = attributes.getValue(BRANCH_PARENT); int parentID = parent == null ? 0 : Integer.parseInt(parent); branch = handler.handleBranch(id, name, time, parentID); } else if (REVISION.equals(qName)) { CDOClassifierRef classifierRef = new CDOClassifierRef(attributes.getValue(REVISION_CLASS)); EClass eClass = (EClass)classifierRef.resolve(packageRegistry); revision = (InternalCDORevision)CDORevisionFactory.DEFAULT.createRevision(eClass); revision.setID(id(attributes.getValue(REVISION_ID))); revision.setBranchPoint(branch.getPoint(Long.parseLong(attributes.getValue(REVISION_TIME)))); revision.setVersion(Integer.parseInt(attributes.getValue(REVISION_VERSION))); String revised = attributes.getValue(REVISION_REVISED); if (revised != null) { revision.setRevised(Long.parseLong(revised)); } String resourceID = attributes.getValue(REVISION_RESOURCE); if (resourceID != null) { revision.setResourceID(id(resourceID)); } String containerID = attributes.getValue(REVISION_CONTAINER); if (containerID != null) { revision.setContainerID(id(containerID)); } String featureID = attributes.getValue(REVISION_FEATURE); if (featureID != null) { revision.setContainingFeatureID(Integer.parseInt(featureID)); } } else if (FEATURE.equals(qName)) { String name = attributes.getValue(FEATURE_NAME); Object value = value(attributes); EClass eClass = revision.getEClass(); EStructuralFeature feature = eClass.getEStructuralFeature(name); if (feature == null) { throw new IllegalStateException("Feature " + name + " not found in class " + eClass.getName()); } if (feature.isMany()) { CDOList list = revision.getList(feature); list.add(value); } else { if (value != null) { revision.setValue(feature, value); } } } else if (BLOB.equals(qName)) { try { byte[] id = HexUtil.hexToBytes(attributes.getValue(LOB_ID)); long size = Long.parseLong(attributes.getValue(LOB_SIZE)); blob = handler.handleBlob(id, size); } catch (IOException ex) { throw WrappedException.wrap(ex); } } else if (CLOB.equals(qName)) { try { byte[] id = HexUtil.hexToBytes(attributes.getValue(LOB_ID)); long size = Long.parseLong(attributes.getValue(LOB_SIZE)); clob = handler.handleClob(id, size); } catch (IOException ex) { throw WrappedException.wrap(ex); } } else if (COMMIT.equals(qName)) { long time = Long.parseLong(attributes.getValue(COMMIT_TIME)); String value = attributes.getValue(COMMIT_PREVIOUS); long previous = value == null ? CDOBranchPoint.UNSPECIFIED_DATE : Long.parseLong(value); value = attributes.getValue(COMMIT_BRANCH); int branch = value == null ? CDOBranch.MAIN_BRANCH_ID : Integer.parseInt(value); String user = attributes.getValue(COMMIT_USER); String comment = attributes.getValue(COMMIT_COMMENT); if (handler instanceof Handler2) { Handler2 handler2 = (Handler2)handler; value = attributes.getValue(MERGE_SOURCE_BRANCH); if (value != null) { int mergeSourceBranch = Integer.parseInt(value); long mergeSourceTime = Long.parseLong(attributes.getValue(MERGE_SOURCE_TIME)); handler2.handleCommitInfo(time, previous, branch, user, comment, mergeSourceBranch, mergeSourceTime); return; } } handler.handleCommitInfo(time, previous, branch, user, comment); } } @Override public void characters(char[] ch, int start, int length) throws SAXException { if (blob != null) { try { if (blobChar != null) { char[] firstChars = { blobChar, ch[start] }; blobChar = null; byte[] firstByte = HexUtil.hexToBytes(new String(firstChars)); blob.write(firstByte, 0, 1); ++start; --length; } if ((length & 1) == 1) // odd length? { --length; blobChar = ch[length]; } if (start != 0 || length != ch.length) { char[] tmp = new char[length]; System.arraycopy(ch, start, tmp, 0, length); ch = tmp; } byte[] buf = HexUtil.hexToBytes(new String(ch)); blob.write(buf); } catch (IOException ex) { throw WrappedException.wrap(ex); } } else if (clob != null) { try { clob.write(ch, start, length); } catch (IOException ex) { throw WrappedException.wrap(ex); } } } @Override public void endElement(String uri, String localName, String qName) throws SAXException { if (MODELS.equals(qName)) { packageRegistry = handler.handleModels(); } else if (BRANCH.equals(qName)) { branch = null; } else if (REVISION.equals(qName)) { handler.handleRevision(revision); revision = null; } else if (BLOB.equals(qName)) { IOUtil.close(blob); blob = null; } else if (CLOB.equals(qName)) { IOUtil.close(clob); clob = null; } } protected final CDOID id(String str) { return CDOIDUtil.read(str); } protected Object value(Attributes attributes) { String type = attributes.getValue(FEATURE_TYPE); if (TYPE_BLOB.equals(type)) { byte[] id = HexUtil.hexToBytes(attributes.getValue(FEATURE_ID)); long size = Long.parseLong(attributes.getValue(FEATURE_SIZE)); return CDOLobUtil.createBlob(id, size); } if (TYPE_CLOB.equals(type)) { byte[] id = HexUtil.hexToBytes(attributes.getValue(FEATURE_ID)); long size = Long.parseLong(attributes.getValue(FEATURE_SIZE)); return CDOLobUtil.createClob(id, size); } if (TYPE_FEATURE_MAP.equals(type)) { String innerFeatureName = attributes.getValue(FEATURE_INNER_FEATURE); EStructuralFeature innerFeature = revision.getEClass().getEStructuralFeature(innerFeatureName); String innerType = attributes.getValue(FEATURE_INNER_TYPE); Object innerValue = value(attributes, innerType); return CDORevisionUtil.createFeatureMapEntry(innerFeature, innerValue); } return value(attributes, type); } protected Object value(Attributes attributes, String type) { String str = attributes.getValue(FEATURE_VALUE); if (str == null) { return null; } if (type == null || String.class.getSimpleName().equals(type)) { return str; } if (Object.class.getSimpleName().equals(type)) { return id(str); } if (Boolean.class.getSimpleName().equals(type)) { return Boolean.valueOf(str); } if (Character.class.getSimpleName().equals(type)) { return str.charAt(0); } if (Byte.class.getSimpleName().equals(type)) { return Byte.valueOf(str); } if (Short.class.getSimpleName().equals(type)) { return Short.valueOf(str); } if (Integer.class.getSimpleName().equals(type)) { return Integer.valueOf(str); } if (Long.class.getSimpleName().equals(type)) { return Long.valueOf(str); } if (Float.class.getSimpleName().equals(type)) { return Float.valueOf(str); } if (Double.class.getSimpleName().equals(type)) { return Double.valueOf(str); } if (Date.class.getSimpleName().equals(type)) { return new Date(Long.valueOf(str)); } if (BigDecimal.class.getSimpleName().equals(type)) { return new BigDecimal(str); } if (BigInteger.class.getSimpleName().equals(type)) { return new BigInteger(str); } throw new IllegalArgumentException("Invalid type: " + type); } } } }