├── .gitattributes ├── .github └── workflows │ └── gradle.yml ├── .gitignore ├── COPYRIGHT ├── LICENSE ├── README.adoc ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── jsplitpkgscan └── src └── main ├── java ├── jdk │ └── jsplitpkgscan │ │ ├── JsplitpgkscanTask.java │ │ ├── Library.java │ │ └── Main.java └── module-info.java └── resources └── jdk └── jsplitpkgscan └── resources └── jsplitpkgscan.properties /.gitattributes: -------------------------------------------------------------------------------- 1 | # Set the default behavior, in case people don't have core.autocrlf set. 2 | * text=auto 3 | 4 | # Explicitly declare text files you want to always be normalized and converted 5 | # to native line endings on checkout. 6 | *.py text 7 | *.md text 8 | *.adoc text 9 | -------------------------------------------------------------------------------- /.github/workflows/gradle.yml: -------------------------------------------------------------------------------- 1 | # This workflow will build a Java project with Gradle 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-gradle 3 | 4 | name: CI 5 | 6 | on: 7 | push: 8 | branches: 9 | - master 10 | pull_request: 11 | types: 12 | - opened 13 | - synchronize 14 | - reopened 15 | jobs: 16 | build: 17 | name: ${{ matrix.os }} / OpenJDK ${{ matrix.jdk }} 18 | runs-on: ${{ matrix.os }} 19 | strategy: 20 | matrix: 21 | jdk: ['11', '15'] 22 | os: [ubuntu-latest, windows-latest, macOS-latest] 23 | 24 | steps: 25 | - uses: actions/checkout@v2 26 | - name: Set up JDK ${{ matrix.jdk }} 27 | uses: actions/setup-java@v1 28 | with: 29 | java-version: ${{ matrix.jdk }} 30 | - name: Grant execute permission for gradlew 31 | run: chmod +x gradlew 32 | - name: Build with Gradle 33 | run: ./gradlew build 34 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | build/ 3 | bin/ 4 | out/ 5 | .gradle/ 6 | .settings/ 7 | .idea/ 8 | .classpath 9 | .project 10 | .DS_Store 11 | *.iml 12 | *.iws 13 | -------------------------------------------------------------------------------- /COPYRIGHT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions 5 | are met: 6 | 7 | - Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | 10 | - Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in the 12 | documentation and/or other materials provided with the distribution. 13 | 14 | - Neither the name of Oracle nor the names of its 15 | contributors may be used to endorse or promote products derived 16 | from this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 19 | IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 20 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 21 | PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 22 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 23 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 24 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 25 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 26 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 27 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 28 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | 30 | This source code is provided to illustrate the usage of a given feature 31 | or technique and has been deliberately simplified. Additional steps 32 | required for a production-quality application, such as security checks, 33 | input validation, and proper error handling, might not be present in 34 | this sample code. 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The GNU General Public License (GPL) 2 | 3 | Version 2, June 1991 4 | 5 | Copyright (C) 1989, 1991 Free Software Foundation, Inc. 6 | 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 7 | 8 | Everyone is permitted to copy and distribute verbatim copies of this license 9 | document, but changing it is not allowed. 10 | 11 | Preamble 12 | 13 | The licenses for most software are designed to take away your freedom to share 14 | and change it. By contrast, the GNU General Public License is intended to 15 | guarantee your freedom to share and change free software--to make sure the 16 | software is free for all its users. This General Public License applies to 17 | most of the Free Software Foundation's software and to any other program whose 18 | authors commit to using it. (Some other Free Software Foundation software is 19 | covered by the GNU Library General Public License instead.) You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not price. Our 23 | General Public Licenses are designed to make sure that you have the freedom to 24 | distribute copies of free software (and charge for this service if you wish), 25 | that you receive source code or can get it if you want it, that you can change 26 | the software or use pieces of it in new free programs; and that you know you 27 | can do these things. 28 | 29 | To protect your rights, we need to make restrictions that forbid anyone to deny 30 | you these rights or to ask you to surrender the rights. These restrictions 31 | translate to certain responsibilities for you if you distribute copies of the 32 | software, or if you modify it. 33 | 34 | For example, if you distribute copies of such a program, whether gratis or for 35 | a fee, you must give the recipients all the rights that you have. You must 36 | make sure that they, too, receive or can get the source code. And you must 37 | show them these terms so they know their rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and (2) 40 | offer you this license which gives you legal permission to copy, distribute 41 | and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain that 44 | everyone understands that there is no warranty for this free software. If the 45 | software is modified by someone else and passed on, we want its recipients to 46 | know that what they have is not the original, so that any problems introduced 47 | by others will not reflect on the original authors' reputations. 48 | 49 | Finally, any free program is threatened constantly by software patents. We 50 | wish to avoid the danger that redistributors of a free program will 51 | individually obtain patent licenses, in effect making the program proprietary. 52 | To prevent this, we have made it clear that any patent must be licensed for 53 | everyone's free use or not licensed at all. 54 | 55 | The precise terms and conditions for copying, distribution and modification 56 | follow. 57 | 58 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 59 | 60 | 0. This License applies to any program or other work which contains a notice 61 | placed by the copyright holder saying it may be distributed under the terms of 62 | this General Public License. The "Program", below, refers to any such program 63 | or work, and a "work based on the Program" means either the Program or any 64 | derivative work under copyright law: that is to say, a work containing the 65 | Program or a portion of it, either verbatim or with modifications and/or 66 | translated into another language. (Hereinafter, translation is included 67 | without limitation in the term "modification".) Each licensee is addressed as 68 | "you". 69 | 70 | Activities other than copying, distribution and modification are not covered by 71 | this License; they are outside its scope. The act of running the Program is 72 | not restricted, and the output from the Program is covered only if its contents 73 | constitute a work based on the Program (independent of having been made by 74 | running the Program). Whether that is true depends on what the Program does. 75 | 76 | 1. You may copy and distribute verbatim copies of the Program's source code as 77 | you receive it, in any medium, provided that you conspicuously and 78 | appropriately publish on each copy an appropriate copyright notice and 79 | disclaimer of warranty; keep intact all the notices that refer to this License 80 | and to the absence of any warranty; and give any other recipients of the 81 | Program a copy of this License along with the Program. 82 | 83 | You may charge a fee for the physical act of transferring a copy, and you may 84 | at your option offer warranty protection in exchange for a fee. 85 | 86 | 2. You may modify your copy or copies of the Program or any portion of it, thus 87 | forming a work based on the Program, and copy and distribute such modifications 88 | or work under the terms of Section 1 above, provided that you also meet all of 89 | these conditions: 90 | 91 | a) You must cause the modified files to carry prominent notices stating 92 | that you changed the files and the date of any change. 93 | 94 | b) You must cause any work that you distribute or publish, that in whole or 95 | in part contains or is derived from the Program or any part thereof, to be 96 | licensed as a whole at no charge to all third parties under the terms of 97 | this License. 98 | 99 | c) If the modified program normally reads commands interactively when run, 100 | you must cause it, when started running for such interactive use in the 101 | most ordinary way, to print or display an announcement including an 102 | appropriate copyright notice and a notice that there is no warranty (or 103 | else, saying that you provide a warranty) and that users may redistribute 104 | the program under these conditions, and telling the user how to view a copy 105 | of this License. (Exception: if the Program itself is interactive but does 106 | not normally print such an announcement, your work based on the Program is 107 | not required to print an announcement.) 108 | 109 | These requirements apply to the modified work as a whole. If identifiable 110 | sections of that work are not derived from the Program, and can be reasonably 111 | considered independent and separate works in themselves, then this License, and 112 | its terms, do not apply to those sections when you distribute them as separate 113 | works. But when you distribute the same sections as part of a whole which is a 114 | work based on the Program, the distribution of the whole must be on the terms 115 | of this License, whose permissions for other licensees extend to the entire 116 | whole, and thus to each and every part regardless of who wrote it. 117 | 118 | Thus, it is not the intent of this section to claim rights or contest your 119 | rights to work written entirely by you; rather, the intent is to exercise the 120 | right to control the distribution of derivative or collective works based on 121 | the Program. 122 | 123 | In addition, mere aggregation of another work not based on the Program with the 124 | Program (or with a work based on the Program) on a volume of a storage or 125 | distribution medium does not bring the other work under the scope of this 126 | License. 127 | 128 | 3. You may copy and distribute the Program (or a work based on it, under 129 | Section 2) in object code or executable form under the terms of Sections 1 and 130 | 2 above provided that you also do one of the following: 131 | 132 | a) Accompany it with the complete corresponding machine-readable source 133 | code, which must be distributed under the terms of Sections 1 and 2 above 134 | on a medium customarily used for software interchange; or, 135 | 136 | b) Accompany it with a written offer, valid for at least three years, to 137 | give any third party, for a charge no more than your cost of physically 138 | performing source distribution, a complete machine-readable copy of the 139 | corresponding source code, to be distributed under the terms of Sections 1 140 | and 2 above on a medium customarily used for software interchange; or, 141 | 142 | c) Accompany it with the information you received as to the offer to 143 | distribute corresponding source code. (This alternative is allowed only 144 | for noncommercial distribution and only if you received the program in 145 | object code or executable form with such an offer, in accord with 146 | Subsection b above.) 147 | 148 | The source code for a work means the preferred form of the work for making 149 | modifications to it. For an executable work, complete source code means all 150 | the source code for all modules it contains, plus any associated interface 151 | definition files, plus the scripts used to control compilation and installation 152 | of the executable. However, as a special exception, the source code 153 | distributed need not include anything that is normally distributed (in either 154 | source or binary form) with the major components (compiler, kernel, and so on) 155 | of the operating system on which the executable runs, unless that component 156 | itself accompanies the executable. 157 | 158 | If distribution of executable or object code is made by offering access to copy 159 | from a designated place, then offering equivalent access to copy the source 160 | code from the same place counts as distribution of the source code, even though 161 | third parties are not compelled to copy the source along with the object code. 162 | 163 | 4. You may not copy, modify, sublicense, or distribute the Program except as 164 | expressly provided under this License. Any attempt otherwise to copy, modify, 165 | sublicense or distribute the Program is void, and will automatically terminate 166 | your rights under this License. However, parties who have received copies, or 167 | rights, from you under this License will not have their licenses terminated so 168 | long as such parties remain in full compliance. 169 | 170 | 5. You are not required to accept this License, since you have not signed it. 171 | However, nothing else grants you permission to modify or distribute the Program 172 | or its derivative works. These actions are prohibited by law if you do not 173 | accept this License. Therefore, by modifying or distributing the Program (or 174 | any work based on the Program), you indicate your acceptance of this License to 175 | do so, and all its terms and conditions for copying, distributing or modifying 176 | the Program or works based on it. 177 | 178 | 6. Each time you redistribute the Program (or any work based on the Program), 179 | the recipient automatically receives a license from the original licensor to 180 | copy, distribute or modify the Program subject to these terms and conditions. 181 | You may not impose any further restrictions on the recipients' exercise of the 182 | rights granted herein. You are not responsible for enforcing compliance by 183 | third parties to this License. 184 | 185 | 7. If, as a consequence of a court judgment or allegation of patent 186 | infringement or for any other reason (not limited to patent issues), conditions 187 | are imposed on you (whether by court order, agreement or otherwise) that 188 | contradict the conditions of this License, they do not excuse you from the 189 | conditions of this License. If you cannot distribute so as to satisfy 190 | simultaneously your obligations under this License and any other pertinent 191 | obligations, then as a consequence you may not distribute the Program at all. 192 | For example, if a patent license would not permit royalty-free redistribution 193 | of the Program by all those who receive copies directly or indirectly through 194 | you, then the only way you could satisfy both it and this License would be to 195 | refrain entirely from distribution of the Program. 196 | 197 | If any portion of this section is held invalid or unenforceable under any 198 | particular circumstance, the balance of the section is intended to apply and 199 | the section as a whole is intended to apply in other circumstances. 200 | 201 | It is not the purpose of this section to induce you to infringe any patents or 202 | other property right claims or to contest validity of any such claims; this 203 | section has the sole purpose of protecting the integrity of the free software 204 | distribution system, which is implemented by public license practices. Many 205 | people have made generous contributions to the wide range of software 206 | distributed through that system in reliance on consistent application of that 207 | system; it is up to the author/donor to decide if he or she is willing to 208 | distribute software through any other system and a licensee cannot impose that 209 | choice. 210 | 211 | This section is intended to make thoroughly clear what is believed to be a 212 | consequence of the rest of this License. 213 | 214 | 8. If the distribution and/or use of the Program is restricted in certain 215 | countries either by patents or by copyrighted interfaces, the original 216 | copyright holder who places the Program under this License may add an explicit 217 | geographical distribution limitation excluding those countries, so that 218 | distribution is permitted only in or among countries not thus excluded. In 219 | such case, this License incorporates the limitation as if written in the body 220 | of this License. 221 | 222 | 9. The Free Software Foundation may publish revised and/or new versions of the 223 | General Public License from time to time. Such new versions will be similar in 224 | spirit to the present version, but may differ in detail to address new problems 225 | or concerns. 226 | 227 | Each version is given a distinguishing version number. If the Program 228 | specifies a version number of this License which applies to it and "any later 229 | version", you have the option of following the terms and conditions either of 230 | that version or of any later version published by the Free Software Foundation. 231 | If the Program does not specify a version number of this License, you may 232 | choose any version ever published by the Free Software Foundation. 233 | 234 | 10. If you wish to incorporate parts of the Program into other free programs 235 | whose distribution conditions are different, write to the author to ask for 236 | permission. For software which is copyrighted by the Free Software Foundation, 237 | write to the Free Software Foundation; we sometimes make exceptions for this. 238 | Our decision will be guided by the two goals of preserving the free status of 239 | all derivatives of our free software and of promoting the sharing and reuse of 240 | software generally. 241 | 242 | NO WARRANTY 243 | 244 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR 245 | THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE 246 | STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE 247 | PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, 248 | INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 249 | FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND 250 | PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, 251 | YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 252 | 253 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL 254 | ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE 255 | PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 256 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR 257 | INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA 258 | BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A 259 | FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER 260 | OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 261 | 262 | END OF TERMS AND CONDITIONS 263 | 264 | How to Apply These Terms to Your New Programs 265 | 266 | If you develop a new program, and you want it to be of the greatest possible 267 | use to the public, the best way to achieve this is to make it free software 268 | which everyone can redistribute and change under these terms. 269 | 270 | To do so, attach the following notices to the program. It is safest to attach 271 | them to the start of each source file to most effectively convey the exclusion 272 | of warranty; and each file should have at least the "copyright" line and a 273 | pointer to where the full notice is found. 274 | 275 | One line to give the program's name and a brief idea of what it does. 276 | 277 | Copyright (C) 278 | 279 | This program is free software; you can redistribute it and/or modify it 280 | under the terms of the GNU General Public License as published by the Free 281 | Software Foundation; either version 2 of the License, or (at your option) 282 | any later version. 283 | 284 | This program is distributed in the hope that it will be useful, but WITHOUT 285 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 286 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 287 | more details. 288 | 289 | You should have received a copy of the GNU General Public License along 290 | with this program; if not, write to the Free Software Foundation, Inc., 59 291 | Temple Place, Suite 330, Boston, MA 02111-1307 USA 292 | 293 | Also add information on how to contact you by electronic and paper mail. 294 | 295 | If the program is interactive, make it output a short notice like this when it 296 | starts in an interactive mode: 297 | 298 | Gnomovision version 69, Copyright (C) year name of author Gnomovision comes 299 | with ABSOLUTELY NO WARRANTY; for details type 'show w'. This is free 300 | software, and you are welcome to redistribute it under certain conditions; 301 | type 'show c' for details. 302 | 303 | The hypothetical commands 'show w' and 'show c' should show the appropriate 304 | parts of the General Public License. Of course, the commands you use may be 305 | called something other than 'show w' and 'show c'; they could even be 306 | mouse-clicks or menu items--whatever suits your program. 307 | 308 | You should also get your employer (if you work as a programmer) or your school, 309 | if any, to sign a "copyright disclaimer" for the program, if necessary. Here 310 | is a sample; alter the names: 311 | 312 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 313 | 'Gnomovision' (which makes passes at compilers) written by James Hacker. 314 | 315 | signature of Ty Coon, 1 April 1989 316 | 317 | Ty Coon, President of Vice 318 | 319 | This General Public License does not permit incorporating your program into 320 | proprietary programs. If your program is a subroutine library, you may 321 | consider it more useful to permit linking proprietary applications with the 322 | library. If this is what you want to do, use the GNU Library General Public 323 | License instead of this License. 324 | 325 | 326 | "CLASSPATH" EXCEPTION TO THE GPL 327 | 328 | Certain source files distributed by Oracle America and/or its affiliates are 329 | subject to the following clarification and special exception to the GPL, but 330 | only where Oracle has expressly included in the particular source file's header 331 | the words "Oracle designates this particular file as subject to the "Classpath" 332 | exception as provided by Oracle in the LICENSE file that accompanied this code." 333 | 334 | Linking this library statically or dynamically with other modules is making 335 | a combined work based on this library. Thus, the terms and conditions of 336 | the GNU General Public License cover the whole combination. 337 | 338 | As a special exception, the copyright holders of this library give you 339 | permission to link this library with independent modules to produce an 340 | executable, regardless of the license terms of these independent modules, 341 | and to copy and distribute the resulting executable under terms of your 342 | choice, provided that you also meet, for each linked independent module, 343 | the terms and conditions of the license of that module. An independent 344 | module is a module which is not derived from or based on this library. If 345 | you modify this library, you may extend this exception to your version of 346 | the library, but you are not obligated to do so. If you do not wish to do 347 | so, delete this exception statement from your version. 348 | -------------------------------------------------------------------------------- /README.adoc: -------------------------------------------------------------------------------- 1 | = OpenJDK split package scanner 2 | Patrick Reinhart 3 | :project-name: jsplitpkgscan 4 | :group-name: org.adoptopenjdk 5 | :jsplitpkgscan-version: 1.1.0-SNAPSHOT 6 | :project-full-path: AdoptOpenJDK/{project-name} 7 | :github-branch: master 8 | :jdk-version: 11 9 | :jdk-url: https://jdk.java.net/{jdk-version} 10 | :adoptopenjdk-url: https://adoptopenjdk.net 11 | 12 | image:https://img.shields.io/badge/license-GPL2+CPE-blue.svg["GNU General Public License, version 2, with the Classpath Exception", link="https://github.com/{project-full-path}/blob/{github-branch}/LICENSE"] 13 | image:https://img.shields.io/badge/Java-{jdk-version}-blue.svg["Supported Versions", link="https://travis-ci.org/{project-full-path}"] 14 | image:https://github.com/{project-full-path}/actions/workflows/gradle.yml/badge.svg["Java CI with Gradle", link="https://github.com/{project-full-path}/actions/workflows/gradle.yml"] 15 | image:https://img.shields.io/maven-central/v/{group-name}/{project-name}.svg?label=Maven%20Central["Maven Central", link="https://search.maven.org/search?q=g:%22{group-name}%22%20AND%20a:%22{project-name}%22"] 16 | 17 | This project contains a proposal for http://openjdk.java.net[OpenJDK] split package scanner addition. 18 | 19 | The diving force starting this tool was the lack of tooling to prepare existing class path based modules 20 | for later migration to modules. For split packages with the current _JDK_, they will be shown by the `jdeps` 21 | tool as of today, but not class path based modules so far. 22 | 23 | The `jsplitpgkscan` utility will search for split packages within all given `.jar` files and the modules 24 | known to the _JDK_ itself and report them. 25 | 26 | == Build and run 27 | To build the tool you need to have a actual _JDK_ build that you can get from 28 | {jdk-url}[{jdk-url}], {adoptopenjdk-url}[{adoptopenjdk-url}] or other providers. 29 | Make sure that you have at least version _jdk-{jdk-version}_ installed. 30 | 31 | To build use: 32 | 33 | [source, bash] 34 | ---- 35 | ./gradlew clean build 36 | ---- 37 | 38 | After the build was successful you can now start it using: 39 | 40 | [source, bash, subs="attributes"] 41 | ---- 42 | java -p build/libs/jsplitpkgscan-{jsplitpkgscan-version}.jar -m jdk.jsplitpkgscan 43 | ---- 44 | 45 | The usage is as follows 46 | [source] 47 | ---- 48 | Usage: jsplitpkgscan ] 49 | can be a pathname to a JAR/WAR/RAR file or exploded JAR/WAR/RAR file. 50 | Possible options include: 51 | -h -? --help Print this usage message 52 | -a Report all packages for each found JAR/RAR/WAR 53 | file or exploded directory 54 | -d Directory containing JAR/RAR/WAR files or 55 | exploded directories to scan 56 | (scans subdirectories as well) 57 | -f File containing a list of JAR/RAR/WAR files 58 | or exploded directories to scan 59 | -p Package prefix to report only split packages 60 | that matches this prefix 61 | --dot-output Destination directory for DOT file output 62 | ---- 63 | 64 | In order to create your own distribution using jlink you can enter the following command 65 | if you have `JAVA_HOME` set: 66 | 67 | [source, bash] 68 | ---- 69 | $JAVA_HOME/bin/jlink --module-path $JAVA_HOME/jmods:build/libs --add-modules jsplitpkgscan \ 70 | --launcher jsplitpkgscan=jsplitpkgscan --output /tmp/jsplitpkgscan 71 | ---- 72 | 73 | For using the tool from inside you're code, you can access it looking it up a an 74 | `java.util.spi.ToolProvider` implementation as follows: 75 | 76 | [source, java] 77 | ---- 78 | int rc = ToolProvider.findFirst("jsplitpgkscan") 79 | .ifPresent(tool -> tool.run(System.out, System.err, "-h") 80 | ---- 81 | 82 | 83 | == Create DOT graphic 84 | To get a `.png` graphic for any `.dot` file output for directed graphs you could use: 85 | 86 | [source, bash] 87 | ---- 88 | dot -Tpng summary.dot -O -Gratio=0.5 89 | ---- 90 | 91 | If your experience problems due undirected graphs you could also use this command: 92 | 93 | [source, bash] 94 | ---- 95 | twopi -Tpng summary.dot -O -Gratio=0.5 -Goverlap=false 96 | ---- 97 | 98 | == Contribute 99 | Contributions are always welcome. 100 | 101 | == License 102 | This project is licensed under the https://github.com/{project-full-path}/blob/{github-branch}/LICENSE[GNU General Public License, version 2 with the Classpath Exception] 103 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'application' 3 | id 'signing' 4 | id 'maven-publish' 5 | id 'com.github.ben-manes.versions' version '0.36.0' 6 | id 'net.nemerosa.versioning' version '2.14.0' 7 | id 'org.owasp.dependencycheck' version '6.1.1' 8 | } 9 | 10 | group = 'org.adoptopenjdk' 11 | 12 | java { 13 | modularity.inferModulePath = true 14 | sourceCompatibility = JavaVersion.VERSION_11 15 | withJavadocJar() 16 | withSourcesJar() 17 | } 18 | 19 | application { 20 | mainClass = "jdk.jsplitpkgscan.Main" 21 | mainModule = "jdk.jsplitpkgscan" 22 | } 23 | 24 | // Uncomment the section below if you want to just ./gradlew publishToMavenLocal locally 25 | /* 26 | repositories { 27 | mavenLocal() 28 | } 29 | */ 30 | 31 | jar { 32 | manifest { 33 | metaInf { 34 | from file('.') 35 | include 'LICENSE' 36 | } 37 | } 38 | } 39 | 40 | signing { 41 | required { project.hasProperty('signing.password') && !project.version.contains('-SNAPSHOT') } 42 | sign configurations.archives 43 | } 44 | 45 | /* 46 | * Comment out this whole section if you don't want to upload the artifact on 47 | * a ./gradlew install 48 | */ 49 | publishing { 50 | publications { 51 | mavenJava(MavenPublication) { 52 | from components.java 53 | pom { 54 | name = 'Split package scanner' 55 | description = 'Tool to scan existing JAR/WAR files for split packages' 56 | url = 'https://github.com/AdoptOpenJDK/jsplitpkgscan' 57 | scm { 58 | connection = 'scm:git://github.com/AdoptOpenJDK/jsplitpkgscan.git' 59 | developerConnection = 'scm:git://github.com/AdoptOpenJDK/jsplitpkgscan.git' 60 | url = 'https://github.com/AdoptOpenJDK/jsplitpkgscan' 61 | } 62 | licenses { 63 | license { 64 | name = 'GNU General Public License, version 2, with the Classpath Exception' 65 | url = 'http://openjdk.java.net/legal/gplv2+ce.html' 66 | distribution = 'repo' 67 | } 68 | } 69 | developers { 70 | developer { 71 | id = 'reinhapa' 72 | name = 'Patrick Reinhart' 73 | email = 'patrick@reini.net' 74 | } 75 | developer { 76 | id = 'ohumbel' 77 | name = 'Otmar Humbel' 78 | email = 'ohumbel@gmail.com' 79 | } 80 | } 81 | } 82 | } 83 | } 84 | repositories { 85 | if (project.hasProperty('ossrhUsername') && project.hasProperty('ossrhPassword')) { 86 | maven { 87 | def releasesRepoUrl = 'https://oss.sonatype.org/service/local/staging/deploy/maven2/' 88 | def snapshotsRepoUrl = 'https://oss.sonatype.org/content/repositories/snapshots/' 89 | url = version.endsWith('SNAPSHOT') ? snapshotsRepoUrl : releasesRepoUrl 90 | credentials { 91 | username = ossrhUsername 92 | password = ossrhPassword 93 | } 94 | } 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # override from command line with -Pversion=x.y.z 2 | version = 1.1.0-SNAPSHOT 3 | 4 | org.gradle.jvmargs=-ea -Xmx1G 5 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdoptOpenJDK/jsplitpkgscan/6c62971fb80ae8c7401d5c7cf583c5f67b375488/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.3-all.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | # Determine the Java command to use to start the JVM. 86 | if [ -n "$JAVA_HOME" ] ; then 87 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 88 | # IBM's JDK on AIX uses strange locations for the executables 89 | JAVACMD="$JAVA_HOME/jre/sh/java" 90 | else 91 | JAVACMD="$JAVA_HOME/bin/java" 92 | fi 93 | if [ ! -x "$JAVACMD" ] ; then 94 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 95 | 96 | Please set the JAVA_HOME variable in your environment to match the 97 | location of your Java installation." 98 | fi 99 | else 100 | JAVACMD="java" 101 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 102 | 103 | Please set the JAVA_HOME variable in your environment to match the 104 | location of your Java installation." 105 | fi 106 | 107 | # Increase the maximum file descriptors if we can. 108 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 109 | MAX_FD_LIMIT=`ulimit -H -n` 110 | if [ $? -eq 0 ] ; then 111 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 112 | MAX_FD="$MAX_FD_LIMIT" 113 | fi 114 | ulimit -n $MAX_FD 115 | if [ $? -ne 0 ] ; then 116 | warn "Could not set maximum file descriptor limit: $MAX_FD" 117 | fi 118 | else 119 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 120 | fi 121 | fi 122 | 123 | # For Darwin, add options to specify how the application appears in the dock 124 | if $darwin; then 125 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 126 | fi 127 | 128 | # For Cygwin, switch paths to Windows format before running java 129 | if $cygwin ; then 130 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 131 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 132 | JAVACMD=`cygpath --unix "$JAVACMD"` 133 | 134 | # We build the pattern for arguments to be converted via cygpath 135 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 136 | SEP="" 137 | for dir in $ROOTDIRSRAW ; do 138 | ROOTDIRS="$ROOTDIRS$SEP$dir" 139 | SEP="|" 140 | done 141 | OURCYGPATTERN="(^($ROOTDIRS))" 142 | # Add a user-defined pattern to the cygpath arguments 143 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 144 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 145 | fi 146 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 147 | i=0 148 | for arg in "$@" ; do 149 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 150 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 151 | 152 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 153 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 154 | else 155 | eval `echo args$i`="\"$arg\"" 156 | fi 157 | i=$((i+1)) 158 | done 159 | case $i in 160 | (0) set -- ;; 161 | (1) set -- "$args0" ;; 162 | (2) set -- "$args0" "$args1" ;; 163 | (3) set -- "$args0" "$args1" "$args2" ;; 164 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 165 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 166 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 167 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 168 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 169 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 170 | esac 171 | fi 172 | 173 | # Escape application args 174 | save () { 175 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 176 | echo " " 177 | } 178 | APP_ARGS=$(save "$@") 179 | 180 | # Collect all arguments for the java command, following the shell quoting and substitution rules 181 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 182 | 183 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 184 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 185 | cd "$(dirname "$0")" 186 | fi 187 | 188 | exec "$JAVACMD" "$@" 189 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 33 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 34 | 35 | @rem Find java.exe 36 | if defined JAVA_HOME goto findJavaFromJavaHome 37 | 38 | set JAVA_EXE=java.exe 39 | %JAVA_EXE% -version >NUL 2>&1 40 | if "%ERRORLEVEL%" == "0" goto init 41 | 42 | echo. 43 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 44 | echo. 45 | echo Please set the JAVA_HOME variable in your environment to match the 46 | echo location of your Java installation. 47 | 48 | goto fail 49 | 50 | :findJavaFromJavaHome 51 | set JAVA_HOME=%JAVA_HOME:"=% 52 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 53 | 54 | if exist "%JAVA_EXE%" goto init 55 | 56 | echo. 57 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 58 | echo. 59 | echo Please set the JAVA_HOME variable in your environment to match the 60 | echo location of your Java installation. 61 | 62 | goto fail 63 | 64 | :init 65 | @rem Get command-line arguments, handling Windows variants 66 | 67 | if not "%OS%" == "Windows_NT" goto win9xME_args 68 | 69 | :win9xME_args 70 | @rem Slurp the command line arguments. 71 | set CMD_LINE_ARGS= 72 | set _SKIP=2 73 | 74 | :win9xME_args_slurp 75 | if "x%~1" == "x" goto execute 76 | 77 | set CMD_LINE_ARGS=%* 78 | 79 | :execute 80 | @rem Setup the command line 81 | 82 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 83 | 84 | @rem Execute Gradle 85 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 86 | 87 | :end 88 | @rem End local scope for the variables with windows NT shell 89 | if "%ERRORLEVEL%"=="0" goto mainEnd 90 | 91 | :fail 92 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 93 | rem the _cmd.exe /c_ return code! 94 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 95 | exit /b 1 96 | 97 | :mainEnd 98 | if "%OS%"=="Windows_NT" endlocal 99 | 100 | :omega 101 | -------------------------------------------------------------------------------- /jsplitpkgscan: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | $JAVA_HOME/bin/java -p build/libs -m jsplitpkgscan $@ 3 | -------------------------------------------------------------------------------- /src/main/java/jdk/jsplitpkgscan/JsplitpgkscanTask.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 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 jdk.jsplitpkgscan; 27 | 28 | import java.io.BufferedWriter; 29 | import java.io.IOException; 30 | import java.io.PrintWriter; 31 | import java.io.UncheckedIOException; 32 | import java.nio.file.*; 33 | import java.nio.file.attribute.BasicFileAttributes; 34 | import java.text.MessageFormat; 35 | import java.util.*; 36 | import java.util.function.Consumer; 37 | import java.util.function.Function; 38 | import java.util.regex.Matcher; 39 | import java.util.regex.Pattern; 40 | import java.util.stream.Collectors; 41 | import java.util.stream.Stream; 42 | 43 | class JsplitpgkscanTask { 44 | interface BadArguments { 45 | String getKey(); 46 | 47 | Object[] getArgs(); 48 | 49 | boolean showUsage(); 50 | } 51 | 52 | static class BadArgs extends Exception implements BadArguments { 53 | static final long serialVersionUID = 1L; 54 | 55 | BadArgs(String key, Object... args) { 56 | super(JsplitpgkscanTask.getMessage(key, args)); 57 | this.key = key; 58 | this.args = args; 59 | } 60 | 61 | BadArgs showUsage(boolean b) { 62 | showUsage = b; 63 | return this; 64 | } 65 | 66 | final String key; 67 | final Object[] args; 68 | boolean showUsage; 69 | 70 | @Override 71 | public String getKey() { 72 | return key; 73 | } 74 | 75 | @Override 76 | public Object[] getArgs() { 77 | return args; 78 | } 79 | 80 | @Override 81 | public boolean showUsage() { 82 | return showUsage; 83 | } 84 | } 85 | 86 | class DirectoryEntryVisitor extends SimpleFileVisitor { 87 | private final Path libraryDirectory; 88 | 89 | public DirectoryEntryVisitor(Path libraryDirectory) { 90 | this.libraryDirectory = libraryDirectory; 91 | } 92 | 93 | @Override 94 | public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { 95 | if (!libraryDirectory.equals(dir)) { 96 | if (dir.endsWith("WEB-INF/classes")) { 97 | addAnalyzer(dir, libraryDirectory::relativize); 98 | return FileVisitResult.SKIP_SUBTREE; 99 | } 100 | } else if (dir.endsWith("jar") || dir.endsWith("rar")) { 101 | addAnalyzer(dir, libraryDirectory::relativize); 102 | return FileVisitResult.SKIP_SUBTREE; 103 | } 104 | return FileVisitResult.CONTINUE; 105 | } 106 | 107 | @Override 108 | public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { 109 | addAnalyzer(file, libraryDirectory::relativize); 110 | return FileVisitResult.CONTINUE; 111 | } 112 | } 113 | 114 | private static class Options { 115 | final List libraries; 116 | boolean help; 117 | boolean all; 118 | Path dotOutputDirectory; 119 | String packageArg; 120 | 121 | Options() { 122 | libraries = new ArrayList<>(); 123 | packageArg = ""; 124 | } 125 | } 126 | 127 | private static class ResourceBundleHelper { 128 | static final ResourceBundle bundle; 129 | 130 | static { 131 | Locale locale = Locale.getDefault(); 132 | try { 133 | bundle = ResourceBundle.getBundle("jdk.jsplitpkgscan.resources.jsplitpkgscan", locale); 134 | } catch (MissingResourceException e) { 135 | throw new InternalError("Cannot find jdk.jsplitpkgscan resource bundle for locale " + locale); 136 | } 137 | } 138 | } 139 | 140 | private static final String PROGNAME = "jsplitpkgscan"; 141 | private static final Pattern JAR_FILE_PATTERN = Pattern. 142 | compile("^.+\\.(jar|rar|war)$", Pattern.CASE_INSENSITIVE); 143 | 144 | private final ResourceBundleHelper bundleHelper = new ResourceBundleHelper(); 145 | private final Options options = new Options(); 146 | private PrintWriter log; 147 | 148 | void setLog(PrintWriter out) { 149 | log = out; 150 | } 151 | 152 | int run(String... arguments) { 153 | if (log == null) { 154 | log = new PrintWriter(System.out); 155 | } 156 | int rc = 0; 157 | try { 158 | handleOptions(arguments); 159 | if (options.help) { 160 | showHelp(); 161 | } else { 162 | Consumer>> packageProcessor; 163 | if (options.all) { 164 | packageProcessor = this::reportAllPackages; 165 | } else if (options.dotOutputDirectory != null) { 166 | packageProcessor = this::writeDotFiles; 167 | } else { 168 | packageProcessor = this::reportSplitPackages; 169 | } 170 | doAnalyze(packageProcessor); 171 | } 172 | } catch (BadArgs badArguments) { 173 | reportError(badArguments.getKey(), badArguments.getArgs()); 174 | if (badArguments.showUsage()) { 175 | log.println(getMessage("main.usage.summary", PROGNAME)); 176 | } 177 | rc = 1; 178 | } finally { 179 | log.flush(); 180 | } 181 | return rc; 182 | } 183 | 184 | private void handleOptions(String[] arguments) throws BadArgs { 185 | options.help = arguments.length == 0; 186 | for (Iterator argIt = Arrays.asList(arguments).iterator(); argIt.hasNext(); ) { 187 | String argument = argIt.next(); 188 | switch (argument) { 189 | case "-?": 190 | case "-h": 191 | case "--help": 192 | options.help = true; 193 | break; 194 | case "-f": 195 | if (argIt.hasNext()) { 196 | Path libraryDefinitionFile = Paths.get(argIt.next()); 197 | if (Files.exists(libraryDefinitionFile) && Files.isRegularFile(libraryDefinitionFile)) { 198 | try (Stream lines = Files.lines(libraryDefinitionFile)) { 199 | lines 200 | .map(Paths::get) 201 | .forEach(path -> addAnalyzer(path, Path::getFileName)); 202 | continue; 203 | } catch (IOException ioe) { 204 | ioe.printStackTrace(log); 205 | throw new BadArgs("err.scanning.dir", libraryDefinitionFile); 206 | } 207 | } 208 | throw new BadArgs("err.invalid.path", libraryDefinitionFile); 209 | } 210 | throw new BadArgs("err.missing.arg", argument); 211 | case "-d": 212 | if (argIt.hasNext()) { 213 | Path libraryDirectory = Paths.get(argIt.next()).toAbsolutePath(); 214 | if (Files.isDirectory(libraryDirectory)) { 215 | try { 216 | Files.walkFileTree(libraryDirectory, new DirectoryEntryVisitor(libraryDirectory)); 217 | continue; 218 | } catch (IOException ioe) { 219 | ioe.printStackTrace(log); 220 | throw new BadArgs("err.scanning.dir", libraryDirectory); 221 | } 222 | } 223 | throw new BadArgs("err.invalid.path", libraryDirectory); 224 | } 225 | throw new BadArgs("err.missing.arg", argument); 226 | case "-a": 227 | options.all = true; 228 | continue; 229 | case "-p": 230 | if (argIt.hasNext()) { 231 | options.packageArg = argIt.next(); 232 | continue; 233 | } 234 | throw new BadArgs("err.missing.arg", argument); 235 | case "--dot-output": 236 | if (argIt.hasNext()) { 237 | Path dotOutputDirectory = Paths.get(argIt.next()); 238 | if (Files.exists(dotOutputDirectory) && Files.isDirectory(dotOutputDirectory)) { 239 | options.dotOutputDirectory = dotOutputDirectory; 240 | continue; 241 | } 242 | throw new BadArgs("err.missing.outputdir", dotOutputDirectory); 243 | } 244 | throw new BadArgs("err.missing.arg", argument); 245 | default: 246 | if (argument.startsWith("-")) { 247 | throw new BadArgs("err.unknown.option", argument).showUsage(true); 248 | } 249 | addAnalyzer(Paths.get(argument), Path::getFileName); 250 | } 251 | } 252 | } 253 | 254 | private void doAnalyze(Consumer>> analyseConsumer) { 255 | Map packageToModule = Library.packageToModule(); 256 | Map> packages = new HashMap<>(); 257 | for (Library analyzer : options.libraries) { 258 | analyzer.packages().keySet().stream() 259 | .forEach(packageName -> { 260 | List values = 261 | packages.computeIfAbsent(packageName, key -> new ArrayList<>()); 262 | values.add(analyzer); 263 | if (packageToModule.containsKey(packageName)) { 264 | values.add(packageToModule.get(packageName)); 265 | } 266 | }); 267 | } 268 | analyseConsumer.accept(packages); 269 | } 270 | 271 | private Stream>> splitPackages(Map> packages) { 272 | return packages.entrySet() 273 | .stream() 274 | .filter(element -> element.getValue().size() > 1) 275 | .filter(element -> element.getKey().startsWith(options.packageArg)) 276 | .sorted(Map.Entry.comparingByKey()); 277 | } 278 | 279 | private void reportSplitPackages(Map> packages) { 280 | List>> splitPkgs = splitPackages(packages).collect(Collectors.toList()); 281 | if (!splitPkgs.isEmpty()) { 282 | log.println("- Split packages:"); 283 | splitPkgs.forEach(element -> { 284 | log.println(element.getKey()); // the package name 285 | element.getValue().stream() 286 | .distinct() 287 | .sorted() 288 | .forEach(library -> log.format(" %5d %s%n", library.count(element.getKey()), library.location())); 289 | }); 290 | } 291 | } 292 | 293 | private void reportAllPackages(Map> packages) { 294 | log.println("- All packages:"); 295 | for (Library analyzer : options.libraries) { 296 | List allPkgs = analyzer.packages().keySet() 297 | .stream() 298 | .filter(element -> element.startsWith(options.packageArg)) 299 | .sorted() 300 | .collect(Collectors.toList()); 301 | if (!allPkgs.isEmpty()) { 302 | log.println(analyzer.location()); 303 | allPkgs.forEach(packageName -> log.format(" %s%n", packageName)); 304 | } 305 | } 306 | } 307 | 308 | private void writeDotFiles(Map> packages) { 309 | try (BufferedWriter bw = Files.newBufferedWriter(options.dotOutputDirectory.resolve("summary.dot")); 310 | PrintWriter writer = new PrintWriter(bw)) { 311 | writer.format("digraph \"summary\" {%n"); 312 | splitPackages(packages).forEach(element -> { 313 | element.getValue().forEach(library -> 314 | writer.format(" %-50s -> \"%s\";%n", String.format("\"%s\"", library.location()), element.getKey()) 315 | ); 316 | }); 317 | writer.println("}"); 318 | } catch (IOException ioe) { 319 | ioe.printStackTrace(log); 320 | } 321 | } 322 | 323 | private static String getMessage(String key, Object... arguments) { 324 | try { 325 | return MessageFormat.format(ResourceBundleHelper.bundle.getString(key), arguments); 326 | } catch (MissingResourceException e) { 327 | throw new InternalError("Missing message: " + key); 328 | } 329 | } 330 | 331 | private void addAnalyzer(Path path, Function relativizeFunction) { 332 | if (Files.exists(path)) { 333 | try { 334 | if (Files.isDirectory(path)) { 335 | options.libraries.add(new Library(path, relativizeFunction, Library::packages)); 336 | } else { 337 | Matcher m = JAR_FILE_PATTERN.matcher(String.valueOf(path.getFileName())); 338 | if (m.matches()) { 339 | options.libraries.add(new Library(path, relativizeFunction, Library::jarFilePackages)); 340 | } 341 | } 342 | } catch (IOException e) { 343 | throw new UncheckedIOException(e); 344 | } 345 | } 346 | } 347 | 348 | private void showHelp() { 349 | log.println(getMessage("main.usage", PROGNAME)); 350 | } 351 | 352 | private void reportError(String key, Object... args) { 353 | log.println(getMessage("error.prefix") + " " + getMessage(key, args)); 354 | } 355 | 356 | void warning(String key, Object... args) { 357 | log.println(getMessage("warn.prefix") + " " + getMessage(key, args)); 358 | } 359 | } 360 | -------------------------------------------------------------------------------- /src/main/java/jdk/jsplitpkgscan/Library.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 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 jdk.jsplitpkgscan; 27 | 28 | import java.io.File; 29 | import java.io.IOException; 30 | import java.io.UncheckedIOException; 31 | import java.lang.module.ModuleFinder; 32 | import java.lang.module.ModuleReference; 33 | import java.net.URI; 34 | import java.nio.file.Files; 35 | import java.nio.file.Path; 36 | import java.util.HashMap; 37 | import java.util.Map; 38 | import java.util.function.Function; 39 | import java.util.jar.JarEntry; 40 | import java.util.jar.JarFile; 41 | import java.util.stream.Collectors; 42 | 43 | /** 44 | * Lists the packages of the given JAR file or exploded directory 45 | * and reports the list of split packages 46 | */ 47 | class Library implements Comparable{ 48 | private static final String MODULE_INFO = "module-info.class"; 49 | private static final Long ZERO = Long.valueOf(0); // marks count inside a module, where we do not know the number of classes 50 | 51 | private final URI location; 52 | private final Map packages; 53 | 54 | Library(Path path, Function relativizeFunction, Function> pkgFunction) throws IOException { 55 | String relativePath = relativizeFunction.apply(path).toString().replace("\\", "/"); 56 | this.location = URI.create("file:/" + relativePath); 57 | this.packages = pkgFunction.apply(path); 58 | } 59 | 60 | private Library(ModuleReference mref) { 61 | this.location = mref.location().get(); 62 | this.packages = mref.descriptor().packages().stream().collect(Collectors.toMap(Function.identity(), pkg -> ZERO)); 63 | } 64 | 65 | /** 66 | * Returns a set of all packages within this library. 67 | * 68 | * @return the package names contained in this library 69 | */ 70 | Map packages() { 71 | return packages; 72 | } 73 | 74 | /** 75 | * Returns the location {@link URI} of this library 76 | * 77 | * @return the library location 78 | */ 79 | URI location() { 80 | return location; 81 | } 82 | 83 | /** 84 | * Returns the number of classes in a given package. 85 | *

86 | * If the library represents a module, the number is unknown and {@code 0} is returned. 87 | * 88 | * @param packageName the full name of the package 89 | * @return the number of classes in package {@code packageName} 90 | */ 91 | Long count(String packageName){ 92 | return packages.get(packageName); 93 | } 94 | 95 | @Override 96 | public int hashCode() { 97 | return location.hashCode(); 98 | } 99 | 100 | @Override 101 | public boolean equals(Object obj) { 102 | if (this == obj) { 103 | return true; 104 | } else if (!(obj instanceof Library)) { 105 | return false; 106 | } 107 | Library other = (Library)obj; 108 | return compareTo(other) == 0; 109 | } 110 | 111 | @Override 112 | public int compareTo(Library other) { 113 | return location.compareTo(other.location); 114 | } 115 | 116 | /** 117 | * Walks the given directory and returns all packages. 118 | *

119 | * This method needs to be updated to include resources 120 | * for #ResourceEncapsulation. 121 | */ 122 | static Map packages(Path dir) { 123 | try { 124 | return Files.find(dir, Integer.MAX_VALUE, 125 | (p, attr) -> p.getFileName().toString().endsWith(".class") && 126 | !p.getFileName().toString().equals(MODULE_INFO)) 127 | .map(Path::getParent) 128 | .map(dir::relativize) 129 | .map(Path::toString) 130 | .map(pathName -> pathName.replace(File.separatorChar, '.')) 131 | .map(Library::specialCaseTranslator) 132 | .sorted() 133 | .collect(Collectors.groupingBy(pkg -> pkg, Collectors.counting())); 134 | } catch (IOException e) { 135 | throw new UncheckedIOException(e); 136 | } 137 | } 138 | 139 | /** 140 | * Returns all packages of the given JAR file. 141 | */ 142 | static Map jarFilePackages(Path path) { 143 | try (JarFile jf = new JarFile(path.toFile())) { 144 | return jf.stream() 145 | .map(JarEntry::getName) 146 | .filter(entryName -> entryName.endsWith(".class") && !entryName.equals(MODULE_INFO)) 147 | .map(Library::toPackage) 148 | .map(Library::specialCaseTranslator) 149 | .sorted() 150 | .collect(Collectors.groupingBy(pkg -> pkg, Collectors.counting())); 151 | } catch (IOException e) { 152 | throw new UncheckedIOException(e); 153 | } 154 | } 155 | 156 | static Map packageToModule() { 157 | Map map = new HashMap<>(); 158 | ModuleFinder.ofSystem().findAll() 159 | .stream() 160 | .map(Library::new) 161 | .forEach(library -> library.packages().forEach((packageName, count) -> map.put(packageName, library))); 162 | return map; 163 | } 164 | 165 | private static String toPackage(String name) { 166 | int i = name.lastIndexOf('/'); 167 | return i != -1 ? name.substring(0, i).replace('/', '.') : ""; 168 | } 169 | 170 | private static String specialCaseTranslator(String packageName) { 171 | if (packageName.startsWith("WEB-INF.classes.")) { 172 | return packageName.substring(16); 173 | } 174 | return packageName; 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /src/main/java/jdk/jsplitpkgscan/Main.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 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 jdk.jsplitpkgscan; 27 | 28 | import java.io.InputStream; 29 | import java.io.OutputStream; 30 | import java.io.PrintWriter; 31 | import java.util.Collections; 32 | import java.util.Set; 33 | import java.util.spi.ToolProvider; 34 | import javax.lang.model.SourceVersion; 35 | import javax.tools.Tool; 36 | 37 | public class Main { 38 | private static final String jsplitpgkscan = "jsplitpgkscan"; 39 | 40 | /** 41 | * Default main entry point used as main class. At the end of the analyzing 42 | * run, the {@link System#exit(int)} is being called. 43 | * 44 | * @param arguments command line arguments 45 | */ 46 | public static void main(String... arguments) { 47 | JsplitpgkscanTask task = new JsplitpgkscanTask(); 48 | int rc = task.run(arguments); 49 | System.exit(rc); 50 | } 51 | 52 | /** 53 | * Entry point that does not call System.exit. 54 | * 55 | * @param arguments command line arguments 56 | * @param out output stream 57 | * @return an exit code. 0 means success, non-zero means an error occurred. 58 | */ 59 | public static int run(String[] arguments, PrintWriter out) { 60 | JsplitpgkscanTask task = new JsplitpgkscanTask(); 61 | task.setLog(out); 62 | return task.run(arguments); 63 | } 64 | 65 | 66 | public static class JsplitpgkscanToolProvider implements ToolProvider { 67 | @Override 68 | public String name() { 69 | return jsplitpgkscan; 70 | } 71 | 72 | @Override 73 | public int run(PrintWriter out, PrintWriter err, String... arguments) { 74 | return Main.run(arguments, out); 75 | } 76 | 77 | @Override 78 | public String toString() { 79 | return jsplitpgkscan; 80 | } 81 | } 82 | 83 | public static class JsplitpgkscanTool implements Tool { 84 | @Override 85 | public String name() { 86 | return jsplitpgkscan; 87 | } 88 | 89 | @Override 90 | public int run(InputStream in, OutputStream out, OutputStream err, String... arguments) { 91 | return Main.run(arguments, new PrintWriter(out)); 92 | } 93 | 94 | @Override 95 | public Set getSourceVersions() { 96 | return Collections.emptySet(); 97 | } 98 | 99 | @Override 100 | public String toString() { 101 | return jsplitpgkscan; 102 | } 103 | } 104 | } 105 | 106 | -------------------------------------------------------------------------------- /src/main/java/module-info.java: -------------------------------------------------------------------------------- 1 | module jdk.jsplitpkgscan { 2 | requires java.compiler; 3 | provides java.util.spi.ToolProvider 4 | with jdk.jsplitpkgscan.Main.JsplitpgkscanToolProvider; 5 | provides javax.tools.Tool 6 | with jdk.jsplitpkgscan.Main.JsplitpgkscanTool; 7 | } 8 | -------------------------------------------------------------------------------- /src/main/resources/jdk/jsplitpkgscan/resources/jsplitpkgscan.properties: -------------------------------------------------------------------------------- 1 | main.usage.summary=\ 2 | Usage: {0} ]\n\ 3 | use -h, -? or --help for a list of possible options 4 | 5 | main.usage=\ 6 | Usage: {0} ]\n\ 7 | can be a pathname to a JAR/WAR/RAR file or exploded JAR/WAR/RAR file.\n\ 8 | \n\ 9 | \Possible options include:\n\ 10 | \ -h -? --help Print this usage message\n\ 11 | \ -a Report all packages for each found JAR/RAR/WAR\n\ 12 | \ file or exploded directory\n\ 13 | \ -d Directory containing JAR/RAR/WAR files or\n\ 14 | \ exploded directories to scan\n\ 15 | \ (scans subdirectories as well)\n\ 16 | \ -f File containing a list of JAR/RAR/WAR files\n\ 17 | \ or exploded directories to scan\n\ 18 | \ -p Package prefix to report only split packages\n\ 19 | \ that matches this prefix\n\ 20 | \ --dot-output

Destination directory for DOT file output\n\ 21 | 22 | error.prefix=Error: 23 | warn.prefix=Warning: 24 | 25 | err.unknown.option=unknown option: {0} 26 | err.missing.arg=no value given for {0} 27 | err.scanning.dir=unable to scan directory: {0} 28 | err.invalid.path=invalid path: {0} 29 | err.missing.outputdir=output directory {0} not found --------------------------------------------------------------------------------