/* * Part of the CCNx Java Library. * * Copyright (C) 2008-2012 Palo Alto Research Center, Inc. * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 2.1 * as published by the Free Software Foundation. * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. You should have received * a copy of the GNU Lesser General Public License along with this library; * if not, write to the Free Software Foundation, Inc., 51 Franklin Street, * Fifth Floor, Boston, MA 02110-1301 USA. */ package org.ccnx.ccn.profiles.metadata; import java.io.IOException; import java.util.Arrays; import org.ccnx.ccn.CCNHandle; import org.ccnx.ccn.profiles.CCNProfile; import org.ccnx.ccn.profiles.CommandMarker; import org.ccnx.ccn.profiles.SegmentationProfile; import org.ccnx.ccn.profiles.VersioningProfile; import org.ccnx.ccn.protocol.Component; import org.ccnx.ccn.protocol.ContentName; import org.ccnx.ccn.protocol.ContentObject; /** * Includes routines to find correct version of a metadata file based on its base file */ public class MetadataProfile implements CCNProfile { public static final String METADATA_NAMESPACE = "META"; public static final CommandMarker METADATA_MARKER = CommandMarker.commandMarker(METADATA_NAMESPACE, "M"); public static final byte [] OLD_METADATA_NAMESPACE = (CCNProfile.MARKER + "meta" + CCNProfile.MARKER).getBytes(); public static final byte [] HEADER_NAME = Component.parseNative(".header"); /** * This interface allows getLatestVersion of metadata within one of the supported meta * namespaces. */ public interface MetaNamer { public ContentName getMetaName(ContentName baseName, ContentName metaName); } /** * General getter for generic metadata */ private static class LocalMetaNamer implements MetaNamer { public ContentName getMetaName(ContentName baseName, ContentName metaName) { return metadataName(baseName).append(metaName); } } /** * Get a standard metadata path for a base file * @param baseName the base file * @return metadata path for base file */ public static ContentName metadataName(ContentName baseName) { return new ContentName(baseName, METADATA_MARKER); } /** * Get the latest version of a metadata file which is associated with a base file. Before * searching for the metadata version, we find the latest version of the base file. This * call requires the calling function to check for a terminal version before attempting to * retrieve the metadata content. If the base file name does not have a version, this * function will return null. If the metadata file for the latest version of the base file * does not exist, this function will return a content name without a terminal version. * * Example where both versions are found: * baseName/version/metadataMarkers/version * * Example where only the base file name version is found: * baseName/version/metadataMarkers * * Example where no base file version is found: * null * * @param baseName the base file * @param metaName the meta file. This should be a ContentName containing only the relative path * from the base file. * @param timeout time to search for the latest version in ms. Applies separately to each latest * version search. * @param handle CCNHandle to use for search. * @return * @throws IOException */ public static ContentName getLatestVersion(ContentName baseName, ContentName metaName, long timeout, CCNHandle handle) throws IOException { return getLatestVersion(baseName, new LocalMetaNamer(), metaName, timeout, handle); } /** * This call requires the calling function to check for a terminal version before * attempting to retrieve the metadata content. * * If the * base file name does not have a version, this function will return null. If the metadata * file for the latest version of the base file does not exist, this function will return a * content name without a terminal version. * * Example where both versions are found: * baseName/version/metadataMarkers/version * * Example where only the base file name version is found: * baseName/version/metadataMarkers * * Example where no base file version is found: * null * * * @param baseName * @param namer * @param metaName * @param timeout * @param handle * @return * @throws IOException */ public static ContentName getLatestVersion(ContentName baseName, MetaNamer namer, ContentName metaName, long timeout, CCNHandle handle) throws IOException { ContentName baseVersion = baseName; //removing the stream instance: 1 - we are not actually attempting to get the stream, we just want to discover the latest version //CCNInputStream checker = new CCNInputStream(baseName, handle); //if (null == checker) // return null; if (!VersioningProfile.containsVersion(baseVersion)) { //ContentObject co = VersioningProfile.getFirstBlockOfLatestVersion(baseName, null, checker.publisher(), timeout, checker, handle); ContentObject co = VersioningProfile.getFirstBlockOfLatestVersion(baseName, null, null, timeout, null, handle); if (null == co) return null; baseVersion = co.name(); baseVersion = SegmentationProfile.segmentRoot(baseVersion); } ContentName unversionedName = namer.getMetaName(baseVersion, metaName); //ContentObject meta = VersioningProfile.getFirstBlockOfLatestVersion(unversionedName, null, checker.publisher(), timeout, checker, handle); ContentObject meta = VersioningProfile.getFirstBlockOfLatestVersion(unversionedName, null, null, timeout, null, handle); if (null == meta) { //we did not find a version of the metadata content... do not append the current time. This can be //misleading to the calling application because it cannot discern between metadata that exists and metadata that //is not available. //return VersioningProfile.addVersion(unversionedName); return unversionedName; } return meta.name(); } /** * Check to see if we have (a block of) the header. Headers are also versioned. * @param baseName The name of the object whose header we are looking for (including version, but * not including segmentation information). * @param headerName The name of the object we think might be a header block (can include * segmentation). * @return */ public static boolean isHeader(ContentName baseName, ContentName headerName) { if (!baseName.isPrefixOf(headerName)) { return false; } return MetadataProfile.isHeader(headerName); } /** * Slightly more heuristic isHeader; looks to see if this is a segment of something that * ends in the header name (and version), without knowing the prefix.. */ public static boolean isHeader(ContentName potentialHeaderName) { if (SegmentationProfile.isSegment(potentialHeaderName)) { potentialHeaderName = SegmentationProfile.segmentRoot(potentialHeaderName); } // Header itself is likely versioned. if (VersioningProfile.isVersionComponent(potentialHeaderName.lastComponent())) { potentialHeaderName = potentialHeaderName.parent(); } if (potentialHeaderName.count() < 2) return false; if (!Arrays.equals(potentialHeaderName.lastComponent(), MetadataProfile.HEADER_NAME)) return false; if (!Arrays.equals(potentialHeaderName.component(potentialHeaderName.count()-2), METADATA_MARKER.getBytes())) return false; return true; } /** * Move header from <content>/<version> as its name to * <content>/<version>/_metadata_marker_/HEADER/<version> * where the second version is imposed by the use of versioning * network objects (i.e. this function should return up through HEADER above) * Header name generation may want to move to a MetadataProfile. * * @param name * @return */ public static ContentName headerName(ContentName name) { // Want to make sure we don't add a header name // to a fragment. Go back up to the fragment root. // Currently no header name added. if (SegmentationProfile.isSegment(name)) { name = SegmentationProfile.segmentRoot(name); } return new ContentName(name, METADATA_MARKER, MetadataProfile.HEADER_NAME); } public static ContentName oldHeaderName(ContentName name) { if (SegmentationProfile.isSegment(name)) { name = SegmentationProfile.segmentRoot(name); } return new ContentName(name, OLD_METADATA_NAMESPACE, MetadataProfile.HEADER_NAME); } }