src/share/classes/sun/tools/jar/Main.java

Print this page

        

@@ -27,10 +27,11 @@
 
 import java.lang.module.ModuleId;
 import java.io.*;
 import java.nio.file.Path;
 import java.nio.file.Files;
+import java.nio.charset.Charset;
 import java.util.*;
 import java.util.zip.*;
 import java.util.jar.*;
 import java.util.jar.Manifest;
 import java.text.MessageFormat;

@@ -604,10 +605,11 @@
                                ? new ModuleInfo(moduleid) : null;
         try (ZipInputStream zis = new ZipInputStream(in);
              ZipOutputStream zos = new JarOutputStream(out)) {
             ZipEntry e = null;
             Manifest mf = null;
+            Map<String,Set<String>> providers = new HashMap<>();
 
             if (entryMap.containsKey(MODULEINFO_NAME) && moduleid != null) {
                 error(getMsg("error.bad.moduleid"));
                 return false;
             } 

@@ -620,10 +622,17 @@
             while ((e = zis.getNextEntry()) != null) {
                 String name = e.getName();
 
                 boolean isManifestEntry = equalsIgnoreCase(name, MANIFEST_NAME);
 
+                String service = null;
+                if (moduleid != null && !e.isDirectory()
+                        && name.startsWith("META-INF/services/")) {
+                    int last = name.lastIndexOf("/");
+                    service = name.substring(last + 1);
+                }
+
                 if (jarIndex != null && equalsIgnoreCase(name, INDEX_NAME)) {
                     continue;
                 }
 
                 if (isManifestEntry) {

@@ -660,13 +669,22 @@
                     continue;
                 }
 
                 if (!entryMap.containsKey(name)) { // copy the old stuff
                     if (!name.equals(MODULEINFO_NAME) || moduleid == null) {
-                        // add exports in the generated module-info.java
                         copyZipEntry(e, zos);
+
+                        // when generating a module-info file and this entry is 
+                        // a service configuration file then parse it to get the
+                        // names of the service implementations.
+                        if (service != null) {
+                            byte[] bytes = readAllBytes(zis);
+                            providers.put(service, parseServicesEntry(bytes));
+                            zos.write(bytes);   // copy the content
+                        } else {
                         copy(e, zis, zos, minfo);   // copy the content
+                        }     
                         zos.closeEntry();
                     }
                 } else { // replace with the new files
                     File f = entryMap.get(name);
                     addFile(zos, f, minfo);

@@ -693,10 +711,19 @@
 
             if (minfo != null) {
                 // -I is specified
                 minfo.setMainClass(getMainClass(mf));
                 addModuleRequires(minfo, getClassPath(mf));
+                
+                // service providers
+                for (Map.Entry<String,Set<String>> entry: providers.entrySet()) {
+                    String service = entry.getKey();
+                    for (String impl: entry.getValue()) {
+                        minfo.addProvidesService(service, impl);
+                    }                    
+                }
+                
                 writeModuleInfo(minfo, zos, System.currentTimeMillis());
                 if (vflag) {
                     output(getMsg("out.update.moduleinfo"));
                 }
             }

@@ -746,10 +773,28 @@
         if (vflag) {
             output(getMsg("out.update.manifest"));
         }
     }
 
+    /**
+     * Parse the given byte array as the raw bytes of a services configuration
+     * file, returning the names of the entries.
+     */
+    private Set<String> parseServicesEntry(byte[] bytes) throws IOException {
+        ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
+        BufferedReader reader =
+            new BufferedReader(new InputStreamReader(bis, Charset.forName("UTF-8")));
+        Set<String> result = new HashSet<>();
+        String s;
+        while ((s = reader.readLine()) != null) {
+            s = s.trim();
+            if (s.length() > 0 && !s.startsWith("#"))
+                result.add(s);
+        }
+        return result;
+    }    
+
     private void writeModuleInfo(ModuleInfo minfo, ZipOutputStream zos, long ts)
         throws IOException
     {
         ZipEntry e = new ZipEntry(MODULEINFO_NAME);
         e.setTime(ts);

@@ -936,10 +981,41 @@
             helper.copyTo(to);
             helper.addExports(minfo, e);
         }
     }
     
+    /**
+     * Read all bytes from the input stream, returning them in a byte array.
+     */
+    private byte[] readAllBytes(InputStream from) throws IOException {
+        int capacity = 8192;
+        byte[] buf = new byte[capacity];
+        int nread = 0;
+        int rem = buf.length;
+        int n;
+        // read to EOF which may read more or less than initialSize (eg: file
+        // is truncated while we are reading)
+        while ((n = from.read(buf, nread, rem)) > 0) {
+            nread += n;
+            rem -= n;
+            assert rem >= 0;
+            if (rem == 0) {
+                // need larger buffer
+                int newCapacity = capacity << 1;
+                if (newCapacity < 0) {
+                    if (capacity == Integer.MAX_VALUE)
+                        throw new OutOfMemoryError("Required array size too large");
+                    newCapacity = Integer.MAX_VALUE;
+                }
+                rem = newCapacity - capacity;
+                buf = Arrays.copyOf(buf, newCapacity);
+                capacity = newCapacity;
+            }
+        }
+        return (capacity == nread) ? buf : Arrays.copyOf(buf, nread);
+    }    
+    
     private ByteStreamHelper helper = new ByteStreamHelper();
     class ByteStreamHelper extends ByteArrayOutputStream {
         void copyFrom(InputStream from) throws IOException {
             reset();
             

@@ -963,15 +1039,12 @@
      * @param from the input file to read from
      * @param to the output stream to write to
      * @throws IOException if an I/O error occurs
      */
     private void copy(File from, OutputStream to) throws IOException {
-        InputStream in = new FileInputStream(from);
-        try {
+        try (InputStream in = new FileInputStream(from)) {
             copy(in, to);
-        } finally {
-            in.close();
         }
     }
 
     /**
      * Copies all bytes from the input stream to the output file.

@@ -980,15 +1053,12 @@
      * @param from the input stream to read from
      * @param to the output file to write to
      * @throws IOException if an I/O error occurs
      */
     private void copy(InputStream from, File to) throws IOException {
-        OutputStream out = new FileOutputStream(to);
-        try {
+        try (OutputStream out = new FileOutputStream(to)) {
             copy(from, out);
-        } finally {
-            out.close();
         }
     }
 
     /**
      * Computes the crc32 of a File.  This is necessary when the