package co.codewizards.cloudstore.core.repo.transport;
import static co.codewizards.cloudstore.core.util.AssertUtil.*;
import java.net.URL;
import java.util.UUID;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import co.codewizards.cloudstore.core.util.UrlDecoder;
import co.codewizards.cloudstore.core.util.UrlUtil;
public abstract class AbstractRepoTransport implements RepoTransport {
private static final Logger logger = LoggerFactory.getLogger(AbstractRepoTransport.class);
private static final String SLASH = "/";
private RepoTransportFactory repoTransportFactory;
private URL remoteRoot;
private URL remoteRootWithoutPathPrefix;
private String pathPrefix;
private UUID clientRepositoryId;
// Don't know, if fillInStackTrace() is necessary, but better do it.
// I did a small test: 1 million invocations of new Exception() vs. new Exception() with fillInStackTrace(): 3 s vs 2.2 s
private volatile Throwable repoTransportCreatedStackTraceException = new Exception("repoTransportCreatedStackTraceException").fillInStackTrace();
@Override
public RepoTransportFactory getRepoTransportFactory() {
return repoTransportFactory;
}
@Override
public void setRepoTransportFactory(final RepoTransportFactory repoTransportFactory) {
this.repoTransportFactory = assertNotNull(repoTransportFactory, "repoTransportFactory");
}
@Override
public URL getRemoteRoot() {
return remoteRoot;
}
@Override
public void setRemoteRoot(URL remoteRoot) {
remoteRoot = UrlUtil.canonicalizeURL(remoteRoot);
final URL rr = this.remoteRoot;
if (rr != null && !rr.equals(remoteRoot))
throw new IllegalStateException("Cannot re-assign remoteRoot!");
this.remoteRoot = remoteRoot;
}
public UUID getClientRepositoryIdOrFail() {
final UUID clientRepositoryId = getClientRepositoryId();
if (clientRepositoryId == null)
throw new IllegalStateException("clientRepositoryId == null :: You must invoke setClientRepositoryId(...) before!");
return clientRepositoryId;
}
@Override
public UUID getClientRepositoryId() {
return clientRepositoryId;
}
@Override
public void setClientRepositoryId(final UUID clientRepositoryId) {
this.clientRepositoryId = clientRepositoryId;
}
@Override
public URL getRemoteRootWithoutPathPrefix() {
if (remoteRootWithoutPathPrefix == null) {
remoteRootWithoutPathPrefix = UrlUtil.canonicalizeURL(determineRemoteRootWithoutPathPrefix());
}
return remoteRootWithoutPathPrefix;
}
protected abstract URL determineRemoteRootWithoutPathPrefix();
@Override
public String getPathPrefix() {
String pathPrefix = this.pathPrefix;
if (pathPrefix == null)
this.pathPrefix = pathPrefix = determinePathPrefix();
return pathPrefix;
}
protected String determinePathPrefix() {
final URL rr = getRemoteRoot();
if (rr == null)
throw new IllegalStateException("remoteRoot not yet assigned!");
final String remoteRoot = rr.toExternalForm();
final String remoteRootWithoutPathPrefix = getRemoteRootWithoutPathPrefix().toExternalForm();
if (!remoteRoot.startsWith(remoteRootWithoutPathPrefix))
throw new IllegalStateException(String.format(
"remoteRoot='%s' does not start with remoteRootWithoutPathPrefix='%s'",
remoteRoot, remoteRootWithoutPathPrefix));
String urlEncodedPathPrefix;
if (remoteRoot.equals(remoteRootWithoutPathPrefix))
urlEncodedPathPrefix = "";
else {
urlEncodedPathPrefix = remoteRoot.substring(remoteRootWithoutPathPrefix.length());
if (!urlEncodedPathPrefix.startsWith(SLASH))
urlEncodedPathPrefix = SLASH + urlEncodedPathPrefix;
if (urlEncodedPathPrefix.endsWith(SLASH))
throw new IllegalStateException("pathPrefix should not end with '" + SLASH + "', but it does!");
}
final String pathPrefix = UrlDecoder.decode(urlEncodedPathPrefix);
return pathPrefix;
}
@Override
public String prefixPath(final String path) {
assertNotNull(path, "path");
if ("".equals(path) || SLASH.equals(path))
return getPathPrefix();
if (path.startsWith(SLASH))
return getPathPrefix() + path;
else
return getPathPrefix() + SLASH + path;
}
@Override
public String unprefixPath(String path) {
assertNotNull(path, "path");
final String pathPrefix = getPathPrefix();
if (pathPrefix.isEmpty())
return path;
if (!path.startsWith(SLASH))
path = SLASH + path;
if (!path.startsWith(pathPrefix))
throw new IllegalArgumentException(String.format("path='%s' does not start with pathPrefix='%s'!", path, pathPrefix));
final String result = path.substring(pathPrefix.length());
if (!result.isEmpty() && !result.startsWith(SLASH))
throw new IllegalStateException(String.format("pathAfterPathPrefix='%s' is neither empty nor does it start with a '/'! path='%s' pathPrefix='%s'", result, path, pathPrefix));
return result;
}
protected boolean isPathUnderPathPrefix(final String path) {
assertNotNull(path, "path");
final String pathPrefix = getPathPrefix();
if (pathPrefix.isEmpty())
return true;
return path.startsWith(pathPrefix);
}
@Override
protected void finalize() throws Throwable {
if (repoTransportCreatedStackTraceException != null) {
logger.warn("finalize: Detected forgotten close() invocation!", repoTransportCreatedStackTraceException);
}
super.finalize();
}
@Override
public void close() {
repoTransportCreatedStackTraceException = null;
}
}