< prev index next >

src/jdk.jdeps/share/classes/com/sun/tools/jdeps/DepsAnalyzer.java

Print this page




  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 com.sun.tools.jdeps;
  27 
  28 import com.sun.tools.classfile.Dependency.Location;
  29 import java.io.IOException;
  30 import java.util.ArrayList;

  31 import java.util.Deque;
  32 import java.util.LinkedHashSet;
  33 import java.util.LinkedList;
  34 import java.util.List;
  35 import java.util.Optional;
  36 import java.util.Set;
  37 import java.util.concurrent.ConcurrentLinkedDeque;
  38 import java.util.stream.Collectors;
  39 import java.util.stream.Stream;
  40 
  41 import static com.sun.tools.jdeps.Analyzer.Type.CLASS;
  42 import static com.sun.tools.jdeps.Analyzer.Type.VERBOSE;
  43 import static com.sun.tools.jdeps.Module.trace;
  44 import static java.util.stream.Collectors.*;
  45 
  46 /**
  47  * Dependency Analyzer.
  48  *
  49  * Type of filters:
  50  * source filter: -include <pattern>


  74                         JdepsFilter filter,
  75                         JdepsWriter writer,
  76                         Analyzer.Type verbose,
  77                         boolean apiOnly) {
  78         this.configuration = config;
  79         this.filter = filter;
  80         this.writer = writer;
  81         this.verbose = verbose;
  82         this.apiOnly = apiOnly;
  83 
  84         this.finder = new DependencyFinder(config, filter);
  85         this.analyzer = new Analyzer(configuration, verbose, filter);
  86 
  87         // determine initial archives to be analyzed
  88         this.rootArchives.addAll(configuration.initialArchives());
  89 
  90         // if -include pattern is specified, add the matching archives on
  91         // classpath to the root archives
  92         if (filter.hasIncludePattern() || filter.hasTargetFilter()) {
  93             configuration.getModules().values().stream()
  94                 .filter(source -> filter.include(source) && filter.matches(source))
  95                 .forEach(this.rootArchives::add);
  96         }
  97 
  98         // class path archives
  99         configuration.classPathArchives().stream()
 100             .filter(filter::matches)
 101             .forEach(this.rootArchives::add);
 102 
 103         // Include the root modules for analysis
 104         this.rootArchives.addAll(configuration.rootModules());
 105 
 106         trace("analyze root archives: %s%n", this.rootArchives);
 107     }
 108 
 109     /*
 110      * Perform runtime dependency analysis
 111      */
 112     public boolean run() throws IOException {
 113         return run(false, 1);
 114     }


 144             // analyze the dependencies collected
 145             analyzer.run(archives, finder.locationToArchive());
 146 
 147             if (writer != null) {
 148                 writer.generateOutput(archives, analyzer);
 149             }
 150         } finally {
 151             finder.shutdown();
 152         }
 153         return true;
 154     }
 155 
 156     /**
 157      * Returns the archives for reporting that has matching dependences.
 158      *
 159      * If --require is set, they should be excluded.
 160      */
 161     Set<Archive> archives() {
 162         if (filter.requiresFilter().isEmpty()) {
 163             return archives.stream()
 164                 .filter(filter::include)
 165                 .filter(Archive::hasDependences)
 166                 .collect(Collectors.toSet());
 167         } else {
 168             // use the archives that have dependences and not specified in --require
 169             return archives.stream()
 170                 .filter(filter::include)
 171                 .filter(source -> !filter.requiresFilter().contains(source))
 172                 .filter(source ->
 173                         source.getDependencies()
 174                               .map(finder::locationToArchive)
 175                               .anyMatch(a -> a != source))
 176                 .collect(Collectors.toSet());
 177         }
 178     }
 179 
 180     /**
 181      * Returns the dependences, either class name or package name
 182      * as specified in the given verbose level.
 183      */
 184     Set<String> dependences() {
 185         return analyzer.archives().stream()
 186                        .map(analyzer::dependences)
 187                        .flatMap(Set::stream)
 188                        .collect(Collectors.toSet());
 189     }
 190 
 191     /**
 192      * Returns the archives that contains the given locations and
 193      * not parsed and analyzed.
 194      */
 195     private Set<Archive> unresolvedArchives(Stream<Location> locations) {
 196         return locations.filter(l -> !finder.isParsed(l))
 197                         .distinct()
 198                         .map(configuration::findClass)
 199                         .flatMap(Optional::stream)
 200                         .filter(filter::include)
 201                         .collect(toSet());
 202     }
 203 
 204     /*
 205      * Recursively analyzes entire module/archives.
 206     */
 207     private void transitiveArchiveDeps(int depth) throws IOException {
 208         Stream<Location> deps = archives.stream()
 209                                         .flatMap(Archive::getDependencies);
 210 
 211         // start with the unresolved archives
 212         Set<Archive> unresolved = unresolvedArchives(deps);
 213         do {
 214             // parse all unresolved archives
 215             Set<Location> targets = apiOnly
 216                 ? finder.parseExportedAPIs(unresolved.stream())
 217                 : finder.parse(unresolved.stream());
 218             archives.addAll(unresolved);
 219 
 220             // Add dependencies to the next batch for analysis
 221             unresolved = unresolvedArchives(targets.stream());
 222         } while (!unresolved.isEmpty() && depth-- > 0);
 223     }
 224 
 225     /*
 226      * Recursively analyze the class dependences
 227      */
 228     private void transitiveDeps(int depth) throws IOException {
 229         Stream<Location> deps = archives.stream()
 230                                         .flatMap(Archive::getDependencies);
 231 

 232         Deque<Location> unresolved = deps.collect(Collectors.toCollection(LinkedList::new));
 233         ConcurrentLinkedDeque<Location> deque = new ConcurrentLinkedDeque<>();
 234         do {
 235             Location target;
 236             while ((target = unresolved.poll()) != null) {
 237                 if (finder.isParsed(target))
 238                     continue;
 239 
 240                 Archive archive = configuration.findClass(target).orElse(null);
 241                 if (archive != null && filter.include(archive)) {
 242                     archives.add(archive);
 243 
 244                     String name = target.getName();
 245                     Set<Location> targets = apiOnly
 246                             ? finder.parseExportedAPIs(archive, name)
 247                             : finder.parse(archive, name);
 248 
 249                     // build unresolved dependencies
 250                     targets.stream()
 251                            .filter(t -> !finder.isParsed(t))
 252                            .forEach(deque::add);
 253                 }
 254             }
 255             unresolved = deque;
 256             deque = new ConcurrentLinkedDeque<>();
 257         } while (!unresolved.isEmpty() && depth-- > 0);
 258     }
 259 













 260     // ----- for testing purpose -----
 261 
 262     public static enum Info {
 263         REQUIRES,
 264         REQUIRES_TRANSITIVE,
 265         EXPORTED_API,
 266         MODULE_PRIVATE,
 267         QUALIFIED_EXPORTED_API,
 268         INTERNAL_API,
 269         JDK_INTERNAL_API,
 270         JDK_REMOVED_INTERNAL_API
 271     }
 272 
 273     public static class Node {
 274         public final String name;
 275         public final String source;
 276         public final Info info;
 277         Node(String name, Info info) {
 278             this(name, name, info);
 279         }




  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 com.sun.tools.jdeps;
  27 
  28 import com.sun.tools.classfile.Dependency.Location;
  29 import java.io.IOException;
  30 import java.util.ArrayList;
  31 import java.util.Collection;
  32 import java.util.Deque;
  33 import java.util.LinkedHashSet;
  34 import java.util.LinkedList;
  35 import java.util.List;
  36 import java.util.Optional;
  37 import java.util.Set;
  38 import java.util.concurrent.ConcurrentLinkedDeque;
  39 import java.util.stream.Collectors;
  40 import java.util.stream.Stream;
  41 
  42 import static com.sun.tools.jdeps.Analyzer.Type.CLASS;
  43 import static com.sun.tools.jdeps.Analyzer.Type.VERBOSE;
  44 import static com.sun.tools.jdeps.Module.trace;
  45 import static java.util.stream.Collectors.*;
  46 
  47 /**
  48  * Dependency Analyzer.
  49  *
  50  * Type of filters:
  51  * source filter: -include <pattern>


  75                         JdepsFilter filter,
  76                         JdepsWriter writer,
  77                         Analyzer.Type verbose,
  78                         boolean apiOnly) {
  79         this.configuration = config;
  80         this.filter = filter;
  81         this.writer = writer;
  82         this.verbose = verbose;
  83         this.apiOnly = apiOnly;
  84 
  85         this.finder = new DependencyFinder(config, filter);
  86         this.analyzer = new Analyzer(configuration, verbose, filter);
  87 
  88         // determine initial archives to be analyzed
  89         this.rootArchives.addAll(configuration.initialArchives());
  90 
  91         // if -include pattern is specified, add the matching archives on
  92         // classpath to the root archives
  93         if (filter.hasIncludePattern() || filter.hasTargetFilter()) {
  94             configuration.getModules().values().stream()
  95                 .filter(source -> include(source) && filter.matches(source))
  96                 .forEach(this.rootArchives::add);
  97         }
  98 
  99         // class path archives
 100         configuration.classPathArchives().stream()
 101             .filter(filter::matches)
 102             .forEach(this.rootArchives::add);
 103 
 104         // Include the root modules for analysis
 105         this.rootArchives.addAll(configuration.rootModules());
 106 
 107         trace("analyze root archives: %s%n", this.rootArchives);
 108     }
 109 
 110     /*
 111      * Perform runtime dependency analysis
 112      */
 113     public boolean run() throws IOException {
 114         return run(false, 1);
 115     }


 145             // analyze the dependencies collected
 146             analyzer.run(archives, finder.locationToArchive());
 147 
 148             if (writer != null) {
 149                 writer.generateOutput(archives, analyzer);
 150             }
 151         } finally {
 152             finder.shutdown();
 153         }
 154         return true;
 155     }
 156 
 157     /**
 158      * Returns the archives for reporting that has matching dependences.
 159      *
 160      * If --require is set, they should be excluded.
 161      */
 162     Set<Archive> archives() {
 163         if (filter.requiresFilter().isEmpty()) {
 164             return archives.stream()
 165                 .filter(this::include)
 166                 .filter(Archive::hasDependences)
 167                 .collect(Collectors.toSet());
 168         } else {
 169             // use the archives that have dependences and not specified in --require
 170             return archives.stream()
 171                 .filter(source -> !filter.requiresFilter().contains(source.getName()))
 172                 .filter(this::include)
 173                 .filter(source ->
 174                         source.getDependencies()
 175                               .map(finder::locationToArchive)
 176                               .anyMatch(a -> a != source))
 177                 .collect(Collectors.toSet());
 178         }
 179     }
 180 
 181     /**
 182      * Returns the dependences, either class name or package name
 183      * as specified in the given verbose level.
 184      */
 185     Set<String> dependences() {
 186         return analyzer.archives().stream()
 187                        .map(analyzer::dependences)
 188                        .flatMap(Set::stream)
 189                        .collect(Collectors.toSet());
 190     }
 191 
 192     /**
 193      * Returns the archives that contains the given locations and
 194      * not parsed and analyzed.
 195      */
 196     private Set<Archive> unresolvedArchives(Stream<Location> locations) {
 197         return locations.filter(l -> !finder.isParsed(l))
 198                         .distinct()
 199                         .map(configuration::findClass)
 200                         .flatMap(Optional::stream)

 201                         .collect(toSet());
 202     }
 203 
 204     /*
 205      * Recursively analyzes entire module/archives.
 206     */
 207     private void transitiveArchiveDeps(int depth) throws IOException {
 208         Stream<Location> deps = archives.stream()
 209                                         .flatMap(Archive::getDependencies);
 210 
 211         // start with the unresolved archives
 212         Set<Archive> unresolved = unresolvedArchives(deps);
 213         do {
 214             // parse all unresolved archives
 215             Set<Location> targets = apiOnly
 216                 ? finder.parseExportedAPIs(unresolved.stream())
 217                 : finder.parse(unresolved.stream());
 218             archives.addAll(unresolved);
 219 
 220             // Add dependencies to the next batch for analysis
 221             unresolved = unresolvedArchives(targets.stream());
 222         } while (!unresolved.isEmpty() && depth-- > 0);
 223     }
 224 
 225     /*
 226      * Recursively analyze the class dependences
 227      */
 228     private void transitiveDeps(int depth) throws IOException {
 229         Stream<Location> deps = archives.stream()
 230                                         .flatMap(Archive::getDependencies);
 231 
 232         Collection<Module> modules = configuration.getModules().values();
 233         Deque<Location> unresolved = deps.collect(Collectors.toCollection(LinkedList::new));
 234         ConcurrentLinkedDeque<Location> deque = new ConcurrentLinkedDeque<>();
 235         do {
 236             Location target;
 237             while ((target = unresolved.poll()) != null) {
 238                 if (finder.isParsed(target))
 239                     continue;
 240 
 241                 Archive archive = configuration.findClass(target).orElse(null);
 242                 if (archive != null) {
 243                     archives.add(archive);
 244 
 245                     String name = target.getName();
 246                     Set<Location> targets = apiOnly
 247                             ? finder.parseExportedAPIs(archive, name)
 248                             : finder.parse(archive, name);
 249 
 250                     // build unresolved dependencies
 251                     targets.stream()
 252                            .filter(t -> !finder.isParsed(t))
 253                            .forEach(deque::add);
 254                 }
 255             }
 256             unresolved = deque;
 257             deque = new ConcurrentLinkedDeque<>();
 258         } while (!unresolved.isEmpty() && depth-- > 0);
 259     }
 260 
 261     /*
 262      * Tests if the given archive is requested for analysis.
 263      * It includes the root modules specified in --module, --add-modules and
 264      * dependences specified in --require
 265      */
 266     public boolean include(Archive source) {
 267         Module module = source.getModule();
 268         // skip system module by default
 269         return  !module.isSystem()
 270                     || configuration.rootModules().contains(source)
 271                     || filter.requiresFilter().contains(module.name());
 272     }
 273 
 274     // ----- for testing purpose -----
 275 
 276     public static enum Info {
 277         REQUIRES,
 278         REQUIRES_TRANSITIVE,
 279         EXPORTED_API,
 280         MODULE_PRIVATE,
 281         QUALIFIED_EXPORTED_API,
 282         INTERNAL_API,
 283         JDK_INTERNAL_API,
 284         JDK_REMOVED_INTERNAL_API
 285     }
 286 
 287     public static class Node {
 288         public final String name;
 289         public final String source;
 290         public final Info info;
 291         Node(String name, Info info) {
 292             this(name, name, info);
 293         }


< prev index next >