| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.apache.jackrabbit.spi2dav; |
| |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.InputStreamReader; |
| import java.io.Reader; |
| import java.io.StringWriter; |
| import java.net.URI; |
| import java.net.URISyntaxException; |
| import java.security.KeyManagementException; |
| import java.security.KeyStoreException; |
| import java.security.NoSuchAlgorithmException; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.UUID; |
| import java.util.concurrent.ConcurrentHashMap; |
| import java.util.concurrent.ConcurrentMap; |
| |
| import javax.jcr.AccessDeniedException; |
| import javax.jcr.Credentials; |
| import javax.jcr.InvalidItemStateException; |
| import javax.jcr.ItemNotFoundException; |
| import javax.jcr.LoginException; |
| import javax.jcr.NamespaceException; |
| import javax.jcr.PathNotFoundException; |
| import javax.jcr.PropertyType; |
| import javax.jcr.RepositoryException; |
| import javax.jcr.UnsupportedRepositoryOperationException; |
| import javax.jcr.Value; |
| import javax.jcr.ValueFactory; |
| import javax.jcr.lock.LockException; |
| import javax.net.ssl.SSLContext; |
| import javax.xml.parsers.ParserConfigurationException; |
| |
| import org.apache.http.HttpEntity; |
| import org.apache.http.HttpHost; |
| import org.apache.http.HttpResponse; |
| import org.apache.http.HttpStatus; |
| import org.apache.http.auth.AuthScope; |
| import org.apache.http.auth.UsernamePasswordCredentials; |
| import org.apache.http.client.AuthCache; |
| import org.apache.http.client.CredentialsProvider; |
| import org.apache.http.client.HttpClient; |
| import org.apache.http.client.config.RequestConfig; |
| import org.apache.http.client.methods.HttpGet; |
| import org.apache.http.client.methods.HttpHead; |
| import org.apache.http.client.methods.HttpPut; |
| import org.apache.http.client.methods.HttpRequestBase; |
| import org.apache.http.client.methods.HttpUriRequest; |
| import org.apache.http.client.protocol.HttpClientContext; |
| import org.apache.http.config.Registry; |
| import org.apache.http.config.RegistryBuilder; |
| import org.apache.http.conn.socket.ConnectionSocketFactory; |
| import org.apache.http.conn.socket.PlainConnectionSocketFactory; |
| import org.apache.http.conn.ssl.NoopHostnameVerifier; |
| import org.apache.http.conn.ssl.SSLConnectionSocketFactory; |
| import org.apache.http.conn.ssl.TrustSelfSignedStrategy; |
| import org.apache.http.entity.ContentType; |
| import org.apache.http.entity.InputStreamEntity; |
| import org.apache.http.entity.StringEntity; |
| import org.apache.http.impl.auth.BasicScheme; |
| import org.apache.http.impl.client.BasicAuthCache; |
| import org.apache.http.impl.client.BasicCredentialsProvider; |
| import org.apache.http.impl.client.HttpClientBuilder; |
| import org.apache.http.impl.client.HttpClients; |
| import org.apache.http.impl.client.ProxyAuthenticationStrategy; |
| import org.apache.http.impl.conn.DefaultProxyRoutePlanner; |
| import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; |
| import org.apache.http.protocol.HttpContext; |
| import org.apache.http.ssl.SSLContextBuilder; |
| import org.apache.jackrabbit.commons.webdav.AtomFeedConstants; |
| import org.apache.jackrabbit.commons.webdav.EventUtil; |
| import org.apache.jackrabbit.commons.webdav.JcrRemotingConstants; |
| import org.apache.jackrabbit.commons.webdav.JcrValueType; |
| import org.apache.jackrabbit.commons.webdav.NodeTypeConstants; |
| import org.apache.jackrabbit.commons.webdav.NodeTypeUtil; |
| import org.apache.jackrabbit.commons.webdav.ValueUtil; |
| import org.apache.jackrabbit.spi.Batch; |
| import org.apache.jackrabbit.spi.ChildInfo; |
| import org.apache.jackrabbit.spi.Event; |
| import org.apache.jackrabbit.spi.EventBundle; |
| import org.apache.jackrabbit.spi.EventFilter; |
| import org.apache.jackrabbit.spi.IdFactory; |
| import org.apache.jackrabbit.spi.ItemId; |
| import org.apache.jackrabbit.spi.ItemInfo; |
| import org.apache.jackrabbit.spi.ItemInfoCache; |
| import org.apache.jackrabbit.spi.LockInfo; |
| import org.apache.jackrabbit.spi.Name; |
| import org.apache.jackrabbit.spi.NameFactory; |
| import org.apache.jackrabbit.spi.NodeId; |
| import org.apache.jackrabbit.spi.NodeInfo; |
| import org.apache.jackrabbit.spi.Path; |
| import org.apache.jackrabbit.spi.PathFactory; |
| import org.apache.jackrabbit.spi.PrivilegeDefinition; |
| import org.apache.jackrabbit.spi.PropertyId; |
| import org.apache.jackrabbit.spi.PropertyInfo; |
| import org.apache.jackrabbit.spi.QItemDefinition; |
| import org.apache.jackrabbit.spi.QNodeDefinition; |
| import org.apache.jackrabbit.spi.QNodeTypeDefinition; |
| import org.apache.jackrabbit.spi.QPropertyDefinition; |
| import org.apache.jackrabbit.spi.QValue; |
| import org.apache.jackrabbit.spi.QValueFactory; |
| import org.apache.jackrabbit.spi.QueryInfo; |
| import org.apache.jackrabbit.spi.RepositoryService; |
| import org.apache.jackrabbit.spi.SessionInfo; |
| import org.apache.jackrabbit.spi.Subscription; |
| import org.apache.jackrabbit.spi.Tree; |
| import org.apache.jackrabbit.spi.commons.ChildInfoImpl; |
| import org.apache.jackrabbit.spi.commons.EventBundleImpl; |
| import org.apache.jackrabbit.spi.commons.EventFilterImpl; |
| import org.apache.jackrabbit.spi.commons.ItemInfoCacheImpl; |
| import org.apache.jackrabbit.spi.commons.conversion.IdentifierResolver; |
| import org.apache.jackrabbit.spi.commons.conversion.IllegalNameException; |
| import org.apache.jackrabbit.spi.commons.conversion.MalformedPathException; |
| import org.apache.jackrabbit.spi.commons.conversion.NameException; |
| import org.apache.jackrabbit.spi.commons.conversion.NamePathResolver; |
| import org.apache.jackrabbit.spi.commons.conversion.NameResolver; |
| import org.apache.jackrabbit.spi.commons.conversion.ParsingNameResolver; |
| import org.apache.jackrabbit.spi.commons.conversion.ParsingPathResolver; |
| import org.apache.jackrabbit.spi.commons.conversion.PathResolver; |
| import org.apache.jackrabbit.spi.commons.iterator.Iterators; |
| import org.apache.jackrabbit.spi.commons.name.NameConstants; |
| import org.apache.jackrabbit.spi.commons.namespace.AbstractNamespaceResolver; |
| import org.apache.jackrabbit.spi.commons.namespace.NamespaceResolver; |
| import org.apache.jackrabbit.spi.commons.nodetype.compact.CompactNodeTypeDefWriter; |
| import org.apache.jackrabbit.spi.commons.privilege.PrivilegeDefinitionImpl; |
| import org.apache.jackrabbit.spi.commons.value.QValueValue; |
| import org.apache.jackrabbit.spi.commons.value.ValueFactoryQImpl; |
| import org.apache.jackrabbit.spi.commons.value.ValueFormat; |
| import org.apache.jackrabbit.util.Text; |
| import org.apache.jackrabbit.webdav.DavConstants; |
| import org.apache.jackrabbit.webdav.DavException; |
| import org.apache.jackrabbit.webdav.DavMethods; |
| import org.apache.jackrabbit.webdav.DavServletResponse; |
| import org.apache.jackrabbit.webdav.MultiStatus; |
| import org.apache.jackrabbit.webdav.MultiStatusResponse; |
| import org.apache.jackrabbit.webdav.client.methods.BaseDavRequest; |
| import org.apache.jackrabbit.webdav.client.methods.HttpCheckin; |
| import org.apache.jackrabbit.webdav.client.methods.HttpCheckout; |
| import org.apache.jackrabbit.webdav.client.methods.HttpCopy; |
| import org.apache.jackrabbit.webdav.client.methods.HttpDelete; |
| import org.apache.jackrabbit.webdav.client.methods.HttpLabel; |
| import org.apache.jackrabbit.webdav.client.methods.HttpLock; |
| import org.apache.jackrabbit.webdav.client.methods.HttpMerge; |
| import org.apache.jackrabbit.webdav.client.methods.HttpMkcol; |
| import org.apache.jackrabbit.webdav.client.methods.HttpMkworkspace; |
| import org.apache.jackrabbit.webdav.client.methods.HttpMove; |
| import org.apache.jackrabbit.webdav.client.methods.HttpOptions; |
| import org.apache.jackrabbit.webdav.client.methods.HttpOrderpatch; |
| import org.apache.jackrabbit.webdav.client.methods.HttpPoll; |
| import org.apache.jackrabbit.webdav.client.methods.HttpPropfind; |
| import org.apache.jackrabbit.webdav.client.methods.HttpProppatch; |
| import org.apache.jackrabbit.webdav.client.methods.HttpReport; |
| import org.apache.jackrabbit.webdav.client.methods.HttpSearch; |
| import org.apache.jackrabbit.webdav.client.methods.HttpSubscribe; |
| import org.apache.jackrabbit.webdav.client.methods.HttpUnlock; |
| import org.apache.jackrabbit.webdav.client.methods.HttpUnsubscribe; |
| import org.apache.jackrabbit.webdav.client.methods.HttpUpdate; |
| import org.apache.jackrabbit.webdav.client.methods.XmlEntity; |
| import org.apache.jackrabbit.webdav.header.CodedUrlHeader; |
| import org.apache.jackrabbit.webdav.header.IfHeader; |
| import org.apache.jackrabbit.webdav.lock.ActiveLock; |
| import org.apache.jackrabbit.webdav.lock.LockDiscovery; |
| import org.apache.jackrabbit.webdav.lock.Scope; |
| import org.apache.jackrabbit.webdav.lock.Type; |
| import org.apache.jackrabbit.webdav.observation.DefaultEventType; |
| import org.apache.jackrabbit.webdav.observation.EventDiscovery; |
| import org.apache.jackrabbit.webdav.observation.EventType; |
| import org.apache.jackrabbit.webdav.observation.ObservationConstants; |
| import org.apache.jackrabbit.webdav.observation.SubscriptionInfo; |
| import org.apache.jackrabbit.webdav.ordering.OrderPatch; |
| import org.apache.jackrabbit.webdav.ordering.OrderingConstants; |
| import org.apache.jackrabbit.webdav.ordering.Position; |
| import org.apache.jackrabbit.webdav.property.DavProperty; |
| import org.apache.jackrabbit.webdav.property.DavPropertyName; |
| import org.apache.jackrabbit.webdav.property.DavPropertyNameSet; |
| import org.apache.jackrabbit.webdav.property.DavPropertySet; |
| import org.apache.jackrabbit.webdav.property.DefaultDavProperty; |
| import org.apache.jackrabbit.webdav.property.HrefProperty; |
| import org.apache.jackrabbit.webdav.search.SearchInfo; |
| import org.apache.jackrabbit.webdav.security.CurrentUserPrivilegeSetProperty; |
| import org.apache.jackrabbit.webdav.security.Privilege; |
| import org.apache.jackrabbit.webdav.security.SecurityConstants; |
| import org.apache.jackrabbit.webdav.security.SupportedPrivilege; |
| import org.apache.jackrabbit.webdav.security.SupportedPrivilegeSetProperty; |
| import org.apache.jackrabbit.webdav.transaction.TransactionConstants; |
| import org.apache.jackrabbit.webdav.transaction.TransactionInfo; |
| import org.apache.jackrabbit.webdav.version.DeltaVConstants; |
| import org.apache.jackrabbit.webdav.version.LabelInfo; |
| import org.apache.jackrabbit.webdav.version.MergeInfo; |
| import org.apache.jackrabbit.webdav.version.UpdateInfo; |
| import org.apache.jackrabbit.webdav.version.VersionControlledResource; |
| import org.apache.jackrabbit.webdav.version.report.ReportInfo; |
| import org.apache.jackrabbit.webdav.xml.DomUtil; |
| import org.apache.jackrabbit.webdav.xml.ElementIterator; |
| import org.apache.jackrabbit.webdav.xml.XmlSerializable; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| import org.w3c.dom.Document; |
| import org.w3c.dom.Element; |
| import org.xml.sax.SAXException; |
| |
| /** |
| * <code>RepositoryServiceImpl</code>... |
| */ |
| // TODO: encapsulate URI building, escaping, unescaping... |
| // TODO: TO-BE-FIXED. caches don't get adjusted upon removal/move of items |
| public class RepositoryServiceImpl implements RepositoryService, DavConstants { |
| |
| private static Logger log = LoggerFactory.getLogger(RepositoryServiceImpl.class); |
| |
| private static final SubscriptionInfo S_INFO = new SubscriptionInfo(DefaultEventType.create(EventUtil.EVENT_ALL, ItemResourceConstants.NAMESPACE), true, INFINITE_TIMEOUT); |
| |
| /** |
| * Key for the client map during repo creation (no sessionInfo present) |
| */ |
| private static final String CLIENT_KEY = "repoCreation"; |
| |
| /** |
| * Default value for the maximum number of connections per host such as |
| * configured with {@link PoolingHttpClientConnectionManager#setDefaultMaxPerRoute(int)}. |
| */ |
| public static final int MAX_CONNECTIONS_DEFAULT = 20; |
| |
| private final IdFactory idFactory; |
| private final NameFactory nameFactory; |
| private final PathFactory pathFactory; |
| private final QValueFactory qValueFactory; |
| private final ValueFactory valueFactory; |
| |
| private final int itemInfoCacheSize; |
| |
| private final NamespaceCache nsCache; |
| private final URIResolverImpl uriResolver; |
| |
| private final HttpHost httpHost; |
| private final ConcurrentMap<Object, HttpClient> clients; |
| private final HttpClientBuilder httpClientBuilder; |
| private final Map<AuthScope, org.apache.http.auth.Credentials> commonCredentials; |
| |
| private final Map<Name, QNodeTypeDefinition> nodeTypeDefinitions = new HashMap<Name, QNodeTypeDefinition>(); |
| |
| /** Repository descriptors. */ |
| private final Map<String, QValue[]> descriptors = |
| new HashMap<String, QValue[]>(); |
| |
| /** Observation features. */ |
| private boolean remoteServerProvidesNodeTypes = false; |
| private boolean remoteServerProvidesNoLocalFlag = false; |
| |
| /* DAV conformance levels */ |
| private Set<String> remoteDavComplianceClasses = null; |
| |
| /** |
| * Same as {@link #RepositoryServiceImpl(String, IdFactory, NameFactory, PathFactory, QValueFactory, int, ConnectionOptions)} |
| * using {@link ItemInfoCacheImpl#DEFAULT_CACHE_SIZE} as size for the item |
| * cache and {@link #MAX_CONNECTIONS_DEFAULT} for the maximum number of |
| * connections on the client and {@link ConnectionOptions#DEFAULT}. |
| * |
| * @param uri The server uri. |
| * @param idFactory The id factory. |
| * @param nameFactory The name factory. |
| * @param pathFactory The path factory. |
| * @param qValueFactory The value factory. |
| * @throws RepositoryException If an error occurs. |
| */ |
| public RepositoryServiceImpl(String uri, IdFactory idFactory, |
| NameFactory nameFactory, PathFactory pathFactory, |
| QValueFactory qValueFactory) throws RepositoryException { |
| this(uri, idFactory, nameFactory, pathFactory, qValueFactory, ItemInfoCacheImpl.DEFAULT_CACHE_SIZE); |
| } |
| |
| /** |
| * Same as {@link #RepositoryServiceImpl(String, IdFactory, NameFactory, PathFactory, QValueFactory, int, ConnectionOptions)} |
| * using {@link #MAX_CONNECTIONS_DEFAULT} for the maximum number of |
| * connections on the client and {@link ConnectionOptions#DEFAULT}. |
| * |
| * @param uri The server uri. |
| * @param idFactory The id factory. |
| * @param nameFactory The name factory. |
| * @param pathFactory The path factory. |
| * @param qValueFactory The value factory. |
| * @param itemInfoCacheSize The size of the item info cache. |
| * @throws RepositoryException If an error occurs. |
| */ |
| public RepositoryServiceImpl(String uri, IdFactory idFactory, |
| NameFactory nameFactory, PathFactory pathFactory, |
| QValueFactory qValueFactory, int itemInfoCacheSize) throws RepositoryException { |
| this(uri, idFactory, nameFactory, pathFactory, qValueFactory, itemInfoCacheSize, ConnectionOptions.DEFAULT); |
| } |
| |
| /** |
| * Creates a new instance of this repository service. |
| * |
| * @param uri The server uri. |
| * @param idFactory The id factory. |
| * @param nameFactory The name factory. |
| * @param pathFactory The path factory. |
| * @param qValueFactory The value factory. |
| * @param itemInfoCacheSize The size of the item info cache. |
| * @param connectionOptions The advanced connection options. |
| * @throws RepositoryException If an error occurs. |
| */ |
| public RepositoryServiceImpl(String uri, IdFactory idFactory, |
| NameFactory nameFactory, PathFactory pathFactory, |
| QValueFactory qValueFactory, int itemInfoCacheSize, |
| ConnectionOptions connectionOptions) throws RepositoryException { |
| if (uri == null || "".equals(uri)) { |
| throw new RepositoryException("Invalid repository uri '" + uri + "'."); |
| } |
| |
| if (idFactory == null || qValueFactory == null) { |
| throw new RepositoryException("IdFactory and QValueFactory may not be null."); |
| } |
| this.idFactory = idFactory; |
| this.nameFactory = nameFactory; |
| this.pathFactory = pathFactory; |
| this.qValueFactory = qValueFactory; |
| this.itemInfoCacheSize = itemInfoCacheSize; |
| this.commonCredentials = new HashMap<>(); |
| |
| try { |
| URI repositoryUri = computeRepositoryUri(uri); |
| httpHost = new HttpHost(repositoryUri.getHost(), repositoryUri.getPort(), repositoryUri.getScheme()); |
| |
| nsCache = new NamespaceCache(); |
| uriResolver = new URIResolverImpl(repositoryUri, this, DomUtil.createDocument()); |
| NamePathResolver resolver = new NamePathResolverImpl(nsCache); |
| valueFactory = new ValueFactoryQImpl(qValueFactory, resolver); |
| |
| } catch (URISyntaxException e) { |
| throw new RepositoryException(e); |
| } catch (ParserConfigurationException e) { |
| throw new RepositoryException(e); |
| } |
| |
| |
| HttpClientBuilder hcb = HttpClients.custom(); |
| |
| // request config |
| RequestConfig requestConfig = RequestConfig.custom(). |
| setConnectTimeout(connectionOptions.getConnectionTimeoutMs()). |
| setConnectionRequestTimeout(connectionOptions.getRequestTimeoutMs()). |
| setSocketTimeout(connectionOptions.getSocketTimeoutMs()).build(); |
| hcb.setDefaultRequestConfig(requestConfig); |
| if (Boolean.getBoolean("jackrabbit.client.useSystemProperties") || connectionOptions.isUseSystemPropertes()) { |
| log.debug("Using system properties for establishing connection!"); |
| // support Java system proxy? (JCR-3211) |
| hcb.useSystemProperties(); |
| } |
| |
| // TLS settings (via connection manager) |
| final SSLContext sslContext; |
| try { |
| if (connectionOptions.isAllowSelfSignedCertificates()) { |
| log.warn("Nonsecure TLS setting: Accepting self-signed certificates!"); |
| sslContext = SSLContextBuilder.create().loadTrustMaterial(new TrustSelfSignedStrategy()).build(); |
| hcb.setSSLContext(sslContext); |
| } else { |
| sslContext = SSLContextBuilder.create().build(); |
| } |
| } catch (KeyManagementException | NoSuchAlgorithmException | KeyStoreException e) { |
| throw new RepositoryException(e); |
| } |
| final SSLConnectionSocketFactory sslSocketFactory; |
| if (connectionOptions.isDisableHostnameVerification()) { |
| log.warn("Nonsecure TLS setting: Host name verification of TLS certificates disabled!"); |
| // we can optionally disable hostname verification. |
| sslSocketFactory = new SSLConnectionSocketFactory(sslContext, NoopHostnameVerifier.INSTANCE); |
| } else { |
| sslSocketFactory = new SSLConnectionSocketFactory(sslContext); |
| } |
| |
| Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create() |
| .register("http", PlainConnectionSocketFactory.getSocketFactory()) |
| .register("https", sslSocketFactory) |
| .build(); |
| |
| PoolingHttpClientConnectionManager cmgr = new PoolingHttpClientConnectionManager(socketFactoryRegistry); |
| int maxConnections = connectionOptions.getMaxConnections(); |
| if (maxConnections > 0) { |
| cmgr.setDefaultMaxPerRoute(connectionOptions.getMaxConnections()); |
| cmgr.setMaxTotal(connectionOptions.getMaxConnections()); |
| } else { |
| maxConnections = ConnectionOptions.MAX_CONNECTIONS_DEFAULT; |
| } |
| hcb.setConnectionManager(cmgr); |
| |
| if (connectionOptions.getProxyHost() != null) { |
| // https://hc.apache.org/httpcomponents-client-4.5.x/tutorial/html/connmgmt.html#d5e485 |
| HttpHost proxy = new HttpHost(connectionOptions.getProxyHost(), connectionOptions.getProxyPort(), connectionOptions.getProxyProtocol()); |
| DefaultProxyRoutePlanner routePlanner = new DefaultProxyRoutePlanner(proxy); |
| hcb.setRoutePlanner(routePlanner); |
| log.debug("Connection via proxy {}", proxy); |
| if (connectionOptions.getProxyUsername() != null) { |
| log.debug("Proxy connection with credentials {}", proxy); |
| commonCredentials.put( |
| new AuthScope(proxy), |
| new UsernamePasswordCredentials(connectionOptions.getProxyUsername(), connectionOptions.getProxyPassword())); |
| hcb.setProxyAuthenticationStrategy(new ProxyAuthenticationStrategy()); |
| } |
| } |
| httpClientBuilder = hcb; |
| |
| // This configuration of the clients cache assumes that the level of |
| // concurrency on this map will be equal to the default number of maximum |
| // connections allowed on the httpClient level. |
| // TODO: review again |
| clients = new ConcurrentHashMap<Object, HttpClient>(maxConnections, .75f, maxConnections); |
| } |
| |
| private static void checkSessionInfo(SessionInfo sessionInfo) throws RepositoryException { |
| if (!(sessionInfo instanceof SessionInfoImpl)) { |
| throw new RepositoryException("Unknown SessionInfo implementation."); |
| } |
| } |
| |
| /** |
| * Resolve the given URI against a base URI (usually the request URI of an HTTP request) |
| */ |
| private static String resolve(String baseUri, String relUri) throws RepositoryException { |
| try { |
| java.net.URI base = new java.net.URI(baseUri); |
| java.net.URI rel = new java.net.URI(relUri); |
| return base.resolve(rel).toString(); |
| } catch (URISyntaxException ex) { |
| throw new RepositoryException(ex); |
| } |
| } |
| |
| private static void checkSubscription(Subscription subscription) throws RepositoryException { |
| if (!(subscription instanceof EventSubscriptionImpl)) { |
| throw new RepositoryException("Unknown Subscription implementation."); |
| } |
| } |
| |
| private static boolean isUnLockMethod(HttpUriRequest request) { |
| int code = DavMethods.getMethodCode(request.getMethod()); |
| return DavMethods.DAV_UNLOCK == code; |
| } |
| |
| protected static void initMethod(HttpUriRequest request, SessionInfo sessionInfo, boolean addIfHeader) throws RepositoryException { |
| if (addIfHeader) { |
| checkSessionInfo(sessionInfo); |
| Set<String> allLockTokens = ((SessionInfoImpl) sessionInfo).getAllLockTokens(); |
| // TODO: ev. build tagged if header |
| if (!allLockTokens.isEmpty()) { |
| String[] locktokens = allLockTokens.toArray(new String[allLockTokens.size()]); |
| IfHeader ifH = new IfHeader(locktokens); |
| request.setHeader(ifH.getHeaderName(), ifH.getHeaderValue()); |
| } |
| } |
| |
| initMethod(request, sessionInfo); |
| } |
| |
| // set of HTTP methods that will not change the remote state |
| private static final Set<String> readMethods; |
| static { |
| Set<String> tmp = new HashSet<String>(); |
| tmp.add("GET"); |
| tmp.add("HEAD"); |
| tmp.add("PROPFIND"); |
| tmp.add("POLL"); |
| tmp.add("REPORT"); |
| tmp.add("SEARCH"); |
| readMethods = Collections.unmodifiableSet(tmp); |
| } |
| |
| // set headers for user data and session identification |
| protected static void initMethod(HttpUriRequest request, SessionInfo sessionInfo) throws RepositoryException { |
| |
| boolean isReadAccess = readMethods.contains(request.getMethod()); |
| boolean needsSessionId = !isReadAccess || "POLL".equals(request.getMethod()); |
| |
| if (sessionInfo instanceof SessionInfoImpl && needsSessionId) { |
| request.addHeader("Link", generateLinkHeaderFieldValue(sessionInfo, isReadAccess)); |
| } |
| } |
| |
| private static String generateLinkHeaderFieldValue(SessionInfo sessionInfo, boolean isReadAccess) { |
| StringBuilder linkHeaderField = new StringBuilder(); |
| |
| String sessionIdentifier = ((SessionInfoImpl) sessionInfo).getSessionIdentifier(); |
| linkHeaderField.append("<").append(sessionIdentifier).append(">; rel=\"") |
| .append(JcrRemotingConstants.RELATION_REMOTE_SESSION_ID).append("\""); |
| |
| String userdata = ((SessionInfoImpl) sessionInfo).getUserData(); |
| if (userdata != null && !isReadAccess) { |
| String escaped = Text.escape(userdata); |
| linkHeaderField.append(", <data:,").append(escaped).append(">; rel=\"").append(JcrRemotingConstants.RELATION_USER_DATA) |
| .append("\""); |
| } |
| return linkHeaderField.toString(); |
| } |
| |
| private static void initMethod(HttpUriRequest request, BatchImpl batchImpl, boolean addIfHeader) throws RepositoryException { |
| initMethod(request, batchImpl.sessionInfo, addIfHeader); |
| |
| // add batchId as separate header, TODO: could probably re-use session id Link relation |
| CodedUrlHeader ch = new CodedUrlHeader(TransactionConstants.HEADER_TRANSACTIONID, batchImpl.batchId); |
| request.setHeader(ch.getHeaderName(), ch.getHeaderValue()); |
| } |
| |
| private static boolean isSameResource(String requestURI, MultiStatusResponse response) { |
| try { |
| String href = resolve(requestURI, response.getHref()); |
| if (href.endsWith("/") && !requestURI.endsWith("/")) { |
| href = href.substring(0, href.length() - 1); |
| } |
| return requestURI.equals(href); |
| } catch (RepositoryException e) { |
| return false; |
| } |
| } |
| |
| private String saveGetIdString(ItemId id, SessionInfo sessionInfo) { |
| NamePathResolver resolver = null; |
| try { |
| resolver = getNamePathResolver(sessionInfo); |
| } catch (RepositoryException e) { |
| // ignore. |
| } |
| return saveGetIdString(id, resolver); |
| } |
| |
| private String saveGetIdString(ItemId id, NamePathResolver resolver) { |
| StringBuffer bf = new StringBuffer(); |
| String uid = id.getUniqueID(); |
| if (uid != null) { |
| bf.append(uid); |
| } |
| Path p = id.getPath(); |
| if (p != null) { |
| if (resolver == null) { |
| bf.append(p.toString()); |
| } else { |
| try { |
| bf.append(resolver.getJCRPath(p)); |
| } catch (NamespaceException e) { |
| bf.append(p.toString()); |
| } |
| } |
| } |
| return bf.toString(); |
| } |
| |
| protected NamePathResolver getNamePathResolver(SessionInfo sessionInfo) throws RepositoryException { |
| checkSessionInfo(sessionInfo); |
| return getNamePathResolver(((SessionInfoImpl) sessionInfo)); |
| } |
| |
| private NamePathResolver getNamePathResolver(SessionInfoImpl sessionInfo) { |
| NamePathResolver resolver = sessionInfo.getNamePathResolver(); |
| if (resolver == null) { |
| resolver = new NamePathResolverImpl(sessionInfo); |
| sessionInfo.setNamePathResolver(resolver); |
| } |
| return resolver; |
| } |
| |
| /** |
| * Returns a key for the httpClient hash. The key is either the specified |
| * SessionInfo or a marker if the session info is null (used during |
| * repository instantiation). |
| * |
| * @param sessionInfo |
| * @return Key for the client map. |
| */ |
| private static Object getClientKey(SessionInfo sessionInfo) { |
| return (sessionInfo == null) ? CLIENT_KEY : sessionInfo; |
| } |
| |
| protected HttpClient getClient(SessionInfo sessionInfo) throws RepositoryException { |
| Object clientKey = getClientKey(sessionInfo); |
| HttpClient client = clients.get(clientKey); |
| if (client == null) { |
| client = httpClientBuilder.build(); |
| if (sessionInfo != null) { |
| checkSessionInfo(sessionInfo); |
| clients.put(clientKey, client); |
| log.debug("Created Client " + client + " for SessionInfo " + sessionInfo); |
| } |
| } |
| return client; |
| } |
| |
| protected HttpContext getContext(SessionInfo sessionInfo) throws RepositoryException { |
| HttpClientContext result = HttpClientContext.create(); |
| CredentialsProvider credsProvider = new BasicCredentialsProvider(); |
| result.setCredentialsProvider(credsProvider); |
| // take over default credentials (e.g. for proxy) |
| for (Map.Entry<AuthScope, org.apache.http.auth.Credentials> entry : commonCredentials.entrySet()) { |
| credsProvider.setCredentials(entry.getKey(), entry.getValue()); |
| } |
| if (sessionInfo != null) { |
| checkSessionInfo(sessionInfo); |
| org.apache.http.auth.Credentials creds = ((SessionInfoImpl) sessionInfo).getCredentials().getHttpCredentials(); |
| if (creds != null) { |
| credsProvider.setCredentials(new org.apache.http.auth.AuthScope(httpHost.getHostName(), httpHost.getPort()), creds); |
| BasicScheme basicAuth = new BasicScheme(); |
| AuthCache authCache = new BasicAuthCache(); |
| authCache.put(httpHost, basicAuth); |
| result.setAuthCache(authCache); |
| } |
| } |
| return result; |
| } |
| |
| private void removeClient(SessionInfo sessionInfo) { |
| HttpClient cl = clients.remove(getClientKey(sessionInfo)); |
| log.debug("Removed Client " + cl + " for SessionInfo " + sessionInfo); |
| } |
| |
| protected String getItemUri(ItemId itemId, SessionInfo sessionInfo) throws RepositoryException { |
| return getItemUri(itemId, sessionInfo, sessionInfo.getWorkspaceName()); |
| } |
| |
| protected String getItemUri(ItemId itemId, SessionInfo sessionInfo, String workspaceName) throws RepositoryException { |
| return uriResolver.getItemUri(itemId, workspaceName, sessionInfo); |
| } |
| |
| /** |
| * Clear all URI mappings. This is required after hierarchy operations such |
| * as e.g. MOVE. |
| * |
| * @param sessionInfo |
| */ |
| protected void clearItemUriCache(SessionInfo sessionInfo) { |
| uriResolver.clearCacheEntries(sessionInfo); |
| } |
| |
| private String getItemUri(NodeId parentId, Name childName, |
| SessionInfo sessionInfo) throws RepositoryException { |
| String parentUri = uriResolver.getItemUri(parentId, sessionInfo.getWorkspaceName(), sessionInfo); |
| NamePathResolver resolver = getNamePathResolver(sessionInfo); |
| // JCR-2920: don't append '/' to a trailing '/' |
| if (!parentUri.endsWith("/")) { |
| parentUri += "/"; |
| } |
| return parentUri + Text.escape(resolver.getJCRName(childName)); |
| } |
| |
| private NodeId getParentId(String baseUri, DavPropertySet propSet, SessionInfo sessionInfo) |
| throws RepositoryException { |
| NodeId parentId = null; |
| DavProperty<?> p = propSet.get(JcrRemotingConstants.JCR_PARENT_LN, ItemResourceConstants.NAMESPACE); |
| if (p != null) { |
| HrefProperty parentProp = new HrefProperty(p); |
| String parentHref = parentProp.getHrefs().get(0); |
| if (parentHref != null && parentHref.length() > 0) { |
| parentId = uriResolver.getNodeId(resolve(baseUri, parentHref), sessionInfo); |
| } |
| } |
| return parentId; |
| } |
| |
| String getUniqueID(DavPropertySet propSet) { |
| DavProperty<?> prop = propSet.get(JcrRemotingConstants.JCR_UUID_LN, ItemResourceConstants.NAMESPACE); |
| if (prop != null) { |
| return prop.getValue().toString(); |
| } else { |
| return null; |
| } |
| } |
| |
| Name getQName(DavPropertySet propSet, NamePathResolver resolver) throws RepositoryException { |
| DavProperty<?> nameProp = propSet.get(JcrRemotingConstants.JCR_NAME_LN, ItemResourceConstants.NAMESPACE); |
| if (nameProp != null && nameProp.getValue() != null) { |
| // not root node. Note that 'unespacing' is not required since |
| // the jcr:name property does not provide the value in escaped form. |
| String jcrName = nameProp.getValue().toString(); |
| try { |
| return resolver.getQName(jcrName); |
| } catch (NameException e) { |
| throw new RepositoryException(e); |
| } |
| } else { |
| return NameConstants.ROOT; |
| } |
| } |
| |
| int getIndex(DavPropertySet propSet) { |
| int index = Path.INDEX_UNDEFINED; |
| DavProperty<?> indexProp = propSet.get(JcrRemotingConstants.JCR_INDEX_LN, ItemResourceConstants.NAMESPACE); |
| if (indexProp != null && indexProp.getValue() != null) { |
| index = Integer.parseInt(indexProp.getValue().toString()); |
| } |
| return index; |
| } |
| |
| //-------------------------------------------------------------------------- |
| /** |
| * Execute a 'Workspace' operation. |
| */ |
| private HttpResponse execute(BaseDavRequest request, SessionInfo sessionInfo) throws RepositoryException { |
| try { |
| initMethod(request, sessionInfo, !isUnLockMethod(request)); |
| |
| HttpResponse response = executeRequest(sessionInfo, request); |
| request.checkSuccess(response); |
| return response; |
| } catch (IOException e) { |
| throw new RepositoryException(e); |
| } catch (DavException e) { |
| throw ExceptionConverter.generate(e, request); |
| } |
| } |
| |
| //--------------------------------------------------< RepositoryService >--- |
| |
| @Override |
| public IdFactory getIdFactory() { |
| return idFactory; |
| } |
| |
| @Override |
| public NameFactory getNameFactory() { |
| return nameFactory; |
| } |
| |
| @Override |
| public PathFactory getPathFactory() { |
| return pathFactory; |
| } |
| |
| @Override |
| public QValueFactory getQValueFactory() { |
| return qValueFactory; |
| } |
| |
| @Override |
| public ItemInfoCache getItemInfoCache(SessionInfo sessionInfo) throws RepositoryException { |
| return new ItemInfoCacheImpl(itemInfoCacheSize); |
| } |
| |
| @Override |
| public Map<String, QValue[]> getRepositoryDescriptors() throws RepositoryException { |
| if (descriptors.isEmpty()) { |
| ReportInfo info = new ReportInfo(JcrRemotingConstants.REPORT_REPOSITORY_DESCRIPTORS, ItemResourceConstants.NAMESPACE); |
| HttpReport request = null; |
| try { |
| request = new HttpReport(uriResolver.getRepositoryUri(), info); |
| HttpResponse response = executeRequest(null, request); |
| int sc = response.getStatusLine().getStatusCode(); |
| if (sc == HttpStatus.SC_UNAUTHORIZED |
| || sc == HttpStatus.SC_PROXY_AUTHENTICATION_REQUIRED) { |
| // JCR-3076: Mandatory authentication prevents us from |
| // accessing the descriptors on the server, so instead |
| // of failing with an exception we simply return an empty |
| // set of descriptors |
| log.warn("Authentication required to access repository descriptors"); |
| return descriptors; |
| } |
| |
| request.checkSuccess(response); |
| Document doc = request.getResponseBodyAsDocument(response.getEntity()); |
| |
| if (doc != null) { |
| Element rootElement = doc.getDocumentElement(); |
| ElementIterator nsElems = DomUtil.getChildren(rootElement, JcrRemotingConstants.XML_DESCRIPTOR, ItemResourceConstants.NAMESPACE); |
| while (nsElems.hasNext()) { |
| Element elem = nsElems.nextElement(); |
| String key = DomUtil.getChildText(elem, JcrRemotingConstants.XML_DESCRIPTORKEY, ItemResourceConstants.NAMESPACE); |
| ElementIterator it = DomUtil.getChildren(elem, JcrRemotingConstants.XML_DESCRIPTORVALUE, ItemResourceConstants.NAMESPACE); |
| List<QValue> vs = new ArrayList<QValue>(); |
| while (it.hasNext()) { |
| Element dv = it.nextElement(); |
| String descriptor = DomUtil.getText(dv); |
| if (key != null && descriptor != null) { |
| String typeStr = (DomUtil.getAttribute(dv, JcrRemotingConstants.ATTR_VALUE_TYPE, null)); |
| int type = (typeStr == null) ? PropertyType.STRING : PropertyType.valueFromName(typeStr); |
| vs.add(getQValueFactory().create(descriptor, type)); |
| } else { |
| log.error("Invalid descriptor key / value pair: " + key + " -> " + descriptor); |
| } |
| |
| } |
| descriptors.put(key, vs.toArray(new QValue[vs.size()])); |
| } |
| } |
| } catch (IOException e) { |
| throw new RepositoryException(e); |
| } catch (DavException e) { |
| throw ExceptionConverter.generate(e); |
| } finally { |
| if (request != null) { |
| request.releaseConnection(); |
| } |
| } |
| } |
| return descriptors; |
| } |
| |
| @Override |
| public SessionInfo obtain(Credentials credentials, String workspaceName) |
| throws RepositoryException { |
| CredentialsWrapper dc = new CredentialsWrapper(credentials); |
| return obtain(dc, workspaceName); |
| } |
| |
| @Override |
| public SessionInfo obtain(SessionInfo sessionInfo, String workspaceName) |
| throws RepositoryException { |
| checkSessionInfo(sessionInfo); |
| return obtain(((SessionInfoImpl) sessionInfo).getCredentials(), workspaceName); |
| } |
| |
| @Override |
| public SessionInfo impersonate(SessionInfo sessionInfo, Credentials credentials) throws RepositoryException { |
| throw new UnsupportedOperationException("Not implemented yet."); |
| } |
| |
| private SessionInfo obtain(CredentialsWrapper credentials, String workspaceName) |
| throws RepositoryException { |
| // check if the workspace with the given name is accessible |
| HttpPropfind request = null; |
| SessionInfoImpl sessionInfo = new SessionInfoImpl(credentials, workspaceName); |
| try { |
| DavPropertyNameSet nameSet = new DavPropertyNameSet(); |
| // for backwards compat. -> retrieve DAV:workspace if the newly |
| // added property (workspaceName) is not supported by the server. |
| nameSet.add(DeltaVConstants.WORKSPACE); |
| nameSet.add(JcrRemotingConstants.JCR_WORKSPACE_NAME_LN, ItemResourceConstants.NAMESPACE); |
| |
| request = new HttpPropfind(uriResolver.getWorkspaceUri(workspaceName), nameSet, DEPTH_0); |
| HttpResponse response = executeRequest(sessionInfo, request); |
| request.checkSuccess(response); |
| |
| MultiStatusResponse[] responses = request.getResponseBodyAsMultiStatus(response).getResponses(); |
| if (responses.length != 1) { |
| throw new LoginException("Login failed: Unknown workspace '" + workspaceName + "'."); |
| } |
| |
| DavPropertySet props = responses[0].getProperties(DavServletResponse.SC_OK); |
| DavProperty<?> prop = props.get(JcrRemotingConstants.JCR_WORKSPACE_NAME_LN, ItemResourceConstants.NAMESPACE); |
| if (prop != null) { |
| String wspName = prop.getValue().toString(); |
| if (workspaceName == null) { |
| // login with 'null' workspace name -> retrieve the effective |
| // workspace name from the property and recreate the SessionInfo. |
| sessionInfo = new SessionInfoImpl(credentials, wspName); |
| } else if (!wspName.equals(workspaceName)) { |
| throw new LoginException("Login failed: Invalid workspace name '" + workspaceName + "'."); |
| } |
| } else if (props.contains(DeltaVConstants.WORKSPACE)) { |
| String wspHref = new HrefProperty(props.get(DeltaVConstants.WORKSPACE)).getHrefs().get(0); |
| String wspName = Text.unescape(Text.getName(wspHref, true)); |
| if (!wspName.equals(workspaceName)) { |
| throw new LoginException("Login failed: Invalid workspace name " + workspaceName); |
| } |
| } else { |
| throw new LoginException("Login failed: Unknown workspace '" + workspaceName + "'."); |
| } |
| } catch (IOException e) { |
| throw new RepositoryException(e.getMessage(), e); |
| } catch (DavException e) { |
| throw ExceptionConverter.generate(e); |
| } finally { |
| if (request != null) { |
| request.releaseConnection(); |
| } |
| } |
| |
| // make sure the general namespace mappings have been loaded once |
| // before additional requests are executed that rely on the namespace |
| // mappings. |
| if (nsCache.prefixToURI.isEmpty()) { |
| try { |
| getRegisteredNamespaces(sessionInfo); |
| } catch (RepositoryException e) { |
| // ignore |
| } |
| } |
| |
| // return the sessionInfo |
| return sessionInfo; |
| } |
| |
| @Override |
| public void dispose(SessionInfo sessionInfo) throws RepositoryException { |
| checkSessionInfo(sessionInfo); |
| removeClient(sessionInfo); |
| } |
| |
| @Override |
| public String[] getWorkspaceNames(SessionInfo sessionInfo) throws RepositoryException { |
| DavPropertyNameSet nameSet = new DavPropertyNameSet(); |
| nameSet.add(DeltaVConstants.WORKSPACE); |
| HttpPropfind request = null; |
| try { |
| request = new HttpPropfind(uriResolver.getRepositoryUri(), nameSet, DEPTH_1); |
| HttpResponse response = executeRequest(sessionInfo, request); |
| request.checkSuccess(response); |
| MultiStatusResponse[] mresponses = request.getResponseBodyAsMultiStatus(response).getResponses(); |
| Set<String> wspNames = new HashSet<String>(); |
| for (MultiStatusResponse mresponse : mresponses) { |
| DavPropertySet props = mresponse.getProperties(DavServletResponse.SC_OK); |
| if (props.contains(DeltaVConstants.WORKSPACE)) { |
| HrefProperty hp = new HrefProperty(props.get(DeltaVConstants.WORKSPACE)); |
| String wspHref = hp.getHrefs().get(0); |
| String name = Text.unescape(Text.getName(wspHref, true)); |
| wspNames.add(name); |
| } |
| } |
| return wspNames.toArray(new String[wspNames.size()]); |
| } catch (IOException e) { |
| throw new RepositoryException(e); |
| } catch (DavException e) { |
| throw ExceptionConverter.generate(e); |
| } finally { |
| if (request != null) { |
| request.releaseConnection(); |
| } |
| } |
| } |
| |
| @Override |
| public boolean isGranted(SessionInfo sessionInfo, ItemId itemId, String[] actions) throws RepositoryException { |
| HttpReport request = null; |
| try { |
| String uri = obtainAbsolutePathFromUri(getItemUri(itemId, sessionInfo)); |
| ReportInfo reportInfo = new ReportInfo(JcrRemotingConstants.REPORT_PRIVILEGES, ItemResourceConstants.NAMESPACE); |
| reportInfo.setContentElement(DomUtil.hrefToXml(uri, DomUtil.createDocument())); |
| |
| request = new HttpReport(uriResolver.getWorkspaceUri(sessionInfo.getWorkspaceName()), reportInfo); |
| HttpResponse response = executeRequest(sessionInfo, request); |
| request.checkSuccess(response); |
| |
| MultiStatusResponse[] responses = request.getResponseBodyAsMultiStatus(response).getResponses(); |
| if (responses.length < 1) { |
| throw new ItemNotFoundException("Unable to retrieve permissions for item " + saveGetIdString(itemId, sessionInfo)); |
| } |
| DavProperty<?> p = responses[0].getProperties(DavServletResponse.SC_OK).get(SecurityConstants.CURRENT_USER_PRIVILEGE_SET); |
| if (p == null) { |
| return false; |
| } |
| // build set of privileges from given actions. NOTE: since the actions |
| // have no qualifying namespace, the {@link ItemResourceConstants#NAMESPACE} |
| // is used. |
| Set<Privilege> requiredPrivileges = new HashSet<Privilege>(); |
| for (String action : actions) { |
| requiredPrivileges.add(Privilege.getPrivilege(action, ItemResourceConstants.NAMESPACE)); |
| } |
| // build set of privileges granted to the current user. |
| CurrentUserPrivilegeSetProperty privSet = new CurrentUserPrivilegeSetProperty(p); |
| Collection<Privilege> privileges = privSet.getValue(); |
| |
| // check privileges present against required privileges. |
| return privileges.containsAll(requiredPrivileges); |
| } catch (IOException e) { |
| throw new RepositoryException(e); |
| } catch (ParserConfigurationException e) { |
| throw new RepositoryException(e); |
| } catch (DavException e) { |
| throw ExceptionConverter.generate(e); |
| } finally { |
| if (request != null) { |
| request.releaseConnection(); |
| } |
| } |
| } |
| |
| @Override |
| public PrivilegeDefinition[] getPrivilegeDefinitions(SessionInfo sessionInfo) throws RepositoryException { |
| return internalGetPrivilegeDefinitions(sessionInfo, uriResolver.getRepositoryUri()); |
| } |
| |
| @Override |
| public PrivilegeDefinition[] getSupportedPrivileges(SessionInfo sessionInfo, NodeId nodeId) throws RepositoryException { |
| String uri = (nodeId == null) ? uriResolver.getWorkspaceUri(sessionInfo.getWorkspaceName()) : getItemUri(nodeId, sessionInfo); |
| return internalGetPrivilegeDefinitions(sessionInfo, uri); |
| } |
| |
| @Override |
| public Name[] getPrivilegeNames(SessionInfo sessionInfo, NodeId nodeId) throws RepositoryException { |
| String uri = (nodeId == null) ? uriResolver.getWorkspaceUri(sessionInfo.getWorkspaceName()) : getItemUri(nodeId, sessionInfo); |
| DavPropertyNameSet nameSet = new DavPropertyNameSet(); |
| nameSet.add(SecurityConstants.CURRENT_USER_PRIVILEGE_SET); |
| |
| HttpPropfind propfindRequest = null; |
| try { |
| propfindRequest = new HttpPropfind(uri, nameSet, DEPTH_0); |
| HttpResponse response = execute(propfindRequest, sessionInfo); |
| propfindRequest.checkSuccess(response); |
| |
| MultiStatusResponse[] mresponses = propfindRequest.getResponseBodyAsMultiStatus(response).getResponses(); |
| if (mresponses.length < 1) { |
| throw new PathNotFoundException("Unable to retrieve privileges definitions."); |
| } |
| |
| DavPropertyName displayName = SecurityConstants.CURRENT_USER_PRIVILEGE_SET; |
| DavProperty<?> p = mresponses[0].getProperties(DavServletResponse.SC_OK).get(displayName); |
| if (p == null) { |
| return new Name[0]; |
| } else { |
| Collection<Privilege> privs = new CurrentUserPrivilegeSetProperty(p).getValue(); |
| Set<Name> privNames = new HashSet<Name>(privs.size()); |
| for (Privilege priv : privs) { |
| privNames.add(nameFactory.create(priv.getNamespace().getURI(), priv.getName())); |
| } |
| return privNames.toArray(new Name[privNames.size()]); |
| } |
| } catch (IOException e) { |
| throw new RepositoryException(e); |
| } catch (DavException e) { |
| throw ExceptionConverter.generate(e); |
| } finally { |
| if (propfindRequest != null) { |
| propfindRequest.releaseConnection(); |
| } |
| } |
| } |
| |
| private PrivilegeDefinition[] internalGetPrivilegeDefinitions(SessionInfo sessionInfo, String uri) throws RepositoryException { |
| DavPropertyNameSet nameSet = new DavPropertyNameSet(); |
| nameSet.add(SecurityConstants.SUPPORTED_PRIVILEGE_SET); |
| HttpPropfind request = null; |
| try { |
| request = new HttpPropfind(uri, nameSet, DEPTH_0); |
| HttpResponse response = executeRequest(sessionInfo, request); |
| request.checkSuccess(response); |
| |
| MultiStatusResponse[] mresponses = request.getResponseBodyAsMultiStatus(response).getResponses(); |
| if (mresponses.length < 1) { |
| throw new PathNotFoundException("Unable to retrieve privileges definitions."); |
| } |
| |
| DavPropertyName displayName = SecurityConstants.SUPPORTED_PRIVILEGE_SET; |
| DavProperty<?> p = mresponses[0].getProperties(DavServletResponse.SC_OK).get(displayName); |
| if (p == null) { |
| return new PrivilegeDefinition[0]; |
| } else { |
| // build PrivilegeDefinition(s) from the supported-privileges dav property |
| Map<Name, SupportedPrivilege> spMap = new HashMap<Name, SupportedPrivilege>(); |
| fillSupportedPrivilegeMap(new SupportedPrivilegeSetProperty(p).getValue(), spMap, getNameFactory()); |
| |
| List<PrivilegeDefinition> pDefs = new ArrayList<PrivilegeDefinition>(); |
| for (Name privilegeName : spMap.keySet()) { |
| SupportedPrivilege sp = spMap.get(privilegeName); |
| Set<Name> aggrnames = null; |
| SupportedPrivilege[] aggregates = sp.getSupportedPrivileges(); |
| if (aggregates != null && aggregates.length > 0) { |
| aggrnames = new HashSet<Name>(); |
| for (SupportedPrivilege aggregate : aggregates) { |
| Name aggregateName = nameFactory.create(aggregate.getPrivilege().getNamespace().getURI(), |
| aggregate.getPrivilege().getName()); |
| aggrnames.add(aggregateName); |
| } |
| } |
| PrivilegeDefinition def = new PrivilegeDefinitionImpl(privilegeName, sp.isAbstract(), aggrnames); |
| pDefs.add(def); |
| } |
| return pDefs.toArray(new PrivilegeDefinition[pDefs.size()]); |
| } |
| } catch (IOException e) { |
| throw new RepositoryException(e); |
| } catch (DavException e) { |
| throw ExceptionConverter.generate(e); |
| } finally { |
| if (request != null) { |
| request.releaseConnection(); |
| } |
| } |
| } |
| |
| private static void fillSupportedPrivilegeMap(List<SupportedPrivilege> sps, Map<Name, SupportedPrivilege> spMap, NameFactory nameFactory) throws NamespaceException, IllegalNameException { |
| for (SupportedPrivilege sp : sps) { |
| Privilege p = sp.getPrivilege(); |
| Name privName = nameFactory.create(p.getNamespace().getURI(), p.getName()); |
| spMap.put(privName, sp); |
| List<SupportedPrivilege> agg = Arrays.asList(sp.getSupportedPrivileges()); |
| if (!agg.isEmpty()) { |
| fillSupportedPrivilegeMap(agg, spMap, nameFactory); |
| } |
| } |
| } |
| |
| @Override |
| public QNodeDefinition getNodeDefinition(SessionInfo sessionInfo, NodeId nodeId) throws RepositoryException { |
| return (QNodeDefinition) getItemDefinition(sessionInfo, nodeId); |
| } |
| |
| @Override |
| public QPropertyDefinition getPropertyDefinition(SessionInfo sessionInfo, PropertyId propertyId) throws RepositoryException { |
| return (QPropertyDefinition) getItemDefinition(sessionInfo, propertyId); |
| } |
| |
| /** |
| * |
| * @param sessionInfo |
| * @param itemId |
| * @return |
| * @throws RepositoryException |
| */ |
| private QItemDefinition getItemDefinition(SessionInfo sessionInfo, ItemId itemId) throws RepositoryException { |
| // set of properties to be retrieved |
| DavPropertyNameSet nameSet = new DavPropertyNameSet(); |
| nameSet.add(JcrRemotingConstants.JCR_DEFINITION_LN, ItemResourceConstants.NAMESPACE); |
| nameSet.add(DavPropertyName.RESOURCETYPE); |
| |
| HttpPropfind request = null; |
| try { |
| String uri = getItemUri(itemId, sessionInfo); |
| request = new HttpPropfind(uri, nameSet, DEPTH_0); |
| HttpResponse response = executeRequest(sessionInfo, request); |
| request.checkSuccess(response); |
| |
| MultiStatusResponse[] mresponses = request.getResponseBodyAsMultiStatus(response).getResponses(); |
| if (mresponses.length < 1) { |
| throw new ItemNotFoundException( |
| "Unable to retrieve the item definition for " + saveGetIdString(itemId, sessionInfo)); |
| } |
| if (mresponses.length > 1) { |
| throw new RepositoryException( |
| "Internal error: ambigous item definition found '" + saveGetIdString(itemId, sessionInfo) + "'."); |
| } |
| DavPropertySet propertySet = mresponses[0].getProperties(DavServletResponse.SC_OK); |
| |
| // check if definition matches the type of the id |
| DavProperty<?> rType = propertySet.get(DavPropertyName.RESOURCETYPE); |
| if (rType.getValue() == null && itemId.denotesNode()) { |
| throw new RepositoryException("Internal error: requested node definition and got property definition."); |
| } |
| |
| NamePathResolver resolver = getNamePathResolver(sessionInfo); |
| |
| // build the definition |
| QItemDefinition definition = null; |
| DavProperty<?> prop = propertySet.get(JcrRemotingConstants.JCR_DEFINITION_LN, ItemResourceConstants.NAMESPACE); |
| if (prop != null) { |
| Object value = prop.getValue(); |
| if (value != null && value instanceof Element) { |
| Element idfElem = (Element) value; |
| if (itemId.denotesNode()) { |
| definition = DefinitionUtil.createQNodeDefinition(null, idfElem, resolver); |
| } else { |
| definition = DefinitionUtil.createQPropertyDefinition(null, idfElem, resolver, getQValueFactory()); |
| } |
| } |
| } |
| if (definition == null) { |
| throw new RepositoryException("Unable to retrieve definition for item with id '" + saveGetIdString(itemId, resolver) + "'."); |
| } |
| return definition; |
| } catch (IOException e) { |
| throw new RepositoryException(e); |
| } catch (DavException e) { |
| throw ExceptionConverter.generate(e); |
| } finally { |
| if (request != null) { |
| request.releaseConnection(); |
| } |
| } |
| } |
| |
| @Override |
| public NodeInfo getNodeInfo(SessionInfo sessionInfo, NodeId nodeId) throws RepositoryException { |
| // set of properties to be retrieved |
| DavPropertyNameSet nameSet = new DavPropertyNameSet(); |
| nameSet.add(JcrRemotingConstants.JCR_INDEX_LN, ItemResourceConstants.NAMESPACE); |
| nameSet.add(JcrRemotingConstants.JCR_PARENT_LN, ItemResourceConstants.NAMESPACE); |
| nameSet.add(JcrRemotingConstants.JCR_NAME_LN, ItemResourceConstants.NAMESPACE); |
| nameSet.add(JcrRemotingConstants.JCR_PRIMARYNODETYPE_LN, ItemResourceConstants.NAMESPACE); |
| nameSet.add(JcrRemotingConstants.JCR_MIXINNODETYPES_LN, ItemResourceConstants.NAMESPACE); |
| nameSet.add(JcrRemotingConstants.JCR_REFERENCES_LN, ItemResourceConstants.NAMESPACE); |
| nameSet.add(JcrRemotingConstants.JCR_UUID_LN, ItemResourceConstants.NAMESPACE); |
| nameSet.add(JcrRemotingConstants.JCR_PATH_LN, ItemResourceConstants.NAMESPACE); |
| nameSet.add(DavPropertyName.RESOURCETYPE); |
| |
| HttpPropfind request = null; |
| try { |
| String uri = getItemUri(nodeId, sessionInfo); |
| request = new HttpPropfind(uri, nameSet, DEPTH_1); |
| HttpResponse response = executeRequest(sessionInfo, request); |
| request.checkSuccess(response); |
| |
| MultiStatusResponse[] mresponses = request.getResponseBodyAsMultiStatus(response).getResponses(); |
| if (mresponses.length < 1) { |
| throw new ItemNotFoundException("Unable to retrieve the node with id " + saveGetIdString(nodeId, sessionInfo)); |
| } |
| |
| MultiStatusResponse nodeResponse = null; |
| List<MultiStatusResponse> childResponses = new ArrayList<MultiStatusResponse>(); |
| for (MultiStatusResponse mresponse : mresponses) { |
| if (isSameResource(uri, mresponse)) { |
| nodeResponse = mresponse; |
| } else { |
| childResponses.add(mresponse); |
| } |
| } |
| |
| if (nodeResponse == null) { |
| throw new ItemNotFoundException("Unable to retrieve the node " + saveGetIdString(nodeId, sessionInfo)); |
| } |
| |
| DavPropertySet propSet = nodeResponse.getProperties(DavServletResponse.SC_OK); |
| Object type = propSet.get(DavPropertyName.RESOURCETYPE).getValue(); |
| if (type == null) { |
| // the given id points to a Property instead of a Node |
| throw new ItemNotFoundException("No node for id " + saveGetIdString(nodeId, sessionInfo)); |
| } |
| |
| NamePathResolver resolver = getNamePathResolver(sessionInfo); |
| NodeId parentId = getParentId(uri, propSet, sessionInfo); |
| |
| NodeInfoImpl nInfo = buildNodeInfo(uri, nodeResponse, parentId, propSet, sessionInfo, resolver); |
| |
| for (MultiStatusResponse resp : childResponses) { |
| DavPropertySet childProps = resp.getProperties(DavServletResponse.SC_OK); |
| if (childProps.contains(DavPropertyName.RESOURCETYPE) && |
| childProps.get(DavPropertyName.RESOURCETYPE).getValue() != null) { |
| // any other resource type than default (empty) is represented by a node item |
| // --> build child info object |
| nInfo.addChildInfo(buildChildInfo(childProps, sessionInfo)); |
| } else { |
| PropertyId childId = uriResolver.buildPropertyId(nInfo.getId(), resp, sessionInfo.getWorkspaceName(), getNamePathResolver(sessionInfo)); |
| nInfo.addPropertyId(childId); |
| } |
| } |
| return nInfo; |
| } catch (IOException e) { |
| throw new RepositoryException(e); |
| } catch (DavException e) { |
| throw ExceptionConverter.generate(e); |
| } catch (NameException e) { |
| throw new RepositoryException(e); |
| } finally { |
| if (request != null) { |
| request.releaseConnection(); |
| } |
| } |
| } |
| |
| @Override |
| public Iterator<? extends ItemInfo> getItemInfos(SessionInfo sessionInfo, ItemId itemId) throws RepositoryException { |
| // TODO: implement batch read properly: |
| // currently: missing 'value/values' property PropertyInfo cannot be built |
| // currently: missing prop-names with child-NodeInfo |
| if (itemId.denotesNode()) { |
| List<ItemInfo> l = new ArrayList<ItemInfo>(); |
| NodeInfo nInfo = getNodeInfo(sessionInfo, (NodeId) itemId); |
| l.add(nInfo); |
| // at least add propertyInfos for the meta-props already known from the |
| // nodeInfo. |
| l.addAll(buildPropertyInfos(nInfo)); |
| return l.iterator(); |
| } else { |
| PropertyInfo pInfo = getPropertyInfo(sessionInfo, (PropertyId) itemId); |
| return Iterators.singleton(pInfo); |
| } |
| } |
| |
| private NodeInfoImpl buildNodeInfo(String baseUri, MultiStatusResponse nodeResponse, |
| NodeId parentId, DavPropertySet propSet, |
| SessionInfo sessionInfo, |
| NamePathResolver resolver) throws RepositoryException { |
| NodeId id = uriResolver.buildNodeId(parentId, baseUri, nodeResponse, sessionInfo.getWorkspaceName(), getNamePathResolver(sessionInfo)); |
| NodeInfoImpl nInfo = new NodeInfoImpl(id, propSet, resolver); |
| DavProperty p = propSet.get(JcrRemotingConstants.JCR_REFERENCES_LN, ItemResourceConstants.NAMESPACE); |
| if (p != null) { |
| HrefProperty refProp = new HrefProperty(p); |
| for (String propertyHref : refProp.getHrefs()) { |
| PropertyId propertyId = uriResolver.getPropertyId(propertyHref, sessionInfo); |
| nInfo.addReference(propertyId); |
| } |
| } |
| return nInfo; |
| } |
| |
| private List<PropertyInfo> buildPropertyInfos(NodeInfo nInfo) throws RepositoryException { |
| List<PropertyInfo> l = new ArrayList<PropertyInfo>(3); |
| NodeId nid = nInfo.getId(); |
| Path nPath = nInfo.getPath(); |
| |
| if (nid.getPath() == null) { |
| PropertyId id = getIdFactory().createPropertyId(nid, NameConstants.JCR_UUID); |
| QValue[] vs = new QValue[] { getQValueFactory().create(nid.getUniqueID(), PropertyType.STRING) }; |
| Path p = getPathFactory().create(nPath, NameConstants.JCR_UUID, true); |
| PropertyInfo pi = new PropertyInfoImpl(id, p, PropertyType.STRING, false, vs); |
| l.add(pi); |
| } |
| |
| Name pName = NameConstants.JCR_PRIMARYTYPE; |
| QValue[] vs = new QValue[] { getQValueFactory().create(nInfo.getNodetype()) }; |
| PropertyInfo pi = new PropertyInfoImpl(getIdFactory().createPropertyId(nid, pName), |
| getPathFactory().create(nPath, pName, true), PropertyType.NAME, false, vs); |
| l.add(pi); |
| |
| Name[] mixins = nInfo.getMixins(); |
| if (mixins.length > 0) { |
| pName = NameConstants.JCR_MIXINTYPES; |
| vs = new QValue[mixins.length]; |
| for (int i = 0; i < mixins.length; i++) { |
| vs[i] = getQValueFactory().create(mixins[i]); |
| } |
| pi = new PropertyInfoImpl(getIdFactory().createPropertyId(nid, pName), |
| getPathFactory().create(nPath, pName, true), PropertyType.NAME, |
| true, vs); |
| l.add(pi); |
| } |
| |
| return l; |
| } |
| |
| @Override |
| public Iterator<ChildInfo> getChildInfos(SessionInfo sessionInfo, NodeId parentId) throws RepositoryException { |
| // set of properties to be retrieved |
| DavPropertyNameSet nameSet = new DavPropertyNameSet(); |
| nameSet.add(JcrRemotingConstants.JCR_NAME_LN, ItemResourceConstants.NAMESPACE); |
| nameSet.add(JcrRemotingConstants.JCR_INDEX_LN, ItemResourceConstants.NAMESPACE); |
| nameSet.add(JcrRemotingConstants.JCR_PARENT_LN, ItemResourceConstants.NAMESPACE); |
| nameSet.add(JcrRemotingConstants.JCR_UUID_LN, ItemResourceConstants.NAMESPACE); |
| nameSet.add(DavPropertyName.RESOURCETYPE); |
| |
| HttpPropfind request = null; |
| try { |
| String uri = getItemUri(parentId, sessionInfo); |
| request = new HttpPropfind(uri, nameSet, DEPTH_1); |
| HttpResponse response = executeRequest(sessionInfo, request); |
| request.checkSuccess(response); |
| |
| List<ChildInfo> childEntries; |
| MultiStatusResponse[] mresponses = request.getResponseBodyAsMultiStatus(response).getResponses(); |
| if (mresponses.length < 1) { |
| throw new ItemNotFoundException("Unable to retrieve the node with id " + saveGetIdString(parentId, sessionInfo)); |
| } else if (mresponses.length == 1) { |
| // no child nodes nor properties |
| childEntries = Collections.emptyList(); |
| return childEntries.iterator(); |
| } |
| |
| childEntries = new ArrayList<ChildInfo>(); |
| for (MultiStatusResponse mresponse : mresponses) { |
| if (!isSameResource(uri, mresponse)) { |
| DavPropertySet childProps = mresponse.getProperties(DavServletResponse.SC_OK); |
| if (childProps.contains(DavPropertyName.RESOURCETYPE) && |
| childProps.get(DavPropertyName.RESOURCETYPE).getValue() != null) { |
| childEntries.add(buildChildInfo(childProps, sessionInfo)); |
| } // else: property -> ignore |
| } // else: ignore the response related to the parent |
| } |
| return childEntries.iterator(); |
| } catch (IOException e) { |
| throw new RepositoryException(e); |
| } catch (DavException e) { |
| throw ExceptionConverter.generate(e); |
| } finally { |
| if (request != null) { |
| request.releaseConnection(); |
| } |
| } |
| } |
| |
| private ChildInfo buildChildInfo(DavPropertySet properties, SessionInfo sessionInfo) throws RepositoryException { |
| Name qName = getQName(properties, getNamePathResolver(sessionInfo)); |
| int index = getIndex(properties); |
| String uuid = getUniqueID(properties); |
| |
| return new ChildInfoImpl(qName, uuid, index); |
| } |
| |
| @Override |
| public Iterator<PropertyId> getReferences(SessionInfo sessionInfo, NodeId nodeId, Name propertyName, boolean weakReferences) throws RepositoryException { |
| // set of properties to be retrieved |
| DavPropertyNameSet nameSet = new DavPropertyNameSet(); |
| String refType = weakReferences ? JcrRemotingConstants.JCR_WEAK_REFERENCES_LN : JcrRemotingConstants.JCR_REFERENCES_LN; |
| nameSet.add(refType, ItemResourceConstants.NAMESPACE); |
| |
| HttpPropfind request = null; |
| try { |
| String uri = getItemUri(nodeId, sessionInfo); |
| request = new HttpPropfind(uri, nameSet, DEPTH_0); |
| HttpResponse response = executeRequest(sessionInfo, request); |
| request.checkSuccess(response); |
| |
| MultiStatusResponse[] mresponses = request.getResponseBodyAsMultiStatus(response).getResponses(); |
| if (mresponses.length < 1) { |
| throw new ItemNotFoundException("Unable to retrieve the node with id " + saveGetIdString(nodeId, sessionInfo)); |
| } |
| |
| List<PropertyId> refIds = Collections.emptyList(); |
| for (MultiStatusResponse mresponse : mresponses) { |
| if (isSameResource(uri, mresponse)) { |
| DavPropertySet props = mresponse.getProperties(DavServletResponse.SC_OK); |
| DavProperty<?> p = props.get(refType, ItemResourceConstants.NAMESPACE); |
| |
| if (p != null) { |
| refIds = new ArrayList<PropertyId>(); |
| HrefProperty hp = new HrefProperty(p); |
| for (String propHref : hp.getHrefs()) { |
| PropertyId propId = uriResolver.getPropertyId(resolve(uri, propHref), sessionInfo); |
| if (propertyName == null || propertyName.equals(propId.getName())) { |
| refIds.add(propId); |
| } |
| } |
| } |
| } |
| } |
| return refIds.iterator(); |
| } catch (IOException e) { |
| throw new RepositoryException(e); |
| } catch (DavException e) { |
| throw ExceptionConverter.generate(e); |
| } finally { |
| if (request != null) { |
| request.releaseConnection(); |
| } |
| } |
| } |
| |
| @Override |
| public PropertyInfo getPropertyInfo(SessionInfo sessionInfo, PropertyId propertyId) throws RepositoryException { |
| HttpGet request = null; |
| try { |
| String uri = getItemUri(propertyId, sessionInfo); |
| request = new HttpGet(uri); |
| HttpResponse response = executeRequest(sessionInfo, request); |
| |
| int status = response.getStatusLine().getStatusCode(); |
| if (status != DavServletResponse.SC_OK) { |
| throw ExceptionConverter.generate(new DavException(status, response.getStatusLine().getReasonPhrase())); |
| } |
| |
| Path path = uriResolver.getQPath(uri, sessionInfo); |
| |
| HttpEntity entity = response.getEntity(); |
| ContentType ct = ContentType.get(entity); |
| |
| boolean isMultiValued; |
| QValue[] values; |
| int type; |
| |
| NamePathResolver resolver = getNamePathResolver(sessionInfo); |
| |
| if (ct != null && ct.getMimeType().startsWith("jcr-value")) { |
| type = JcrValueType.typeFromContentType(ct.getMimeType()); |
| QValue v; |
| if (type == PropertyType.BINARY) { |
| v = getQValueFactory().create(entity.getContent()); |
| } else { |
| Reader reader = new InputStreamReader(entity.getContent(), ct.getCharset()); |
| StringBuffer sb = new StringBuffer(); |
| int c; |
| while ((c = reader.read()) > -1) { |
| sb.append((char) c); |
| } |
| Value jcrValue = valueFactory.createValue(sb.toString(), type); |
| if (jcrValue instanceof QValueValue) { |
| v = ((QValueValue) jcrValue).getQValue(); |
| } else { |
| v = ValueFormat.getQValue(jcrValue, resolver, getQValueFactory()); |
| } |
| } |
| values = new QValue[] { v }; |
| isMultiValued = false; |
| } else if (ct != null && ct.getMimeType().equals("text/xml")) { |
| // jcr:values property spooled |
| values = getValues(entity.getContent(), resolver, propertyId); |
| type = (values.length > 0) ? values[0].getType() : loadType(uri, getClient(sessionInfo), propertyId, sessionInfo, resolver); |
| isMultiValued = true; |
| } else { |
| throw new ItemNotFoundException("Unable to retrieve the property with id " + saveGetIdString(propertyId, resolver)); |
| } |
| |
| return new PropertyInfoImpl(propertyId, path, type, isMultiValued, values); |
| } catch (IOException e) { |
| throw new RepositoryException(e); |
| } catch (DavException e) { |
| throw ExceptionConverter.generate(e); |
| } catch (NameException e) { |
| throw new RepositoryException(e); |
| } finally { |
| if (request != null) { |
| request.releaseConnection(); |
| } |
| } |
| } |
| |
| private QValue[] getValues(InputStream response, NamePathResolver resolver, ItemId id) throws RepositoryException { |
| try { |
| Document doc = DomUtil.parseDocument(response); |
| Element prop = DomUtil.getChildElement(doc, JcrRemotingConstants.JCR_VALUES_LN, ItemResourceConstants.NAMESPACE); |
| if (prop == null) { |
| // no jcr-values present in the response body -> apparently |
| // not representation of a jcr-property |
| throw new ItemNotFoundException("No property found at " + saveGetIdString(id, resolver)); |
| } else { |
| DavProperty<?> p = DefaultDavProperty.createFromXml(prop); |
| Value[] jcrVs = ValueUtil.valuesFromXml(p.getValue(), PropertyType.STRING, valueFactory); |
| QValue[] qvs = new QValue[jcrVs.length]; |
| int type = (jcrVs.length > 0) ? jcrVs[0].getType() : PropertyType.STRING; |
| |
| for (int i = 0; i < jcrVs.length; i++) { |
| if (jcrVs[i] instanceof QValueValue) { |
| qvs[i] = ((QValueValue) jcrVs[i]).getQValue(); |
| } else if (type == PropertyType.BINARY) { |
| qvs[i] = qValueFactory.create(jcrVs[i].getStream()); |
| } else { |
| qvs[i] = ValueFormat.getQValue(jcrVs[i], resolver, qValueFactory); |
| } |
| } |
| return qvs; |
| } |
| } catch (SAXException e) { |
| log.warn("Internal error: {}", e.getMessage()); |
| throw new RepositoryException(e); |
| } catch (IOException e) { |
| log.warn("Internal error: {}", e.getMessage()); |
| throw new RepositoryException(e); |
| } catch (ParserConfigurationException e) { |
| log.warn("Internal error: {}", e.getMessage()); |
| throw new RepositoryException(e); |
| } |
| } |
| |
| private int loadType(String propertyURI, HttpClient client, PropertyId propertyId, SessionInfo sessionInfo, NamePathResolver resolver) throws IOException, DavException, RepositoryException { |
| DavPropertyNameSet nameSet = new DavPropertyNameSet(); |
| nameSet.add(JcrRemotingConstants.JCR_TYPE_LN, ItemResourceConstants.NAMESPACE); |
| |
| HttpPropfind request = null; |
| try { |
| request = new HttpPropfind(propertyURI, nameSet, DEPTH_0); |
| HttpResponse response = executeRequest(sessionInfo, request); |
| request.checkSuccess(response); |
| |
| MultiStatusResponse[] mresponses = request.getResponseBodyAsMultiStatus(response).getResponses(); |
| if (mresponses.length == 1) { |
| DavPropertySet props = mresponses[0].getProperties(DavServletResponse.SC_OK); |
| DavProperty<?> type = props.get(JcrRemotingConstants.JCR_TYPE_LN, ItemResourceConstants.NAMESPACE); |
| if (type != null) { |
| return PropertyType.valueFromName(type.getValue().toString()); |
| } else { |
| throw new RepositoryException("Internal error. Cannot retrieve property type at " + saveGetIdString(propertyId, resolver)); |
| } |
| } else { |
| throw new ItemNotFoundException("Internal error. Cannot retrieve property type at " + saveGetIdString(propertyId, resolver)); |
| } |
| } finally { |
| if (request != null) { |
| request.releaseConnection(); |
| } |
| } |
| } |
| |
| @Override |
| public Batch createBatch(SessionInfo sessionInfo, ItemId itemId) throws RepositoryException { |
| checkSessionInfo(sessionInfo); |
| return new BatchImpl(itemId, sessionInfo); |
| } |
| |
| @Override |
| public void submit(Batch batch) throws RepositoryException { |
| if (!(batch instanceof BatchImpl)) { |
| throw new RepositoryException("Unknown Batch implementation."); |
| } |
| BatchImpl batchImpl = (BatchImpl) batch; |
| if (batchImpl.isEmpty()) { |
| batchImpl.dispose(); |
| return; |
| } |
| |
| HttpRequestBase request = null; |
| try { |
| HttpClient client = batchImpl.start(); |
| boolean success = false; |
| |
| try { |
| Iterator<HttpRequestBase> it = batchImpl.requests(); |
| while (it.hasNext()) { |
| request = it.next(); |
| initMethod(request, batchImpl, true); |
| |
| HttpResponse response = client.execute(request); |
| if (request instanceof BaseDavRequest) { |
| ((BaseDavRequest) request).checkSuccess(response); |
| } else { |
| // use generic HTTP status code checking |
| int statusCode = response.getStatusLine().getStatusCode(); |
| if (statusCode < 200 || statusCode >= 300) { |
| throw new DavException(statusCode, "Unexpected status code " + statusCode + " in response to " |
| + request.getMethod() + " request."); |
| } |
| } |
| request.releaseConnection(); |
| } |
| success = true; |
| } finally { |
| // make sure the lock is removed. if any of the methods |
| // failed the unlock is used to abort any pending changes |
| // on the server. |
| batchImpl.end(client, success); |
| } |
| } catch (IOException e) { |
| throw new RepositoryException(e); |
| } catch (DavException e) { |
| throw ExceptionConverter.generate(e, request); |
| } finally { |
| batchImpl.dispose(); |
| } |
| } |
| |
| @Override |
| public Tree createTree(SessionInfo sessionInfo, Batch batch, Name nodeName, Name primaryTypeName, String uniqueId) throws RepositoryException { |
| return new DocumentTree(nodeName, primaryTypeName, uniqueId, getNamePathResolver(sessionInfo)); |
| } |
| |
| @Override |
| public void importXml(SessionInfo sessionInfo, NodeId parentId, InputStream xmlStream, int uuidBehaviour) throws RepositoryException { |
| // TODO: improve. currently random name is built instead of retrieving name of new resource from top-level xml element within stream |
| Name nodeName = getNameFactory().create(Name.NS_DEFAULT_URI, UUID.randomUUID().toString()); |
| String uri = getItemUri(parentId, nodeName, sessionInfo); |
| HttpMkcol mkcolRequest = new HttpMkcol(uri); |
| mkcolRequest.addHeader(JcrRemotingConstants.IMPORT_UUID_BEHAVIOR, Integer.toString(uuidBehaviour)); |
| mkcolRequest.setEntity(new InputStreamEntity(xmlStream, ContentType.create("text/xml"))); |
| execute(mkcolRequest, sessionInfo); |
| } |
| |
| @Override |
| public void move(SessionInfo sessionInfo, NodeId srcNodeId, NodeId destParentNodeId, Name destName) throws RepositoryException { |
| String uri = getItemUri(srcNodeId, sessionInfo); |
| String destUri = getItemUri(destParentNodeId, destName, sessionInfo); |
| if (isDavClass3(sessionInfo)) { |
| destUri = obtainAbsolutePathFromUri(destUri); |
| } |
| HttpMove request = new HttpMove(uri, destUri, false); |
| try { |
| initMethod(request, sessionInfo); |
| HttpResponse response = executeRequest(sessionInfo, request); |
| request.checkSuccess(response); |
| // need to clear the cache as the move may have affected nodes with |
| // uuid. |
| clearItemUriCache(sessionInfo); |
| } catch (IOException ex) { |
| throw new RepositoryException(ex); |
| } catch (DavException e) { |
| throw ExceptionConverter.generate(e, request); |
| } finally { |
| request.releaseConnection(); |
| } |
| } |
| |
| @Override |
| public void copy(SessionInfo sessionInfo, String srcWorkspaceName, NodeId srcNodeId, NodeId destParentNodeId, Name destName) throws RepositoryException { |
| String uri = uriResolver.getItemUri(srcNodeId, srcWorkspaceName, sessionInfo); |
| String destUri = getItemUri(destParentNodeId, destName, sessionInfo); |
| if (isDavClass3(sessionInfo)) { |
| destUri = obtainAbsolutePathFromUri(destUri); |
| } |
| HttpCopy request = new HttpCopy(uri, destUri, false, false); |
| try { |
| initMethod(request, sessionInfo); |
| HttpResponse response = executeRequest(sessionInfo, request); |
| request.checkSuccess(response); |
| } catch (IOException ex) { |
| throw new RepositoryException(ex); |
| } catch (DavException e) { |
| throw ExceptionConverter.generate(e, request); |
| } finally { |
| request.releaseConnection(); |
| } |
| } |
| |
| @Override |
| public void update(SessionInfo sessionInfo, NodeId nodeId, String srcWorkspaceName) throws RepositoryException { |
| String uri = getItemUri(nodeId, sessionInfo); |
| String workspUri = uriResolver.getWorkspaceUri(srcWorkspaceName); |
| |
| update(uri, null, new String[] { workspUri }, UpdateInfo.UPDATE_BY_WORKSPACE, false, sessionInfo); |
| } |
| |
| @Override |
| public void clone(SessionInfo sessionInfo, String srcWorkspaceName, NodeId srcNodeId, NodeId destParentNodeId, Name destName, boolean removeExisting) throws RepositoryException { |
| // TODO: missing implementation |
| throw new UnsupportedOperationException("Missing implementation"); |
| } |
| |
| @Override |
| public LockInfo getLockInfo(SessionInfo sessionInfo, NodeId nodeId) throws RepositoryException { |
| // set of Dav-properties to be retrieved |
| DavPropertyNameSet nameSet = new DavPropertyNameSet(); |
| nameSet.add(DavPropertyName.LOCKDISCOVERY); |
| nameSet.add(JcrRemotingConstants.JCR_PARENT_LN, ItemResourceConstants.NAMESPACE); |
| |
| HttpPropfind request = null; |
| try { |
| String uri = getItemUri(nodeId, sessionInfo); |
| request = new HttpPropfind(uri, nameSet, DEPTH_0); |
| initMethod(request, sessionInfo, false); |
| |
| HttpResponse response = executeRequest(sessionInfo, request); |
| request.checkSuccess(response); |
| |
| MultiStatusResponse[] mresponses = request.getResponseBodyAsMultiStatus(response).getResponses(); |
| if (mresponses.length != 1) { |
| throw new ItemNotFoundException( |
| "Unable to retrieve the LockInfo. No such node " + saveGetIdString(nodeId, sessionInfo)); |
| } |
| |
| DavPropertySet ps = mresponses[0].getProperties(DavServletResponse.SC_OK); |
| if (ps.contains(DavPropertyName.LOCKDISCOVERY)) { |
| DavProperty<?> p = ps.get(DavPropertyName.LOCKDISCOVERY); |
| LockDiscovery ld = LockDiscovery.createFromXml(p.toXml(DomUtil.createDocument())); |
| NodeId parentId = getParentId(uri, ps, sessionInfo); |
| return retrieveLockInfo(ld, sessionInfo, nodeId, parentId); |
| } else { |
| // no lock present |
| log.debug("No Lock present on node with id " + saveGetIdString(nodeId, sessionInfo)); |
| return null; |
| } |
| } catch (IOException e) { |
| throw new RepositoryException(e); |
| } catch (ParserConfigurationException e) { |
| throw new RepositoryException(e); |
| } catch (DavException e) { |
| throw ExceptionConverter.generate(e); |
| } finally { |
| if (request != null) { |
| request.releaseConnection(); |
| } |
| } |
| } |
| |
| @Override |
| public LockInfo lock(SessionInfo sessionInfo, NodeId nodeId, boolean deep, |
| boolean sessionScoped) throws RepositoryException { |
| return lock(sessionInfo, nodeId, deep, sessionScoped, Long.MAX_VALUE, null); |
| } |
| |
| @Override |
| public LockInfo lock(SessionInfo sessionInfo, NodeId nodeId, boolean deep, boolean sessionScoped, long timeoutHint, String ownerHint) throws RepositoryException { |
| HttpLock request = null; |
| try { |
| checkSessionInfo(sessionInfo); |
| long davTimeout = (timeoutHint == Long.MAX_VALUE) ? INFINITE_TIMEOUT : timeoutHint * 1000; |
| String ownerInfo = (ownerHint == null) ? sessionInfo.getUserID() : ownerHint; |
| |
| String uri = getItemUri(nodeId, sessionInfo); |
| Scope scope = (sessionScoped) ? ItemResourceConstants.EXCLUSIVE_SESSION : Scope.EXCLUSIVE; |
| request = new HttpLock(uri, |
| new org.apache.jackrabbit.webdav.lock.LockInfo(scope, Type.WRITE, ownerInfo, davTimeout, deep)); |
| HttpResponse response = execute(request, sessionInfo); |
| |
| String lockToken = request.getLockToken(response); |
| ((SessionInfoImpl) sessionInfo).addLockToken(lockToken, sessionScoped); |
| |
| LockDiscovery disc = request.getResponseBodyAsLockDiscovery(response); |
| return retrieveLockInfo(disc, sessionInfo, nodeId, null); |
| } catch (IOException e) { |
| throw new RepositoryException(e); |
| } catch (DavException e) { |
| throw ExceptionConverter.generate(e); |
| } finally { |
| if (request != null) { |
| request.releaseConnection(); |
| } |
| } |
| } |
| |
| @Override |
| public void refreshLock(SessionInfo sessionInfo, NodeId nodeId) throws RepositoryException { |
| checkSessionInfo(sessionInfo); |
| String uri = getItemUri(nodeId, sessionInfo); |
| // since sessionInfo does not allow to retrieve token by NodeId, |
| // pass all available lock tokens to the LOCK method (TODO: correct?) |
| Set<String> allLockTokens = ((SessionInfoImpl) sessionInfo).getAllLockTokens(); |
| String[] locktokens = allLockTokens.toArray(new String[allLockTokens.size()]); |
| HttpLock httpLock = null; |
| try { |
| httpLock = new HttpLock(uri, INFINITE_TIMEOUT, locktokens); |
| execute(httpLock, sessionInfo); |
| } finally { |
| if (httpLock != null) { |
| httpLock.releaseConnection(); |
| } |
| } |
| } |
| |
| @Override |
| public void unlock(SessionInfo sessionInfo, NodeId nodeId) throws RepositoryException { |
| checkSessionInfo(sessionInfo); |
| String uri = getItemUri(nodeId, sessionInfo); |
| // Note: since sessionInfo does not allow to identify the id of the |
| // lock holding node, we need to access the token via lockInfo |
| // TODO: review this. |
| LockInfoImpl lInfo = (LockInfoImpl) getLockInfo(sessionInfo, nodeId); |
| if (lInfo == null) { |
| throw new LockException("No Lock present on Node with id " + saveGetIdString(nodeId, sessionInfo)); |
| } |
| |
| String lockToken = lInfo.getActiveLock().getToken(); |
| boolean isSessionScoped = lInfo.isSessionScoped(); |
| |
| if (!((SessionInfoImpl) sessionInfo).getAllLockTokens().contains(lockToken)) { |
| throw new LockException("Lock " + lockToken + " not owned by this session"); |
| } |
| |
| HttpUnlock unlockRequest = new HttpUnlock(uri, lockToken); |
| try { |
| execute(unlockRequest, sessionInfo); |
| ((SessionInfoImpl) sessionInfo).removeLockToken(lockToken, isSessionScoped); |
| } finally { |
| unlockRequest.releaseConnection(); |
| } |
| } |
| |
| private LockInfo retrieveLockInfo(LockDiscovery lockDiscovery, SessionInfo sessionInfo, |
| NodeId nodeId, NodeId parentId) throws RepositoryException { |
| checkSessionInfo(sessionInfo); |
| List<ActiveLock> activeLocks = lockDiscovery.getValue(); |
| ActiveLock activeLock = null; |
| for (ActiveLock l : activeLocks) { |
| Scope sc = l.getScope(); |
| if (l.getType() == Type.WRITE && (Scope.EXCLUSIVE.equals(sc) || sc == ItemResourceConstants.EXCLUSIVE_SESSION)) { |
| if (activeLock != null) { |
| throw new RepositoryException("Node " + saveGetIdString(nodeId, sessionInfo) + " contains multiple exclusive write locks."); |
| } else { |
| activeLock = l; |
| } |
| } |
| } |
| if (activeLock == null) { |
| log.debug("No lock present on node " + saveGetIdString(nodeId, sessionInfo)); |
| return null; |
| } |
| |
| NodeId holder = null; |
| String lockroot = activeLock.getLockroot(); |
| if (activeLock.getLockroot() != null) { |
| holder = uriResolver.getNodeId(lockroot, sessionInfo); |
| } |
| |
| if (activeLock.isDeep() && holder == null && parentId != null) { |
| // deep lock, parent known, but holder is not |
| LockInfo pLockInfo = getLockInfo(sessionInfo, parentId); |
| if (pLockInfo != null) { |
| return pLockInfo; |
| } |
| } |
| return new LockInfoImpl(activeLock, holder == null ? nodeId : holder, ((SessionInfoImpl) sessionInfo).getAllLockTokens()); |
| } |
| |
| @Override |
| public NodeId checkin(SessionInfo sessionInfo, NodeId nodeId) throws RepositoryException { |
| String uri = getItemUri(nodeId, sessionInfo); |
| HttpCheckin request = new HttpCheckin(uri); |
| try { |
| initMethod(request, sessionInfo, !isUnLockMethod(request)); |
| HttpResponse response = executeRequest(sessionInfo, request); |
| request.checkSuccess(response); |
| org.apache.http.Header rh = response.getFirstHeader(DeltaVConstants.HEADER_LOCATION); |
| return uriResolver.getNodeId(resolve(uri, rh.getValue()), sessionInfo); |
| } catch (IOException e) { |
| throw new RepositoryException(e); |
| } catch (DavException ex) { |
| throw ExceptionConverter.generate(ex); |
| } finally { |
| request.releaseConnection(); |
| } |
| } |
| |
| @Override |
| public void checkout(SessionInfo sessionInfo, NodeId nodeId) throws RepositoryException { |
| String uri = getItemUri(nodeId, sessionInfo); |
| HttpCheckout request = new HttpCheckout(uri); |
| try { |
| initMethod(request, sessionInfo, !isUnLockMethod(request)); |
| HttpResponse response = executeRequest(sessionInfo, request); |
| request.checkSuccess(response); |
| } catch (IOException e) { |
| throw new RepositoryException(e); |
| } catch (DavException ex) { |
| throw ExceptionConverter.generate(ex); |
| } finally { |
| request.releaseConnection(); |
| } |
| } |
| |
| @Override |
| public void checkout(SessionInfo sessionInfo, NodeId nodeId, NodeId activityId) throws RepositoryException { |
| if (activityId == null) { |
| checkout(sessionInfo, nodeId); |
| } else { |
| // TODO |
| throw new UnsupportedOperationException("JCR-2104: JSR 283 Versioning. Implementation missing"); |
| } |
| } |
| |
| @Override |
| public NodeId checkpoint(SessionInfo sessionInfo, NodeId nodeId) throws RepositoryException { |
| // TODO review again. |
| NodeId vID = checkin(sessionInfo, nodeId); |
| checkout(sessionInfo, nodeId); |
| return vID; |
| } |
| |
| @Override |
| public NodeId checkpoint(SessionInfo sessionInfo, NodeId nodeId, NodeId activityId) throws RepositoryException { |
| if (activityId == null) { |
| return checkpoint(sessionInfo, nodeId); |
| } else { |
| // TODO |
| throw new UnsupportedOperationException("JCR-2104: JSR 283 Versioning. Implementation missing"); |
| } |
| } |
| |
| @Override |
| public void removeVersion(SessionInfo sessionInfo, NodeId versionHistoryId, NodeId versionId) throws RepositoryException { |
| String uri = getItemUri(versionId, sessionInfo); |
| HttpDelete request = new HttpDelete(uri); |
| try { |
| initMethod(request, sessionInfo, !isUnLockMethod(request)); |
| HttpResponse response = executeRequest(sessionInfo, request); |
| request.checkSuccess(response); |
| } catch (IOException ex) { |
| throw new RepositoryException(ex); |
| } catch (DavException ex) { |
| throw ExceptionConverter.generate(ex); |
| } finally { |
| request.releaseConnection(); |
| } |
| } |
| |
| @Override |
| public void restore(SessionInfo sessionInfo, NodeId nodeId, NodeId versionId, boolean removeExisting) throws RepositoryException { |
| String uri = getItemUri(nodeId, sessionInfo); |
| String vUri = getItemUri(versionId, sessionInfo); |
| |
| Path relPath = null; |
| if (!exists(sessionInfo, uri)) { |
| // restore with rel-Path part |
| Path path = nodeId.getPath(); |
| if (nodeId.getUniqueID() != null) { |
| uri = getItemUri(idFactory.createNodeId(nodeId.getUniqueID(), null), sessionInfo); |
| relPath = (path.isAbsolute()) ? getPathFactory().getRootPath().computeRelativePath(path) : path; |
| } else { |
| int degree = 0; |
| while (degree < path.getLength()) { |
| Path ancestorPath = path.getAncestor(degree); |
| NodeId parentId = idFactory.createNodeId(nodeId.getUniqueID(), ancestorPath); |
| if (exists(sessionInfo, getItemUri(parentId, sessionInfo))) { |
| uri = getItemUri(parentId, sessionInfo); |
| relPath = ancestorPath.computeRelativePath(path); |
| break; |
| } |
| degree++; |
| } |
| } |
| } |
| |
| update(uri, relPath, new String[] { vUri }, UpdateInfo.UPDATE_BY_VERSION, removeExisting, sessionInfo); |
| } |
| |
| private boolean exists(SessionInfo sInfo, String uri) { |
| HttpHead request = new HttpHead(uri); |
| try { |
| int statusCode = executeRequest(sInfo, request).getStatusLine().getStatusCode(); |
| return (statusCode == DavServletResponse.SC_OK); |
| } catch (IOException e) { |
| log.error("Unexpected error while testing existence of item.", e); |
| return false; |
| } catch (RepositoryException e) { |
| log.error(e.getMessage()); |
| return false; |
| } finally { |
| request.releaseConnection(); |
| } |
| } |
| |
| @Override |
| public void restore(SessionInfo sessionInfo, NodeId[] versionIds, boolean removeExisting) throws RepositoryException { |
| String uri = uriResolver.getWorkspaceUri(sessionInfo.getWorkspaceName()); |
| String[] vUris = new String[versionIds.length]; |
| for (int i = 0; i < versionIds.length; i++) { |
| vUris[i] = getItemUri(versionIds[i], sessionInfo); |
| } |
| |
| update(uri, null, vUris, UpdateInfo.UPDATE_BY_VERSION, removeExisting, sessionInfo); |
| } |
| |
| private void update(String uri, Path relPath, String[] updateSource, int updateType, boolean removeExisting, SessionInfo sessionInfo) throws RepositoryException { |
| HttpUpdate request = null; |
| try { |
| UpdateInfo uInfo; |
| String tmpUpdateSource[] = obtainAbsolutePathsFromUris(updateSource); |
| if (removeExisting || relPath != null) { |
| Element uElem = UpdateInfo.createUpdateElement(tmpUpdateSource, updateType, DomUtil.createDocument()); |
| if (removeExisting) { |
| DomUtil.addChildElement(uElem, JcrRemotingConstants.XML_REMOVEEXISTING, ItemResourceConstants.NAMESPACE); |
| } |
| if (relPath != null) { |
| DomUtil.addChildElement(uElem, JcrRemotingConstants.XML_RELPATH, ItemResourceConstants.NAMESPACE, getNamePathResolver(sessionInfo).getJCRPath(relPath)); |
| } |
| |
| uInfo = new UpdateInfo(uElem); |
| } else { |
| uInfo = new UpdateInfo(tmpUpdateSource, updateType, new DavPropertyNameSet()); |
| } |
| |
| request = new HttpUpdate(uri, uInfo); |
| initMethod(request, sessionInfo, !isUnLockMethod(request)); |
| HttpResponse response = executeRequest(sessionInfo, request); |
| request.checkSuccess(response); |
| } catch (IOException e) { |
| throw new RepositoryException(e); |
| } catch (ParserConfigurationException e) { |
| throw new RepositoryException(e); |
| } catch (DavException e) { |
| throw ExceptionConverter.generate(e); |
| } finally { |
| if (request != null) { |
| request.releaseConnection(); |
| } |
| } |
| } |
| |
| @Override |
| public Iterator<NodeId> merge(SessionInfo sessionInfo, NodeId nodeId, String srcWorkspaceName, boolean bestEffort) throws RepositoryException { |
| return merge(sessionInfo, nodeId, srcWorkspaceName, bestEffort, false); |
| } |
| |
| @Override |
| public Iterator<NodeId> merge(SessionInfo sessionInfo, NodeId nodeId, String srcWorkspaceName, boolean bestEffort, boolean isShallow) throws RepositoryException { |
| HttpMerge request = null; |
| try { |
| Document doc = DomUtil.createDocument(); |
| String wspHref = obtainAbsolutePathFromUri(uriResolver.getWorkspaceUri(srcWorkspaceName)); |
| Element mElem = MergeInfo.createMergeElement(new String[] { wspHref }, !bestEffort, false, doc); |
| if (isShallow) { |
| mElem.appendChild(DomUtil.depthToXml(false, doc)); |
| } |
| MergeInfo mInfo = new MergeInfo(mElem); |
| |
| String uri = getItemUri(nodeId, sessionInfo); |
| request = new HttpMerge(uri, mInfo); |
| initMethod(request, sessionInfo, !isUnLockMethod(request)); |
| HttpResponse response = executeRequest(sessionInfo, request); |
| request.checkSuccess(response); |
| |
| MultiStatusResponse[] resps = request.getResponseBodyAsMultiStatus(response).getResponses(); |
| List<NodeId> failedIds = new ArrayList<NodeId>(resps.length); |
| for (MultiStatusResponse resp : resps) { |
| String href = resolve(uri, resp.getHref()); |
| failedIds.add(uriResolver.getNodeId(href, sessionInfo)); |
| } |
| return failedIds.iterator(); |
| } catch (IOException e) { |
| throw new RepositoryException(e); |
| } catch (ParserConfigurationException e) { |
| throw new RepositoryException(e); |
| } catch (DavException e) { |
| throw ExceptionConverter.generate(e); |
| } finally { |
| if (request != null) { |
| request.releaseConnection(); |
| } |
| } |
| } |
| |
| @Override |
| public void resolveMergeConflict(SessionInfo sessionInfo, NodeId nodeId, NodeId[] mergeFailedIds, NodeId[] predecessorIds) throws RepositoryException { |
| HttpProppatch request = null; |
| try { |
| List<HrefProperty> changeList = new ArrayList<HrefProperty>(); |
| String[] mergeFailedHref = new String[mergeFailedIds.length]; |
| for (int i = 0; i < mergeFailedIds.length; i++) { |
| mergeFailedHref[i] = getItemUri(mergeFailedIds[i], sessionInfo); |
| } |
| changeList.add(new HrefProperty(VersionControlledResource.AUTO_MERGE_SET, mergeFailedHref, false)); |
| |
| if (predecessorIds != null && predecessorIds.length > 0) { |
| String[] pdcHrefs = new String[predecessorIds.length]; |
| for (int i = 0; i < predecessorIds.length; i++) { |
| pdcHrefs[i] = getItemUri(predecessorIds[i], sessionInfo); |
| } |
| changeList.add(new HrefProperty(VersionControlledResource.PREDECESSOR_SET, pdcHrefs, false)); |
| } |
| |
| request = new HttpProppatch(getItemUri(nodeId, sessionInfo), changeList); |
| initMethod(request, sessionInfo, !isUnLockMethod(request)); |
| HttpResponse response = executeRequest(sessionInfo, request); |
| request.checkSuccess(response); |
| } catch (IOException e) { |
| throw new RepositoryException(e); |
| } catch (DavException e) { |
| throw ExceptionConverter.generate(e); |
| } finally { |
| if (request != null) { |
| request.releaseConnection(); |
| } |
| } |
| } |
| |
| @Override |
| public void addVersionLabel(SessionInfo sessionInfo, NodeId versionHistoryId, NodeId versionId, Name label, boolean moveLabel) throws RepositoryException { |
| HttpLabel request = null; |
| try { |
| String uri = getItemUri(versionId, sessionInfo); |
| String strLabel = getNamePathResolver(sessionInfo).getJCRName(label); |
| request = new HttpLabel(uri, new LabelInfo(strLabel, moveLabel ? LabelInfo.TYPE_SET : LabelInfo.TYPE_ADD)); |
| initMethod(request, sessionInfo, !isUnLockMethod(request)); |
| HttpResponse response = executeRequest(sessionInfo, request); |
| request.checkSuccess(response); |
| } catch (IOException e) { |
| throw new RepositoryException(e); |
| } catch (DavException ex) { |
| throw ExceptionConverter.generate(ex); |
| } finally { |
| request.releaseConnection(); |
| } |
| } |
| |
| @Override |
| public void removeVersionLabel(SessionInfo sessionInfo, NodeId versionHistoryId, NodeId versionId, Name label) throws RepositoryException { |
| HttpLabel request = null; |
| try { |
| String uri = getItemUri(versionId, sessionInfo); |
| String strLabel = getNamePathResolver(sessionInfo).getJCRName(label); |
| request = new HttpLabel(uri, new LabelInfo(strLabel, LabelInfo.TYPE_REMOVE)); |
| initMethod(request, sessionInfo, !isUnLockMethod(request)); |
| HttpResponse response = executeRequest(sessionInfo, request); |
| request.checkSuccess(response); |
| } catch (IOException e) { |
| throw new RepositoryException(e); |
| } catch (DavException ex) { |
| throw ExceptionConverter.generate(ex); |
| } finally { |
| request.releaseConnection(); |
| } |
| } |
| |
| @Override |
| public NodeId createActivity(SessionInfo sessionInfo, String title) throws RepositoryException { |
| // TODO |
| throw new UnsupportedOperationException("JCR-2104: JSR 283 Versioning. Implementation missing"); |
| } |
| |
| @Override |
| public void removeActivity(SessionInfo sessionInfo, NodeId activityId) throws RepositoryException { |
| // TODO |
| throw new UnsupportedOperationException("JCR-2104: JSR 283 Versioning. Implementation missing"); |
| } |
| |
| @Override |
| public Iterator<NodeId> mergeActivity(SessionInfo sessionInfo, NodeId activityId) throws RepositoryException { |
| // TODO |
| throw new UnsupportedOperationException("JCR-2104: JSR 283 Versioning. Implementation missing"); |
| } |
| |
| @Override |
| public NodeId createConfiguration(SessionInfo sessionInfo, NodeId nodeId) throws RepositoryException { |
| // TODO |
| throw new UnsupportedOperationException("JCR-2104: JSR 283 Versioning. Implementation missing"); |
| } |
| |
| @Override |
| public String[] getSupportedQueryLanguages(SessionInfo sessionInfo) throws RepositoryException { |
| HttpOptions request = new HttpOptions(uriResolver.getWorkspaceUri(sessionInfo.getWorkspaceName())); |
| try { |
| HttpResponse response = executeRequest(sessionInfo, request); |
| int status = response.getStatusLine().getStatusCode(); |
| if (status != DavServletResponse.SC_OK) { |
| throw new DavException(status); |
| } |
| return request.getSearchGrammars(response).toArray(new String[0]); |
| } catch (IOException e) { |
| throw new RepositoryException(e); |
| } catch (DavException e) { |
| throw ExceptionConverter.generate(e); |
| } finally { |
| request.releaseConnection(); |
| } |
| } |
| |
| @Override |
| public String[] checkQueryStatement(SessionInfo sessionInfo, |
| String statement, |
| String language, |
| Map<String, String> namespaces) |
| throws RepositoryException { |
| // TODO implement |
| return new String[0]; |
| } |
| |
| @Override |
| public QueryInfo executeQuery(SessionInfo sessionInfo, String statement, String language, Map<String, String> namespaces, long limit, long offset, Map<String, QValue> values) throws RepositoryException { |
| HttpSearch request = null; |
| try { |
| String uri = uriResolver.getWorkspaceUri(sessionInfo.getWorkspaceName()); |
| SearchInfo sInfo = new SearchInfo( |
| language, ItemResourceConstants.NAMESPACE, |
| statement, namespaces); |
| |
| if (limit != -1) { |
| sInfo.setNumberResults(limit); |
| } |
| if (offset != -1) { |
| sInfo.setOffset(offset); |
| } |
| |
| if (!(values == null || values.isEmpty())) { |
| throw new UnsupportedOperationException("Implementation missing: JCR-2107"); |
| } |
| |
| request = new HttpSearch(uri, sInfo); |
| HttpResponse response = executeRequest(sessionInfo, request); |
| request.checkSuccess(response); |
| |
| MultiStatus ms = request.getResponseBodyAsMultiStatus(response); |
| NamePathResolver resolver = getNamePathResolver(sessionInfo); |
| return new QueryInfoImpl(ms, idFactory, resolver, valueFactory, getQValueFactory()); |
| } catch (IOException e) { |
| throw new RepositoryException(e); |
| } catch (DavException e) { |
| throw ExceptionConverter.generate(e); |
| } finally { |
| if (request != null) { |
| request.releaseConnection(); |
| } |
| } |
| } |
| |
| @Override |
| public EventFilter createEventFilter(SessionInfo sessionInfo, |
| int eventTypes, |
| Path absPath, |
| boolean isDeep, |
| String[] uuids, |
| Name[] nodeTypeNames, |
| boolean noLocal) |
| throws RepositoryException { |
| // resolve node type names |
| // todo what if new node types become available while event filter is still in use? |
| Set<Name> resolvedTypeNames = null; |
| if (nodeTypeNames != null) { |
| resolvedTypeNames = new HashSet<Name>(); |
| // make sure node type definitions are available |
| if (nodeTypeDefinitions.size() == 0) { |
| getQNodeTypeDefinitions(sessionInfo); |
| } |
| synchronized (nodeTypeDefinitions) { |
| for (Name nodeTypeName : nodeTypeNames) { |
| resolveNodeType(resolvedTypeNames, nodeTypeName); |
| } |
| } |
| } |
| return new EventFilterImpl(eventTypes, absPath, isDeep, uuids, |
| resolvedTypeNames, noLocal); |
| } |
| |
| @Override |
| public EventBundle[] getEvents(Subscription subscription, long timeout) |
| throws RepositoryException { |
| checkSubscription(subscription); |
| |
| EventSubscriptionImpl subscr = (EventSubscriptionImpl) subscription; |
| String rootUri = uriResolver.getRootItemUri(subscr.getSessionInfo().getWorkspaceName()); |
| |
| return poll(rootUri, subscr.getId(), timeout, subscr.getSessionInfo()); |
| } |
| |
| @Override |
| public EventBundle getEvents(SessionInfo sessionInfo, EventFilter filter, long after) throws RepositoryException { |
| // TODO: use filters remotely (JCR-3179) |
| |
| HttpGet request = null; |
| String rootUri = uriResolver.getWorkspaceUri(sessionInfo.getWorkspaceName()); |
| rootUri += "?type=journal"; // TODO should have a way to discover URI template |
| |
| try { |
| request = new HttpGet(rootUri); |
| request.addHeader("If-None-Match", "\"" + Long.toHexString(after) + "\""); // TODO |
| initMethod(request, sessionInfo); |
| |
| HttpResponse response = executeRequest(sessionInfo, request); |
| int status = response.getStatusLine().getStatusCode(); |
| if (status != 200) { |
| throw new RepositoryException("getEvents to " + rootUri + " failed with " + response.getStatusLine()); |
| } |
| |
| HttpEntity entity = response.getEntity(); |
| InputStream in = entity.getContent(); |
| Document doc = null; |
| if (in != null) { |
| // read response and try to build a xml document |
| try { |
| doc = DomUtil.parseDocument(in); |
| } catch (ParserConfigurationException e) { |
| throw new IOException("XML parser configuration error", e); |
| } catch (SAXException e) { |
| throw new IOException("XML parsing error", e); |
| } finally { |
| in.close(); |
| } |
| } |
| |
| List<Event> events = new ArrayList<Event>(); |
| |
| ElementIterator entries = DomUtil.getChildren(doc.getDocumentElement(), AtomFeedConstants.N_ENTRY); |
| while (entries.hasNext()) { |
| Element entryElem = entries.next(); |
| |
| Element contentElem = DomUtil.getChildElement(entryElem, AtomFeedConstants.N_CONTENT); |
| if (contentElem != null |
| && "application/vnd.apache.jackrabbit.event+xml".equals(contentElem.getAttribute("type"))) { |
| List<Event> el = buildEventList(contentElem, (SessionInfoImpl) sessionInfo, rootUri); |
| for (Event e : el) { |
| if (e.getDate() > after && (filter == null || filter.accept(e, false))) { |
| events.add(e); |
| } |
| } |
| } |
| } |
| |
| return new EventBundleImpl(events, false); |
| } catch (Exception ex) { |
| log.error("extracting events from journal feed", ex); |
| throw new RepositoryException("extracting events from journal feed: " + ex.getMessage(), ex); |
| } finally { |
| if (request != null) { |
| request.releaseConnection(); |
| } |
| } |
| } |
| |
| @Override |
| public Subscription createSubscription(SessionInfo sessionInfo, |
| EventFilter[] filters) |
| throws RepositoryException { |
| |
| checkSessionInfo(sessionInfo); |
| String rootUri = uriResolver.getRootItemUri(sessionInfo.getWorkspaceName()); |
| String subscriptionId = subscribe(rootUri, S_INFO, null, sessionInfo, null); |
| log.debug("Subscribed on server for session info " + sessionInfo); |
| |
| try { |
| checkEventFilterSupport(filters); |
| } catch (UnsupportedRepositoryOperationException ex) { |
| unsubscribe(rootUri, subscriptionId, sessionInfo); |
| throw (ex); |
| } |
| return new EventSubscriptionImpl(subscriptionId, (SessionInfoImpl) sessionInfo); |
| } |
| |
| @Override |
| public void updateEventFilters(Subscription subscription, |
| EventFilter[] filters) |
| throws RepositoryException { |
| // do nothing ... |
| // this is actually not correct because we listen for everything and |
| // rely on the client of the repository service to filter the events |
| checkEventFilterSupport(filters); |
| } |
| |
| private void checkEventFilterSupport(EventFilter[] filters) |
| throws UnsupportedRepositoryOperationException { |
| for (EventFilter ef : filters) { |
| if (ef instanceof EventFilterImpl) { |
| EventFilterImpl efi = (EventFilterImpl) ef; |
| if (efi.getNodeTypeNames() != null |
| && !remoteServerProvidesNodeTypes) { |
| throw new UnsupportedRepositoryOperationException( |
| "Remote server does not provide node type information in events"); |
| } |
| if (efi.getNoLocal() && !remoteServerProvidesNoLocalFlag) { |
| throw new UnsupportedRepositoryOperationException( |
| "Remote server does not provide local flag in events"); |
| } |
| } |
| } |
| } |
| |
| @Override |
| public void dispose(Subscription subscription) throws RepositoryException { |
| checkSubscription(subscription); |
| EventSubscriptionImpl subscr = (EventSubscriptionImpl) subscription; |
| String rootUri = uriResolver.getRootItemUri( |
| subscr.getSessionInfo().getWorkspaceName()); |
| unsubscribe(rootUri, subscr.getId(), subscr.getSessionInfo()); |
| } |
| |
| private String subscribe(String uri, SubscriptionInfo subscriptionInfo, |
| String subscriptionId, SessionInfo sessionInfo, |
| String batchId) throws RepositoryException { |
| HttpSubscribe request = null; |
| try { |
| request = new HttpSubscribe(uri, subscriptionInfo, subscriptionId); |
| initMethod(request, sessionInfo); |
| |
| if (batchId != null) { |
| // add batchId as separate header |
| CodedUrlHeader ch = new CodedUrlHeader(TransactionConstants.HEADER_TRANSACTIONID, batchId); |
| request.setHeader(ch.getHeaderName(), ch.getHeaderValue()); |
| } |
| |
| HttpResponse response = executeRequest(sessionInfo, request); |
| request.checkSuccess(response); |
| |
| org.apache.jackrabbit.webdav.observation.Subscription[] subs = request.getResponseBodyAsSubscriptionDiscovery(response) |
| .getValue(); |
| if (subs.length == 1) { |
| this.remoteServerProvidesNodeTypes = subs[0].eventsProvideNodeTypeInformation(); |
| this.remoteServerProvidesNoLocalFlag = subs[0].eventsProvideNoLocalFlag(); |
| } |
| |
| return request.getSubscriptionId(response); |
| } catch (IOException e) { |
| throw new RepositoryException(e); |
| } catch (DavException e) { |
| throw ExceptionConverter.generate(e); |
| } finally { |
| if (request != null) { |
| request.releaseConnection(); |
| } |
| } |
| } |
| |
| private void unsubscribe(String uri, String subscriptionId, SessionInfo sessionInfo) throws RepositoryException { |
| HttpUnsubscribe request = null; |
| try { |
| request = new HttpUnsubscribe(uri, subscriptionId); |
| initMethod(request, sessionInfo); |
| HttpResponse response = executeRequest(sessionInfo, request); |
| request.checkSuccess(response); |
| } catch (IOException e) { |
| throw new RepositoryException(e); |
| } catch (DavException e) { |
| throw ExceptionConverter.generate(e); |
| } finally { |
| if (request != null) { |
| request.releaseConnection(); |
| } |
| } |
| } |
| |
| private void resolveNodeType(Set<Name> resolved, Name ntName) { |
| if (!resolved.add(ntName)) { |
| return; |
| } |
| QNodeTypeDefinition def = nodeTypeDefinitions.get(ntName); |
| if (def != null) { |
| for (Name supertype : def.getSupertypes()) { |
| resolveNodeType(resolved, supertype); |
| } |
| } |
| } |
| |
| private EventBundle[] poll(String uri, String subscriptionId, long timeout, SessionInfoImpl sessionInfo) throws RepositoryException { |
| HttpPoll request = null; |
| try { |
| request = new HttpPoll(uri, subscriptionId, timeout); |
| HttpResponse response = executeRequest(sessionInfo, request); |
| request.checkSuccess(response); |
| |
| EventDiscovery disc = request.getResponseBodyAsEventDiscovery(response); |
| EventBundle[] events; |
| if (disc.isEmpty()) { |
| events = new EventBundle[0]; |
| } else { |
| Element discEl = disc.toXml(DomUtil.createDocument()); |
| ElementIterator it = DomUtil.getChildren(discEl, |
| ObservationConstants.N_EVENTBUNDLE); |
| List<EventBundle> bundles = new ArrayList<EventBundle>(); |
| while (it.hasNext()) { |
| Element bundleElement = it.nextElement(); |
| String value = DomUtil.getAttribute(bundleElement, |
| ObservationConstants.XML_EVENT_LOCAL, null); |
| // check if it matches a batch id recently submitted |
| boolean isLocal = false; |
| if (value != null) { |
| isLocal = Boolean.parseBoolean(value); |
| } |
| bundles.add(new EventBundleImpl( |
| buildEventList(bundleElement, sessionInfo, uri), |
| isLocal)); |
| } |
| events = bundles.toArray(new EventBundle[bundles.size()]); |
| } |
| return events; |
| } catch (IOException e) { |
| throw new RepositoryException(e); |
| } catch (ParserConfigurationException e) { |
| throw new RepositoryException(e); |
| } catch (DavException e) { |
| throw ExceptionConverter.generate(e); |
| } finally { |
| if (request != null) { |
| request.releaseConnection(); |
| } |
| } |
| } |
| |
| private List<Event> buildEventList(Element bundleElement, SessionInfoImpl sessionInfo, String baseUri) |
| throws RepositoryException { |
| List<Event> events = new ArrayList<Event>(); |
| ElementIterator eventElementIterator = DomUtil.getChildren(bundleElement, ObservationConstants.N_EVENT); |
| |
| String userId = null; |
| |
| // get user id from enclosing Atom entry element in case this was a feed |
| if (DomUtil.matches(bundleElement, AtomFeedConstants.N_ENTRY)) { |
| Element authorEl = DomUtil.getChildElement(bundleElement, AtomFeedConstants.N_AUTHOR); |
| Element nameEl = authorEl != null ? DomUtil.getChildElement(authorEl, AtomFeedConstants.N_NAME) : null; |
| if (nameEl != null) { |
| userId = DomUtil.getTextTrim(nameEl); |
| } |
| } |
| |
| while (eventElementIterator.hasNext()) { |
| Element evElem = eventElementIterator.nextElement(); |
| Element typeEl = DomUtil.getChildElement(evElem, ObservationConstants.N_EVENTTYPE); |
| EventType[] et = DefaultEventType.createFromXml(typeEl); |
| if (et.length == 0 || et.length > 1) { |
| // should not occur. |
| log.error("Ambiguous event type definition: expected one single event type."); |
| continue; |
| } |
| |
| String href = DomUtil.getChildTextTrim(evElem, XML_HREF, NAMESPACE); |
| |
| int type = EventUtil.getJcrEventType(et[0].getName()); |
| Path eventPath = null; |
| ItemId eventId = null; |
| NodeId parentId = null; |
| |
| if (href != null) { |
| href = resolve(baseUri, href); |
| try { |
| eventPath = uriResolver.getQPath(href, sessionInfo); |
| } catch (RepositoryException e) { |
| // should not occur |
| log.error("Internal error while building Event: ()", e.getMessage()); |
| continue; |
| } |
| |
| boolean isForNode = (type == Event.NODE_ADDED |
| || type == Event.NODE_REMOVED || type == Event.NODE_MOVED); |
| |
| try { |
| if (isForNode) { |
| eventId = uriResolver.getNodeIdAfterEvent(href, |
| sessionInfo, type == Event.NODE_REMOVED); |
| } else { |
| eventId = uriResolver.getPropertyId(href, sessionInfo); |
| } |
| } catch (RepositoryException e) { |
| if (isForNode) { |
| eventId = idFactory.createNodeId((String) null, eventPath); |
| } else { |
| try { |
| eventId = idFactory.createPropertyId( |
| idFactory.createNodeId((String) null, |
| eventPath.getAncestor(1)), |
| eventPath.getName()); |
| } catch (RepositoryException e1) { |
| log.warn("Unable to build event itemId: {}", |
| e.getMessage()); |
| } |
| } |
| } |
| |
| String parentHref = Text.getRelativeParent(href, 1, true); |
| try { |
| parentId = uriResolver.getNodeId(parentHref, sessionInfo); |
| } catch (RepositoryException e) { |
| log.warn("Unable to build event parentId: {}", e.getMessage()); |
| } |
| } |
| |
| if (userId == null) { |
| // user id not retrieved from container |
| userId = DomUtil.getChildTextTrim(evElem, ObservationConstants.N_EVENTUSERID); |
| } |
| |
| events.add(new EventImpl(eventId, eventPath, parentId, type, userId, evElem, |
| getNamePathResolver(sessionInfo), getQValueFactory())); |
| } |
| |
| return events; |
| } |
| |
| @Override |
| public Map<String, String> getRegisteredNamespaces(SessionInfo sessionInfo) throws RepositoryException { |
| ReportInfo info = new ReportInfo(JcrRemotingConstants.REPORT_REGISTERED_NAMESPACES, ItemResourceConstants.NAMESPACE); |
| HttpReport request = null; |
| try { |
| request = new HttpReport(uriResolver.getWorkspaceUri(sessionInfo.getWorkspaceName()), info); |
| HttpResponse response = executeRequest(sessionInfo, request); |
| request.checkSuccess(response); |
| |
| Document doc = request.getResponseBodyAsDocument(response.getEntity()); |
| Map<String, String> namespaces = new HashMap<String, String>(); |
| if (doc != null) { |
| Element rootElement = doc.getDocumentElement(); |
| ElementIterator nsElems = DomUtil.getChildren(rootElement, JcrRemotingConstants.XML_NAMESPACE, ItemResourceConstants.NAMESPACE); |
| while (nsElems.hasNext()) { |
| Element elem = nsElems.nextElement(); |
| String prefix = DomUtil.getChildText(elem, JcrRemotingConstants.XML_PREFIX, ItemResourceConstants.NAMESPACE); |
| String uri = DomUtil.getChildText(elem, JcrRemotingConstants.XML_URI, ItemResourceConstants.NAMESPACE); |
| // default namespace |
| if (prefix == null && uri == null) { |
| prefix = uri = ""; |
| } |
| // any other uri must not be null |
| if (uri != null) { |
| namespaces.put(prefix, uri); |
| // TODO: not correct since nsRegistry is retrieved from each session |
| nsCache.add(prefix, uri); |
| } else { |
| log.error("Invalid prefix / uri pair: " + prefix + " -> " + uri); |
| } |
| } |
| } |
| return namespaces; |
| } catch (IOException e) { |
| throw new RepositoryException(e); |
| } catch (DavException e) { |
| throw ExceptionConverter.generate(e); |
| } finally { |
| if (request != null) { |
| request.releaseConnection(); |
| } |
| } |
| } |
| |
| @Override |
| public String getNamespaceURI(SessionInfo sessionInfo, String prefix) |
| throws RepositoryException { |
| try { |
| return nsCache.getURI(prefix); |
| } catch (NamespaceException e) { |
| // refresh namespaces and try again |
| getRegisteredNamespaces(sessionInfo); |
| return nsCache.getURI(prefix); |
| } |
| } |
| |
| @Override |
| public String getNamespacePrefix(SessionInfo sessionInfo, String uri) |
| throws RepositoryException { |
| try { |
| return nsCache.getPrefix(uri); |
| } catch (NamespaceException e) { |
| // refresh namespaces and try again |
| getRegisteredNamespaces(sessionInfo); |
| return nsCache.getPrefix(uri); |
| } |
| } |
| |
| @Override |
| public void registerNamespace(SessionInfo sessionInfo, String prefix, String uri) throws RepositoryException { |
| // make sure we have them all |
| getRegisteredNamespaces(sessionInfo); |
| |
| Map<String, String> namespaces = new HashMap<String, String>(nsCache.getNamespaces()); |
| // add new pair that needs to be registered. |
| namespaces.put(prefix, uri); |
| |
| internalSetNamespaces(sessionInfo, namespaces); |
| // adjust internal mappings: |
| // TODO: not correct since nsRegistry is retrieved from each session |
| nsCache.add(prefix, uri); |
| } |
| |
| @Override |
| public void unregisterNamespace(SessionInfo sessionInfo, String uri) throws RepositoryException { |
| // make sure we have them all |
| getRegisteredNamespaces(sessionInfo); |
| |
| String prefix = nsCache.getPrefix(uri); |
| Map<String, String> namespaces = new HashMap<String, String>(nsCache.getNamespaces()); |
| // remove pair that needs to be unregistered |
| namespaces.remove(prefix); |
| |
| internalSetNamespaces(sessionInfo, namespaces); |
| // adjust internal mappings: |
| nsCache.remove(prefix, uri); |
| } |
| |
| /** |
| * |
| * @param sessionInfo |
| * @param namespaces |
| * @throws NamespaceException |
| * @throws UnsupportedRepositoryOperationException |
| * @throws AccessDeniedException |
| * @throws RepositoryException |
| */ |
| private void internalSetNamespaces(SessionInfo sessionInfo, Map<String, String> namespaces) throws RepositoryException { |
| DavPropertySet setProperties = new DavPropertySet(); |
| setProperties.add(createNamespaceProperty(namespaces)); |
| |
| HttpProppatch request = null; |
| try { |
| String uri = uriResolver.getWorkspaceUri(sessionInfo.getWorkspaceName()); |
| |
| request = new HttpProppatch(uri, setProperties, new DavPropertyNameSet()); |
| initMethod(request, sessionInfo, true); |
| |
| HttpResponse response = executeRequest(sessionInfo, request); |
| request.checkSuccess(response); |
| } catch (IOException e) { |
| throw new RepositoryException(e); |
| } catch (DavException e) { |
| throw ExceptionConverter.generate(e); |
| } finally { |
| if (request != null) { |
| request.releaseConnection(); |
| } |
| } |
| } |
| |
| @Override |
| public Iterator<QNodeTypeDefinition> getQNodeTypeDefinitions(SessionInfo sessionInfo) throws RepositoryException { |
| HttpReport request = null; |
| try { |
| ReportInfo info = new ReportInfo(JcrRemotingConstants.REPORT_NODETYPES, ItemResourceConstants.NAMESPACE); |
| info.setContentElement(DomUtil.createElement(DomUtil.createDocument(), NodeTypeConstants.XML_REPORT_ALLNODETYPES, ItemResourceConstants.NAMESPACE)); |
| |
| String workspaceUri = uriResolver.getWorkspaceUri(sessionInfo.getWorkspaceName()); |
| request = new HttpReport(workspaceUri, info); |
| HttpResponse response = executeRequest(sessionInfo, request); |
| request.checkSuccess(response); |
| |
| Document reportDoc = request.getResponseBodyAsDocument(response.getEntity()); |
| return retrieveQNodeTypeDefinitions(sessionInfo, reportDoc); |
| } catch (IOException e) { |
| throw new RepositoryException(e); |
| } catch (ParserConfigurationException e) { |
| throw new RepositoryException(e); |
| } catch (DavException e) { |
| throw ExceptionConverter.generate(e); |
| } finally { |
| if (request != null) { |
| request.releaseConnection(); |
| } |
| } |
| } |
| |
| @Override |
| public Iterator<QNodeTypeDefinition> getQNodeTypeDefinitions(SessionInfo sessionInfo, Name[] nodetypeNames) throws RepositoryException { |
| // in order to avoid individual calls for every nodetype, retrieve |
| // the complete set from the server (again). |
| return getQNodeTypeDefinitions(sessionInfo); |
| } |
| |
| @Override |
| public void registerNodeTypes(SessionInfo sessionInfo, QNodeTypeDefinition[] nodeTypeDefinitions, boolean allowUpdate) throws RepositoryException { |
| HttpProppatch request = null; |
| try { |
| DavPropertySet setProperties = new DavPropertySet(); |
| setProperties.add(createRegisterNodeTypesProperty(sessionInfo, nodeTypeDefinitions, allowUpdate)); |
| String uri = uriResolver.getWorkspaceUri(sessionInfo.getWorkspaceName()); |
| request = new HttpProppatch(uri, setProperties, new DavPropertyNameSet()); |
| initMethod(request, sessionInfo, true); |
| HttpResponse response = executeRequest(sessionInfo, request); |
| request.checkSuccess(response); |
| } catch (IOException e) { |
| throw new RepositoryException(e); |
| } catch (DavException e) { |
| throw ExceptionConverter.generate(e); |
| } finally { |
| if (request != null) { |
| request.releaseConnection(); |
| } |
| } |
| } |
| |
| @Override |
| public void unregisterNodeTypes(SessionInfo sessionInfo, Name[] nodeTypeNames) throws RepositoryException { |
| HttpProppatch request = null; |
| try { |
| DavPropertySet setProperties = new DavPropertySet(); |
| setProperties.add(createUnRegisterNodeTypesProperty(sessionInfo, nodeTypeNames)); |
| String uri = uriResolver.getWorkspaceUri(sessionInfo.getWorkspaceName()); |
| request = new HttpProppatch(uri, setProperties, new DavPropertyNameSet()); |
| initMethod(request, sessionInfo, true); |
| HttpResponse response = executeRequest(sessionInfo, request); |
| request.checkSuccess(response); |
| } catch (IOException e) { |
| throw new RepositoryException(e); |
| } catch (DavException e) { |
| throw ExceptionConverter.generate(e); |
| } finally { |
| if (request != null) { |
| request.releaseConnection(); |
| } |
| } |
| } |
| |
| @Override |
| public void createWorkspace(SessionInfo sessionInfo, String name, String srcWorkspaceName) throws RepositoryException { |
| if (srcWorkspaceName != null) { |
| throw new UnsupportedOperationException("JCR-2003. Implementation missing"); |
| } |
| |
| HttpMkworkspace request = null; |
| try { |
| request = new HttpMkworkspace(uriResolver.getWorkspaceUri(name)); |
| initMethod(request, sessionInfo, true); |
| HttpResponse response = executeRequest(sessionInfo, request); |
| request.checkSuccess(response); |
| } catch (IOException e) { |
| throw new RepositoryException(e); |
| } catch (DavException e) { |
| throw ExceptionConverter.generate(e); |
| } finally { |
| if (request != null) { |
| request.releaseConnection(); |
| } |
| } |
| } |
| |
| @Override |
| public void deleteWorkspace(SessionInfo sessionInfo, String name) throws RepositoryException { |
| HttpDelete request = null; |
| try { |
| request = new HttpDelete(uriResolver.getWorkspaceUri(name)); |
| initMethod(request, sessionInfo, true); |
| HttpResponse response = executeRequest(sessionInfo, request); |
| request.checkSuccess(response); |
| } catch (IOException e) { |
| throw new RepositoryException(e); |
| } catch (DavException e) { |
| throw ExceptionConverter.generate(e); |
| } finally { |
| if (request != null) { |
| request.releaseConnection(); |
| } |
| } |
| } |
| |
| /** |
| * Compute the repository URI (while dealing with trailing / and port number |
| * defaulting) |
| */ |
| public static URI computeRepositoryUri(String uri) throws URISyntaxException { |
| URI repositoryUri = URI.create((uri.endsWith("/")) ? uri : uri + "/"); |
| // workaround for JCR-3228: normalize default port numbers because of |
| // the weak URI matching code elsewhere (the remote server is unlikely |
| // to include the port number in URIs when it's the default for the |
| // protocol) |
| boolean useDefaultPort = ("http".equalsIgnoreCase(repositoryUri.getScheme()) && repositoryUri.getPort() == 80) |
| || (("https".equalsIgnoreCase(repositoryUri.getScheme()) && repositoryUri.getPort() == 443)); |
| if (useDefaultPort) { |
| repositoryUri = new URI(repositoryUri.getScheme(), repositoryUri.getUserInfo(), repositoryUri.getHost(), -1, |
| repositoryUri.getPath(), repositoryUri.getQuery(), repositoryUri.getFragment()); |
| } |
| |
| return repositoryUri; |
| } |
| |
| public HttpResponse executeRequest(SessionInfo sessionInfo, HttpUriRequest request) throws IOException, RepositoryException { |
| return getClient(sessionInfo).execute(request, getContext(sessionInfo)); |
| } |
| |
| /** |
| * |
| * @param sessionInfo |
| * @param reportDoc |
| * @return |
| * @throws RepositoryException |
| */ |
| private Iterator<QNodeTypeDefinition> retrieveQNodeTypeDefinitions(SessionInfo sessionInfo, Document reportDoc) throws RepositoryException { |
| ElementIterator it = DomUtil.getChildren(reportDoc.getDocumentElement(), NodeTypeConstants.NODETYPE_ELEMENT, null); |
| List<QNodeTypeDefinition> ntDefs = new ArrayList<QNodeTypeDefinition>(); |
| NamePathResolver resolver = getNamePathResolver(sessionInfo); |
| while (it.hasNext()) { |
| ntDefs.add(DefinitionUtil.createQNodeTypeDefinition(it.nextElement(), resolver, getQValueFactory())); |
| } |
| // refresh node type definitions map |
| synchronized (nodeTypeDefinitions) { |
| nodeTypeDefinitions.clear(); |
| for (Object ntDef : ntDefs) { |
| QNodeTypeDefinition def = (QNodeTypeDefinition) ntDef; |
| nodeTypeDefinitions.put(def.getName(), def); |
| } |
| } |
| return ntDefs.iterator(); |
| } |
| |
| private DavProperty<List<XmlSerializable>> createRegisterNodeTypesProperty(SessionInfo sessionInfo, QNodeTypeDefinition[] nodeTypeDefinitions, final boolean allowUpdate) throws IOException { |
| // create xml elements for both cnd and allow update value. |
| List<XmlSerializable> val = new ArrayList<XmlSerializable>(); |
| |
| StringWriter sw = new StringWriter(); |
| CompactNodeTypeDefWriter writer = new CompactNodeTypeDefWriter(sw, new NamespaceResolverImpl(sessionInfo), true); |
| writer.write(Arrays.asList(nodeTypeDefinitions)); |
| writer.close(); |
| |
| final String cnd = sw.toString(); |
| val.add(new XmlSerializable() { |
| public Element toXml(Document document) { |
| Element cndElem = document.createElementNS(JcrRemotingConstants.NS_URI, JcrRemotingConstants.NS_PREFIX + ":" + JcrRemotingConstants.XML_CND); |
| DomUtil.setText(cndElem, cnd); |
| return cndElem; |
| } |
| }); |
| val.add(new XmlSerializable() { |
| public Element toXml(Document document) { |
| Element allowElem = document.createElementNS(JcrRemotingConstants.NS_URI, JcrRemotingConstants.NS_PREFIX + ":" + JcrRemotingConstants.XML_ALLOWUPDATE); |
| DomUtil.setText(allowElem, Boolean.toString(allowUpdate)); |
| return allowElem; |
| } |
| }); |
| |
| return new DefaultDavProperty<List<XmlSerializable>>(JcrRemotingConstants.JCR_NODETYPES_CND_LN, val, ItemResourceConstants.NAMESPACE, false); |
| } |
| |
| private DavProperty<List<XmlSerializable>> createUnRegisterNodeTypesProperty(SessionInfo sessionInfo, Name[] nodeTypeNames) throws IOException, RepositoryException { |
| NamePathResolver resolver = getNamePathResolver(sessionInfo); |
| List<XmlSerializable> val = new ArrayList<XmlSerializable>(); |
| for (Name ntName : nodeTypeNames) { |
| final String jcrName = resolver.getJCRName(ntName); |
| val.add(new XmlSerializable() { |
| public Element toXml(Document document) { |
| Element ntNameElem = document.createElementNS(JcrRemotingConstants.NS_URI, JcrRemotingConstants.NS_PREFIX + ":" + JcrRemotingConstants.XML_NODETYPENAME); |
| org.w3c.dom.Text txt = document.createTextNode(jcrName); |
| ntNameElem.appendChild(txt); |
| return ntNameElem; |
| } |
| }); |
| } |
| return new DefaultDavProperty<List<XmlSerializable>>(JcrRemotingConstants.JCR_NODETYPES_CND_LN, val, ItemResourceConstants.NAMESPACE, false); |
| } |
| |
| private static DavProperty<List<XmlSerializable>> createValuesProperty(Value[] jcrValues) { |
| // convert the specified jcr values to a xml-serializable value |
| List<XmlSerializable> val = new ArrayList<XmlSerializable>(); |
| for (final Value jcrValue : jcrValues) { |
| val.add(new XmlSerializable() { |
| public Element toXml(Document document) { |
| try { |
| return ValueUtil.valueToXml(jcrValue, document); |
| } catch (RepositoryException e) { |
| throw new RuntimeException(e); |
| } |
| } |
| }); |
| } |
| return new DefaultDavProperty<List<XmlSerializable>>(JcrRemotingConstants.JCR_VALUES_LN, val, ItemResourceConstants.NAMESPACE, false); |
| } |
| |
| private static DavProperty<List<XmlSerializable>> createNamespaceProperty(final Map<String, String> namespaces) { |
| // convert the specified namespace to a xml-serializable value |
| List<XmlSerializable> val = new ArrayList<XmlSerializable>(); |
| for (final String prefix : namespaces.keySet()) { |
| val.add(new XmlSerializable() { |
| |
| public Element toXml(Document document) { |
| Element nsElem = document.createElementNS(JcrRemotingConstants.NS_URI, JcrRemotingConstants.NS_PREFIX + ":" + JcrRemotingConstants.XML_NAMESPACE); |
| Element prefixElem = document.createElementNS(JcrRemotingConstants.NS_URI, JcrRemotingConstants.NS_PREFIX + ":" + JcrRemotingConstants.XML_PREFIX); |
| org.w3c.dom.Text txt = document.createTextNode(prefix); |
| prefixElem.appendChild(txt); |
| |
| final String uri = namespaces.get(prefix); |
| Element uriElem = document.createElementNS(JcrRemotingConstants.NS_URI, JcrRemotingConstants.NS_PREFIX + ":" + JcrRemotingConstants.XML_URI); |
| org.w3c.dom.Text txt2 = document.createTextNode(uri); |
| uriElem.appendChild(txt2); |
| |
| nsElem.appendChild(prefixElem); |
| nsElem.appendChild(uriElem); |
| |
| return nsElem; |
| } |
| }); |
| } |
| return new DefaultDavProperty<List<XmlSerializable>>(JcrRemotingConstants.JCR_NAMESPACES_LN, val, ItemResourceConstants.NAMESPACE, false); |
| } |
| |
| private static DavProperty<List<XmlSerializable>> createNodeTypeProperty(String localName, String[] ntNames) { |
| // convert the specified node type names to a xml-serializable value |
| List<XmlSerializable> val = new ArrayList<XmlSerializable>(); |
| for (final String ntName : ntNames) { |
| val.add(new XmlSerializable() { |
| public Element toXml(Document document) { |
| return NodeTypeUtil.ntNameToXml(ntName, document); |
| } |
| }); |
| } |
| return new DefaultDavProperty<List<XmlSerializable>>(localName, val, ItemResourceConstants.NAMESPACE, false); |
| } |
| |
| private Set<String> getDavComplianceClasses(SessionInfo sessionInfo) throws RepositoryException { |
| if (this.remoteDavComplianceClasses == null) { |
| HttpOptions request = new HttpOptions(uriResolver.getWorkspaceUri(sessionInfo.getWorkspaceName())); |
| try { |
| HttpResponse response = executeRequest(sessionInfo, request); |
| int status = response.getStatusLine().getStatusCode(); |
| if (status != DavServletResponse.SC_OK) { |
| throw new DavException(status); |
| } |
| this.remoteDavComplianceClasses = request.getDavComplianceClasses(response); |
| } catch (IOException e) { |
| throw new RepositoryException(e); |
| } catch (DavException e) { |
| throw ExceptionConverter.generate(e); |
| } finally { |
| request.releaseConnection(); |
| } |
| } |
| return this.remoteDavComplianceClasses; |
| } |
| |
| private boolean isDavClass3(SessionInfo sessionInfo) { |
| try { |
| return getDavComplianceClasses(sessionInfo).contains("3"); |
| } catch (RepositoryException ex) { |
| log.warn("failure to obtain OPTIONS response", ex); |
| return false; |
| } |
| } |
| |
| private static String obtainAbsolutePathFromUri(String uri) { |
| try { |
| java.net.URI u = new java.net.URI(uri); |
| StringBuilder sb = new StringBuilder(); |
| sb.append(u.getRawPath()); |
| if (u.getRawQuery() != null) { |
| sb.append("?").append(u.getRawQuery()); |
| } |
| return sb.toString(); |
| } catch (java.net.URISyntaxException ex) { |
| log.warn("parsing " + uri, ex); |
| return uri; |
| } |
| } |
| |
| private static String[] obtainAbsolutePathsFromUris(String[] uris) { |
| if (uris == null) { |
| return null; |
| } else { |
| String result[] = new String[uris.length]; |
| |
| for (int i = 0; i < result.length; i++) { |
| result[i] = obtainAbsolutePathFromUri(uris[i]); |
| } |
| return result; |
| } |
| } |
| |
| //------------------------------------------------< Inner Class 'Batch' >--- |
| private class BatchImpl implements Batch { |
| |
| private final SessionInfo sessionInfo; |
| private final ItemId targetId; |
| private final List<HttpRequestBase> requests = new ArrayList<HttpRequestBase>(); |
| private final NamePathResolver resolver; |
| |
| private String batchId; |
| |
| private boolean isConsumed = false; |
| private boolean clear = false; |
| |
| private BatchImpl(ItemId targetId, SessionInfo sessionInfo) throws RepositoryException { |
| this.targetId = targetId; |
| this.sessionInfo = sessionInfo; |
| this.resolver = getNamePathResolver(sessionInfo); |
| } |
| |
| private HttpClient start() throws RepositoryException { |
| checkConsumed(); |
| String uri = getItemUri(targetId, sessionInfo); |
| HttpLock request = null; |
| try { |
| // start special 'lock' |
| request = new HttpLock(uri, new org.apache.jackrabbit.webdav.lock.LockInfo(TransactionConstants.LOCAL, TransactionConstants.TRANSACTION, null, |
| INFINITE_TIMEOUT, true)); |
| initMethod(request, sessionInfo, true); |
| |
| HttpClient client = getClient(sessionInfo); |
| HttpResponse response = client.execute(request,getContext(sessionInfo)); |
| if (response.getStatusLine().getStatusCode() == DavServletResponse.SC_PRECONDITION_FAILED) { |
| throw new InvalidItemStateException("Unable to persist transient changes."); |
| } |
| request.checkSuccess(response); |
| |
| batchId = request.getLockToken(response); |
| |
| return client; |
| } catch (IOException e) { |
| throw new RepositoryException(e); |
| } catch (DavException e) { |
| throw ExceptionConverter.generate(e); |
| } finally { |
| if (request != null) { |
| request.releaseConnection(); |
| } |
| } |
| } |
| |
| private void end(HttpClient client, boolean commit) throws RepositoryException { |
| checkConsumed(); |
| |
| String uri = getItemUri(targetId, sessionInfo); |
| HttpUnlock request = null; |
| try { |
| // make sure the lock initially created is removed again on the |
| // server, asking the server to persist the modifications |
| request = new HttpUnlock(uri, batchId); |
| initMethod(request, sessionInfo, true); |
| |
| // in contrast to standard UNLOCK, the tx-unlock provides a |
| // request body. |
| request.setEntity(XmlEntity.create(new TransactionInfo(commit))); |
| HttpResponse response = client.execute(request, getContext(sessionInfo)); |
| request.checkSuccess(response); |
| if (sessionInfo instanceof SessionInfoImpl) { |
| ((SessionInfoImpl) sessionInfo).setLastBatchId(batchId); |
| } |
| if (clear) { |
| clearItemUriCache(sessionInfo); |
| } |
| } catch (IOException e) { |
| throw new RepositoryException(e); |
| } catch (DavException e) { |
| throw ExceptionConverter.generate(e); |
| } finally { |
| if (request != null) { |
| // release UNLOCK method |
| request.releaseConnection(); |
| } |
| } |
| } |
| |
| private void dispose() { |
| requests.clear(); |
| isConsumed = true; |
| } |
| |
| private void checkConsumed() { |
| if (isConsumed) { |
| throw new IllegalStateException("Batch has already been consumed."); |
| } |
| } |
| |
| private boolean isEmpty() { |
| return requests.isEmpty(); |
| } |
| |
| private Iterator<HttpRequestBase> requests() { |
| return requests.iterator(); |
| } |
| |
| //----------------------------------------------------------< Batch >--- |
| @Override |
| public void addNode(NodeId parentId, Name nodeName, Name nodetypeName, String uuid) throws RepositoryException { |
| checkConsumed(); |
| try { |
| // TODO: TOBEFIXED. WebDAV does not allow MKCOL for existing resource -> problem with SNS |
| // use fake name instead (see also #importXML) |
| Name fakeName = getNameFactory().create(Name.NS_DEFAULT_URI, UUID.randomUUID().toString()); |
| String uri = getItemUri(parentId, fakeName, sessionInfo); |
| HttpMkcol request = new HttpMkcol(uri); |
| |
| // build 'sys-view' for the node to create and append it as request body |
| Document body = DomUtil.createDocument(); |
| BatchUtils.createNodeElement(body, nodeName, nodetypeName, uuid, resolver); |
| request.setEntity(XmlEntity.create(body)); |
| |
| requests.add(request); |
| } catch (IOException e) { |
| throw new RepositoryException(e); |
| } catch (ParserConfigurationException e) { |
| throw new RepositoryException(e); |
| } |
| } |
| |
| @Override |
| public void addProperty(NodeId parentId, Name propertyName, QValue value) throws RepositoryException { |
| checkConsumed(); |
| String uri = getItemUri(parentId, propertyName, sessionInfo); |
| |
| HttpPut request = new HttpPut(uri); |
| request.setHeader(HEADER_CONTENT_TYPE, JcrValueType.contentTypeFromType(value.getType())); |
| request.setEntity(getEntity(value)); |
| requests.add(request); |
| } |
| |
| @Override |
| public void addProperty(NodeId parentId, Name propertyName, QValue[] values) throws RepositoryException { |
| checkConsumed(); |
| // TODO: avoid usage of the ValuesProperty. specially for binary props. |
| // TODO: replace by a multipart-POST |
| try { |
| String uri = getItemUri(parentId, propertyName, sessionInfo); |
| Value[] jcrValues = new Value[values.length]; |
| for (int i = 0; i < values.length; i++) { |
| jcrValues[i] = ValueFormat.getJCRValue(values[i], resolver, valueFactory); |
| } |
| DavProperty<List<XmlSerializable>> vp = createValuesProperty(jcrValues); |
| HttpPut request = new HttpPut(uri); |
| request.setEntity(XmlEntity.create(vp)); |
| |
| requests.add(request); |
| } catch (IOException e) { |
| throw new RepositoryException(e); |
| } |
| } |
| |
| @Override |
| public void setValue(PropertyId propertyId, QValue value) throws RepositoryException { |
| checkConsumed(); |
| if (value == null) { |
| // setting property value to 'null' is identical to a removal |
| remove(propertyId); |
| } else { |
| HttpEntity ent = getEntity(value); |
| String uri = getItemUri(propertyId, sessionInfo); |
| // TODO: use PUT in order to avoid the ValuesProperty-PROPPATCH call. |
| // TODO: actually not quite correct for PROPPATCH assert that prop really exists. |
| HttpPut request = new HttpPut(uri); |
| request.setHeader(HEADER_CONTENT_TYPE, JcrValueType.contentTypeFromType(value.getType())); |
| request.setEntity(ent); |
| requests.add(request); |
| } |
| } |
| |
| @Override |
| public void setValue(PropertyId propertyId, QValue[] values) throws RepositoryException { |
| checkConsumed(); |
| if (values == null) { |
| // setting property value to 'null' is identical to a removal |
| remove(propertyId); |
| } else { |
| // TODO: use multipart-POST instead of ValuesProperty |
| DavPropertySet setProperties = new DavPropertySet(); |
| // SPI values must be converted to jcr values |
| Value[] jcrValues = new Value[values.length]; |
| for (int i = 0; i < values.length; i++) { |
| jcrValues[i] = ValueFormat.getJCRValue(values[i], resolver, valueFactory); |
| } |
| setProperties.add(createValuesProperty(jcrValues)); |
| try { |
| String uri = getItemUri(propertyId, sessionInfo); |
| HttpProppatch request = new HttpProppatch(uri, setProperties, new DavPropertyNameSet()); |
| requests.add(request); |
| } catch (IOException e) { |
| throw new RepositoryException(e); |
| } |
| } |
| } |
| |
| private HttpEntity getEntity(QValue value) throws RepositoryException { |
| // SPI value must be converted to jcr value |
| int type = value.getType(); |
| String contentType = JcrValueType.contentTypeFromType(type); |
| HttpEntity ent; |
| switch (type) { |
| case PropertyType.NAME: |
| case PropertyType.PATH: |
| String str = ValueFormat.getJCRString(value, resolver); |
| ent = new StringEntity(str, ContentType.create(contentType, "UTF-8")); |
| break; |
| case PropertyType.BINARY: |
| InputStream in = value.getStream(); |
| ent = new InputStreamEntity(in, ContentType.create(contentType)); |
| break; |
| default: |
| str = value.getString(); |
| ent = new StringEntity(str, ContentType.create(contentType, "UTF-8")); |
| break; |
| } |
| return ent; |
| } |
| |
| @Override |
| public void remove(ItemId itemId) throws RepositoryException { |
| checkConsumed(); |
| String uri = getItemUri(itemId, sessionInfo); |
| HttpDelete request = new HttpDelete(uri); |
| |
| requests.add(request); |
| if (itemId.getPath() == null) { |
| clear = true; |
| } |
| } |
| |
| @Override |
| public void reorderNodes(NodeId parentId, NodeId srcNodeId, NodeId beforeNodeId) throws RepositoryException { |
| checkConsumed(); |
| try { |
| String uri = getItemUri(parentId, sessionInfo); |
| String srcUri = getItemUri(srcNodeId, sessionInfo); |
| String srcSegment = Text.getName(srcUri, true); |
| |
| Position p; |
| if (beforeNodeId == null) { |
| // move src to the end |
| p = new Position(OrderingConstants.XML_LAST); |
| } else { |
| // insert src before the targetSegment |
| String beforeUri = getItemUri(beforeNodeId, sessionInfo); |
| String targetSegment = Text.getName(beforeUri, true); |
| p = new Position(OrderingConstants.XML_BEFORE, targetSegment); |
| } |
| OrderPatch op = new OrderPatch(OrderingConstants.ORDERING_TYPE_CUSTOM, new OrderPatch.Member(srcSegment, p)); |
| HttpOrderpatch request = new HttpOrderpatch(uri, op); |
| requests.add(request); |
| } catch (IOException e) { |
| throw new RepositoryException(e); |
| } |
| } |
| |
| @Override |
| public void setMixins(NodeId nodeId, Name[] mixinNodeTypeIds) throws RepositoryException { |
| checkConsumed(); |
| try { |
| DavPropertySet setProperties; |
| DavPropertyNameSet removeProperties; |
| if (mixinNodeTypeIds == null || mixinNodeTypeIds.length == 0) { |
| setProperties = new DavPropertySet(); |
| removeProperties = new DavPropertyNameSet(); |
| removeProperties.add(JcrRemotingConstants.JCR_MIXINNODETYPES_LN, ItemResourceConstants.NAMESPACE); |
| } else { |
| String[] ntNames = new String[mixinNodeTypeIds.length]; |
| for (int i = 0; i < mixinNodeTypeIds.length; i++) { |
| ntNames[i] = resolver.getJCRName(mixinNodeTypeIds[i]); |
| } |
| setProperties = new DavPropertySet(); |
| setProperties.add(createNodeTypeProperty(JcrRemotingConstants.JCR_MIXINNODETYPES_LN, ntNames)); |
| removeProperties = new DavPropertyNameSet(); |
| } |
| |
| String uri = getItemUri(nodeId, sessionInfo); |
| HttpProppatch request = new HttpProppatch(uri, setProperties, removeProperties); |
| |
| requests.add(request); |
| } catch (IOException e) { |
| throw new RepositoryException(e); |
| } |
| } |
| |
| @Override |
| public void setPrimaryType(NodeId nodeId, Name primaryNodeTypeName) throws RepositoryException { |
| checkConsumed(); |
| try { |
| DavPropertySet setProperties = new DavPropertySet(); |
| setProperties.add(createNodeTypeProperty(JcrRemotingConstants.JCR_PRIMARYNODETYPE_LN, new String[] {resolver.getJCRName(primaryNodeTypeName)})); |
| |
| String uri = getItemUri(nodeId, sessionInfo); |
| HttpProppatch request = new HttpProppatch(uri, setProperties, new DavPropertyNameSet()); |
| |
| requests.add(request); |
| } catch (IOException e) { |
| throw new RepositoryException(e); |
| } |
| } |
| |
| @Override |
| public void move(NodeId srcNodeId, NodeId destParentNodeId, Name destName) throws RepositoryException { |
| checkConsumed(); |
| String uri = getItemUri(srcNodeId, sessionInfo); |
| String destUri = getItemUri(destParentNodeId, destName, sessionInfo); |
| if (isDavClass3(sessionInfo)) { |
| destUri = obtainAbsolutePathFromUri(destUri); |
| } |
| HttpMove request = new HttpMove(uri, destUri, false); |
| |
| requests.add(request); |
| clear = true; |
| } |
| |
| @Override |
| public void setTree(NodeId parentId, Tree tree) throws RepositoryException { |
| checkConsumed(); |
| |
| if (!(tree instanceof DocumentTree)) { |
| throw new RepositoryException("Invalid tree implementation " + tree.getClass().getName()); |
| } |
| try { |
| // TODO: TOBEFIXED. WebDAV does not allow MKCOL for existing resource -> problem with SNS |
| // use fake name instead (see also #importXML) |
| Name fakeName = getNameFactory().create(Name.NS_DEFAULT_URI, UUID.randomUUID().toString()); |
| String uri = getItemUri(parentId, fakeName, sessionInfo); |
| HttpMkcol request = new HttpMkcol(uri); |
| |
| request.setEntity(XmlEntity.create(((DocumentTree) tree).toDocument())); |
| |
| requests.add(request); |
| } catch (IOException e) { |
| throw new RepositoryException(e); |
| } |
| } |
| } |
| |
| //----------------------------------------------< NamespaceResolverImpl >--- |
| /** |
| * NamespaceResolver implementation that uses a sessionInfo to determine |
| * namespace mappings either from cache or from the server. |
| */ |
| private class NamespaceResolverImpl implements NamespaceResolver { |
| |
| private final SessionInfo sessionInfo; |
| |
| /** |
| * Creates a new namespace resolver using the given session info. |
| * |
| * @param sessionInfo the session info to contact the repository. |
| */ |
| private NamespaceResolverImpl(SessionInfo sessionInfo) { |
| this.sessionInfo = sessionInfo; |
| } |
| |
| @Override |
| public String getURI(String prefix) throws NamespaceException { |
| try { |
| return getNamespaceURI(sessionInfo, prefix); |
| } catch (RepositoryException e) { |
| String msg = "Error retrieving namespace uri"; |
| throw new NamespaceException(msg, e); |
| } |
| } |
| |
| @Override |
| public String getPrefix(String uri) throws NamespaceException { |
| try { |
| return getNamespacePrefix(sessionInfo, uri); |
| } catch (RepositoryException e) { |
| String msg = "Error retrieving namespace prefix"; |
| throw new NamespaceException(msg, e); |
| } |
| } |
| } |
| |
| //---------------------------------------------< IdentifierResolverImpl >--- |
| private class IdentifierResolverImpl implements IdentifierResolver { |
| |
| private final SessionInfo sessionInfo; |
| |
| private IdentifierResolverImpl(SessionInfo sessionInfo) { |
| this.sessionInfo = sessionInfo; |
| } |
| |
| private Path buildPath(String uniqueID) throws RepositoryException { |
| String uri = uriResolver.getItemUri(getIdFactory().createNodeId(uniqueID), sessionInfo.getWorkspaceName(), sessionInfo); |
| return uriResolver.getQPath(uri, sessionInfo); |
| } |
| |
| private Path resolvePath(String jcrPath) throws RepositoryException { |
| return ((SessionInfoImpl) sessionInfo).getNamePathResolver().getQPath(jcrPath); |
| } |
| |
| @Override |
| public Path getPath(String identifier) throws MalformedPathException { |
| try { |
| int pos = identifier.indexOf('/'); |
| if (pos == -1) { |
| // unique id identifier |
| return buildPath(identifier); |
| } else if (pos == 0) { |
| // jcr-path identifier |
| return resolvePath(identifier); |
| } else { |
| Path p1 = buildPath(identifier.substring(0, pos)); |
| Path p2 = resolvePath(identifier.substring(pos)); |
| return getPathFactory().create(p1, p2, true); |
| } |
| } catch (RepositoryException e) { |
| throw new MalformedPathException(identifier); |
| } |
| } |
| |
| @Override |
| public void checkFormat(String identifier) throws MalformedPathException { |
| // cannot be determined. assume ok. |
| } |
| } |
| //-----------------------------------------------< NamePathResolverImpl >--- |
| /** |
| * Implements a namespace resolver based on a session info. |
| */ |
| private class NamePathResolverImpl implements NamePathResolver { |
| |
| private final NameResolver nResolver; |
| private final PathResolver pResolver; |
| |
| private NamePathResolverImpl(SessionInfo sessionInfo) { |
| NamespaceResolver nsResolver = new NamespaceResolverImpl(sessionInfo); |
| nResolver = new ParsingNameResolver(getNameFactory(), nsResolver); |
| IdentifierResolver idResolver = new IdentifierResolverImpl(sessionInfo); |
| pResolver = new ParsingPathResolver(getPathFactory(), nResolver, idResolver); |
| } |
| |
| private NamePathResolverImpl(NamespaceResolver nsResolver) { |
| nResolver = new ParsingNameResolver(getNameFactory(), nsResolver); |
| pResolver = new ParsingPathResolver(getPathFactory(), nResolver); |
| } |
| |
| @Override |
| public Name getQName(String jcrName) throws IllegalNameException, NamespaceException { |
| return nResolver.getQName(jcrName); |
| } |
| |
| @Override |
| public String getJCRName(Name qName) throws NamespaceException { |
| return nResolver.getJCRName(qName); |
| } |
| |
| @Override |
| public Path getQPath(String path) throws MalformedPathException, IllegalNameException, NamespaceException { |
| return pResolver.getQPath(path); |
| } |
| |
| @Override |
| public Path getQPath(String path, boolean normalizeIdentifier) throws MalformedPathException, IllegalNameException, NamespaceException { |
| return pResolver.getQPath(path, normalizeIdentifier); |
| } |
| |
| @Override |
| public String getJCRPath(Path path) throws NamespaceException { |
| return pResolver.getJCRPath(path); |
| } |
| } |
| |
| /** |
| * Namespace Cache |
| */ |
| private static class NamespaceCache extends AbstractNamespaceResolver { |
| |
| private final HashMap<String, String> prefixToURI = new HashMap<String, String>(); |
| private final HashMap<String, String> uriToPrefix = new HashMap<String, String>(); |
| |
| public Map<String, String> getNamespaces() { |
| return new HashMap<String, String>(prefixToURI); |
| } |
| |
| public void add(String prefix, String uri) { |
| prefixToURI.put(prefix, uri); |
| uriToPrefix.put(uri, prefix); |
| } |
| |
| public void remove(String prefix, String uri) { |
| prefixToURI.remove(prefix); |
| uriToPrefix.remove(uri); |
| } |
| |
| //----------------------------------------------< NamespaceResolver >--- |
| |
| @Override |
| public String getURI(String prefix) throws NamespaceException { |
| String uri = prefixToURI.get(prefix); |
| if (uri != null) { |
| return uri; |
| } else { |
| throw new NamespaceException(prefix + ": is not a registered namespace prefix."); |
| } |
| } |
| |
| @Override |
| public String getPrefix(String uri) throws NamespaceException { |
| String prefix = uriToPrefix.get(uri); |
| if (prefix != null) { |
| return prefix; |
| } else { |
| throw new NamespaceException(uri + ": is not a registered namespace uri."); |
| } |
| } |
| } |
| } |