1 /** 2 * 3 * Licensed to the Apache Software Foundation (ASF) under one 4 * or more contributor license agreements. See the NOTICE file 5 * distributed with this work for additional information 6 * regarding copyright ownership. The ASF licenses this file 7 * to you under the Apache License, Version 2.0 (the 8 * "License"); you may not use this file except in compliance 9 * with the License. You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, software 14 * distributed under the License is distributed on an "AS IS" BASIS, 15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 * See the License for the specific language governing permissions and 17 * limitations under the License. 18 */ 19 package org.apache.hadoop.hbase.util; 20 21 import java.util.StringTokenizer; 22 import java.util.regex.Matcher; 23 import java.util.regex.Pattern; 24 25 import org.apache.hadoop.classification.InterfaceAudience; 26 import org.apache.hadoop.classification.InterfaceStability; 27 28 /** 29 * Utility creating hbase friendly keys. 30 * Use fabricating row names or column qualifiers. 31 * <p>TODO: Add createSchemeless key, a key that doesn't care if scheme is 32 * http or https. 33 * @see Bytes#split(byte[], byte[], int) 34 */ 35 @InterfaceAudience.Public 36 @InterfaceStability.Stable 37 public class Keying { 38 private static final String SCHEME = "r:"; 39 private static final Pattern URI_RE_PARSER = 40 Pattern.compile("^([^:/?#]+://(?:[^/?#@]+@)?)([^:/?#]+)(.*)$"); 41 42 /** 43 * Makes a key out of passed URI for use as row name or column qualifier. 44 * 45 * This method runs transforms on the passed URI so it sits better 46 * as a key (or portion-of-a-key) in hbase. The <code>host</code> portion of 47 * the URI authority is reversed so subdomains sort under their parent 48 * domain. The returned String is an opaque URI of an artificial 49 * <code>r:</code> scheme to prevent the result being considered an URI of 50 * the original scheme. Here is an example of the transform: The url 51 * <code>http://lucene.apache.org/index.html?query=something#middle<code> is 52 * returned as 53 * <code>r:http://org.apache.lucene/index.html?query=something#middle</code> 54 * The transforms are reversible. No transform is done if passed URI is 55 * not hierarchical. 56 * 57 * <p>If authority <code>userinfo</code> is present, will mess up the sort 58 * (until we do more work).</p> 59 * 60 * @param u URL to transform. 61 * @return An opaque URI of artificial 'r' scheme with host portion of URI 62 * authority reversed (if present). 63 * @see #keyToUri(String) 64 * @see <a href="http://www.ietf.org/rfc/rfc2396.txt">RFC2396</a> 65 */ 66 public static String createKey(final String u) { 67 if (u.startsWith(SCHEME)) { 68 throw new IllegalArgumentException("Starts with " + SCHEME); 69 } 70 Matcher m = getMatcher(u); 71 if (m == null || !m.matches()) { 72 // If no match, return original String. 73 return u; 74 } 75 return SCHEME + m.group(1) + reverseHostname(m.group(2)) + m.group(3); 76 } 77 78 /** 79 * Reverse the {@link #createKey(String)} transform. 80 * 81 * @param s <code>URI</code> made by {@link #createKey(String)}. 82 * @return 'Restored' URI made by reversing the {@link #createKey(String)} 83 * transform. 84 */ 85 public static String keyToUri(final String s) { 86 if (!s.startsWith(SCHEME)) { 87 return s; 88 } 89 Matcher m = getMatcher(s.substring(SCHEME.length())); 90 if (m == null || !m.matches()) { 91 // If no match, return original String. 92 return s; 93 } 94 return m.group(1) + reverseHostname(m.group(2)) + m.group(3); 95 } 96 97 private static Matcher getMatcher(final String u) { 98 if (u == null || u.length() <= 0) { 99 return null; 100 } 101 return URI_RE_PARSER.matcher(u); 102 } 103 104 private static String reverseHostname(final String hostname) { 105 if (hostname == null) { 106 return ""; 107 } 108 StringBuilder sb = new StringBuilder(hostname.length()); 109 for (StringTokenizer st = new StringTokenizer(hostname, ".", false); 110 st.hasMoreElements();) { 111 Object next = st.nextElement(); 112 if (sb.length() > 0) { 113 sb.insert(0, "."); 114 } 115 sb.insert(0, next); 116 } 117 return sb.toString(); 118 } 119 120 }