1 /*
   2  * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package java.lang.module;
  27 
  28 import java.io.IOException;
  29 import java.io.UncheckedIOException;
  30 import java.net.URI;
  31 import java.util.Objects;
  32 import java.util.Optional;
  33 import java.util.function.Supplier;
  34 
  35 import jdk.internal.module.ModuleHashes.HashSupplier;
  36 
  37 
  38 /**
  39  * A reference to a module's content.
  40  *
  41  * <p> A module reference contains the module's descriptor and its location, if
  42  * known.  It also has the ability to create a {@link ModuleReader} in order to
  43  * access the module's content, which may be inside the Java run-time system
  44  * itself or in an artifact such as a modular JAR file.
  45  *
  46  * @see ModuleFinder
  47  * @see ModuleReader
  48  * @since 9
  49  */
  50 
  51 public final class ModuleReference {
  52 
  53     private final ModuleDescriptor descriptor;
  54     private final URI location;
  55     private final Supplier<ModuleReader> readerSupplier;
  56 
  57     // true if this is a reference to a patched module
  58     private boolean patched;
  59 
  60     // the function that computes the hash of this module reference
  61     private final HashSupplier hasher;
  62 
  63     // cached hash string to avoid needing to compute it many times
  64     private String cachedHash;
  65 
  66 
  67     /**
  68      * Constructs a new instance of this class.
  69      */
  70     ModuleReference(ModuleDescriptor descriptor,
  71                     URI location,
  72                     Supplier<ModuleReader> readerSupplier,
  73                     boolean patched,
  74                     HashSupplier hasher)
  75 
  76     {
  77         this.descriptor = Objects.requireNonNull(descriptor);
  78         this.location = location;
  79         this.readerSupplier = Objects.requireNonNull(readerSupplier);
  80         this.patched = patched;
  81         this.hasher = hasher;
  82     }
  83 
  84     /**
  85      * Constructs a new instance of this class.
  86      */
  87     ModuleReference(ModuleDescriptor descriptor,
  88                     URI location,
  89                     Supplier<ModuleReader> readerSupplier,
  90                     HashSupplier hasher)
  91 
  92     {
  93         this(descriptor, location, readerSupplier, false, hasher);
  94     }
  95 
  96 
  97     /**
  98      * Constructs a new instance of this class.
  99      *
 100      * <p> The {@code readSupplier} parameter is the supplier of the {@link
 101      * ModuleReader} that may be used to read the module content. Its {@link
 102      * Supplier#get() get()} method throws {@link UncheckedIOException} if an
 103      * I/O error occurs opening the module content. The {@code get()} method
 104      * throws {@link SecurityException} if opening the module is denied by the
 105      * security manager.
 106      *
 107      * @param descriptor
 108      *        The module descriptor
 109      * @param location
 110      *        The module location or {@code null} if not known
 111      * @param readerSupplier
 112      *        The {@code Supplier} of the {@code ModuleReader}
 113      */
 114     public ModuleReference(ModuleDescriptor descriptor,
 115                            URI location,
 116                            Supplier<ModuleReader> readerSupplier)
 117     {
 118         this(descriptor, location, readerSupplier, false, null);
 119     }
 120 
 121     /**
 122      * Returns the module descriptor.
 123      *
 124      * @return The module descriptor
 125      */
 126     public ModuleDescriptor descriptor() {
 127         return descriptor;
 128     }
 129 
 130 
 131     /**
 132      * Returns the location of this module's content, if known.
 133      *
 134      * <p> This URI, when present, is used as the {@linkplain
 135      * java.security.CodeSource#getLocation location} value of a {@link
 136      * java.security.CodeSource CodeSource} so that a module's classes can be
 137      * granted specific permissions when loaded by a {@link
 138      * java.security.SecureClassLoader SecureClassLoader}.
 139      *
 140      * @return The location or an empty {@code Optional} if not known
 141      */
 142     public Optional<URI> location() {
 143         return Optional.ofNullable(location);
 144     }
 145 
 146 
 147     /**
 148      * Opens the module content for reading.
 149      *
 150      * <p> This method opens the module content by invoking the {@link
 151      * Supplier#get() get()} method of the {@code readSupplier} specified at
 152      * construction time. </p>
 153      *
 154      * @return A {@code ModuleReader} to read the module
 155      *
 156      * @throws IOException
 157      *         If an I/O error occurs
 158      * @throws SecurityException
 159      *         If denied by the security manager
 160      */
 161     public ModuleReader open() throws IOException {
 162         try {
 163             return readerSupplier.get();
 164         } catch (UncheckedIOException e) {
 165             throw e.getCause();
 166         }
 167 
 168     }
 169 
 170 
 171     /**
 172      * Returns {@code true} if this module has been patched via --patch-module.
 173      */
 174     boolean isPatched() {
 175         return patched;
 176     }
 177 
 178     /**
 179      * Returns the hash supplier for this module.
 180      */
 181     HashSupplier hasher() {
 182         return hasher;
 183     }
 184 
 185     /**
 186      * Computes the hash of this module, returning it as a hex string.
 187      * Returns {@code null} if the hash cannot be computed.
 188      *
 189      * @throws java.io.UncheckedIOException if an I/O error occurs
 190      */
 191     String computeHash(String algorithm) {
 192         String result = cachedHash;
 193         if (result != null)
 194             return result;
 195         if (hasher == null)
 196             return null;
 197         cachedHash = result = hasher.generate(algorithm);
 198         return result;
 199     }
 200 
 201     /**
 202      * Computes a hash code for this module reference.
 203      *
 204      * <p> The hash code is based upon the components of the reference, and
 205      * satisfies the general contract of the {@link Object#hashCode
 206      * Object.hashCode} method. </p>
 207      *
 208      * @return The hash-code value for this module reference
 209      */
 210     @Override
 211     public int hashCode() {
 212         int hc = hash;
 213         if (hc == 0) {
 214             hc = Objects.hash(descriptor, location, readerSupplier, hasher,
 215                     Boolean.valueOf(patched));
 216             if (hc == 0)
 217                 hc = -1;
 218             hash = hc;
 219         }
 220         return hc;
 221     }
 222 
 223     private int hash;
 224 
 225     /**
 226      * Tests this module reference for equality with the given object.
 227      *
 228      * <p> If the given object is not a {@code ModuleReference} then this
 229      * method returns {@code false}. Two module references are equal if their
 230      * module descriptors are equal, their locations are equal or both unknown,
 231      * and were created with equal supplier objects to access the module
 232      * content. </p>
 233      *
 234      * <p> This method satisfies the general contract of the {@link
 235      * java.lang.Object#equals(Object) Object.equals} method. </p>
 236      *
 237      * @param   ob
 238      *          the object to which this object is to be compared
 239      *
 240      * @return  {@code true} if, and only if, the given object is a module
 241      *          reference that is equal to this module reference
 242      */
 243     @Override
 244     public boolean equals(Object ob) {
 245         if (!(ob instanceof ModuleReference))
 246             return false;
 247         ModuleReference that = (ModuleReference)ob;
 248 
 249         return Objects.equals(this.descriptor, that.descriptor)
 250                 && Objects.equals(this.location, that.location)
 251                 && Objects.equals(this.readerSupplier, that.readerSupplier)
 252                 && Objects.equals(this.hasher, that.hasher)
 253                 && this.patched == that.patched;
 254     }
 255 
 256     /**
 257      * Returns a string describing this module reference.
 258      *
 259      * @return A string describing this module reference
 260      */
 261     @Override
 262     public String toString() {
 263         return ("[module " + descriptor().name()
 264                 + ", location=" + location + "]");
 265     }
 266 
 267 }