1 /*
   2  * Copyright (c) 2010, 2012 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 org.openjdk.jigsaw;
  27 
  28 import java.io.*;
  29 import java.lang.module.*;
  30 import java.util.*;
  31 
  32 import static org.openjdk.jigsaw.Repository.ModuleFileType;
  33 import static org.openjdk.jigsaw.FileConstants.ModuleFile.HashType;
  34 
  35 
  36 /**
  37  * <p> A {@linkplain Repository module repository's} catalog </p>
  38  */
  39 
  40 public abstract class RepositoryCatalog {
  41 
  42     // ## Elements in this class are public only to enable unit tests
  43 
  44     private static final JigsawModuleSystem jms
  45         = JigsawModuleSystem.instance();
  46 
  47     public abstract void gatherDeclaringModuleIds(Set<ModuleId> mids)
  48         throws IOException;
  49 
  50     public abstract void gatherModuleIds(String moduleName, Set<ModuleId> mids)
  51         throws IOException;
  52 
  53     public abstract void gatherModuleIds(String moduleName,
  54                                          ModuleArchitecture modArch,
  55                                          Set<ModuleId> mids)
  56         throws IOException;
  57 
  58     public abstract byte[] readModuleInfoBytes(ModuleId mid)
  59         throws IOException;
  60 
  61     static class Entry {
  62 
  63         final ModuleFileType type;
  64         final ModuleArchitecture modArch;
  65         final byte[] mibs;
  66         final long csize;
  67         final long usize;
  68         final HashType hashType;
  69         final byte[] hash;
  70 
  71         Entry(ModuleFileType t, ModuleArchitecture modArch,
  72               byte[] m, long cs, long us, HashType ht, byte[] h) {
  73             type = t;
  74             this.modArch = modArch;
  75             mibs = m;
  76             csize = cs;
  77             usize = us;
  78             hashType = ht;
  79             hash = h;
  80         }
  81 
  82     }
  83 
  84     abstract void add(Entry e);
  85 
  86     public void add(ModuleFileType t, ModuleArchitecture modArch, byte[] mibs,
  87                     long cs, long us, HashType hashType, byte[] hash)
  88     {
  89         add(new Entry(t, modArch, mibs, cs, us, hashType, hash));
  90     }
  91 
  92     public abstract boolean remove(ModuleId mid);
  93 
  94     abstract Entry get(ModuleId mid);
  95 
  96 
  97     /**
  98      * <p> A {@linkplain RepositoryCatalog repository catalog} which can be
  99      * stored to, and then loaded from, a byte stream </p>
 100      */
 101     public static class StreamedRepositoryCatalog
 102         extends RepositoryCatalog
 103     {
 104 
 105         static final int MAJOR_VERSION = 0;
 106         static final int MINOR_VERSION = 0;
 107 
 108         private Map<ModuleId,Entry> modules = new HashMap<>();
 109         private Map<ModuleId,ModuleId> moduleForViewId= new HashMap<>();
 110 
 111         @Override
 112         public void gatherDeclaringModuleIds(Set<ModuleId> mids) {
 113             mids.addAll(modules.keySet());
 114         }
 115 
 116         @Override
 117         public void gatherModuleIds(String moduleName, Set<ModuleId> mids) {
 118             gatherModuleIds(moduleName, ModuleArchitecture.ANY, mids);
 119         }
 120 
 121         @Override
 122         public void gatherModuleIds(String moduleName,
 123                                     ModuleArchitecture modArch,
 124                                     Set<ModuleId> mids)
 125         {
 126             for (ModuleId mid : moduleForViewId.keySet()) {
 127                 if (moduleName == null || mid.name().equals(moduleName)) {
 128                     Entry entry = get(mid);
 129                     if (entry.modArch.equals(modArch))
 130                         mids.add(mid);
 131                 }
 132             }
 133         }
 134 
 135         @Override
 136         public byte[] readModuleInfoBytes(ModuleId mid) {
 137             Entry e = modules.get(moduleForViewId.get(mid));
 138             return (e != null) ? e.mibs : null;
 139         }
 140 
 141         @Override
 142         void add(Entry e) {
 143             ModuleInfo mi = jms.parseModuleInfo(e.mibs); // ## Need fast path
 144             modules.put(mi.id(), e);
 145             for (ModuleView mv : mi.views()) {
 146                 moduleForViewId.put(mv.id(), mi.id());
 147                 for (ModuleId alias : mv.aliases()) {
 148                     moduleForViewId.put(alias, mi.id());
 149                 }
 150             }
 151         }
 152 
 153         @Override
 154         public boolean remove(ModuleId mid) {
 155             for (Iterator<ModuleId> i = moduleForViewId.values().iterator();
 156                  i.hasNext();)
 157             {
 158                 // remove views/aliases defined in the module be removed
 159                 ModuleId id = i.next();
 160                 if (id.equals(mid)) {
 161                     i.remove();
 162                 }
 163             }
 164             return modules.remove(mid) != null;
 165         }
 166 
 167         @Override
 168         Entry get(ModuleId mid) {
 169             return modules.get(moduleForViewId.get(mid));
 170         }
 171 
 172         /* ##
 173         public boolean remove(ModuleIdQuery midq) {
 174             int nd = 0;
 175             for (Iterator<ModuleId> i = modules.keySet().iterator();
 176                  i.hasNext();)
 177             {
 178                 ModuleId mid = i.next();
 179                 if (midq.matches(mid)) {
 180                     i.remove();
 181                     nd++;
 182                 }
 183             }
 184             return nd != 0;
 185         }
 186         */
 187 
 188         private StreamedRepositoryCatalog() { }
 189 
 190         private FileHeader fileHeader() {
 191             return (new FileHeader()
 192                     .type(FileConstants.Type.STREAM_CATALOG)
 193                     .majorVersion(MAJOR_VERSION)
 194                     .minorVersion(MINOR_VERSION));
 195         }
 196 
 197         public void store(OutputStream os) throws IOException {
 198             OutputStream bos = new BufferedOutputStream(os);
 199             try (DataOutputStream out = new DataOutputStream(bos)) {
 200                 fileHeader().write(out);
 201                 out.writeInt(modules.size());
 202                 for (Map.Entry<ModuleId,Entry> me : modules.entrySet()) {
 203                     out.writeUTF(me.getKey().toString()); // ## Redundant
 204                     Entry e = me.getValue();
 205                     out.writeUTF(e.type.getFileNameExtension());
 206                     out.writeUTF(e.modArch.os());
 207                     out.writeUTF(e.modArch.arch());
 208                     out.writeLong(e.csize);
 209                     out.writeLong(e.usize);
 210                     out.writeShort(e.hashType.value());
 211                     out.writeShort(e.hash.length);
 212                     out.write(e.hash);
 213                     out.writeShort(e.mibs.length);
 214                     out.write(e.mibs);
 215                 }
 216                 out.writeInt(moduleForViewId.size());
 217                 for (Map.Entry<ModuleId,ModuleId> me : moduleForViewId.entrySet()) {
 218                     out.writeUTF(me.getKey().toString());
 219                     out.writeUTF(me.getValue().toString());
 220                 }
 221             }
 222         }
 223 
 224         public StreamedRepositoryCatalog loadStream(InputStream is)
 225             throws IOException
 226         {
 227             BufferedInputStream bis = new BufferedInputStream(is);
 228             DataInputStream in = new DataInputStream(bis);
 229             FileHeader fh = fileHeader();
 230             fh.read(in);
 231             int nms = in.readInt();
 232             for (int i = 0; i < nms; i++) {
 233                 ModuleId mid = jms.parseModuleId(in.readUTF());
 234                 ModuleFileType t = ModuleFileType.fromFileNameExtension(in.readUTF());
 235                 ModuleArchitecture modArch = ModuleArchitecture.create(in.readUTF(),
 236                                                                        in.readUTF());
 237                 long cs = in.readLong();
 238                 long us = in.readLong();
 239                 HashType ht = HashType.valueOf(in.readShort());
 240                 int nb = in.readShort();
 241                 byte[] hash = new byte[nb];
 242                 in.readFully(hash);
 243                 nb = in.readShort();
 244                 byte[] mibs = new byte[nb];
 245                 in.readFully(mibs);
 246                 modules.put(mid, new Entry(t, modArch, mibs, cs, us, ht, hash));
 247             }
 248             int nmids = in.readInt();
 249             for (int i = 0; i < nmids; i++) {
 250                 ModuleId id = jms.parseModuleId(in.readUTF());
 251                 ModuleId mid = jms.parseModuleId(in.readUTF());
 252                 moduleForViewId.put(id, mid);
 253             }
 254             return this;
 255         }
 256 
 257     }
 258 
 259     public static StreamedRepositoryCatalog load(InputStream in)
 260         throws IOException
 261     {
 262         StreamedRepositoryCatalog src = new StreamedRepositoryCatalog();
 263         if (in != null) {
 264             try {
 265                 src.loadStream(in);
 266             } finally {
 267                 in.close();
 268             }
 269         }
 270         return src;
 271     }
 272 
 273 
 274     /*
 275 
 276     private static class IndexedRepositoryCatalog {  } // ## Later
 277 
 278     static IndexedRepositoryCatalog open(File fn) throws IOException {
 279         return new IndexedRepositoryCatalog(...);
 280     }
 281 
 282     */
 283 
 284 }
--- EOF ---