blob: 21da85f350aa36032a64b3338d1c986ca1fb0c80 [file] [log] [blame]
/*
* Copyright (c) 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
*/
package org.eclipse.emf.cdo.internal.explorer;
import org.eclipse.emf.cdo.common.commit.CDOCommitInfo;
import org.eclipse.emf.cdo.common.lob.CDOBlob;
import org.eclipse.emf.cdo.common.lob.CDOClob;
import org.eclipse.emf.cdo.common.lob.CDOLobStore;
import org.eclipse.emf.cdo.common.util.CDOException;
import org.eclipse.emf.cdo.common.util.CDOResourceNodeNotFoundException;
import org.eclipse.emf.cdo.eresource.CDOBinaryResource;
import org.eclipse.emf.cdo.eresource.CDOResourceNode;
import org.eclipse.emf.cdo.eresource.CDOTextResource;
import org.eclipse.emf.cdo.explorer.CDOExplorerUtil;
import org.eclipse.emf.cdo.explorer.checkouts.CDOCheckout;
import org.eclipse.emf.cdo.transaction.CDOTransaction;
import org.eclipse.emf.cdo.view.CDOView;
import org.eclipse.emf.internal.cdo.view.CDOURIHandler;
import org.eclipse.net4j.util.io.IOUtil;
import org.eclipse.net4j.util.io.ReaderInputStream;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.resource.URIConverter;
import org.eclipse.emf.ecore.resource.impl.URIHandlerImpl;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.StringReader;
import java.nio.charset.StandardCharsets;
import java.util.Map;
/**
* @author Eike Stepper
*/
public abstract class CDOExplorerURIHandler<NODE extends CDOResourceNode> extends URIHandlerImpl
{
public static final CDOExplorerURIHandler<CDOTextResource> TEXT = new TextURIHandler();
public static final CDOExplorerURIHandler<CDOBinaryResource> BINARY = new BinaryURIRHandler();
private final String scheme;
protected CDOExplorerURIHandler(String schemeType)
{
this.scheme = CDOExplorerUtil.URI_SCHEME + "." + schemeType;
}
public final String getScheme()
{
return scheme;
}
@Override
public boolean canHandle(URI uri)
{
return scheme.equals(uri.scheme());
}
@Override
public boolean exists(URI uri, Map<?, ?> options)
{
CDOView view = getView(uri);
NODE node = null;
try
{
node = getNode(view, uri.path());
}
catch (CDOResourceNodeNotFoundException ex)
{
//$FALL-THROUGH$
}
return node != null;
}
@Override
public Map<String, ?> getAttributes(URI uri, Map<?, ?> options)
{
CDOView view = getView(uri);
Map<String, ?> attributes = CDOURIHandler.getAttributes(view, uri.path(), options);
// The view of a checkout is always read-only, so remove this attribute.
attributes.remove(URIConverter.ATTRIBUTE_READ_ONLY);
return attributes;
}
@Override
public InputStream createInputStream(URI uri, Map<?, ?> options) throws IOException
{
CDOView view = getView(uri);
NODE node = getNode(view, uri.path());
return createInputStream(node);
}
@Override
public OutputStream createOutputStream(URI uri, Map<?, ?> options) throws IOException
{
return new ByteArrayOutputStream()
{
@Override
public void close() throws IOException
{
super.close();
byte[] bytes = toByteArray();
CDOCheckout checkout = getCheckout(uri);
CDOLobStore lobStore = checkout.getView().getSession().options().getLobCache();
modify(checkout, uri.path(), true, node -> setContents(node, bytes, lobStore));
}
};
}
@Override
public void delete(URI uri, Map<?, ?> options) throws IOException
{
CDOCheckout checkout = getCheckout(uri);
modify(checkout, uri.path(), false, node -> node.delete(options));
}
protected abstract NODE getNode(CDOView view, String path) throws CDOResourceNodeNotFoundException;
protected abstract NODE getOrCreateNode(CDOTransaction transaction, String path);
protected abstract InputStream createInputStream(NODE node) throws IOException;
protected abstract void setContents(NODE node, byte[] bytes, CDOLobStore lobStore) throws IOException;
private CDOCheckout getCheckout(URI uri)
{
String id = uri.authority();
CDOCheckout checkout = CDOExplorerUtil.getCheckout(id);
if (checkout == null)
{
throw new CDOException("No checkout exists for " + uri);
}
if (!checkout.isOpen())
{
throw new CDOException("Checkout '" + checkout.getLabel() + "' is not open");
}
return checkout;
}
private CDOView getView(URI uri)
{
CDOCheckout checkout = getCheckout(uri);
return checkout.getView();
}
private void modify(CDOCheckout checkout, String path, boolean createOnDemand, ResourceNodeModifier<NODE> modifier) throws IOException
{
CDOTransaction transaction = null;
try
{
transaction = checkout.openTransaction();
NODE node = createOnDemand ? getOrCreateNode(transaction, path) : getNode(transaction, path);
modifier.modify(node);
CDOCommitInfo commitInfo = transaction.commit();
if (commitInfo != null)
{
CDOView view = checkout.getView();
view.waitForUpdate(commitInfo.getTimeStamp());
}
}
catch (Exception ex)
{
throw IOUtil.ioException(ex);
}
finally
{
IOUtil.closeSilent(transaction);
}
}
private static CDOExplorerURIHandler<?> getURIHandler(CDOResourceNode node)
{
if (node instanceof CDOTextResource)
{
return TEXT;
}
if (node instanceof CDOBinaryResource)
{
return TEXT;
}
return null;
}
public static URI createURI(CDOCheckout checkout, CDOResourceNode node)
{
CDOExplorerURIHandler<?> uriHandler = getURIHandler(node);
if (uriHandler == null)
{
return null;
}
String scheme = uriHandler.getScheme();
String authority = checkout.getID();
String[] segments = node.getURI().segments();
return URI.createHierarchicalURI(scheme, authority, null, segments, null, null);
}
/**
* @author Eike Stepper
*/
@FunctionalInterface
private interface ResourceNodeModifier<NODE extends CDOResourceNode>
{
public void modify(NODE node) throws IOException;
}
/**
* @author Eike Stepper
*/
private static final class TextURIHandler extends CDOExplorerURIHandler<CDOTextResource>
{
public TextURIHandler()
{
super("text");
}
@Override
protected CDOTextResource getNode(CDOView view, String path) throws CDOResourceNodeNotFoundException
{
return view.getTextResource(path);
}
@Override
protected CDOTextResource getOrCreateNode(CDOTransaction transaction, String path)
{
return transaction.getOrCreateTextResource(path);
}
@Override
protected InputStream createInputStream(CDOTextResource node) throws IOException
{
CDOClob clob = node.getContents();
if (clob == null)
{
return new InputStream()
{
@Override
public int read() throws IOException
{
return IOUtil.EOF;
}
};
}
return new ReaderInputStream(clob.getContents(), node.getEncoding());
}
@Override
protected void setContents(CDOTextResource node, byte[] bytes, CDOLobStore lobStore) throws IOException
{
String encoding = node.getEncoding();
if (encoding == null)
{
encoding = StandardCharsets.UTF_8.toString();
}
CDOClob clob = new CDOClob(new StringReader(new String(bytes, encoding)), lobStore);
node.setContents(clob);
}
}
/**
* @author Eike Stepper
*/
private static final class BinaryURIRHandler extends CDOExplorerURIHandler<CDOBinaryResource>
{
public BinaryURIRHandler()
{
super("binary");
}
@Override
protected CDOBinaryResource getNode(CDOView view, String path) throws CDOResourceNodeNotFoundException
{
return view.getBinaryResource(path);
}
@Override
protected CDOBinaryResource getOrCreateNode(CDOTransaction transaction, String path)
{
return transaction.getOrCreateBinaryResource(path);
}
@Override
protected InputStream createInputStream(CDOBinaryResource node) throws IOException
{
return node.getContents().getContents();
}
@Override
protected void setContents(CDOBinaryResource node, byte[] bytes, CDOLobStore lobStore) throws IOException
{
CDOBlob blob = new CDOBlob(new ByteArrayInputStream(bytes), lobStore);
node.setContents(blob);
}
}
}