LCOV - code coverage report
Current view: top level - server/tools - ToolsCatalog.java (source / functions) Hit Total Coverage
Test: _coverage_report.dat Lines: 62 92 67.4 %
Date: 2022-11-19 15:00:39 Functions: 9 16 56.2 %

          Line data    Source code
       1             : // Copyright (C) 2010 The Android Open Source Project
       2             : //
       3             : // Licensed under the Apache License, Version 2.0 (the "License");
       4             : // you may not use this file except in compliance with the License.
       5             : // You may obtain a copy of the License at
       6             : //
       7             : // http://www.apache.org/licenses/LICENSE-2.0
       8             : //
       9             : // Unless required by applicable law or agreed to in writing, software
      10             : // distributed under the License is distributed on an "AS IS" BASIS,
      11             : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      12             : // See the License for the specific language governing permissions and
      13             : // limitations under the License.
      14             : 
      15             : package com.google.gerrit.server.tools;
      16             : 
      17             : import static java.nio.charset.StandardCharsets.UTF_8;
      18             : 
      19             : import com.google.common.base.Strings;
      20             : import com.google.common.flogger.FluentLogger;
      21             : import com.google.gerrit.common.Nullable;
      22             : import com.google.gerrit.common.Version;
      23             : import com.google.inject.Inject;
      24             : import com.google.inject.Singleton;
      25             : import java.io.BufferedReader;
      26             : import java.io.ByteArrayInputStream;
      27             : import java.io.ByteArrayOutputStream;
      28             : import java.io.IOException;
      29             : import java.io.InputStream;
      30             : import java.io.InputStreamReader;
      31             : import java.util.ArrayList;
      32             : import java.util.Collections;
      33             : import java.util.List;
      34             : import java.util.NavigableMap;
      35             : import java.util.TreeMap;
      36             : import org.eclipse.jgit.lib.Constants;
      37             : import org.eclipse.jgit.util.RawParseUtils;
      38             : 
      39             : /**
      40             :  * Listing of all client side tools stored on this server.
      41             :  *
      42             :  * <p>Clients may download these tools through our file server, as they are packaged with our own
      43             :  * software releases.
      44             :  */
      45             : @Singleton
      46             : public class ToolsCatalog {
      47         138 :   private static final FluentLogger logger = FluentLogger.forEnclosingClass();
      48             : 
      49             :   private final NavigableMap<String, Entry> toc;
      50             : 
      51             :   @Inject
      52         138 :   ToolsCatalog() throws IOException {
      53         138 :     this.toc = readToc();
      54         138 :   }
      55             : 
      56             :   /**
      57             :    * Lookup an entry in the tools catalog.
      58             :    *
      59             :    * @param name path of the item, relative to the root of the catalog.
      60             :    * @return the entry; null if the item is not part of the catalog.
      61             :    */
      62             :   @Nullable
      63             :   public Entry get(@Nullable String name) {
      64           0 :     if (Strings.isNullOrEmpty(name)) {
      65           0 :       return null;
      66             :     }
      67           0 :     if (name.startsWith("/")) {
      68           0 :       name = name.substring(1);
      69             :     }
      70           0 :     if (name.endsWith("/")) {
      71           0 :       name = name.substring(0, name.length() - 1);
      72             :     }
      73           0 :     return toc.get(name);
      74             :   }
      75             : 
      76             :   private static NavigableMap<String, Entry> readToc() throws IOException {
      77         138 :     NavigableMap<String, Entry> toc = new TreeMap<>();
      78         138 :     final BufferedReader br =
      79         138 :         new BufferedReader(new InputStreamReader(new ByteArrayInputStream(read("TOC")), UTF_8));
      80             :     String line;
      81         138 :     while ((line = br.readLine()) != null) {
      82         138 :       if (line.length() > 0 && !line.startsWith("#")) {
      83         138 :         final Entry e = new Entry(Entry.Type.FILE, line);
      84         138 :         toc.put(e.getPath(), e);
      85         138 :       }
      86             :     }
      87             : 
      88         138 :     final List<Entry> all = new ArrayList<>(toc.values());
      89         138 :     for (Entry e : all) {
      90         138 :       String path = dirOf(e.getPath());
      91         138 :       while (path != null) {
      92         138 :         Entry d = toc.get(path);
      93         138 :         if (d == null) {
      94         138 :           d = new Entry(Entry.Type.DIR, 0755, path);
      95         138 :           toc.put(d.getPath(), d);
      96             :         }
      97         138 :         d.children.add(e);
      98         138 :         path = dirOf(path);
      99         138 :         e = d;
     100         138 :       }
     101         138 :     }
     102             : 
     103         138 :     final Entry top = new Entry(Entry.Type.DIR, 0755, "");
     104         138 :     for (Entry e : toc.values()) {
     105         138 :       if (dirOf(e.getPath()) == null) {
     106         138 :         top.children.add(e);
     107             :       }
     108         138 :     }
     109         138 :     toc.put(top.getPath(), top);
     110             : 
     111         138 :     return Collections.unmodifiableNavigableMap(toc);
     112             :   }
     113             : 
     114             :   @Nullable
     115             :   private static byte[] read(String path) {
     116         138 :     String name = "root/" + path;
     117         138 :     try (InputStream in = ToolsCatalog.class.getResourceAsStream(name)) {
     118         138 :       if (in == null) {
     119           0 :         return null;
     120             :       }
     121         138 :       final ByteArrayOutputStream out = new ByteArrayOutputStream();
     122         138 :       final byte[] buf = new byte[8192];
     123             :       int n;
     124         138 :       while ((n = in.read(buf, 0, buf.length)) > 0) {
     125         138 :         out.write(buf, 0, n);
     126             :       }
     127         138 :       return out.toByteArray();
     128           0 :     } catch (Exception e) {
     129           0 :       logger.atFine().withCause(e).log("Cannot read %s", path);
     130           0 :       return null;
     131             :     }
     132             :   }
     133             : 
     134             :   @Nullable
     135             :   private static String dirOf(String path) {
     136         138 :     final int s = path.lastIndexOf('/');
     137         138 :     return s < 0 ? null : path.substring(0, s);
     138             :   }
     139             : 
     140             :   /** A file served out of the tools root directory. */
     141             :   public static class Entry {
     142         138 :     public enum Type {
     143         138 :       DIR,
     144         138 :       FILE
     145             :     }
     146             : 
     147             :     private final Type type;
     148             :     private final int mode;
     149             :     private final String path;
     150             :     private final List<Entry> children;
     151             : 
     152         138 :     Entry(Type type, String line) {
     153         138 :       int s = line.indexOf(' ');
     154         138 :       String mode = line.substring(0, s);
     155         138 :       String path = line.substring(s + 1);
     156             : 
     157         138 :       this.type = type;
     158         138 :       this.mode = Integer.parseInt(mode, 8);
     159         138 :       this.path = path;
     160         138 :       if (type == Type.FILE) {
     161         138 :         this.children = Collections.emptyList();
     162             :       } else {
     163           0 :         this.children = new ArrayList<>();
     164             :       }
     165         138 :     }
     166             : 
     167         138 :     Entry(Type type, int mode, String path) {
     168         138 :       this.type = type;
     169         138 :       this.mode = mode;
     170         138 :       this.path = path;
     171         138 :       this.children = new ArrayList<>();
     172         138 :     }
     173             : 
     174             :     public Type getType() {
     175           0 :       return type;
     176             :     }
     177             : 
     178             :     /** Returns the preferred UNIX file mode, e.g. {@code 0755}. */
     179             :     public int getMode() {
     180           0 :       return mode;
     181             :     }
     182             : 
     183             :     /** Returns path of the entry, relative to the catalog root. */
     184             :     public String getPath() {
     185         138 :       return path;
     186             :     }
     187             : 
     188             :     /** Returns the name of the entry, within its parent directory. */
     189             :     public String getName() {
     190           0 :       final int s = path.lastIndexOf('/');
     191           0 :       return s < 0 ? path : path.substring(s + 1);
     192             :     }
     193             : 
     194             :     /** Returns collection of entries below this one, if this is a directory. */
     195             :     public List<Entry> getChildren() {
     196           0 :       return Collections.unmodifiableList(children);
     197             :     }
     198             : 
     199             :     /** Returns a copy of the file's contents. */
     200             :     public byte[] getBytes() {
     201           0 :       byte[] data = read(getPath());
     202             : 
     203           0 :       if (isScript(data)) {
     204             :         // Embed Gerrit's version number into the top of the script.
     205             :         //
     206           0 :         final String version = Version.getVersion();
     207           0 :         final int lf = RawParseUtils.nextLF(data, 0);
     208           0 :         if (version != null && lf < data.length) {
     209           0 :           byte[] versionHeader = Constants.encode("# From Gerrit Code Review " + version + "\n");
     210             : 
     211           0 :           ByteArrayOutputStream buf = new ByteArrayOutputStream();
     212           0 :           buf.write(data, 0, lf);
     213           0 :           buf.write(versionHeader, 0, versionHeader.length);
     214           0 :           buf.write(data, lf, data.length - lf);
     215           0 :           data = buf.toByteArray();
     216             :         }
     217             :       }
     218             : 
     219           0 :       return data;
     220             :     }
     221             : 
     222             :     private boolean isScript(byte[] data) {
     223           0 :       return data != null
     224             :           && data.length > 3 //
     225             :           && data[0] == '#' //
     226             :           && data[1] == '!' //
     227             :           && data[2] == '/';
     228             :     }
     229             :   }
     230             : }

Generated by: LCOV version 1.16+git.20220603.dfeb750