001/* 002 * Copyright (c) 2003-2010 The Regents of the University of California. 003 * All rights reserved. 004 * 005 * '$Author: jianwu $' 006 * '$Date: 2012-11-15 20:03:09 +0000 (Thu, 15 Nov 2012) $' 007 * '$Revision: 31093 $' 008 * 009 * Permission is hereby granted, without written agreement and without 010 * license or royalty fees, to use, copy, modify, and distribute this 011 * software and its documentation for any purpose, provided that the above 012 * copyright notice and the following two paragraphs appear in all copies 013 * of this software. 014 * 015 * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY 016 * FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 017 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF 018 * THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF 019 * SUCH DAMAGE. 020 * 021 * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, 022 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 023 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE 024 * PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF 025 * CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, 026 * ENHANCEMENTS, OR MODIFICATIONS. 027 * 028 */ 029 030package org.kepler.objectmanager.lsid; 031 032import java.io.IOException; 033import java.io.ObjectInputStream; 034import java.io.ObjectOutputStream; 035import java.io.ObjectStreamField; 036import java.io.Serializable; 037import java.util.StringTokenizer; 038 039import org.apache.commons.logging.Log; 040import org.apache.commons.logging.LogFactory; 041import org.kepler.util.AuthNamespace; 042 043/** 044 * A kepler class to store lsids of the form 045 * urn:lsid:<authority>:<namespace>:<object>:<revision>#<anchor> where 046 * <authority> and <namespace> are strings that do not contain : characters and 047 * where <object> and <revision> are Long, 64 bit integers, and <anchor> is any 048 * string that does not contain a # or : character. 049 * 050 *@created June 20, 2005 051 */ 052 053public class KeplerLSID implements Serializable { 054 private static final long serialVersionUID = 6124781256944559026L; 055 private static final Log log = LogFactory 056 .getLog(KeplerLSID.class.getName()); 057 private static final boolean isDebugging = log.isDebugEnabled(); 058 059 private String _lsidStr = null; 060 061 // the parts of the lsid 062 private String _authority; 063 private String _namespace; 064 private Long _object; 065 private Long _revision; 066 private String _anchor; 067 068 public static final char separatorChar = ':'; 069 public static final String separator = ":"; 070 public static final char anchorSeparatorChar = '#'; 071 public static final String anchorSeparator = "#"; 072 073 /* 074 * The following are the different types of Kepler objects. These are used 075 * to identify entries in KAR files. These are going away soon. 076 */ 077 public static final String ACTOR_METADATA = "actorMetadata"; 078 public static final String JAR = "jar"; 079 public static final String JAVA_CLASS = "class"; 080 public static final String WORKFLOW = "workflow"; 081 public static final String NATIVE_LIBRARY = "nativeLibrary"; 082 public static final String XML_METADATA = "xmlMetadata"; 083 public static final String DATA = "data"; 084 public static final String RESOURCE_FILE = "file"; 085 086 /** 087 * Construct an lsid. 088 * @throws Exception 089 */ 090 public KeplerLSID(String lsidString) throws Exception { 091 if (isDebugging) 092 log.debug("KeplerLSID("+lsidString+")"); 093 // Note - if any changes needs to be made to this constructor, 094 // the changes should be made in initializeLSID so the 095 // deserialization readObject() method can benefit from it as well. 096 _lsidStr = lsidString; 097 initializeLSID(); 098 } 099 100 /** 101 * construct an lsid from components 102 * 103 * @param authority 104 * the authority of the new lsid 105 * @param namespace 106 * the namespace of the new lsid 107 * @param the 108 * object number of the new lsid 109 * @param revision 110 * the revision of the new lsid 111 */ 112 public KeplerLSID(String authority, String namespace, Long object, 113 Long revision) throws Exception { 114 if (isDebugging) 115 log.debug("KeplerLSID("+authority+","+namespace+","+object+","+revision+")"); 116 117 _lsidStr = "urn:lsid:" + authority + ":" + namespace + ":" + object 118 + ":" + revision; 119 initializeLSID(); 120 } 121 122 /** 123 * creates an lsid from a metacat docid of the form 124 * <document>.<object>.<rev> 125 * 126 * @param metacatDocid 127 * the docid to translate 128 * @param authority 129 * the authority to use in the lsid 130 */ 131 public KeplerLSID(String metacatDocid, String authority) throws Exception { 132 if (isDebugging) log.debug("KeplerLSID("+metacatDocid+","+authority+")"); 133 String doc; 134 String obj; 135 String rev = "1"; 136 doc = metacatDocid.substring(0, metacatDocid.indexOf(".")); 137 // check if there is a revision 138 if (metacatDocid.lastIndexOf(".") != metacatDocid.indexOf(".")) { 139 // there is a revision 140 obj = metacatDocid.substring(metacatDocid.indexOf(".") + 1, 141 metacatDocid.lastIndexOf(".")); 142 rev = metacatDocid.substring(metacatDocid.lastIndexOf(".") + 1, 143 metacatDocid.length()); 144 } else { 145 // no revision 146 obj = metacatDocid.substring(metacatDocid.indexOf(".") + 1, 147 metacatDocid.lastIndexOf(".")); 148 } 149 _authority = authority; 150 _namespace = doc; 151 _object = new Long(obj); 152 _revision = new Long(rev); 153 _lsidStr = "urn:lsid:" + authority + ":" + _namespace + ":" + _object 154 + ":" + _revision; 155 if (isDebugging) 156 log.debug("string: " + _lsidStr); 157 } 158 159 /** 160 * returns the namespace component of this lsid 161 */ 162 public String getNamespace() { 163 return _namespace; 164 } 165 166 /** 167 * returns the authority component of this lsid 168 */ 169 public String getAuthority() { 170 return _authority; 171 } 172 173 /** 174 * return the revision componenent of this lsid 175 */ 176 public Long getRevision() { 177 return _revision; 178 } 179 180 /** 181 * returns the object component of this lsid 182 */ 183 public Long getObject() { 184 return _object; 185 } 186 187 public String getAnchor() { 188 return _anchor; 189 } 190 191 public void setAnchor(String anchor) throws Exception { 192 if (anchor.indexOf( separatorChar ) >= 0) { 193 throw new Exception("Anchor may not contain the : character."); 194 } 195 if (anchor.indexOf( anchorSeparatorChar ) >= 0) { 196 throw new Exception("Anchor may not contain the " 197 + anchorSeparator + " character."); 198 } 199 if (hasAnchor()) { 200 _lsidStr = "urn" + separator + "lsid" + separator + getAuthority() 201 + separator + getNamespace() 202 + separator + getObject() + separator + getRevision() 203 + anchorSeparator + anchor; 204 } else { 205 _lsidStr += anchorSeparator + _anchor; 206 } 207 _anchor = anchor; 208 } 209 210 /** 211 * @return true if this KeplerLSID has an anchor 212 */ 213 public boolean hasAnchor() { 214 if (_anchor == null || _anchor.equals("")) { 215 return false; 216 } 217 return true; 218 } 219 220 /** 221 * increment the revision number by 1. 222 */ 223 public void incrementRevision() { 224 long rev = Long.valueOf(_revision); 225 rev++; 226 _revision = Long.valueOf(rev); 227 _lsidStr = toString(); 228 } 229 230 /** 231 * Specifically set the revision on this LSID. You should never be calling this! 232 * Instead, pass your LSID to LSIDGenerator.updateLsidRevision(KeplerLSID). 233 * 234 * @param newRevision 235 */ 236 public void setRevision(Long newRevision) { 237 _revision = newRevision; 238 _lsidStr = toString(); 239 } 240 241 /** 242 * If this LSID was generated by this Kepler Instance return true else 243 * return false 244 * 245 * @return true if this KeplerLSID was generated by this Kepler Instance. 246 */ 247 public boolean isLocalToInstance() { 248 AuthNamespace an = AuthNamespace.getInstance(); 249 if (an.getAuthority().equals(getAuthority()) 250 && an.getNamespace().equals(getNamespace())) { 251 return true; 252 } 253 return false; 254 } 255 256 /** 257 * this will create a valid filename out of the lsid (without an extension). 258 * It will replace any invalid characters in the lsid with '.' This function 259 * is irreversible. 260 */ 261 public String createFilename() { 262 String fname = "urn.lsid." + getAuthority() + "." + getNamespace() 263 + "." + getObject() + "." + getRevision(); 264 // if (hasAnchor()) { 265 // fname += "." + getAnchor(); 266 // } 267 fname = fname.replace('/', '.'); 268 fname = fname.replace('\\', '_'); 269 return fname; 270 } 271 272 /** 273 * return a string representation of the lsid 274 */ 275 public String toString() { 276 String lsidStr = "urn" + separator + "lsid" + separator + getAuthority() 277 + separator + getNamespace() 278 + separator + getObject() + separator + getRevision(); 279 if (hasAnchor()) { 280 lsidStr += anchorSeparator + getAnchor(); 281 } 282 return lsidStr; 283 } 284 285 /** 286 * Return a string that contains only the first five elements of the LSID. 287 * i.e. urn:lsid:<authority>:<namespace>:<object> 288 * 289 * @return String 290 */ 291 public String toStringWithoutRevision() { 292 String lsidStr = "urn" + separator + "lsid" 293 + separator + getAuthority() + separator + getNamespace() 294 + separator + getObject(); 295 return lsidStr; 296 } 297 298 /* 299 * (non-Javadoc) 300 * 301 * @see java.lang.Object#hashCode() 302 */ 303 public int hashCode() { 304 305 int hash = 7; 306 hash = 31 * hash + (null == _authority ? 0 : _authority.hashCode()); 307 hash = 31 * hash + (null == _namespace ? 0 : _namespace.hashCode()); 308 hash = 31 * hash 309 + (int) (_object.longValue() ^ (_object.longValue() >>> 32)); 310 hash = 31 311 * hash 312 + (int) (_revision.longValue() ^ (_revision.longValue() >>> 32)); 313 return hash; 314 } 315 316 /** 317 * return true if this lsid equals the passed in lsid regardless of anchor 318 */ 319 public boolean equals(Object lsidObj) { 320 if (lsidObj instanceof KeplerLSID) { 321 KeplerLSID lsid = (KeplerLSID) lsidObj; 322 Long rev = lsid.getRevision(); 323 if ( equalsWithoutRevision(lsid) 324 && rev.equals(_revision)) { 325 return true; 326 } 327 } 328 return false; 329 } 330 331 /** 332 * return true if this lsid equals the passed in lsid regardless of 333 * revision or anchor 334 */ 335 public boolean equalsWithoutRevision(KeplerLSID lsid) { 336 String auth = lsid.getAuthority(); 337 String ns = lsid.getNamespace(); 338 Long obj = lsid.getObject(); 339 if (auth.trim().equals(_authority.trim()) 340 && ns.trim().equals(_namespace.trim()) && obj.equals(_object)) { 341 return true; 342 } 343 return false; 344 } 345 346 /** 347 * return true if this lsid equals the passed in lsid and the anchors are 348 * the same. 349 * 350 * @param lsid 351 * */ 352 public boolean equalsWithAnchor(KeplerLSID lsid) { 353 if (equals(lsid) && getAnchor().equals(lsid.getAnchor())) { 354 return true; 355 } 356 return false; 357 } 358 359 /** 360 * parses the lsid into its parts 361 * urn:lsid:<authority>:<namespace>:<object>:<revision> 362 */ 363 private void initializeLSID() throws Exception { 364 if (_lsidStr == null) { 365 // to avoid NullPointerException 366 _lsidStr = ""; 367 } 368 369 StringTokenizer st = new StringTokenizer(_lsidStr, separator); 370 if (st.countTokens() == 6) { 371 String u = st.nextToken(); 372 String l = st.nextToken(); 373 if (u.equals("urn") && l.equals("lsid")) { 374 _authority = st.nextToken(); 375 _namespace = st.nextToken(); 376 try { 377 _object = new Long(st.nextToken()); 378 } catch (NumberFormatException nfe) { 379 throw new Exception("Object must be a number"); 380 } 381 try { 382 String rev = st.nextToken(); 383 int anchorInd = rev.indexOf(anchorSeparatorChar); 384 if (anchorInd > 0) { 385 _revision = new Long(rev.substring(0, anchorInd)); 386 _anchor = rev.substring(anchorInd + 1); 387 if (_anchor.indexOf(anchorSeparatorChar) >= 0) 388 throw new Exception( 389 "Anchor '"+ _anchor + "' must not contain " 390 + anchorSeparator + " character"); 391 } else { 392 _revision = new Long(rev); 393 _anchor = ""; 394 } 395 _lsidStr = toString(); 396 } catch (NumberFormatException nfe) { 397 throw new Exception("Object must be a number"); 398 } 399 } else { 400 throw new Exception("KeplerLSID must begin with urn:lsid"); 401 } 402 } else if (st.countTokens() == 4) { 403 handleBackwardsCompatibilityKludgeFormat1(st); 404 } else { 405 throw new Exception( 406 "KeplerLSID format contains six elements urn:lsid:<authority>:<namespace>:<object>:<revision>"); 407 } 408 if (isDebugging) 409 log.debug("toString() -> " + toString()); 410 } 411 412 public static boolean isKeplerLSIDFormat(String lsidStr) { 413 if (isDebugging) log.debug("isKeplerLSIDFormat("+lsidStr+")"); 414 try { 415 new KeplerLSID(lsidStr); 416 } catch (Exception e) { 417 return false; 418 } 419 return true; 420 } 421 422 /** 423 * Define custom metadata representation consisting of a single String named 424 * "lsidString" 425 */ 426 private static final ObjectStreamField[] serialPersistentFields = { new ObjectStreamField( 427 "lsidString", String.class) }; 428 429 /** 430 * Custom deserialization method. The serial representation of the 431 * KeplerLSID object is simply the toString() representation. This string 432 * representation is passed into the initializeFromString method. 433 * 434 * @param ois 435 * @throws IOException 436 */ 437 private void readObject(ObjectInputStream ois) throws IOException, 438 ClassNotFoundException { 439 ObjectInputStream.GetField fields = ois.readFields(); 440 String lsidString = (String) fields.get("lsidString", null); 441 try { 442 _lsidStr = lsidString; 443 initializeLSID(); 444 } catch (Exception e) { 445 throw new IOException("Malformed LSID in serialized representation"); 446 } 447 } 448 449 /** 450 * Custom serialization method. The serial representation of the KeplerLSID 451 * object is simply the toString() representation. 452 * 453 * @param oos 454 * @throws IOException 455 */ 456 private void writeObject(ObjectOutputStream oos) throws IOException { 457 ObjectOutputStream.PutField fields = oos.putFields(); 458 fields.put("lsidString", toString()); 459 oos.writeFields(); 460 } 461 462 /** 463 * BackwardsCompatibilityKludgeFormat1 is an lsid in the following format 464 * urn:lsid:ecoinformatics.org:bob.2.4 where 2 is the object id and 4 is the 465 * revision a dot is used as the separator instead of a colon See 466 * http://bugzilla.ecoinformatics.org/show_bug.cgi?id=4066 467 * 468 * @param st 469 * @throws Exception 470 */ 471 private void handleBackwardsCompatibilityKludgeFormat1(StringTokenizer st) 472 throws Exception { 473 String u = st.nextToken(); 474 String l = st.nextToken(); 475 if (u.equals("urn") && l.equals("lsid")) { 476 _authority = st.nextToken(); 477 String possibleNamespacePlusObjectPlusRevision = st.nextToken(); 478 StringTokenizer stn = new StringTokenizer( 479 possibleNamespacePlusObjectPlusRevision, "."); 480 if (stn.countTokens() >= 3) { 481 String ns = ""; 482 for (int i = 0; i < (stn.countTokens() - 2); i++) { 483 ns += stn.nextToken() + "."; 484 } 485 _namespace = ns.substring(0, ns.length() - 1); 486 487 } else { 488 throw new Exception(""); 489 } 490 try { 491 _object = new Long(stn.nextToken()); 492 } catch (NumberFormatException nfe) { 493 throw new Exception("Object must be a number"); 494 } 495 try { 496 String rev = stn.nextToken(); 497 int anchorInd = rev.indexOf(anchorSeparatorChar); 498 if (anchorInd > 0) { 499 _revision = new Long(rev.substring(0, anchorInd)); 500 _anchor = rev.substring(anchorInd + 1); 501 if (_anchor.indexOf(anchorSeparatorChar) >= 0) 502 throw new Exception( 503 "Anchor must not contain " 504 + anchorSeparator + " character"); 505 } else { 506 _revision = new Long(rev); 507 _anchor = ""; 508 } 509 } catch (NumberFormatException nfe) { 510 throw new Exception("Object must be a number"); 511 } 512 } 513 } 514}