/* * Copyright (c) 2010-2012, 2015, 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.util; import org.eclipse.emf.cdo.common.CDOCommonRepository; import org.eclipse.emf.cdo.common.branch.CDOBranch; import org.eclipse.emf.cdo.common.branch.CDOBranchPoint; import org.eclipse.emf.cdo.eresource.CDOResource; import org.eclipse.net4j.util.security.IUserManager; import org.eclipse.emf.common.util.URI; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.Path; import java.util.Map; import java.util.Map.Entry; /** * Represents a CDO-specific {@link URI} in connection-aware format. * <p> * CDO URIs are in one of two different formats, either canonical or connection-aware. The connection-aware format is: * * <blockquote><b>cdo.net4j.</b> <i>ConnectorType</i> <b>://</b> [<i>User</i> [<b>:</b> <i>Password</i>] <b>@</b>] * <i>ConnectorSpecificAuthority</i> <b>/</b> <i>RepositoryName</i> <b>/</b> <i>ResourcePath</i> [<b>?</b> <i>Param</i><b>=</b><i>Value</i> * (<b>&</b> <i>Param</i><b>=</b><i>Value</i>)*]</blockquote> * * The non-terminals being: * <p> * <ul> * <li><i>ConnectorType</i>: one of <b>tcp</b> | <b>ssl</b> | <b>jvm</b> | <b>http</b> * <li><i>User/Password</i>: to be provided if the repository is configured with an * {@link IUserManager} and, hence, triggers authentication on the client. Note: the * password may be stored in resources in clear text! * <li><i>ConnectorSpecificAuthority</i>: examples are * <ul> * <li><i>Host</i> [<b>:</b> <i>Port</i>] (if <i>ConnectorType</i> is <b>tcp</b>) * <li><i>AcceptorName</i> (if <i>ConnectorType</i> is <b>jvm</b>) * </ul> * <li><i>RepositoryName</i>: the {@link CDOCommonRepository#getName() name} of the repository (not the {@link CDOCommonRepository#getUUID() UUID}!). * <li><i>ResourcePath</i>: the full path of the {@link CDOResource resource} within the repository, segments separated by slashes, no leading slash. * <li><i>Param</i>: one of the following * <ul> * <li><b>branch</b>: the value must be a {@link CDOBranch#getPathName() branch path}, the full path of the branch in the branch tree, segments separated by slashes, no leading slash, defaults to <b>MAIN</b>. * <li><b>time</b>: the value must be the time at which the resource is supposed to be valid, parseable by SimpleDateFormat. The special value <b>HEAD</b> indicates a floating view/transaction that always shows the latest state in the chosen branch, the default if no <i>Time</i> parameter is specified. * <li><b>transactional</b>: a boolean value. The value <b>true</b> forces a the resource to be opened in a transaction rather than in a read-only view. This can not be combined with a <i>Time</i> other than <b>HEAD</b>. * <li><b>prefetch</b>: a boolean value. The value <b>true</b> attempts to load all objects contained by the resource in a single server-round trip and cache the results. * </ul> * </ul> * <p> * Note: With the current design and implementation of connection-aware URI * (mainly CDONet4jViewProvider) it is still unclear when and how the allocated * "resources" (aka IConnector, CDOSession, CDOView, etc) are supposed to be freed! * <p> * For a description of the canonical URI format refer to {@link CDOURIUtil}. * * @author Eike Stepper * @since 4.0 */ public final class CDOURIData { public static final String BRANCH_PARAMETER = "branch"; public static final String TIME_PARAMETER = "time"; /** * @since 4.1 */ public static final String VIEW_ID_PARAMETER = "view"; public static final String TRANSACTIONAL_PARAMETER = "transactional"; private String scheme; private String userName; private String passWord; private String authority; private String repositoryName; private IPath resourcePath; private IPath branchPath = new Path(CDOBranch.MAIN_BRANCH_NAME); private long timeStamp = CDOBranchPoint.UNSPECIFIED_DATE; private String viewID; private boolean transactional; private Map<String, String> extraParameters; public CDOURIData() { } public CDOURIData(String uri) throws InvalidURIException { this(URI.createURI(uri)); } public CDOURIData(URI uri) throws InvalidURIException { try { scheme = uri.scheme(); authority = uri.authority(); String userInfo = uri.userInfo(); if (userInfo != null) { authority = authority.substring(userInfo.length() + 1); int colon = userInfo.indexOf(':'); if (colon != -1) { userName = userInfo.substring(0, colon); passWord = userInfo.substring(colon + 1); } else { userName = userInfo; } } IPath path = new Path(uri.path()).makeAbsolute(); repositoryName = path.segment(0); resourcePath = path.removeFirstSegments(1); if (resourcePath == null || resourcePath.isEmpty()) { resourcePath = new Path(CDOURIUtil.SEGMENT_SEPARATOR); } String query = uri.query(); if (query != null && query.length() != 0) { Map<String, String> parameters = CDOURIUtil.getParameters(query); String branch = parameters.remove(BRANCH_PARAMETER); if (branch != null) { branchPath = new Path(branch).makeRelative(); } String time = parameters.remove(TIME_PARAMETER); if (time != null) { if (!"HEAD".equalsIgnoreCase(time)) { timeStamp = Long.parseLong(time); } } viewID = parameters.remove(VIEW_ID_PARAMETER); String transactional = parameters.remove(TRANSACTIONAL_PARAMETER); if (transactional != null) { this.transactional = Boolean.parseBoolean(transactional); } if (!parameters.isEmpty()) { extraParameters = parameters; } } if (timeStamp != CDOBranchPoint.UNSPECIFIED_DATE && transactional) { throw new IllegalArgumentException("Only HEAD can be transactional"); } } catch (Throwable t) { throw new InvalidURIException(uri, t); } // branchPath = Path.EMPTY.makeAbsolute(); // timeStamp = CDOBranchPoint.UNSPECIFIED_DATE; // // while (resourcePath.segmentCount() != 0) // { // String segment = resourcePath.segment(0); // resourcePath = resourcePath.removeFirstSegments(1); // // if (segment.startsWith("@")) // { // if (segment.length() != 1) // { // if (!segment.equals("@HEAD")) // { // timeStamp = Long.parseLong(segment.substring(1)); // } // } // // break; // } // // branchPath = branchPath.append(segment); // } // // int segments = branchPath.segmentCount(); // if (segments == 0 || segments == 1 && !branchPath.segment(0).equals(CDOBranch.MAIN_BRANCH_NAME)) // { // branchPath = new Path(CDOBranch.MAIN_BRANCH_NAME).append(branchPath); // } } public String getScheme() { return scheme; } public void setScheme(String scheme) { this.scheme = scheme; } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public String getPassWord() { return passWord; } public void setPassWord(String passWord) { this.passWord = passWord; } public String getAuthority() { return authority; } public void setAuthority(String authority) { this.authority = authority; } public String getRepositoryName() { return repositoryName; } public void setRepositoryName(String repositoryName) { this.repositoryName = repositoryName; } public IPath getResourcePath() { return resourcePath; } public void setResourcePath(IPath resourcePath) { this.resourcePath = resourcePath; } public IPath getBranchPath() { return branchPath; } public void setBranchPath(IPath branchPath) { this.branchPath = branchPath; } public long getTimeStamp() { return timeStamp; } public void setTimeStamp(long timeStamp) { this.timeStamp = timeStamp; } /** * @since 4.1 */ public String getViewID() { return viewID; } /** * @since 4.1 */ public void setViewID(String viewID) { this.viewID = viewID; } public boolean isTransactional() { return transactional; } public void setTransactional(boolean transactional) { this.transactional = transactional; } /** * @since 4.1 */ public Map<String, String> getExtraParameters() { return extraParameters; } /** * @since 4.4 */ public void setExtraParameters(Map<String, String> extraParameters) { this.extraParameters = extraParameters; } public URI toURI() { return URI.createURI(toString()); } @Override public String toString() { StringBuilder builder = new StringBuilder(); builder.append(scheme); builder.append("://"); if (userName != null) { builder.append(userName); if (passWord != null) { builder.append(":"); builder.append(passWord); } builder.append("@"); } builder.append(authority); builder.append("/"); builder.append(repositoryName); if (resourcePath != null) { builder.append("/"); builder.append(resourcePath); } int params = 0; if (branchPath != null && !branchPath.equals(new Path(CDOBranch.MAIN_BRANCH_NAME))) { builder.append(params++ == 0 ? "?" : "&"); builder.append(BRANCH_PARAMETER); builder.append("="); builder.append(branchPath.toPortableString()); } if (timeStamp != CDOBranchPoint.UNSPECIFIED_DATE) { builder.append(params++ == 0 ? "?" : "&"); builder.append(TIME_PARAMETER); builder.append("="); builder.append(timeStamp); } if (viewID != null) { builder.append(params++ == 0 ? "?" : "&"); builder.append(VIEW_ID_PARAMETER); builder.append("="); builder.append(viewID); } if (transactional) { builder.append(params++ == 0 ? "?" : "&"); builder.append(TRANSACTIONAL_PARAMETER); builder.append("="); builder.append(transactional); } if (extraParameters != null) { for (Entry<String, String> entry : extraParameters.entrySet()) { builder.append(params++ == 0 ? "?" : "&"); builder.append(entry.getKey()); builder.append("="); builder.append(entry.getValue()); } } return builder.toString(); } }