All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.sling.api.resource.ResourceMetadata Maven / Gradle / Ivy

Go to download

The Apache Sling API defines an extension to the Servlet API 2.4 to provide access to content and unified access to request parameters hiding the differences between the different methods of transferring parameters from client to server. Note that the Apache Sling API bundle does not include the Servlet API but instead requires the API to be provided by the Servlet container in which the Apache Sling framework is running or by another bundle.

There is a newer version: 2.9.0
Show newest version
/*
 * 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.sling.api.resource;

import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;

import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;

/**
 * The ResourceMetadata interface defines the API for the
 * metadata of a Sling {@link Resource}. Essentially the resource's metadata is
 * just a map of objects indexed by string keys.
 * 

* The actual contents of the meta data map is implementation specific with the * exception of the {@link #RESOLUTION_PATH sling.resolutionPath} property which * must be provided by all implementations and contain the part of the request * URI used to resolve the resource. The type of this property value is defined * to be String. *

* Note, that the prefix sling. to key names is reserved for the * Sling implementation. * * Once a resource is returned by the {@link ResourceResolver}, the resource * metadata is made read-only and therefore can't be changed by client code! */ public class ResourceMetadata extends HashMap { private static final long serialVersionUID = 4692666752269523738L; /** * The name of the required property providing the part of the request URI * which was used to the resolve the resource to which the meta data * instance belongs (value is "sling.resolutionPath"). */ public static final String RESOLUTION_PATH = "sling.resolutionPath"; /** * The name of the required property providing the part of the request URI * which was not used to the resolve the resource to which the meta data * instance belongs (value is "sling.resolutionPathInfo"). The value of this * property concatenated to the value of the * {@link #RESOLUTION_PATH sling.resolutionPath} property returns the * original request URI leading to the resource. *

* This property is optional. If missing, it should be assumed equal to an * empty string. * * @since 2.0.4 (Sling API Bundle 2.0.4) */ public static final String RESOLUTION_PATH_INFO = "sling.resolutionPathInfo"; /** * The name of the optional property providing the content type of the * resource if the resource is streamable (value is "sling.contentType"). * This property may be missing if the resource is not streamable or if the * content type is not known. */ public static final String CONTENT_TYPE = "sling.contentType"; /** * The name of the optional property providing the content length of the * resource if the resource is streamable (value is "sling.contentLength"). * This property may be missing if the resource is not streamable or if the * content length is not known. *

* Note, that unlike the other properties, this property may be set only * after the resource has successfully been adapted to an * InputStream for performance reasons. */ public static final String CONTENT_LENGTH = "sling.contentLength"; /** * The name of the optional property providing the character encoding of the * resource if the resource is streamable and contains character data (value * is "sling.characterEncoding"). This property may be missing if the * resource is not streamable or if the character encoding is not known. */ public static final String CHARACTER_ENCODING = "sling.characterEncoding"; /** * Returns the creation time of this resource in the repository in * milliseconds (value is "sling.creationTime"). The type of this property * is java.lang.Long. The property may be missing if the * resource is not streamable or if the creation time is not known. */ public static final String CREATION_TIME = "sling.creationTime"; /** * Returns the last modification time of this resource in the repository in * milliseconds (value is "sling.modificationTime"). The type of this * property is java.lang.Long. The property may be missing * if the resource is not streamable or if the last modification time is not * known. */ public static final String MODIFICATION_TIME = "sling.modificationTime"; /** * Returns whether the resource resolver should continue to search for a * resource. * A resource provider can set this flag to indicate that the resource * resolver should search for a provider with a lower priority. If it * finds a resource using such a provider, that resource is returned * instead. If none is found this resource is returned. * This flag should never be manipulated by application code! * The value of this property has no meaning, the resource resolver * just checks whether this flag is set or not. * @since 2.2 (Sling API Bundle 2.2.0) */ public static final String INTERNAL_CONTINUE_RESOLVING = ":org.apache.sling.resource.internal.continue.resolving"; /** * Returns a map containing parameters added to path after semicolon. * For instance, map for path /content/test;v='1.2.3'.html * will contain one entry key v and value 1.2.3. */ public static final String PARAMETER_MAP = "sling.parameterMap"; private boolean isReadOnly = false; /** * Sets the {@link #CHARACTER_ENCODING} property to encoding * if not null. * @param encoding The encoding */ public void setCharacterEncoding(String encoding) { if (encoding != null) { put(CHARACTER_ENCODING, encoding); } } /** * Returns the {@link #CHARACTER_ENCODING} property if not null * and a String instance. Otherwise null is * returned. * @return The character encoding */ public @CheckForNull String getCharacterEncoding() { Object value = get(CHARACTER_ENCODING); if (value instanceof String) { return (String) value; } return null; } /** * Sets the {@link #CONTENT_TYPE} property to contentType if * not null. * @param contentType The content type */ public void setContentType(String contentType) { if (contentType != null) { put(CONTENT_TYPE, contentType); } } /** * Returns the {@link #CONTENT_TYPE} property if not null and * a String instance. Otherwise null is * returned. * @return The content type */ public @CheckForNull String getContentType() { Object value = get(CONTENT_TYPE); if (value instanceof String) { return (String) value; } return null; } /** * Sets the {@link #CONTENT_LENGTH} property to contentType * if not null. * @param contentLength The content length */ public void setContentLength(long contentLength) { if (contentLength > 0) { put(CONTENT_LENGTH, contentLength); } } /** * Returns the {@link #CONTENT_LENGTH} property if not null * and a long. Otherwise -1 is returned. * @return The content length */ public long getContentLength() { Object value = get(CONTENT_LENGTH); if (value instanceof Long) { return (Long) value; } return -1; } /** * Sets the {@link #CREATION_TIME} property to creationTime * if not negative. * @param creationTime The creation time */ public void setCreationTime(long creationTime) { if (creationTime >= 0) { put(CREATION_TIME, creationTime); } } /** * Returns the {@link #CREATION_TIME} property if not null * and a long. Otherwise -1 is returned. * @return The creation time */ public long getCreationTime() { Object value = get(CREATION_TIME); if (value instanceof Long) { return (Long) value; } return -1; } /** * Sets the {@link #MODIFICATION_TIME} property to * modificationTime if not negative. * @param modificationTime The modification time */ public void setModificationTime(long modificationTime) { if (modificationTime >= 0) { put(MODIFICATION_TIME, modificationTime); } } /** * Returns the {@link #MODIFICATION_TIME} property if not null * and a long. Otherwise -1 is returned. * @return The modification time */ public long getModificationTime() { Object value = get(MODIFICATION_TIME); if (value instanceof Long) { return (Long) value; } return -1; } /** * Sets the {@link #RESOLUTION_PATH} property to resolutionPath * if not null. * @param resolutionPath The resolution path */ public void setResolutionPath(String resolutionPath) { if (resolutionPath != null) { put(RESOLUTION_PATH, resolutionPath); } } /** * Returns the {@link #RESOLUTION_PATH} property if not null * and a String instance. Otherwise null is * returned. * @return The resolution path */ public @CheckForNull String getResolutionPath() { Object value = get(RESOLUTION_PATH); if (value instanceof String) { return (String) value; } return null; } /** * Sets the {@link #RESOLUTION_PATH_INFO} property to * resolutionPathInfo if not null. * @param resolutionPathInfo The resolution path info */ public void setResolutionPathInfo(String resolutionPathInfo) { if (resolutionPathInfo != null) { put(RESOLUTION_PATH_INFO, resolutionPathInfo); } } /** * Returns the {@link #RESOLUTION_PATH_INFO} property if not * null and a String instance. Otherwise * null is returned. * @return The resolution path info */ public @CheckForNull String getResolutionPathInfo() { Object value = get(RESOLUTION_PATH_INFO); if (value instanceof String) { return (String) value; } return null; } /** * Sets the {@link #PARAMETER_MAP} property to * parameterMap if not null. * @param parameterMap The parameter map */ public void setParameterMap(Map parameterMap) { if (parameterMap != null) { if (parameterMap.isEmpty()) { put(PARAMETER_MAP, Collections.emptyMap()); } else { put(PARAMETER_MAP, new LinkedHashMap(parameterMap)); } } } /** * Returns the {@link #PARAMETER_MAP} property if not * null and a Map instance. Otherwise * null is returned. * @return The parameter map */ @SuppressWarnings("unchecked") public @CheckForNull Map getParameterMap() { Object value = get(PARAMETER_MAP); if (value instanceof Map) { return (Map) value; } return null; } /** * Make this object read-only. All method calls trying to modify this object * result in an exception! * @since 2.3 (Sling API Bundle 2.4.0) */ public void lock() { this.isReadOnly = true; } /** * Check if this object is read only and if so throw an unsupported operation exception. */ private void checkReadOnly() { if ( this.isReadOnly ) { throw new UnsupportedOperationException(getClass().getSimpleName() + " is locked"); } } @Override public void clear() { this.checkReadOnly(); super.clear(); } @Override public Object put(@Nonnull final String key, final Object value) { this.checkReadOnly(); return super.put(key, value); } @Override public void putAll(@Nonnull final Map m) { this.checkReadOnly(); super.putAll(m); } @Override public Object remove(@Nonnull final Object key) { this.checkReadOnly(); return super.remove(key); } protected void internalPut(@Nonnull String key, Object value) { super.put(key, value); } @Override public Object clone() { ResourceMetadata result = (ResourceMetadata) super.clone(); result.lockedEntrySet = null; result.lockedKeySet = null; result.lockedValues = null; return result; } // volatile for correct double-checked locking in getLockedData() private transient volatile Set> lockedEntrySet; private transient Set lockedKeySet; private transient Collection lockedValues; private void getLockedData() { if(isReadOnly && lockedEntrySet == null) { synchronized (this) { if(isReadOnly && lockedEntrySet == null) { lockedEntrySet = Collections.unmodifiableSet(super.entrySet()); lockedKeySet = Collections.unmodifiableSet(super.keySet()); lockedValues = Collections.unmodifiableCollection(super.values()); } } } } @Override public @Nonnull Set> entrySet() { getLockedData(); return lockedEntrySet != null ? lockedEntrySet : super.entrySet(); } @Override public @Nonnull Set keySet() { getLockedData(); return lockedKeySet != null ? lockedKeySet : super.keySet(); } @Override public @Nonnull Collection values() { getLockedData(); return lockedValues != null ? lockedValues : super.values(); } }