├── .github
├── FUNDING.yml
└── workflows
│ ├── [A] build and test, release if requested.yml
│ ├── [A] compute coverage.yml
│ ├── [A] update documentation.yml
│ └── [M] build and test, compute coverage, release.yml
├── .gitignore
├── LICENSE
├── README.md
├── docs
├── _config.yml
├── _layouts
│ └── default.html
├── assets
│ ├── css
│ │ └── style.scss
│ └── fonts
│ │ ├── AldoPro-Black.ttf
│ │ ├── AldoPro-Black.woff
│ │ ├── AldoPro-Black.woff2
│ │ ├── AldoPro-Bold.ttf
│ │ ├── AldoPro-Bold.woff
│ │ ├── AldoPro-Bold.woff2
│ │ ├── AldoPro-Book.ttf
│ │ ├── AldoPro-Book.woff
│ │ ├── AldoPro-Book.woff2
│ │ ├── AldoPro-Hairline.ttf
│ │ ├── AldoPro-Hairline.woff
│ │ ├── AldoPro-Hairline.woff2
│ │ ├── AldoPro-Light.ttf
│ │ ├── AldoPro-Light.woff
│ │ ├── AldoPro-Light.woff2
│ │ ├── AldoPro-Medium.ttf
│ │ ├── AldoPro-Medium.woff
│ │ ├── AldoPro-Medium.woff2
│ │ ├── AldoPro-Regular.ttf
│ │ ├── AldoPro-Regular.woff
│ │ ├── AldoPro-Regular.woff2
│ │ ├── AldoPro-Thin.ttf
│ │ ├── AldoPro-Thin.woff
│ │ └── AldoPro-Thin.woff2
└── index.md
├── launcher
└── eclipse
│ └── Burningwave Tools - AllExceptHeavyTestsSuite.launch
├── pom.xml
└── src
├── main
├── java
│ └── org
│ │ └── burningwave
│ │ └── tools
│ │ ├── dependencies
│ │ ├── Capturer.java
│ │ ├── Sniffer.java
│ │ └── TwoPassCapturer.java
│ │ └── net
│ │ ├── DNSClientHostResolver.java
│ │ ├── DefaultHostResolver.java
│ │ ├── HostResolutionRequestInterceptor.java
│ │ ├── HostResolver.java
│ │ ├── IPAddressUtil.java
│ │ └── MappedHostResolver.java
└── resources
│ └── burningwave.static.properties
└── test
├── java
└── org
│ └── burningwave
│ └── tools
│ ├── AllExceptHeavyTestsSuite.java
│ ├── BaseTest.java
│ ├── CapturerTest.java
│ ├── HostsResolverServiceTest.java
│ └── TwoPassCapturerTest.java
└── resources
├── burningwave.properties
└── burningwave.static.properties
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | custom: https://www.paypal.com/donate/?cmd=_donations&business=EY4TMTW8SWDAC&item_name=Support+maintenance+and+improvement+of+Burningwave+Tools¤cy_code=EUR&source=url
2 |
--------------------------------------------------------------------------------
/.github/workflows/[A] build and test, release if requested.yml:
--------------------------------------------------------------------------------
1 | name: Build and Test -> Compute coverage -> Release if requested
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 | paths:
8 | - "src/main/**.java"
9 | - "src/test/**.java"
10 | # - ".github/workflows/**"
11 | - "**.properties"
12 | - "**.xml"
13 |
14 | jobs:
15 |
16 | build-and-test-with-Java-8-and-later:
17 | name: Build -> Test (JVM ${{ matrix.java }}, ${{ matrix.os }})
18 | strategy:
19 | fail-fast: true
20 | max-parallel: 16
21 | matrix:
22 | os: [ubuntu-latest, windows-latest, macOS-latest]
23 | java: [8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24]
24 | architecture: [x64]
25 | exclude:
26 | - os: macOS-latest
27 | java: 13
28 | - os: macOS-latest
29 | java: 14
30 | - os: ubuntu-latest
31 | java: 10
32 | runs-on: ${{ matrix.os }}
33 | steps:
34 | - uses: actions/checkout@v3
35 | - name: Set up JDK ${{ matrix.java }}
36 | uses: actions/setup-java@v3
37 | with:
38 | java-version: ${{ matrix.java }}
39 | distribution: 'zulu'
40 | architecture: ${{ matrix.architecture }}
41 | - name: Build and test
42 | run: mvn -B clean test -Dproject_jdk_version=${{ matrix.java }} -DskipTests=false --file pom.xml -X -U
43 |
44 | release:
45 | name: Release if requested
46 | needs: [build-and-test-with-Java-8-and-later]
47 | runs-on: ubuntu-latest
48 | if: github.event_name == 'push' && endsWith(github.event.head_commit.message, 'Releasing new version')
49 | steps:
50 | - uses: actions/checkout@v3
51 | - name: Set up JDK 24
52 | uses: actions/setup-java@v3
53 | with:
54 | java-version: 24
55 | distribution: 'zulu'
56 | server-id: ossrh
57 | server-username: MAVEN_USERNAME
58 | server-password: MAVEN_PASSWORD
59 | - name: Publish to the Maven Central repository
60 | run: |
61 | export GPG_TTY=$(tty)
62 | echo "${{ secrets.gpg_private_key }}" | gpg --batch --import
63 | git config user.name "${{ github.event.head_commit.committer.name }}"
64 | git config user.email "${{ github.event.head_commit.committer.email }}"
65 | mvn -B release:prepare release:perform -Dproject_jdk_version=8 -DskipTests=true -Dgpg.passphrase=${{ secrets.gpg_passphrase }} -Dgpg.keyname=${{ secrets.gpg_key_id }} -Drepository.url=https://${GITHUB_ACTOR}:${{ secrets.GITHUB_TOKEN }}@github.com/${GITHUB_REPOSITORY}.git
66 | env:
67 | MAVEN_USERNAME: ${{ secrets.nexus_username }}
68 | MAVEN_PASSWORD: ${{ secrets.nexus_password }}
69 |
--------------------------------------------------------------------------------
/.github/workflows/[A] compute coverage.yml:
--------------------------------------------------------------------------------
1 | name: Compute coverage
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 | paths:
8 | - "src/main/**"
9 | - "src/test/**"
10 | # - ".github/workflows/**"
11 | - "**.properties"
12 | - "**.xml"
13 |
14 | jobs:
15 | compute-coverage:
16 | name: Compute coverage
17 | runs-on: ubuntu-latest
18 | steps:
19 | - uses: actions/checkout@v3
20 | - uses: actions/setup-java@v3
21 | with:
22 | java-version: 8
23 | distribution: 'zulu'
24 | architecture: x64
25 | - name: Build and test
26 | run: mvn clean test -DskipTests=false -Dproject_jdk_version=8 -P run-coveralls jacoco:report coveralls:report -DrepoToken=${{ secrets.coveralls_repo_token }}
27 |
28 |
--------------------------------------------------------------------------------
/.github/workflows/[A] update documentation.yml:
--------------------------------------------------------------------------------
1 | name: Update index page
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 | paths:
8 | - "**README.md"
9 |
10 | jobs:
11 | update-index-page:
12 | runs-on: ubuntu-latest
13 | name: Update index page
14 | steps:
15 | - uses: actions/checkout@master
16 | - name: Overwrite the index.md
17 | run: |
18 | git config user.name "${{ github.event.head_commit.committer.name }}"
19 | git config user.email "${{ github.event.head_commit.committer.email }}"
20 | git pull origin ${{github.ref}}
21 | cp "./README.md" "./docs/index.md"
22 | git add .
23 | git commit -am "Update" --allow-empty
24 | git push
25 |
--------------------------------------------------------------------------------
/.github/workflows/[M] build and test, compute coverage, release.yml:
--------------------------------------------------------------------------------
1 | name: Build and test -> Compute coverage -> Release
2 |
3 | on:
4 | watch:
5 | types: [started]
6 |
7 | jobs:
8 |
9 | ask-for-authorization:
10 | name: Ask for authorization
11 | runs-on: ubuntu-latest
12 | steps:
13 | - uses: octokit/request-action@v2.0.0
14 | with:
15 | route: GET /repos/:repository/collaborators/${{ github.actor }}
16 | repository: ${{ github.repository }}
17 | env:
18 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
19 | - name: Send push notification
20 | if: ${{ failure() }}
21 | uses: techulus/push-github-action@1.0.0
22 | env:
23 | API_KEY: ${{ secrets.PUSH_NOTIFICATION_API_KEY }}
24 | MESSAGE: ${{ format('New star for {0}!', github.repository) }}
25 |
26 | build-and-test-with-Java-8-and-later:
27 | name: Build -> Test (JVM ${{ matrix.java }}, ${{ matrix.os }})
28 | needs: [ask-for-authorization]
29 | strategy:
30 | fail-fast: true
31 | max-parallel: 15
32 | matrix:
33 | os: [windows-latest, macOS-latest, ubuntu-latest]
34 | java: [8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24]
35 | architecture: [x64]
36 | exclude:
37 | - os: macOS-latest
38 | java: 13
39 | - os: macOS-latest
40 | java: 14
41 | - os: ubuntu-latest
42 | java: 10
43 | runs-on: ${{ matrix.os }}
44 | steps:
45 | - uses: actions/checkout@v3
46 | - name: Set up JDK ${{ matrix.java }}
47 | uses: actions/setup-java@v3
48 | with:
49 | java-version: ${{ matrix.java }}
50 | distribution: 'zulu'
51 | architecture: ${{ matrix.architecture }}
52 | - name: Build and test with
53 | run: mvn -B clean test -DskipTests=false --file pom.xml
54 |
55 | test-and-compute-coverage:
56 | name: Test -> Compute coverage
57 | needs: [build-and-test-with-Java-8-and-later]
58 | runs-on: ubuntu-latest
59 | steps:
60 | - uses: actions/checkout@v3
61 | - uses: actions/setup-java@v3
62 | with:
63 | java-version: 8
64 | distribution: 'zulu'
65 | architecture: x64
66 | - name: Build and test
67 | run: mvn clean test -DskipTests=false -Dproject.test.testSuite=AllTestsSuite -P run-coveralls jacoco:report coveralls:report -DrepoToken=${{ secrets.coveralls_repo_token }}
68 |
69 | release:
70 | name: Release
71 | needs: [build-and-test-with-Java-8-and-later]
72 | # This is an organization variable: see https://docs.github.com/en/actions/learn-github-actions/variables
73 | if: ${{ fromJSON(vars.MANUAL_RELEASE_ENABLED) }}
74 | runs-on: ubuntu-latest
75 | steps:
76 | - uses: actions/checkout@v3
77 | - name: Set up JDK 24
78 | uses: actions/setup-java@v3
79 | with:
80 | java-version: 24
81 | distribution: 'zulu'
82 | server-id: ossrh
83 | server-username: MAVEN_USERNAME
84 | server-password: MAVEN_PASSWORD
85 | - name: Publish to the Maven Central repository
86 | run: |
87 | export GPG_TTY=$(tty)
88 | echo "${{ secrets.gpg_private_key }}" | gpg --batch --import
89 | git config user.name "${GITHUB_ACTOR}"
90 | git config user.email "info@burningwave.org"
91 | mvn -B release:prepare release:perform -Dproject_jdk_version=8 -DskipTests=true -Dgpg.passphrase=${{ secrets.GPG_PASSPHRASE }} -Dgpg.keyname=${{ secrets.gpg_key_id }} -Drepository.url=https://${GITHUB_ACTOR}:${{ secrets.GITHUB_TOKEN }}@github.com/${GITHUB_REPOSITORY}.git
92 | env:
93 | MAVEN_USERNAME: ${{ secrets.nexus_username }}
94 | MAVEN_PASSWORD: ${{ secrets.nexus_password }}
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Log file
2 | *.log
3 |
4 | # BlueJ files
5 | *.ctxt
6 |
7 | # Mobile Tools for Java (J2ME)
8 | .mtj.tmp/
9 |
10 | # Package Files #
11 | *.jar
12 | *.war
13 | *.nar
14 | *.ear
15 | *.zip
16 | *.tar.gz
17 | *.rar
18 |
19 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
20 | hs_err_pid*
21 |
22 | /target/
23 | /.project
24 | /.settings
25 | /.classpath
26 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 Roberto Gentili
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Burningwave Tools [](https://twitter.com/intent/tweet?text=Dependencies%20shrinking%20and%20making%20applications%20created%20with%20old%20%23Java%20versions&url=https://burningwave.github.io/tools/)
2 | ==========
3 |
4 |
5 |
6 |
7 |
8 | [](https://maven-badges.herokuapp.com/maven-central/org.burningwave/tools/)
9 | [](https://github.com/burningwave/tools/blob/master/LICENSE)
10 |
11 | [](https://github.com/burningwave/tools/actions/runs/14547159555)
12 |
13 | [-blueviolet)](https://github.com/burningwave/tools/actions/runs/14547159555)
14 |
15 | [](https://coveralls.io/github/burningwave/tools)
16 | [](https://github.com/burningwave/tools/issues)
17 | [](https://github.com/burningwave/tools/issues?q=is%3Aissue+is%3Aclosed)
18 |
19 | [](https://www.burningwave.org/artifact-downloads/?show-overall-trend-chart=false&artifactId=tools&startDate=2020-01)
20 | [](https://github.com/burningwave/tools/network/dependents)
21 | [](https://www.burningwave.org#bw-counters)
22 |
23 | **Burningwave Tools** is a set of components based on [**Burningwave Core**](https://burningwave.github.io/core/) library that have high-level functionality
24 |
25 | # Dependencies shrinking
26 | By this functionality only the classes and resources strictly used by an application will be extracted and stored in a specified path. At the end of the execution of the task, a script will be created in the destination path to run the application using the extracted classes. **The dependency shrinkers can also be used to adapt applications written with Java old versions to Java 9 or later**.
27 |
28 | The classes that deal the dependencies extraction are:
29 | * **`org.burningwave.tools.dependencies.Capturer`**
30 | * **`org.burningwave.tools.dependencies.TwoPassCapturer`**
31 |
32 | It can be used indiscriminately or one or the other class: the first performs a normal scan, the second a deep scan. **When the operations are finished a batch will be generated in the destination path to run your application with the extracted dependencies**.
33 |
34 | To include Burningwave Tools in your projects simply use with **Apache Maven**:
35 | ```xml
36 |
37 | org.burningwave
38 | tools
39 | 0.27.2
40 |
41 | ```
42 |
43 |
44 | ## Extractor mode
45 | To use this mode simply pass to the method **`captureAndStore`**, as first parameter, the name of the class of your application that contains the main method.
46 | ```java
47 | package org.burningwave.tools.examples.twopasscapturer;
48 |
49 | import static
50 | org.burningwave.core.assembler.StaticComponentContainer.ManagedLoggerRepository;
51 |
52 | import java.util.Collection;
53 |
54 | import org.burningwave.core.assembler.ComponentContainer;
55 | import org.burningwave.core.assembler.ComponentSupplier;
56 | import org.burningwave.core.io.PathHelper;
57 | import org.burningwave.tools.dependencies.Capturer.Result;
58 | import org.burningwave.tools.dependencies.TwoPassCapturer;
59 |
60 | public class DependenciesExtractor {
61 |
62 | public static void main(String[] args) throws Exception {
63 | long initialTime = System.currentTimeMillis();
64 | ComponentSupplier componentSupplier = ComponentContainer.getInstance();
65 | PathHelper pathHelper = componentSupplier.getPathHelper();
66 | Collection paths = pathHelper.getAllMainClassPaths();
67 | Result result = TwoPassCapturer.getInstance().captureAndStore(
68 | //Here you indicate the main class of your application
69 | "my.class.that.contains.a.MainMethod",
70 | paths,
71 | //Here you indicate the destination path where extracted
72 | //classes and resources will be stored
73 | System.getProperty("user.home") + "/Desktop/dependencies",
74 | true,
75 | //Here you indicate the waiting time after the main of your
76 | //application has been executed. This is useful, for example,
77 | //for spring boot applications to make it possible, once started,
78 | //to run rest methods to continue extracting the dependencies
79 | 0L
80 | );
81 | result.waitForTaskEnding();
82 | ManagedLoggerRepository.logInfo(
83 | () -> DependenciesExtractor.class.getName(),
84 | "Elapsed time: " + getFormattedDifferenceOfMillis(
85 | System.currentTimeMillis(), initialTime
86 | )
87 | );
88 | }
89 |
90 | private static String getFormattedDifferenceOfMillis(long value1, long value2) {
91 | String valueFormatted = String.format("%04d", (value1 - value2));
92 | return valueFormatted.substring(0, valueFormatted.length() - 3) + "," +
93 | valueFormatted.substring(valueFormatted.length() -3);
94 | }
95 |
96 | }
97 | ```
98 |
99 |
100 | ## Adapter mode
101 | In this mode you can adapt a Java old version application to Java 9 or later. To use this mode you must **run the main of the application adapter with a jdk 9 or later**, load, by using `PathHelper`, the jdk libraries by which the target application was developed and pass to the method **`captureAndStore`**, as first parameter, the name of the class of your application that contains the main method. In the example below we adapt a Java 8 application to Java 9 or later.
102 | ```java
103 | package org.burningwave.tools.examples.twopasscapturer;
104 |
105 | import static
106 | org.burningwave.core.assembler.StaticComponentContainer.ManagedLoggerRepository;
107 |
108 | import java.util.Collection;
109 |
110 | import org.burningwave.core.assembler.ComponentContainer;
111 | import org.burningwave.core.assembler.ComponentSupplier;
112 | import org.burningwave.core.io.PathHelper;
113 | import org.burningwave.tools.dependencies.Capturer.Result;
114 | import org.burningwave.tools.dependencies.TwoPassCapturer;
115 |
116 | public class ApplicationAdapter {
117 |
118 | public static void main(String[] args) throws Exception {
119 | long initialTime = System.currentTimeMillis();
120 | ComponentSupplier componentSupplier = ComponentContainer.getInstance();
121 | PathHelper pathHelper = componentSupplier.getPathHelper();
122 | Collection paths = pathHelper.getAllMainClassPaths();
123 | String jdk8Home = "C:/Program Files/Java/jdk1.8.0_172";
124 | //Add jdk 8 library
125 | paths.addAll(
126 | pathHelper.loadAndMapPaths(
127 | "dependencies-capturer.additional-resources-path",
128 | "//" + jdk8Home + "/jre/lib//children:.*\\.jar;" +
129 | "//" + jdk8Home + "/jre/lib/ext//children:.*\\.jar;"
130 | )
131 | );
132 | Result result = TwoPassCapturer.getInstance().captureAndStore(
133 | //Here you indicate the main class of your application
134 | "my.class.that.contains.a.MainMethod",
135 | paths,
136 | //Here you indicate the destination path where extracted
137 | //classes and resources will be stored
138 | System.getProperty("user.home") + "/Desktop/dependencies",
139 | true,
140 | //Here you indicate the waiting time after the main of your
141 | //application has been executed. This is useful, for example,
142 | //for spring boot applications to make it possible, once started,
143 | //to run rest methods to continue extracting the dependencies
144 | 0L
145 | );
146 | result.waitForTaskEnding();
147 | ManagedLoggerRepository.logInfo(
148 | () -> ApplicationAdapter.class.getName(),
149 | "Elapsed time: " + getFormattedDifferenceOfMillis(
150 | System.currentTimeMillis(),
151 | initialTime
152 | )
153 | );
154 | }
155 |
156 | private static String getFormattedDifferenceOfMillis(long value1, long value2) {
157 | String valueFormatted = String.format("%04d", (value1 - value2));
158 | return valueFormatted.substring(0, valueFormatted.length() - 3) + "," +
159 | valueFormatted.substring(valueFormatted.length() -3);
160 | }
161 |
162 | }
163 | ```
164 |
165 |
166 |
167 | # Configuring host resolution
168 |
169 | With the **`org.burningwave.tools.net.HostResolutionRequestInterceptor`** you can modify the local machine's default host name resolution in a universal way:
170 |
171 | ```java
172 | Map hostAliases = new LinkedHashMap<>();
173 | hostAliases.put("my.hostname.one", "123.123.123.123");
174 |
175 | //Installing the host resolvers
176 | HostResolutionRequestInterceptor.INSTANCE.install(
177 | new MappedHostResolver(hostAliases),
178 | //This is the system default resolving wrapper
179 | DefaultHostResolver.INSTANCE
180 | );
181 |
182 | InetAddress inetAddress = InetAddress.getByName("my.hostname.one");
183 | ```
184 |
185 |
186 | ## Host resolution via DNS server
187 |
188 | Burningwave Tools provides also a DNS client for host resolution:
189 |
190 | ```java
191 | HostResolutionRequestInterceptor.INSTANCE.install(
192 | new DNSClientHostResolver("208.67.222.222"), //Open DNS server
193 | new DNSClientHostResolver("208.67.222.220"), //Open DNS server
194 | new DNSClientHostResolver("8.8.8.8"), //Google DNS server
195 | new DNSClientHostResolver("8.8.4.4"), //Google DNS server
196 | DefaultHostResolver.INSTANCE
197 | );
198 | InetAddress inetAddress = InetAddress.getByName("github.com");
199 | ```
200 |
201 |
202 | ## Implement a custom host resolver
203 |
204 | You can also define a new custom resolver by implementing the **`org.burningwave.tools.net.HostResolver`** interface:
205 | ```java
206 | HostResolutionRequestInterceptor.INSTANCE.install(
207 | new HostResolver() {
208 |
209 | @Override
210 | public Collection getAllAddressesForHostName(Map argumentMap) {
211 | String hostName = (String)super.getMethodArguments(argumentMap)[0]
212 | //Do the stuff...
213 | }
214 |
215 | @Override
216 | public Collection getAllHostNamesForHostAddress(Map argumentMap) {
217 | byte[] iPAddressAsByteArray = (byte[])super.getMethodArguments(argumentMap)[0];
218 | String iPAddress = IPAddressUtil.INSTANCE.numericToTextFormat(iPAddressAsByteArray);
219 | //Do the stuff...
220 | }
221 |
222 | },
223 | DefaultHostResolver.INSTANCE
224 | );
225 | ```
226 |
227 |
228 |
229 | # Ask for assistance
230 | **For assistance you can**:
231 | * [open a discussion](https://github.com/burningwave/tools/discussions) here on GitHub
232 | * [report a bug](https://github.com/burningwave/tools/issues)
233 | * ask on [Stack Overflow](https://stackoverflow.com/search?q=burningwave)
234 |
--------------------------------------------------------------------------------
/docs/_config.yml:
--------------------------------------------------------------------------------
1 | theme: jekyll-theme-cayman
2 | title: Burningwave Tools
3 | google_analytics: UA-154852845-4
4 | show_downloads: false
5 |
--------------------------------------------------------------------------------
/docs/_layouts/default.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {% if site.google_analytics %}
6 |
7 |
13 | {% endif %}
14 |
15 |
16 | {% seo %}
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
37 |
38 |
39 | {{ content }}
40 |
41 |
50 |
51 |
52 |
53 |
--------------------------------------------------------------------------------
/docs/assets/css/style.scss:
--------------------------------------------------------------------------------
1 | ---
2 | ---
3 |
4 | @import "{{ site.theme }}";
5 | .page-header {
6 | background-image: linear-gradient(120deg, #e54d1d, #f7bc12);
7 | }
8 |
9 | h1.project-name {
10 | font-family: 'Aldo Pro Book';
11 | font-size: 4.25rem;
12 | font-style: italic;
13 | line-height: 1.00em;
14 | }
15 |
16 | .main-content {
17 | h1 {
18 | color: #e54d1d;
19 | }
20 | h2 {
21 | color: #e54d1d;
22 | }
23 | h3 {
24 | color: #e54d1d;
25 | }
26 | }
27 |
28 | @font-face {
29 | font-family: 'Aldo Pro Hairline';
30 | src: local('Aldo Pro Hairline'), local('Aldo-Pro-Hairline'),
31 | url('./../fonts/AldoPro-Hairline.woff2') format('woff2'),
32 | url('./../fonts/AldoPro-Hairline.woff') format('woff'),
33 | url('./../fonts/AldoPro-Hairline.ttf') format('truetype');
34 | font-weight: 100;
35 | font-style: normal;
36 | }
37 |
38 | @font-face {
39 | font-family: 'Aldo Pro Light';
40 | src: local('Aldo Pro Light'), local('Aldo-Pro-Light'),
41 | url('./../fonts/AldoPro-Light.woff2') format('woff2'),
42 | url('./../fonts/AldoPro-Light.woff') format('woff'),
43 | url('./../fonts/AldoPro-Light.ttf') format('truetype');
44 | font-weight: 300;
45 | font-style: normal;
46 | }
47 |
48 | @font-face {
49 | font-family: 'Aldo Pro Book';
50 | src: local('Aldo Pro Book'), local('Aldo-Pro-Book'),
51 | url('./../fonts/AldoPro-Book.woff2') format('woff2'),
52 | url('./../fonts/AldoPro-Book.woff') format('woff'),
53 | url('./../fonts/AldoPro-Book.ttf') format('truetype');
54 | font-weight: normal;
55 | font-style: normal;
56 | }
57 |
58 | @font-face {
59 | font-family: 'Aldo Pro Regular';
60 | src: local('Aldo Pro Regular'), local('Aldo-Pro-Regular'),
61 | url('./../fonts/AldoPro-Regular.woff2') format('woff2'),
62 | url('./../fonts/AldoPro-Regular.woff') format('woff'),
63 | url('./../fonts/AldoPro-Regular.ttf') format('truetype');
64 | font-weight: 400;
65 | font-style: normal;
66 | }
67 |
68 | @font-face {
69 | font-family: 'Aldo Pro Medium';
70 | src: local('Aldo Pro Medium'), local('Aldo-Pro-Medium'),
71 | url('./../fonts/AldoPro-Medium.woff2') format('woff2'),
72 | url('./../fonts/AldoPro-Medium.woff') format('woff'),
73 | url('./../fonts/AldoPro-Medium.ttf') format('truetype');
74 | font-weight: 500;
75 | font-style: normal;
76 | }
77 |
78 | @font-face {
79 | font-family: 'Aldo Pro Bold';
80 | src: local('Aldo Pro Bold'), local('Aldo-Pro-Bold'),
81 | url('./../fonts/AldoPro-Bold.woff2') format('woff2'),
82 | url('./../fonts/AldoPro-Bold.woff') format('woff'),
83 | url('./../fonts/AldoPro-Bold.ttf') format('truetype');
84 | font-weight: 700;
85 | font-style: normal;
86 | }
87 |
88 | @font-face {
89 | font-family: 'Aldo Pro Black';
90 | src: local('Aldo Pro Black'), local('Aldo-Pro-Black'),
91 | url('./../fonts/AldoPro-Black.woff2') format('woff2'),
92 | url('./../fonts/AldoPro-Black.woff') format('woff'),
93 | url('./../fonts/AldoPro-Black.ttf') format('truetype');
94 | font-weight: 900;
95 | font-style: normal;
96 | }
97 |
--------------------------------------------------------------------------------
/docs/assets/fonts/AldoPro-Black.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/burningwave/tools/ef831cc5337c41ef99dda4a219932cd243b0807f/docs/assets/fonts/AldoPro-Black.ttf
--------------------------------------------------------------------------------
/docs/assets/fonts/AldoPro-Black.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/burningwave/tools/ef831cc5337c41ef99dda4a219932cd243b0807f/docs/assets/fonts/AldoPro-Black.woff
--------------------------------------------------------------------------------
/docs/assets/fonts/AldoPro-Black.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/burningwave/tools/ef831cc5337c41ef99dda4a219932cd243b0807f/docs/assets/fonts/AldoPro-Black.woff2
--------------------------------------------------------------------------------
/docs/assets/fonts/AldoPro-Bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/burningwave/tools/ef831cc5337c41ef99dda4a219932cd243b0807f/docs/assets/fonts/AldoPro-Bold.ttf
--------------------------------------------------------------------------------
/docs/assets/fonts/AldoPro-Bold.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/burningwave/tools/ef831cc5337c41ef99dda4a219932cd243b0807f/docs/assets/fonts/AldoPro-Bold.woff
--------------------------------------------------------------------------------
/docs/assets/fonts/AldoPro-Bold.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/burningwave/tools/ef831cc5337c41ef99dda4a219932cd243b0807f/docs/assets/fonts/AldoPro-Bold.woff2
--------------------------------------------------------------------------------
/docs/assets/fonts/AldoPro-Book.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/burningwave/tools/ef831cc5337c41ef99dda4a219932cd243b0807f/docs/assets/fonts/AldoPro-Book.ttf
--------------------------------------------------------------------------------
/docs/assets/fonts/AldoPro-Book.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/burningwave/tools/ef831cc5337c41ef99dda4a219932cd243b0807f/docs/assets/fonts/AldoPro-Book.woff
--------------------------------------------------------------------------------
/docs/assets/fonts/AldoPro-Book.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/burningwave/tools/ef831cc5337c41ef99dda4a219932cd243b0807f/docs/assets/fonts/AldoPro-Book.woff2
--------------------------------------------------------------------------------
/docs/assets/fonts/AldoPro-Hairline.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/burningwave/tools/ef831cc5337c41ef99dda4a219932cd243b0807f/docs/assets/fonts/AldoPro-Hairline.ttf
--------------------------------------------------------------------------------
/docs/assets/fonts/AldoPro-Hairline.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/burningwave/tools/ef831cc5337c41ef99dda4a219932cd243b0807f/docs/assets/fonts/AldoPro-Hairline.woff
--------------------------------------------------------------------------------
/docs/assets/fonts/AldoPro-Hairline.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/burningwave/tools/ef831cc5337c41ef99dda4a219932cd243b0807f/docs/assets/fonts/AldoPro-Hairline.woff2
--------------------------------------------------------------------------------
/docs/assets/fonts/AldoPro-Light.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/burningwave/tools/ef831cc5337c41ef99dda4a219932cd243b0807f/docs/assets/fonts/AldoPro-Light.ttf
--------------------------------------------------------------------------------
/docs/assets/fonts/AldoPro-Light.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/burningwave/tools/ef831cc5337c41ef99dda4a219932cd243b0807f/docs/assets/fonts/AldoPro-Light.woff
--------------------------------------------------------------------------------
/docs/assets/fonts/AldoPro-Light.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/burningwave/tools/ef831cc5337c41ef99dda4a219932cd243b0807f/docs/assets/fonts/AldoPro-Light.woff2
--------------------------------------------------------------------------------
/docs/assets/fonts/AldoPro-Medium.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/burningwave/tools/ef831cc5337c41ef99dda4a219932cd243b0807f/docs/assets/fonts/AldoPro-Medium.ttf
--------------------------------------------------------------------------------
/docs/assets/fonts/AldoPro-Medium.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/burningwave/tools/ef831cc5337c41ef99dda4a219932cd243b0807f/docs/assets/fonts/AldoPro-Medium.woff
--------------------------------------------------------------------------------
/docs/assets/fonts/AldoPro-Medium.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/burningwave/tools/ef831cc5337c41ef99dda4a219932cd243b0807f/docs/assets/fonts/AldoPro-Medium.woff2
--------------------------------------------------------------------------------
/docs/assets/fonts/AldoPro-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/burningwave/tools/ef831cc5337c41ef99dda4a219932cd243b0807f/docs/assets/fonts/AldoPro-Regular.ttf
--------------------------------------------------------------------------------
/docs/assets/fonts/AldoPro-Regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/burningwave/tools/ef831cc5337c41ef99dda4a219932cd243b0807f/docs/assets/fonts/AldoPro-Regular.woff
--------------------------------------------------------------------------------
/docs/assets/fonts/AldoPro-Regular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/burningwave/tools/ef831cc5337c41ef99dda4a219932cd243b0807f/docs/assets/fonts/AldoPro-Regular.woff2
--------------------------------------------------------------------------------
/docs/assets/fonts/AldoPro-Thin.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/burningwave/tools/ef831cc5337c41ef99dda4a219932cd243b0807f/docs/assets/fonts/AldoPro-Thin.ttf
--------------------------------------------------------------------------------
/docs/assets/fonts/AldoPro-Thin.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/burningwave/tools/ef831cc5337c41ef99dda4a219932cd243b0807f/docs/assets/fonts/AldoPro-Thin.woff
--------------------------------------------------------------------------------
/docs/assets/fonts/AldoPro-Thin.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/burningwave/tools/ef831cc5337c41ef99dda4a219932cd243b0807f/docs/assets/fonts/AldoPro-Thin.woff2
--------------------------------------------------------------------------------
/docs/index.md:
--------------------------------------------------------------------------------
1 | Burningwave Tools [](https://twitter.com/intent/tweet?text=Dependencies%20shrinking%20and%20making%20applications%20created%20with%20old%20%23Java%20versions&url=https://burningwave.github.io/tools/)
2 | ==========
3 |
4 |
5 |
6 |
7 |
8 | [](https://maven-badges.herokuapp.com/maven-central/org.burningwave/tools/)
9 | [](https://github.com/burningwave/tools/blob/master/LICENSE)
10 |
11 | [](https://github.com/burningwave/tools/actions/runs/14547159555)
12 |
13 | [-blueviolet)](https://github.com/burningwave/tools/actions/runs/14547159555)
14 |
15 | [](https://coveralls.io/github/burningwave/tools)
16 | [](https://github.com/burningwave/tools/issues)
17 | [](https://github.com/burningwave/tools/issues?q=is%3Aissue+is%3Aclosed)
18 |
19 | [](https://www.burningwave.org/artifact-downloads/?show-overall-trend-chart=false&artifactId=tools&startDate=2020-01)
20 | [](https://github.com/burningwave/tools/network/dependents)
21 | [](https://www.burningwave.org#bw-counters)
22 |
23 | **Burningwave Tools** is a set of components based on [**Burningwave Core**](https://burningwave.github.io/core/) library that have high-level functionality
24 |
25 | # Dependencies shrinking
26 | By this functionality only the classes and resources strictly used by an application will be extracted and stored in a specified path. At the end of the execution of the task, a script will be created in the destination path to run the application using the extracted classes. **The dependency shrinkers can also be used to adapt applications written with Java old versions to Java 9 or later**.
27 |
28 | The classes that deal the dependencies extraction are:
29 | * **`org.burningwave.tools.dependencies.Capturer`**
30 | * **`org.burningwave.tools.dependencies.TwoPassCapturer`**
31 |
32 | It can be used indiscriminately or one or the other class: the first performs a normal scan, the second a deep scan. **When the operations are finished a batch will be generated in the destination path to run your application with the extracted dependencies**.
33 |
34 | To include Burningwave Tools in your projects simply use with **Apache Maven**:
35 | ```xml
36 |
37 | org.burningwave
38 | tools
39 | 0.27.2
40 |
41 | ```
42 |
43 |
44 | ## Extractor mode
45 | To use this mode simply pass to the method **`captureAndStore`**, as first parameter, the name of the class of your application that contains the main method.
46 | ```java
47 | package org.burningwave.tools.examples.twopasscapturer;
48 |
49 | import static
50 | org.burningwave.core.assembler.StaticComponentContainer.ManagedLoggerRepository;
51 |
52 | import java.util.Collection;
53 |
54 | import org.burningwave.core.assembler.ComponentContainer;
55 | import org.burningwave.core.assembler.ComponentSupplier;
56 | import org.burningwave.core.io.PathHelper;
57 | import org.burningwave.tools.dependencies.Capturer.Result;
58 | import org.burningwave.tools.dependencies.TwoPassCapturer;
59 |
60 | public class DependenciesExtractor {
61 |
62 | public static void main(String[] args) throws Exception {
63 | long initialTime = System.currentTimeMillis();
64 | ComponentSupplier componentSupplier = ComponentContainer.getInstance();
65 | PathHelper pathHelper = componentSupplier.getPathHelper();
66 | Collection paths = pathHelper.getAllMainClassPaths();
67 | Result result = TwoPassCapturer.getInstance().captureAndStore(
68 | //Here you indicate the main class of your application
69 | "my.class.that.contains.a.MainMethod",
70 | paths,
71 | //Here you indicate the destination path where extracted
72 | //classes and resources will be stored
73 | System.getProperty("user.home") + "/Desktop/dependencies",
74 | true,
75 | //Here you indicate the waiting time after the main of your
76 | //application has been executed. This is useful, for example,
77 | //for spring boot applications to make it possible, once started,
78 | //to run rest methods to continue extracting the dependencies
79 | 0L
80 | );
81 | result.waitForTaskEnding();
82 | ManagedLoggerRepository.logInfo(
83 | () -> DependenciesExtractor.class.getName(),
84 | "Elapsed time: " + getFormattedDifferenceOfMillis(
85 | System.currentTimeMillis(), initialTime
86 | )
87 | );
88 | }
89 |
90 | private static String getFormattedDifferenceOfMillis(long value1, long value2) {
91 | String valueFormatted = String.format("%04d", (value1 - value2));
92 | return valueFormatted.substring(0, valueFormatted.length() - 3) + "," +
93 | valueFormatted.substring(valueFormatted.length() -3);
94 | }
95 |
96 | }
97 | ```
98 |
99 |
100 | ## Adapter mode
101 | In this mode you can adapt a Java old version application to Java 9 or later. To use this mode you must **run the main of the application adapter with a jdk 9 or later**, load, by using `PathHelper`, the jdk libraries by which the target application was developed and pass to the method **`captureAndStore`**, as first parameter, the name of the class of your application that contains the main method. In the example below we adapt a Java 8 application to Java 9 or later.
102 | ```java
103 | package org.burningwave.tools.examples.twopasscapturer;
104 |
105 | import static
106 | org.burningwave.core.assembler.StaticComponentContainer.ManagedLoggerRepository;
107 |
108 | import java.util.Collection;
109 |
110 | import org.burningwave.core.assembler.ComponentContainer;
111 | import org.burningwave.core.assembler.ComponentSupplier;
112 | import org.burningwave.core.io.PathHelper;
113 | import org.burningwave.tools.dependencies.Capturer.Result;
114 | import org.burningwave.tools.dependencies.TwoPassCapturer;
115 |
116 | public class ApplicationAdapter {
117 |
118 | public static void main(String[] args) throws Exception {
119 | long initialTime = System.currentTimeMillis();
120 | ComponentSupplier componentSupplier = ComponentContainer.getInstance();
121 | PathHelper pathHelper = componentSupplier.getPathHelper();
122 | Collection paths = pathHelper.getAllMainClassPaths();
123 | String jdk8Home = "C:/Program Files/Java/jdk1.8.0_172";
124 | //Add jdk 8 library
125 | paths.addAll(
126 | pathHelper.loadAndMapPaths(
127 | "dependencies-capturer.additional-resources-path",
128 | "//" + jdk8Home + "/jre/lib//children:.*\\.jar;" +
129 | "//" + jdk8Home + "/jre/lib/ext//children:.*\\.jar;"
130 | )
131 | );
132 | Result result = TwoPassCapturer.getInstance().captureAndStore(
133 | //Here you indicate the main class of your application
134 | "my.class.that.contains.a.MainMethod",
135 | paths,
136 | //Here you indicate the destination path where extracted
137 | //classes and resources will be stored
138 | System.getProperty("user.home") + "/Desktop/dependencies",
139 | true,
140 | //Here you indicate the waiting time after the main of your
141 | //application has been executed. This is useful, for example,
142 | //for spring boot applications to make it possible, once started,
143 | //to run rest methods to continue extracting the dependencies
144 | 0L
145 | );
146 | result.waitForTaskEnding();
147 | ManagedLoggerRepository.logInfo(
148 | () -> ApplicationAdapter.class.getName(),
149 | "Elapsed time: " + getFormattedDifferenceOfMillis(
150 | System.currentTimeMillis(),
151 | initialTime
152 | )
153 | );
154 | }
155 |
156 | private static String getFormattedDifferenceOfMillis(long value1, long value2) {
157 | String valueFormatted = String.format("%04d", (value1 - value2));
158 | return valueFormatted.substring(0, valueFormatted.length() - 3) + "," +
159 | valueFormatted.substring(valueFormatted.length() -3);
160 | }
161 |
162 | }
163 | ```
164 |
165 |
166 |
167 | # Configuring host resolution
168 |
169 | With the **`org.burningwave.tools.net.HostResolutionRequestInterceptor`** you can modify the local machine's default host name resolution in a universal way:
170 |
171 | ```java
172 | Map hostAliases = new LinkedHashMap<>();
173 | hostAliases.put("my.hostname.one", "123.123.123.123");
174 |
175 | //Installing the host resolvers
176 | HostResolutionRequestInterceptor.INSTANCE.install(
177 | new MappedHostResolver(hostAliases),
178 | //This is the system default resolving wrapper
179 | DefaultHostResolver.INSTANCE
180 | );
181 |
182 | InetAddress inetAddress = InetAddress.getByName("my.hostname.one");
183 | ```
184 |
185 |
186 | ## Host resolution via DNS server
187 |
188 | Burningwave Tools provides also a DNS client for host resolution:
189 |
190 | ```java
191 | HostResolutionRequestInterceptor.INSTANCE.install(
192 | new DNSClientHostResolver("208.67.222.222"), //Open DNS server
193 | new DNSClientHostResolver("208.67.222.220"), //Open DNS server
194 | new DNSClientHostResolver("8.8.8.8"), //Google DNS server
195 | new DNSClientHostResolver("8.8.4.4"), //Google DNS server
196 | DefaultHostResolver.INSTANCE
197 | );
198 | InetAddress inetAddress = InetAddress.getByName("github.com");
199 | ```
200 |
201 |
202 | ## Implement a custom host resolver
203 |
204 | You can also define a new custom resolver by implementing the **`org.burningwave.tools.net.HostResolver`** interface:
205 | ```java
206 | HostResolutionRequestInterceptor.INSTANCE.install(
207 | new HostResolver() {
208 |
209 | @Override
210 | public Collection getAllAddressesForHostName(Map argumentMap) {
211 | String hostName = (String)super.getMethodArguments(argumentMap)[0]
212 | //Do the stuff...
213 | }
214 |
215 | @Override
216 | public Collection getAllHostNamesForHostAddress(Map argumentMap) {
217 | byte[] iPAddressAsByteArray = (byte[])super.getMethodArguments(argumentMap)[0];
218 | String iPAddress = IPAddressUtil.INSTANCE.numericToTextFormat(iPAddressAsByteArray);
219 | //Do the stuff...
220 | }
221 |
222 | },
223 | DefaultHostResolver.INSTANCE
224 | );
225 | ```
226 |
227 |
228 |
229 | # Ask for assistance
230 | **For assistance you can**:
231 | * [open a discussion](https://github.com/burningwave/tools/discussions) here on GitHub
232 | * [report a bug](https://github.com/burningwave/tools/issues)
233 | * ask on [Stack Overflow](https://stackoverflow.com/search?q=burningwave)
234 |
--------------------------------------------------------------------------------
/launcher/eclipse/Burningwave Tools - AllExceptHeavyTestsSuite.launch:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
29 |
30 | 4.0.0
31 |
32 | org.burningwave
33 | tools
34 |
35 | 0.27.3-SNAPSHOT
36 | jar
37 |
38 | Burningwave Tools
39 |
40 | A set of components based on Burningwave Core library that have high-level functionality such as a dependencies extractor and a Java old versions to Java 9 or later application converter
41 |
42 | https://burningwave.github.io/tools/
43 |
44 |
45 |
46 | MIT License
47 | https://github.com/burningwave/tools/blob/master/LICENSE
48 | repo
49 |
50 |
51 |
52 |
53 | Burningwave
54 | https://www.burningwave.org/
55 |
56 |
57 |
58 |
59 | Roberto Gentili
60 | roberto.gentili
61 | info@burningwave.org
62 | Burningwave
63 | https://www.burningwave.org/
64 |
65 | Administrator
66 | Developer
67 |
68 |
69 |
70 | Alessio Perrotta
71 | info@burningwave.org
72 | Burningwave
73 | https://www.burningwave.org/
74 |
75 | External relationship manager
76 | Developer
77 |
78 |
79 |
80 |
81 |
82 | Roberto Gentili
83 | UTF-8
84 | 8
85 | bin/javadoc
86 | true
87 | **/AllExceptHeavyTestsSuite.java
88 | **/*Test.java
89 | bw
90 | https://burningwave@github.com/burningwave/tools.git
91 |
92 | 12.66.2
93 | 4.3.0
94 | 0.7.6.201602180812
95 | 5.10.0
96 | 1.10.0
97 | 3.8.1
98 | 2.8.2
99 | 1.6
100 | 3.2.0
101 | 3.2.0
102 | 2.5
103 | 2.5.3
104 | 3.0.1
105 | 1.9.5
106 | 2.21.0
107 | 1.6.12
108 |
109 |
110 |
111 | github.com
112 | https://github.com/burningwave/tools/issues
113 |
114 |
115 |
116 |
117 | ossrh
118 | https://oss.sonatype.org/content/repositories/snapshots
119 |
120 |
121 | ossrh
122 | https://oss.sonatype.org/service/local/staging/deploy/maven2/
123 |
124 |
125 |
126 |
127 | scm:git:${repository.url}
128 | scm:git:${repository.url}
129 | https://github.com/burningwave/tools
130 | tools-0.10.113
131 |
132 |
133 |
134 |
135 |
136 | org.burningwave
137 | core
138 | ${burningwave-core.version}
139 |
140 |
141 |
142 | org.junit.jupiter
143 | junit-jupiter-engine
144 | ${junit-jupiter.version}
145 | test
146 |
147 |
148 |
149 | org.junit.platform
150 | junit-platform-engine
151 | ${junit.version}
152 | test
153 |
154 |
155 |
156 | org.junit.platform
157 | junit-platform-commons
158 | ${junit.version}
159 | test
160 |
161 |
162 |
163 | org.junit.platform
164 | junit-platform-runner
165 | ${junit.version}
166 | test
167 |
168 |
169 |
170 |
171 |
172 | ${project.generated.artifacts.prefix}-${project.artifactId}-${project.version}
173 |
174 |
175 | ${project.basedir}
176 |
177 | **LICENSE
178 |
179 | META-INF
180 |
181 |
182 | ${project.basedir}/src/main/resources
183 |
184 | **
185 |
186 |
187 |
188 |
189 |
190 | ${project.basedir}/src/test/java
191 |
192 | **/*.java
193 |
194 |
195 |
196 | ${project.basedir}/src/test/resources
197 |
198 |
199 |
200 |
201 | org.apache.maven.plugins
202 | maven-compiler-plugin
203 | ${maven-compiler-plugin.version}
204 |
205 | ${project_jdk_version}
206 | ${project_jdk_version}
207 | true
208 |
209 |
210 |
211 | org.apache.maven.plugins
212 | maven-surefire-plugin
213 | ${maven-surefire-plugin.version}
214 |
215 | ${skipTests}
216 |
217 | ${project.test.excludes}
218 |
219 |
220 | ${project.test.includes}
221 |
222 |
223 |
224 |
225 | maven-jar-plugin
226 | ${maven-jar-plugin.version}
227 |
228 |
229 | jdk/
230 | META-INF/maven/
231 |
232 |
233 | false
234 |
235 | Burningwave
236 |
237 |
238 |
239 |
240 |
241 | maven-deploy-plugin
242 | ${maven-deploy-plugin.version}
243 |
244 |
245 | default-deploy
246 | deploy
247 |
248 | deploy
249 |
250 |
251 |
252 |
253 |
254 | org.apache.maven.plugins
255 | maven-release-plugin
256 | ${maven-release-plugin.version}
257 |
258 | true
259 | true
260 | forked-path
261 | -Dgpg.passphrase=${gpg.passphrase}
262 |
263 |
264 |
265 |
266 | org.apache.maven.scm
267 | maven-scm-provider-gitexe
268 | ${maven-scm-provider-gitexe.version}
269 |
270 |
271 |
272 |
273 | org.sonatype.plugins
274 | nexus-staging-maven-plugin
275 | ${nexus-staging-maven-plugin.version}
276 | true
277 |
278 | 20
279 | ossrh
280 | https://oss.sonatype.org/
281 | true
282 |
283 |
284 |
285 | org.apache.maven.plugins
286 | maven-source-plugin
287 | ${maven-source-plugin.version}
288 |
289 |
290 | org/burningwave/core/classes/ClassLoaderDelegate.bwc
291 |
292 |
293 |
294 |
295 | attach-sources
296 |
297 | jar
298 |
299 |
300 |
301 |
302 |
303 |
304 | org.apache.maven.plugins
305 | maven-javadoc-plugin
306 | ${maven-javadoc-plugin.version}
307 |
308 | ${java.home}/${javadocExecutable.relativePath}
309 | UTF-8
310 | ${project_jdk_version}
311 |
312 |
313 |
314 | attach-javadoc
315 |
316 | jar
317 |
318 |
319 |
320 |
321 |
322 |
323 |
324 |
325 |
326 |
327 | org.apache.maven.plugins
328 | maven-jxr-plugin
329 | ${maven-jxr-plugin.version}
330 |
331 |
332 |
333 |
334 |
335 |
336 |
337 | release-sign-artifacts
338 |
339 |
340 | performRelease
341 | true
342 |
343 |
344 |
345 |
346 |
347 | org.apache.maven.plugins
348 | maven-gpg-plugin
349 | ${maven-gpg-plugin.version}
350 |
351 |
352 | verify
353 |
354 | sign
355 |
356 |
357 |
358 | --pinentry-mode
359 | loopback
360 |
361 |
362 |
363 |
364 |
365 |
366 |
367 |
368 |
369 | run-coveralls
370 |
371 | false
372 |
373 |
374 |
375 |
376 |
377 | org.eluder.coveralls
378 | coveralls-maven-plugin
379 | ${coveralls-maven-plugin.version}
380 |
381 |
382 | ${project.basedir}/src/main/java
383 |
384 |
385 |
386 |
387 | org.jacoco
388 | jacoco-maven-plugin
389 | ${jacoco-maven-plugin.version}
390 |
391 |
392 | prepare-agent
393 |
394 | prepare-agent
395 |
396 |
397 |
398 |
399 |
400 |
401 |
402 |
403 |
--------------------------------------------------------------------------------
/src/main/java/org/burningwave/tools/dependencies/Capturer.java:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of Burningwave Tools.
3 | *
4 | * Author: Roberto Gentili
5 | *
6 | * Hosted at: https://github.com/burningwave/tools
7 | *
8 | * --
9 | *
10 | * The MIT License (MIT)
11 | *
12 | * Copyright (c) 2021 Roberto Gentili
13 | *
14 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
15 | * documentation files (the "Software"), to deal in the Software without restriction, including without
16 | * limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
17 | * the Software, and to permit persons to whom the Software is furnished to do so, subject to the following
18 | * conditions:
19 | *
20 | * The above copyright notice and this permission notice shall be included in all copies or substantial
21 | * portions of the Software.
22 | *
23 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
24 | * LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
25 | * EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
26 | * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
27 | * OR OTHER DEALINGS IN THE SOFTWARE.
28 | */
29 | package org.burningwave.tools.dependencies;
30 |
31 | import static org.burningwave.core.assembler.StaticComponentContainer.BackgroundExecutor;
32 | import static org.burningwave.core.assembler.StaticComponentContainer.Driver;
33 | import static org.burningwave.core.assembler.StaticComponentContainer.ManagedLoggerRepository;
34 | import static org.burningwave.core.assembler.StaticComponentContainer.Paths;
35 | import static org.burningwave.core.assembler.StaticComponentContainer.Streams;
36 |
37 | import java.io.File;
38 | import java.nio.ByteBuffer;
39 | import java.nio.file.Files;
40 | import java.util.Collection;
41 | import java.util.Set;
42 | import java.util.UUID;
43 | import java.util.concurrent.CopyOnWriteArrayList;
44 | import java.util.function.BiPredicate;
45 | import java.util.function.Function;
46 | import java.util.function.Predicate;
47 | import java.util.stream.Collectors;
48 |
49 | import org.burningwave.core.Component;
50 | import org.burningwave.core.assembler.ComponentContainer;
51 | import org.burningwave.core.assembler.ComponentSupplier;
52 | import org.burningwave.core.classes.ByteCodeHunter;
53 | import org.burningwave.core.classes.JavaClass;
54 | import org.burningwave.core.concurrent.QueuedTaskExecutor;
55 | import org.burningwave.core.concurrent.QueuedTaskExecutor.Task;
56 | import org.burningwave.core.function.TriConsumer;
57 | import org.burningwave.core.io.FileSystemItem;
58 |
59 | public class Capturer implements Component {
60 | protected static final String BURNINGWAVE_CLASSES_RELATIVE_DESTINATION_PATH = "[org.burningwave]";
61 | protected static final String TOOLFACTORY_CLASSES_RELATIVE_DESTINATION_PATH = "[io.github.toolfactory]";
62 | ByteCodeHunter byteCodeHunter;
63 |
64 | Capturer(ByteCodeHunter byteCodeHunter) {
65 | this.byteCodeHunter = byteCodeHunter;
66 | }
67 |
68 | public static Capturer create(ComponentSupplier componentSupplier) {
69 | return new Capturer(componentSupplier.getByteCodeHunter());
70 | }
71 |
72 | public static Capturer getInstance() {
73 | return Holder.getCapturerInstance();
74 | }
75 |
76 | public Result capture(String mainClassName, Collection baseClassPaths,
77 | TriConsumer resourceConsumer, boolean includeMainClass,
78 | Long continueToCaptureAfterSimulatorClassEndExecutionFor) {
79 | return capture(mainClassName, new String[0], baseClassPaths, resourceConsumer, includeMainClass,
80 | continueToCaptureAfterSimulatorClassEndExecutionFor);
81 | }
82 |
83 | @SuppressWarnings("resource")
84 | public Result capture(String mainClassName, String[] mainMethodAruments, Collection baseClassPaths,
85 | TriConsumer resourceConsumer, boolean includeMainClass,
86 | Long continueToCaptureAfterSimulatorClassEndExecutionFor) {
87 | final Result result = new Result();
88 | Function javaClassAdder = includeMainClass ? (javaClass) -> {
89 | result.put(javaClass);
90 | return true;
91 | } : (javaClass) -> {
92 | if (!javaClass.getName().equals(mainClassName)) {
93 | result.put(javaClass);
94 | return true;
95 | }
96 | return false;
97 | };
98 | result.findingTask = BackgroundExecutor.createTask(task -> {
99 | Class> cls;
100 | try (Sniffer resourceSniffer = new Sniffer(null).init(false, baseClassPaths, javaClassAdder,
101 | fileSystemItem -> {
102 | result.putResource(fileSystemItem);
103 | return true;
104 | }, resourceConsumer)) {
105 | try {
106 | cls = Class.forName(mainClassName, false, resourceSniffer);
107 | cls.getMethod("main", String[].class).invoke(null, (Object) mainMethodAruments);
108 | if (continueToCaptureAfterSimulatorClassEndExecutionFor != null
109 | && continueToCaptureAfterSimulatorClassEndExecutionFor > 0) {
110 | Thread.sleep(continueToCaptureAfterSimulatorClassEndExecutionFor);
111 | }
112 | } catch (Throwable exc) {
113 | Driver.throwException(exc);
114 | } finally {
115 | createExecutor(result.getStore().getAbsolutePath(), mainClassName, mainMethodAruments,
116 | UUID.randomUUID().toString());
117 | }
118 | }
119 | }).submit();
120 | return result;
121 | }
122 |
123 | public Result captureAndStore(String mainClassName, Collection baseClassPaths, String destinationPath,
124 | boolean includeMainClass, Long continueToCaptureAfterSimulatorClassEndExecutionFor) {
125 | return captureAndStore(mainClassName, new String[0], baseClassPaths, destinationPath, includeMainClass,
126 | continueToCaptureAfterSimulatorClassEndExecutionFor);
127 | }
128 |
129 | public Result captureAndStore(String mainClassName, String[] mainMethodAruments, Collection baseClassPaths,
130 | String destinationPath, boolean includeMainClass,
131 | Long continueToCaptureAfterSimulatorClassEndExecutionFor) {
132 | Result dependencies = capture(mainClassName, mainMethodAruments, baseClassPaths,
133 | getStoreFunction(destinationPath), includeMainClass,
134 | continueToCaptureAfterSimulatorClassEndExecutionFor);
135 | dependencies.store = FileSystemItem.ofPath(destinationPath);
136 | return dependencies;
137 | }
138 |
139 | TriConsumer getStoreFunction(String destinationPath) {
140 | // Exclude the runtime jdk library
141 | final String javaHome = Paths.clean(System.getProperty("java.home")) + "/";
142 | BiPredicate storePredicate = (resourceAbsolutePath,
143 | fileSystemItem) -> !resourceAbsolutePath.startsWith(javaHome) && !fileSystemItem.exists();
144 | return (resourceAbsolutePath, resourceRelativePath, resourceContent) -> {
145 | String finalPath;
146 | if (resourceRelativePath.startsWith("io/github/toolfactory")) {
147 | finalPath = destinationPath + "/" + TOOLFACTORY_CLASSES_RELATIVE_DESTINATION_PATH;
148 | } else if (resourceRelativePath.startsWith("org/burningwave")) {
149 | finalPath = destinationPath + "/" + BURNINGWAVE_CLASSES_RELATIVE_DESTINATION_PATH;
150 | } else {
151 | finalPath = getStoreEntryBasePath(destinationPath, resourceAbsolutePath, resourceRelativePath);
152 | }
153 | FileSystemItem fileSystemItem = FileSystemItem.ofPath(finalPath + "/" + resourceRelativePath);
154 | if (storePredicate.test(resourceAbsolutePath, fileSystemItem)) {
155 | Streams.store(fileSystemItem.getAbsolutePath(), resourceContent);
156 | ManagedLoggerRepository.logInfo(getClass()::getName, "Resource {} has been stored to class path {}",
157 | resourceRelativePath, fileSystemItem.getAbsolutePath());
158 | }
159 | };
160 | }
161 |
162 | String getStoreEntryBasePath(String storeBasePath, String itemAbsolutePath, String ItemRelativePath) {
163 | String finalPath = itemAbsolutePath;
164 | if (finalPath.chars().filter(ch -> ch == '/').count() > 1) {
165 | finalPath = finalPath.substring(0, finalPath.lastIndexOf(ItemRelativePath) - 1)
166 | .substring(finalPath.indexOf("/") + 1);
167 | finalPath = "[" + finalPath.replace("/", "][") + "]";
168 | } else {
169 | finalPath = finalPath.replace("/", "");
170 | }
171 | return storeBasePath + "/" + getReducedPath(finalPath);
172 | }
173 |
174 | private String getReducedPath(String path) {
175 | String temp = path.substring(0, path.lastIndexOf("["));
176 | temp = temp.substring(0, temp.lastIndexOf("["));
177 | return path.substring(temp.lastIndexOf("["));
178 | }
179 |
180 | void createExecutor(String destinationPath, String mainClassName, String[] mainMethodAruments,
181 | String executorSuffix) {
182 | if (System.getProperty("os.name").toLowerCase().contains("windows")) {
183 | createWindowsExecutor(destinationPath, mainClassName, mainMethodAruments, executorSuffix);
184 | } else {
185 | createUnixExecutor(destinationPath, mainClassName, mainMethodAruments, executorSuffix);
186 | }
187 | }
188 |
189 | void createWindowsExecutor(String destinationPath, String mainClassName, String[] mainMethodAruments,
190 | String executorSuffix) {
191 | try {
192 | Set classPathSet = FileSystemItem.ofPath(destinationPath).refresh()
193 | .findInChildren(FileSystemItem.Criteria.forAllFileThat(FileSystemItem::isFolder)).stream()
194 | .map(fileSystemItem -> fileSystemItem.getAbsolutePath().replace(destinationPath + "/", "%~dp0"))
195 | .collect(Collectors.toSet());
196 | String externalExecutorForWindows = "@echo off\n\""
197 | + FileSystemItem.ofPath(System.getProperty("java.home")).getAbsolutePath()
198 | + "/bin/java\" -classpath \"" + String.join(File.pathSeparator, classPathSet)
199 | + "\" " + mainClassName
200 | + (mainMethodAruments.length > 0
201 | ? " " + String.join(" ",
202 | toDoubleQuotedStringsForStringsThatContainEmptySpace(mainMethodAruments))
203 | : "");
204 | Files.write(java.nio.file.Paths.get(destinationPath + "/executor-" + executorSuffix + ".cmd"),
205 | externalExecutorForWindows.getBytes());
206 | } catch (Throwable exc) {
207 | ManagedLoggerRepository.logError(getClass()::getName, "Exception occurred", exc);
208 | }
209 | }
210 |
211 | void createUnixExecutor(String destinationPath, String mainClassName, String[] mainMethodAruments,
212 | String executorSuffix) {
213 | try {
214 | Set classPathSet = FileSystemItem.ofPath(destinationPath).refresh()
215 | .findInChildren(FileSystemItem.Criteria.forAllFileThat(FileSystemItem::isFolder)).stream()
216 | .map(fileSystemItem -> fileSystemItem.getAbsolutePath()).collect(Collectors.toSet());
217 | String externalExecutorForUnix = FileSystemItem.ofPath(System.getProperty("java.home")).getAbsolutePath()
218 | + "/bin/java -classpath " + String.join(File.pathSeparator, classPathSet) + " "
219 | + mainClassName
220 | + (mainMethodAruments.length > 0
221 | ? " " + String.join(" ",
222 | toDoubleQuotedStringsForStringsThatContainEmptySpace(mainMethodAruments))
223 | : "");
224 | Files.write(java.nio.file.Paths.get(destinationPath + "/executor-" + executorSuffix + ".sh"),
225 | externalExecutorForUnix.getBytes());
226 | } catch (Throwable exc) {
227 | ManagedLoggerRepository.logError(getClass()::getName, "Exception occurred", exc);
228 | }
229 | }
230 |
231 | String[] toDoubleQuotedStringsForStringsThatContainEmptySpace(String[] values) {
232 | String[] newValues = new String[values.length];
233 | for (int i = 0; i < values.length; i++) {
234 | newValues[i] = values[i].contains(" ") ? "\"" + values[i] + "\"" : values[i];
235 | }
236 | return newValues;
237 | }
238 |
239 | public static class Result implements Component {
240 | QueuedTaskExecutor.Task findingTask;
241 | Collection resources;
242 | Collection javaClasses;
243 | FileSystemItem store;
244 |
245 | Result() {
246 | this.javaClasses = new CopyOnWriteArrayList<>();
247 | this.resources = new CopyOnWriteArrayList<>();
248 | }
249 |
250 | public void putResource(FileSystemItem fileSystemItem) {
251 | resources.add(fileSystemItem);
252 | }
253 |
254 | JavaClass put(JavaClass javaClass) {
255 | javaClasses.add(javaClass);
256 | return javaClass;
257 | }
258 |
259 | public Collection getJavaClasses() {
260 | return javaClasses;
261 | }
262 |
263 | public Collection getResources() {
264 | return resources;
265 | }
266 |
267 | public JavaClass getJavaClass(Predicate predicate) {
268 | return getJavaClasses().stream().filter(predicate).findFirst().orElseGet(() -> null);
269 | }
270 |
271 | public Collection getResources(Predicate predicate) {
272 | return getResources().stream().filter(predicate).collect(Collectors.toSet());
273 | }
274 |
275 | public Task getFindingTask() {
276 | return this.findingTask;
277 | }
278 |
279 | public void waitForTaskEnding() {
280 | findingTask.waitForFinish();
281 | }
282 |
283 | public FileSystemItem getStore() {
284 | return store;
285 | }
286 |
287 | @Override
288 | public void close() {
289 | findingTask.abort();
290 | findingTask = null;
291 | resources.clear();
292 | resources = null;
293 | javaClasses.clear();
294 | javaClasses = null;
295 | store = null;
296 | }
297 | }
298 |
299 | static class Holder {
300 | static final Capturer CAPTURER_INSTANCE = Capturer.create(ComponentContainer.getInstance());
301 |
302 | static Capturer getCapturerInstance() {
303 | return CAPTURER_INSTANCE;
304 | }
305 | }
306 | }
307 |
--------------------------------------------------------------------------------
/src/main/java/org/burningwave/tools/dependencies/Sniffer.java:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of Burningwave Tools.
3 | *
4 | * Author: Roberto Gentili
5 | *
6 | * Hosted at: https://github.com/burningwave/tools
7 | *
8 | * --
9 | *
10 | * The MIT License (MIT)
11 | *
12 | * Copyright (c) 2021 Roberto Gentili
13 | *
14 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
15 | * documentation files (the "Software"), to deal in the Software without restriction, including without
16 | * limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
17 | * the Software, and to permit persons to whom the Software is furnished to do so, subject to the following
18 | * conditions:
19 | *
20 | * The above copyright notice and this permission notice shall be included in all copies or substantial
21 | * portions of the Software.
22 | *
23 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
24 | * LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
25 | * EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
26 | * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
27 | * OR OTHER DEALINGS IN THE SOFTWARE.
28 | */
29 | package org.burningwave.tools.dependencies;
30 |
31 | import static org.burningwave.core.assembler.StaticComponentContainer.BackgroundExecutor;
32 | import static org.burningwave.core.assembler.StaticComponentContainer.ClassLoaders;
33 | import static org.burningwave.core.assembler.StaticComponentContainer.Classes;
34 | import static org.burningwave.core.assembler.StaticComponentContainer.ManagedLoggerRepository;
35 | import static org.burningwave.core.assembler.StaticComponentContainer.Strings;
36 |
37 | import java.io.IOException;
38 | import java.io.InputStream;
39 | import java.net.URL;
40 | import java.nio.ByteBuffer;
41 | import java.util.Arrays;
42 | import java.util.Collection;
43 | import java.util.Collections;
44 | import java.util.Enumeration;
45 | import java.util.LinkedHashSet;
46 | import java.util.Map;
47 | import java.util.Set;
48 | import java.util.concurrent.ConcurrentHashMap;
49 | import java.util.function.Function;
50 | import java.util.stream.Collectors;
51 |
52 | import org.burningwave.core.classes.JavaClass;
53 | import org.burningwave.core.classes.MemoryClassLoader;
54 | import org.burningwave.core.concurrent.QueuedTaskExecutor.Task;
55 | import org.burningwave.core.function.ThrowingBiFunction;
56 | import org.burningwave.core.function.TriConsumer;
57 | import org.burningwave.core.io.FileSystemItem;
58 |
59 | public class Sniffer extends MemoryClassLoader {
60 | private Function javaClassFilterAndAdder;
61 | private Function resourceFilterAndAdder;
62 | private Map resources;
63 | // In this map the key is the absolute path
64 | private Map javaClasses;
65 | // In this map the key is the class name
66 | private Map bwJavaClasses;
67 | private TriConsumer resourcesConsumer;
68 | ClassLoader threadContextClassLoader;
69 | Function masterClassLoaderRetrieverAndResetter;
70 | ThrowingBiFunction, ClassNotFoundException> classLoadingFunction;
71 | private Collection tasksInExecution;
72 |
73 | public Sniffer(ClassLoader parent) {
74 | super(parent);
75 | }
76 |
77 | static {
78 | ClassLoader.registerAsParallelCapable();
79 | }
80 |
81 | protected Sniffer init(boolean useAsMasterClassLoader, Collection baseClassPaths,
82 | Function javaClassAdder, Function resourceAdder,
83 | TriConsumer resourcesConsumer) {
84 | this.threadContextClassLoader = Thread.currentThread().getContextClassLoader();
85 | this.javaClassFilterAndAdder = javaClassAdder;
86 | this.resourceFilterAndAdder = resourceAdder;
87 | this.resourcesConsumer = resourcesConsumer;
88 | this.tasksInExecution = ConcurrentHashMap.newKeySet();
89 | initResourceLoader(baseClassPaths);
90 | if (useAsMasterClassLoader) {
91 | // Load in cache defineClass and definePackage methods for
92 | // threadContextClassLoader
93 | ClassLoaders.getDefineClassMethod(threadContextClassLoader);
94 | ClassLoaders.getDefinePackageMethod(threadContextClassLoader);
95 | classLoadingFunction = (className, resolve) -> {
96 | if ((!className.startsWith("org.burningwave.") && !className.startsWith("io.github.toolfactory."))) {
97 | return super.loadClass(className, resolve);
98 | } else {
99 | JavaClass javaClass = bwJavaClasses.get(className);
100 | try {
101 | return ClassLoaders.defineOrLoad(threadContextClassLoader, javaClass);
102 | } catch (NoClassDefFoundError | ReflectiveOperationException exc) {
103 | throw new ClassNotFoundException(Classes.retrieveName(exc));
104 | } catch (NullPointerException exc) {
105 | if (javaClass == null) {
106 | throw new ClassNotFoundException(className);
107 | }
108 | throw exc;
109 | }
110 | }
111 | };
112 | masterClassLoaderRetrieverAndResetter = ClassLoaders.setAsParent(threadContextClassLoader, this);
113 | } else {
114 | classLoadingFunction = (clsName, resolveFlag) -> {
115 | return super.loadClass(clsName, resolveFlag);
116 | };
117 | Thread.currentThread().setContextClassLoader(this);
118 | }
119 |
120 | return this;
121 | }
122 |
123 | @Override
124 | public synchronized void addByteCode(String className, ByteBuffer byteCode) {
125 | super.addByteCode(className, byteCode);
126 | }
127 |
128 | private void initResourceLoader(Collection baseClassPaths) {
129 | this.resources = new ConcurrentHashMap<>();
130 | this.javaClasses = new ConcurrentHashMap<>();
131 | this.bwJavaClasses = new ConcurrentHashMap<>();
132 | ManagedLoggerRepository.logInfo(getClass()::getName, "Scanning paths :\n{}",
133 | String.join("\n", baseClassPaths));
134 | for (String classPath : baseClassPaths) {
135 | FileSystemItem.ofPath(classPath).refresh()
136 | .findInAllChildren(FileSystemItem.Criteria.forAllFileThat((fileSystemItem) -> {
137 | String absolutePath = fileSystemItem.getAbsolutePath();
138 | resources.put(absolutePath, classPath);
139 | JavaClass javaClass = fileSystemItem.toJavaClass();
140 | if (javaClass != null) {
141 | addByteCode(javaClass.getName(), javaClass.getByteCode());
142 | javaClasses.put(absolutePath, javaClass);
143 | if (javaClass.getName().startsWith("org.burningwave.")
144 | || javaClass.getName().startsWith("io.github.toolfactory.")) {
145 | bwJavaClasses.put(javaClass.getName(), javaClass);
146 | }
147 | }
148 | return true;
149 | }));
150 | }
151 | }
152 |
153 | protected void consumeClass(String className) {
154 | consumeClasses(Arrays.asList(className));
155 | }
156 |
157 | public void consumeClasses(Collection currentNotFoundClasses) {
158 | for (Map.Entry entry : javaClasses.entrySet()) {
159 | if (currentNotFoundClasses.contains(entry.getValue().getName())) {
160 | JavaClass javaClass = entry.getValue();
161 | if (javaClassFilterAndAdder.apply(javaClass)) {
162 | Task tsk = BackgroundExecutor.createTask(task -> {
163 | try {
164 | resourcesConsumer.accept(entry.getKey(), javaClass.getPath(), javaClass.getByteCode());
165 | } catch (Throwable exc) {
166 | try {
167 | FileSystemItem classPath = FileSystemItem.ofPath(resources.get(entry.getKey()));
168 | FileSystemItem javaClassFIS = FileSystemItem.ofPath(entry.getKey());
169 | String itemRelativePath = javaClassFIS.getAbsolutePath().substring(classPath.getAbsolutePath().length() + 1);
170 | resourcesConsumer.accept(entry.getKey(), itemRelativePath, javaClass.getByteCode());
171 | } catch (Throwable exception) {
172 | throw exc;
173 | }
174 | }
175 | tasksInExecution.remove(task);
176 | });
177 | tasksInExecution.add(tsk);
178 | tsk.submit();
179 | }
180 | }
181 | }
182 | }
183 |
184 | protected Collection consumeResource(String relativePath, boolean breakWhenFound) {
185 | Set founds = new LinkedHashSet<>();
186 | if (Strings.isNotEmpty(relativePath)) {
187 | for (Map.Entry entry : resources.entrySet()) {
188 | if (entry.getKey().endsWith(relativePath)) {
189 | FileSystemItem fileSystemItem = FileSystemItem.ofPath(entry.getKey());
190 | founds.add(fileSystemItem);
191 | if (resourceFilterAndAdder.apply(fileSystemItem)) {
192 | Task tsk = BackgroundExecutor.createTask(task -> {
193 | resourcesConsumer.accept(entry.getKey(), relativePath, fileSystemItem.toByteBuffer());
194 | tasksInExecution.remove(task);
195 | });
196 | tasksInExecution.add(tsk);
197 | tsk.submit();
198 | }
199 | if (breakWhenFound) {
200 | break;
201 | }
202 | }
203 | }
204 | }
205 | return founds;
206 | }
207 |
208 | @Override
209 | public void addLoadedByteCode(String className, ByteBuffer byteCode) {
210 | super.addLoadedByteCode(className, byteCode);
211 | consumeClass(className);
212 | }
213 |
214 | @Override
215 | protected Class> loadClass(String className, boolean resolve) throws ClassNotFoundException {
216 | Class> cls = classLoadingFunction.apply(className, resolve);
217 | consumeClass(className);
218 | return cls;
219 | }
220 |
221 | public Class> _loadClass(String className, boolean resolve) throws ClassNotFoundException {
222 | Class> cls = classLoadingFunction.apply(className, resolve);
223 | consumeClass(className);
224 | return cls;
225 | }
226 |
227 | @Override
228 | public URL getResource(String name) {
229 | Enumeration urls = getResources(name, true);
230 | if (urls.hasMoreElements()) {
231 | return urls.nextElement();
232 | }
233 | return null;
234 | }
235 |
236 | @Override
237 | public Enumeration getResources(String name) throws IOException {
238 | return getResources(name, false);
239 | }
240 |
241 | private Enumeration getResources(String name, boolean findFirst) {
242 | return Collections.enumeration(consumeResource(name, findFirst).stream().map(fileSystemItem -> {
243 | resourceFilterAndAdder.apply(fileSystemItem);
244 | return fileSystemItem.getURL();
245 | }).collect(Collectors.toSet()));
246 | }
247 |
248 | @Override
249 | public InputStream getResourceAsStream(String name) {
250 | FileSystemItem fileSystemItem = consumeResource(name, true).stream().findFirst().orElseGet(() -> null);
251 | if (fileSystemItem != null) {
252 | return fileSystemItem.toInputStream();
253 | } else {
254 | return getByteCodeAsInputStream(name);
255 | }
256 | }
257 |
258 | @Override
259 | public void close() {
260 | closeResources(() -> tasksInExecution == null, task -> {
261 | if (!tasksInExecution.isEmpty()) {
262 | tasksInExecution.stream().forEach(Task::waitForFinish);
263 | tasksInExecution = null;
264 | }
265 | if (threadContextClassLoader != null) {
266 | Thread.currentThread().setContextClassLoader(threadContextClassLoader);
267 | }
268 | if (masterClassLoaderRetrieverAndResetter != null) {
269 | masterClassLoaderRetrieverAndResetter.apply(true);
270 | masterClassLoaderRetrieverAndResetter = null;
271 | }
272 | resources.clear();
273 | // Nulling resources will cause crash
274 | // resources = null;
275 | javaClasses.clear();
276 | // Nulling javaClasses will cause crash
277 | // javaClasses = null;
278 | // javaClassFilterAndAdder = null;
279 | // resourceFilterAndAdder = null;
280 | // threadContextClassLoader = null;
281 | // classLoadingFunction = null;
282 | // clear();
283 | unregister();
284 | });
285 | }
286 | }
287 |
--------------------------------------------------------------------------------
/src/main/java/org/burningwave/tools/dependencies/TwoPassCapturer.java:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of Burningwave Tools.
3 | *
4 | * Author: Roberto Gentili
5 | *
6 | * Hosted at: https://github.com/burningwave/tools
7 | *
8 | * --
9 | *
10 | * The MIT License (MIT)
11 | *
12 | * Copyright (c) 2021 Roberto Gentili
13 | *
14 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
15 | * documentation files (the "Software"), to deal in the Software without restriction, including without
16 | * limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
17 | * the Software, and to permit persons to whom the Software is furnished to do so, subject to the following
18 | * conditions:
19 | *
20 | * The above copyright notice and this permission notice shall be included in all copies or substantial
21 | * portions of the Software.
22 | *
23 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
24 | * LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
25 | * EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
26 | * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
27 | * OR OTHER DEALINGS IN THE SOFTWARE.
28 | */
29 | package org.burningwave.tools.dependencies;
30 |
31 | import static org.burningwave.core.assembler.StaticComponentContainer.BackgroundExecutor;
32 | import static org.burningwave.core.assembler.StaticComponentContainer.Driver;
33 | import static org.burningwave.core.assembler.StaticComponentContainer.FileSystemHelper;
34 | import static org.burningwave.core.assembler.StaticComponentContainer.ManagedLoggerRepository;
35 | import static org.burningwave.core.assembler.StaticComponentContainer.Paths;
36 |
37 | import java.io.File;
38 | import java.io.IOException;
39 | import java.nio.ByteBuffer;
40 | import java.nio.file.Files;
41 | import java.util.AbstractMap;
42 | import java.util.Arrays;
43 | import java.util.Collection;
44 | import java.util.Iterator;
45 | import java.util.LinkedHashSet;
46 | import java.util.LinkedList;
47 | import java.util.List;
48 | import java.util.Map;
49 | import java.util.Set;
50 | import java.util.TreeSet;
51 | import java.util.UUID;
52 | import java.util.concurrent.ConcurrentHashMap;
53 | import java.util.function.Function;
54 | import java.util.stream.Collectors;
55 |
56 | import org.burningwave.core.assembler.ComponentContainer;
57 | import org.burningwave.core.assembler.ComponentSupplier;
58 | import org.burningwave.core.classes.ByteCodeHunter;
59 | import org.burningwave.core.classes.ClassCriteria;
60 | import org.burningwave.core.classes.ClassPathHunter;
61 | import org.burningwave.core.classes.JavaClass;
62 | import org.burningwave.core.classes.SearchConfig;
63 | import org.burningwave.core.function.ThrowingSupplier;
64 | import org.burningwave.core.function.TriConsumer;
65 | import org.burningwave.core.io.FileSystemItem;
66 | import org.burningwave.core.io.PathHelper;
67 |
68 | public class TwoPassCapturer extends Capturer {
69 | ClassPathHunter classPathHunter;
70 | PathHelper pathHelper;
71 |
72 | private TwoPassCapturer(PathHelper pathHelper, ByteCodeHunter byteCodeHunter, ClassPathHunter classPathHunter) {
73 | super(byteCodeHunter);
74 | this.pathHelper = pathHelper;
75 | this.classPathHunter = classPathHunter;
76 | }
77 |
78 | public static TwoPassCapturer create(ComponentSupplier componentSupplier) {
79 | return new TwoPassCapturer(componentSupplier.getPathHelper(), componentSupplier.getByteCodeHunter(),
80 | componentSupplier.getClassPathHunter());
81 | }
82 |
83 | public static TwoPassCapturer getInstance() {
84 | return Holder.getCapturerInstance();
85 | }
86 |
87 | @Override
88 | public Result capture(String mainClassName, String[] mainMethodAruments, Collection baseClassPaths,
89 | TriConsumer resourceConsumer, boolean includeMainClass,
90 | Long continueToCaptureAfterSimulatorClassEndExecutionFor) {
91 | return capture(mainClassName, mainMethodAruments, baseClassPaths, resourceConsumer, includeMainClass,
92 | continueToCaptureAfterSimulatorClassEndExecutionFor, true);
93 | }
94 |
95 | private Result capture(String mainClassName, String[] mainMethodAruments, Collection baseClassPaths,
96 | TriConsumer resourceConsumer, boolean includeMainClass,
97 | Long continueToCaptureAfterSimulatorClassEndExecutionFor, boolean recursive) {
98 | final Result result = new Result(javaClass -> true, fileSystemItem -> true);
99 | result.findingTask = BackgroundExecutor.createTask(task -> {
100 | try (Sniffer resourceSniffer = new Sniffer(null).init(!recursive, baseClassPaths, result.javaClassFilter,
101 | result.resourceFilter, resourceConsumer)) {
102 | ThrowingSupplier, ClassNotFoundException> mainClassSupplier = recursive
103 | ? () -> Class.forName(mainClassName, false, resourceSniffer)
104 | : () -> Class.forName(mainClassName);
105 | try {
106 | mainClassSupplier.get().getMethod("main", String[].class).invoke(null, (Object) mainMethodAruments);
107 | if (continueToCaptureAfterSimulatorClassEndExecutionFor != null
108 | && continueToCaptureAfterSimulatorClassEndExecutionFor > 0) {
109 | Thread.sleep(continueToCaptureAfterSimulatorClassEndExecutionFor);
110 | }
111 | } catch (Throwable exc) {
112 | ManagedLoggerRepository.logError(getClass()::getName, "Exception occurred", exc);
113 | Driver.throwException(exc);
114 | }
115 | }
116 | if (recursive) {
117 | try {
118 | launchExternalCapturer(mainClassName, mainMethodAruments, result.getStore().getAbsolutePath(),
119 | baseClassPaths, includeMainClass, continueToCaptureAfterSimulatorClassEndExecutionFor);
120 | } catch (IOException | InterruptedException exc) {
121 | Driver.throwException(exc);
122 | }
123 | }
124 | if (recursive && !includeMainClass) {
125 | JavaClass mainJavaClass = result.getJavaClass(javaClass -> javaClass.getName().equals(mainClassName));
126 | Collection mainJavaClassesFiles = result.getResources(
127 | fileSystemItem -> fileSystemItem.getAbsolutePath().endsWith(mainJavaClass.getPath()));
128 | FileSystemItem store = result.getStore();
129 | for (FileSystemItem fileSystemItem : mainJavaClassesFiles) {
130 | FileSystemHelper.delete(fileSystemItem.getAbsolutePath());
131 | fileSystemItem = fileSystemItem.getParent();
132 | while (fileSystemItem != null
133 | && !fileSystemItem.getAbsolutePath().equals(store.getAbsolutePath())) {
134 | if (fileSystemItem.getChildren().isEmpty()) {
135 | FileSystemHelper.delete(fileSystemItem.getAbsolutePath());
136 | } else {
137 | break;
138 | }
139 | fileSystemItem = fileSystemItem.getParent();
140 | }
141 | }
142 | }
143 | }).submit();
144 | return result;
145 | }
146 |
147 | private Result captureAndStore(String mainClassName, String[] mainMethodAruments, Collection baseClassPaths,
148 | String destinationPath, boolean includeMainClass, Long continueToCaptureAfterSimulatorClassEndExecutionFor,
149 | boolean recursive) {
150 | Result dependencies = capture(mainClassName, mainMethodAruments, baseClassPaths,
151 | getStoreFunction(destinationPath), includeMainClass,
152 | continueToCaptureAfterSimulatorClassEndExecutionFor, recursive);
153 | dependencies.store = FileSystemItem.ofPath(destinationPath);
154 | return dependencies;
155 | }
156 |
157 | private void launchExternalCapturer(String mainClassName, String[] mainMethodAruments, String destinationPath,
158 | Collection baseClassPaths, boolean includeMainClass,
159 | Long continueToCaptureAfterSimulatorClassEndExecutionFor) throws IOException, InterruptedException {
160 | // Excluding Burningwave from next process classpath
161 | Set classPaths = FileSystemItem.ofPath(destinationPath).refresh()
162 | .findInChildren(FileSystemItem.Criteria.forAllFileThat(fileSystemItem -> !fileSystemItem
163 | .getAbsolutePath().endsWith(BURNINGWAVE_CLASSES_RELATIVE_DESTINATION_PATH)
164 | && !fileSystemItem.getAbsolutePath().endsWith(TOOLFACTORY_CLASSES_RELATIVE_DESTINATION_PATH)))
165 | .stream().map(child -> child.getAbsolutePath()).collect(Collectors.toSet());
166 |
167 | // Adding Burningwave to next process scanning path
168 | ClassPathHunter.SearchResult searchResult = classPathHunter
169 | .findBy(SearchConfig.forPaths(pathHelper.getMainClassPaths())
170 | .by(ClassCriteria.create()
171 | .packageName(packageName -> packageName.startsWith("io.github.toolfactory")
172 | || packageName.startsWith("org.burningwave.jvm")
173 | || packageName.startsWith("org.burningwave.core")
174 | || packageName.startsWith("org.burningwave.tools"))));
175 | Collection classPathsToBeScanned = new LinkedHashSet<>(baseClassPaths);
176 | classPathsToBeScanned.remove(destinationPath);
177 | Iterator classPathIterator = searchResult.getClassPaths().iterator();
178 | while (classPathIterator.hasNext()) {
179 | FileSystemItem classPath = classPathIterator.next();
180 | if (!classPaths.contains(classPath.getAbsolutePath())) {
181 | classPaths.add(classPath.getAbsolutePath());
182 | // classPathsToBeScanned.remove(classPath.getAbsolutePath());
183 | }
184 | }
185 | ProcessBuilder processBuilder = System.getProperty("os.name").toLowerCase().contains("windows")
186 | ? getProcessBuilderForWindows(classPaths, classPathsToBeScanned, mainClassName, mainMethodAruments,
187 | destinationPath, includeMainClass, continueToCaptureAfterSimulatorClassEndExecutionFor)
188 | : getProcessBuilderForUnix(classPaths, classPathsToBeScanned, mainClassName, mainMethodAruments,
189 | destinationPath, includeMainClass, continueToCaptureAfterSimulatorClassEndExecutionFor);
190 | Process process = processBuilder.start();
191 | process.waitFor();
192 | }
193 |
194 | private ProcessBuilder getProcessBuilderForWindows(Collection classPaths,
195 | Collection classPathsToBeScanned, String mainClassName, String[] mainMethodAruments,
196 | String destinationPath, boolean includeMainClass, Long continueToCaptureAfterSimulatorClassEndExecutionFor)
197 | throws IOException {
198 | String javaExecutablePath = System.getProperty("java.home") + "/bin/java";
199 | List command = new LinkedList<>();
200 | command.add(Paths.clean(javaExecutablePath));
201 | command.add("-classpath");
202 | StringBuffer generatedClassPath = new StringBuffer();
203 | generatedClassPath.append("\"");
204 | if (!classPaths.isEmpty()) {
205 | generatedClassPath.append(String.join(File.pathSeparator, classPaths));
206 | }
207 | generatedClassPath.append("\"");
208 | command.add(generatedClassPath.toString());
209 | command.add(InternalLauncher.class.getName());
210 | command.add("\"" + String.join(File.pathSeparator, classPathsToBeScanned) + "\"");
211 | command.add(mainClassName);
212 | command.add("\"" + destinationPath + "\"");
213 | command.add(Boolean.valueOf(includeMainClass).toString());
214 | command.add(continueToCaptureAfterSimulatorClassEndExecutionFor.toString());
215 | command.addAll(Arrays.asList(mainMethodAruments));
216 | ProcessBuilder processBuilder = new ProcessBuilder(command);
217 |
218 | return processBuilder.inheritIO();
219 | }
220 |
221 | private ProcessBuilder getProcessBuilderForUnix(Collection classPaths,
222 | Collection classPathsToBeScanned, String mainClassName, String[] mainMethodAruments,
223 | String destinationPath, boolean includeMainClass, Long continueToCaptureAfterSimulatorClassEndExecutionFor)
224 | throws IOException {
225 | List command = new LinkedList<>();
226 | String javaExecutablePath = Paths.clean(System.getProperty("java.home") + "/bin/java");
227 | command.add(javaExecutablePath);
228 | command.add("-classpath");
229 | StringBuffer generatedClassPath = new StringBuffer();
230 | if (!classPaths.isEmpty()) {
231 | generatedClassPath.append(String.join(File.pathSeparator, classPaths));
232 | }
233 | command.add(generatedClassPath.toString());
234 | command.add(InternalLauncher.class.getName());
235 | command.add(String.join(File.pathSeparator, classPathsToBeScanned));
236 | command.add(mainClassName);
237 | command.add(destinationPath);
238 | command.add(Boolean.valueOf(includeMainClass).toString());
239 | command.add(continueToCaptureAfterSimulatorClassEndExecutionFor.toString());
240 | command.addAll(Arrays.asList(mainMethodAruments));
241 | ProcessBuilder processBuilder = new ProcessBuilder(command);
242 |
243 | return processBuilder.inheritIO();
244 | }
245 |
246 | private void logReceivedParameters(String[] args, long wait, String fileSuffix) {
247 | try {
248 |
249 | String logs = String.join("\n\n", "classpath:\n\t" + String.join("\n\t",
250 | new TreeSet<>(Arrays.asList(
251 | System.getProperty("java.class.path").split(File.pathSeparator)))),
252 | "path to be scanned:\n\t" + String.join(File.pathSeparator + "\n\t",
253 | new TreeSet<>(Arrays.asList(args[0].split(File.pathSeparator)))),
254 | "mainClassName: " + args[1], "destinationPath: " + args[2], "includeMainClass: " + args[3],
255 | "continueToCaptureAfterSimulatorClassEndExecutionFor: " + args[4],
256 | (args.length > 5 ? "arguments: " + String.join(", ", Arrays.copyOfRange(args, 5, args.length))
257 | : ""));
258 |
259 | Files.write(java.nio.file.Paths.get(args[2] + "/params-" + fileSuffix + ".txt"), logs.getBytes());
260 | ManagedLoggerRepository.logDebug(() -> this.getClass().getName(), "\n\n" + logs + "\n\n");
261 | if (wait > 0) {
262 | Thread.sleep(wait);
263 | }
264 | } catch (Throwable e) {
265 | e.printStackTrace();
266 | }
267 | }
268 |
269 | private static class InternalLauncher {
270 |
271 | @SuppressWarnings("unused")
272 | public static void main(String[] args) {
273 | String[] mainMethodArguments = args.length > 5 ? Arrays.copyOfRange(args, 5, args.length) : new String[0];
274 | TwoPassCapturer capturer = TwoPassCapturer.getInstance();
275 | try {
276 | Collection paths = Arrays.asList(args[0].split(File.pathSeparator));
277 | String mainClassName = args[1];
278 | String destinationPath = args[2];
279 | boolean includeMainClass = Boolean.valueOf(args[3]);
280 | long continueToCaptureAfterSimulatorClassEndExecutionFor = Long.valueOf(args[4]);
281 | capturer.captureAndStore(mainClassName, mainMethodArguments, paths, destinationPath, includeMainClass,
282 | continueToCaptureAfterSimulatorClassEndExecutionFor, false).waitForTaskEnding();
283 | } catch (Throwable exc) {
284 | ManagedLoggerRepository.logError(() -> TwoPassCapturer.class.getName(), "Exception occurred", exc);
285 | } finally {
286 | String suffix = UUID.randomUUID().toString();
287 | capturer.logReceivedParameters(args, 0, suffix);
288 | capturer.createExecutor(args[2], args[1], mainMethodArguments, suffix);
289 | }
290 | }
291 | }
292 |
293 | private static class Result extends Capturer.Result {
294 | Function javaClassFilter;
295 | Function resourceFilter;
296 |
297 | Result(Function javaClassFilter, Function resourceFilter) {
298 | this.javaClassFilter = javaClassFilter;
299 | this.resourceFilter = resourceFilter;
300 | this.javaClasses = null;
301 | this.resources = null;
302 | }
303 |
304 | @Override
305 | public Collection getJavaClasses() {
306 | if (javaClasses != null) {
307 | return javaClasses;
308 | } else {
309 | return loadResourcesAndJavaClasses().getValue();
310 | }
311 | }
312 |
313 | @Override
314 | public Collection getResources() {
315 | if (resources != null) {
316 | return resources;
317 | } else {
318 | return loadResourcesAndJavaClasses().getKey();
319 | }
320 | }
321 |
322 | public Map.Entry, Collection> loadResourcesAndJavaClasses() {
323 | Map.Entry, Collection> itemsFound = null;
324 | if (this.findingTask.hasFinished()) {
325 | if (this.resources == null) {
326 | synchronized (this) {
327 | if (this.resources == null) {
328 | itemsFound = retrieveResources();
329 | this.resources = itemsFound.getKey();
330 | this.javaClasses = itemsFound.getValue();
331 | return itemsFound;
332 | }
333 | }
334 | }
335 | }
336 | return retrieveResources();
337 | }
338 |
339 | private Map.Entry, Collection> retrieveResources() {
340 | Collection resources = ConcurrentHashMap.newKeySet();
341 | Collection javaClasses = ConcurrentHashMap.newKeySet();
342 | Map.Entry, Collection> itemsFound = new AbstractMap.SimpleEntry<>(
343 | resources, javaClasses);
344 | for (FileSystemItem fileSystemItem : store.refresh().findInAllChildren(
345 | FileSystemItem.Criteria.forAllFileThat(fileSystemItem -> resourceFilter.apply(fileSystemItem)))) {
346 | resources.add(fileSystemItem);
347 | if ("class".equals(fileSystemItem.getExtension())) {
348 | javaClasses.add(JavaClass.create(fileSystemItem.toByteBuffer()));
349 | }
350 | }
351 | return itemsFound;
352 | }
353 | }
354 |
355 | private static class Holder {
356 | private static final TwoPassCapturer CAPTURER_INSTANCE = TwoPassCapturer
357 | .create(ComponentContainer.getInstance());
358 |
359 | private static TwoPassCapturer getCapturerInstance() {
360 | return CAPTURER_INSTANCE;
361 | }
362 | }
363 | }
364 |
--------------------------------------------------------------------------------
/src/main/java/org/burningwave/tools/net/DNSClientHostResolver.java:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of Burningwave Tools.
3 | *
4 | * Author: Roberto Gentili
5 | *
6 | * Hosted at: https://github.com/burningwave/tools
7 | *
8 | * --
9 | *
10 | * The MIT License (MIT)
11 | *
12 | * Copyright (c) 2021 Roberto Gentili
13 | *
14 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
15 | * documentation files (the "Software"), to deal in the Software without restriction, including without
16 | * limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
17 | * the Software, and to permit persons to whom the Software is furnished to do so, subject to the following
18 | * conditions:
19 | *
20 | * The above copyright notice and this permission notice shall be included in all copies or substantial
21 | * portions of the Software.
22 | *
23 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
24 | * LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
25 | * EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
26 | * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
27 | * OR OTHER DEALINGS IN THE SOFTWARE.
28 | */
29 | package org.burningwave.tools.net;
30 |
31 | import static org.burningwave.core.assembler.StaticComponentContainer.Driver;
32 | import static org.burningwave.core.assembler.StaticComponentContainer.Fields;
33 | import static org.burningwave.core.assembler.StaticComponentContainer.Strings;
34 |
35 | import java.io.ByteArrayInputStream;
36 | import java.io.ByteArrayOutputStream;
37 | import java.io.DataInputStream;
38 | import java.io.DataOutputStream;
39 | import java.io.IOException;
40 | import java.io.InputStream;
41 | import java.net.DatagramPacket;
42 | import java.net.DatagramSocket;
43 | import java.net.InetAddress;
44 | import java.net.UnknownHostException;
45 | import java.nio.ByteBuffer;
46 | import java.nio.charset.StandardCharsets;
47 | import java.util.ArrayList;
48 | import java.util.Arrays;
49 | import java.util.Collection;
50 | import java.util.LinkedHashMap;
51 | import java.util.List;
52 | import java.util.Map;
53 | import java.util.Map.Entry;
54 | import java.util.Random;
55 | import java.util.function.Supplier;
56 |
57 | import org.burningwave.core.function.ThrowingBiFunction;
58 |
59 |
60 | @SuppressWarnings("unchecked")
61 | public class DNSClientHostResolver implements HostResolver {
62 | public final static int DEFAULT_PORT;
63 |
64 | private static final String IPV6_DOMAIN;
65 | private static final String IPV4_DOMAIN;
66 | private static final short RECORD_TYPE_A;
67 | private static final short RECORD_TYPE_PTR;
68 | private static final short RECORD_TYPE_AAAA;
69 |
70 | public static final ThrowingBiFunction IPV4_RETRIEVER;
71 | public static final ThrowingBiFunction IPV6_RETRIEVER;
72 |
73 | static {
74 | DEFAULT_PORT = 53;
75 | IPV6_DOMAIN = "ip6.arpa.";
76 | IPV4_DOMAIN = "in-addr.arpa.";
77 | RECORD_TYPE_A = 1;
78 | RECORD_TYPE_PTR = 12;
79 | RECORD_TYPE_AAAA = 28;
80 | IPV4_RETRIEVER = (dNSServerHostResolver, hostName) ->
81 | dNSServerHostResolver.sendRequest(hostName, RECORD_TYPE_A);
82 | IPV6_RETRIEVER = (dNSServerHostResolver, hostName) ->
83 | dNSServerHostResolver.sendRequest(hostName, RECORD_TYPE_AAAA);
84 | }
85 |
86 | private ThrowingBiFunction[] resolveHostForNameRequestSenders;
87 |
88 | private static Random requestIdGenerator;
89 |
90 | static {
91 | requestIdGenerator = new Random();
92 | }
93 |
94 | private InetAddress dNSServerIP;
95 | private int dNSServerPort;
96 |
97 | public DNSClientHostResolver(String dNSServerIP) {
98 | this(dNSServerIP, DEFAULT_PORT, IPV4_RETRIEVER, IPV6_RETRIEVER);
99 | }
100 |
101 | public DNSClientHostResolver(String dNSServerIP, int dNSServerPort) {
102 | this(dNSServerIP, dNSServerPort, IPV4_RETRIEVER, IPV6_RETRIEVER);
103 | }
104 |
105 | public DNSClientHostResolver(String dNSServerIP, ThrowingBiFunction... resolveHostForNameRequestSenders) {
106 | this(dNSServerIP, DEFAULT_PORT, resolveHostForNameRequestSenders);
107 | }
108 |
109 | public DNSClientHostResolver(String dNSServerIP, int dNSServerPort, ThrowingBiFunction... resolveHostForNameRequestSenders) {
110 | try {
111 | this.dNSServerIP = InetAddress.getByName(dNSServerIP);
112 | } catch (UnknownHostException exc) {
113 | Driver.throwException(exc);
114 | }
115 | this.dNSServerPort = dNSServerPort;
116 | this.resolveHostForNameRequestSenders = resolveHostForNameRequestSenders != null && resolveHostForNameRequestSenders.length > 0 ?
117 | resolveHostForNameRequestSenders :
118 | new ThrowingBiFunction[] {IPV4_RETRIEVER, IPV6_RETRIEVER};
119 | }
120 |
121 | public static Collection newInstances(Supplier>> configuration) {
122 | Collection dNSClientHostResolvers = new ArrayList<>();
123 | configuration.get().stream().forEach(serverMap ->
124 | dNSClientHostResolvers.add(
125 | new DNSClientHostResolver(
126 | (String)serverMap.get("ip"),
127 | (Integer)serverMap.getOrDefault("port", DEFAULT_PORT),
128 | ((List)serverMap.get("ipTypeToSearchFor")).stream()
129 | .map(ipType -> Fields.getStaticDirect(DNSClientHostResolver.class, Strings.compile("{}_RETRIEVER", ipType.toUpperCase())))
130 | .map(ThrowingBiFunction.class::cast).toArray(size -> new ThrowingBiFunction[size])
131 | )
132 | )
133 | );
134 | return dNSClientHostResolvers;
135 | }
136 |
137 | @Override
138 | public Collection getAllAddressesForHostName(Map argumentMap) {
139 | return resolveHostForName((String)getMethodArguments(argumentMap)[0]);
140 | }
141 |
142 | public Collection resolveHostForName(String hostName) {
143 | try {
144 | Collection addresses = new ArrayList<>();
145 | byte[][] responses = new byte[resolveHostForNameRequestSenders.length][];
146 | for (int i = 0; i < resolveHostForNameRequestSenders.length; i++) {
147 | responses[i] = resolveHostForNameRequestSenders[i].apply(this, hostName);
148 | }
149 | Map iPToDomainMap = new LinkedHashMap<>();
150 | for (byte[] response : responses) {
151 | iPToDomainMap.putAll(parseResponse(response));
152 | }
153 | for (Entry iPToDomain : iPToDomainMap.entrySet()) {
154 | addresses.add(InetAddress.getByAddress(iPToDomain.getValue(), iPToDomain.getKey()));
155 | }
156 | return addresses;
157 | } catch (Throwable exc) {
158 | return Driver.throwException(exc);
159 | }
160 | }
161 |
162 | private byte[] sendRequest(String hostName, int recordType) throws IOException {
163 | short ID = (short)requestIdGenerator.nextInt(32767);
164 | try (
165 | ByteArrayOutputStream requestContentStream = new ByteArrayOutputStream();
166 | DataOutputStream requestWrapper = new DataOutputStream(requestContentStream);
167 | ) {
168 | short requestFlags = Short.parseShort("0000000100000000", 2);
169 | ByteBuffer byteBuffer = ByteBuffer.allocate(2).putShort(requestFlags);
170 | byte[] flagsByteArray = byteBuffer.array();
171 |
172 | short QDCOUNT = 1;
173 | short ANCOUNT = 0;
174 | short NSCOUNT = 0;
175 | short ARCOUNT = 0;
176 |
177 | requestWrapper.writeShort(ID);
178 | requestWrapper.write(flagsByteArray);
179 | requestWrapper.writeShort(QDCOUNT);
180 | requestWrapper.writeShort(ANCOUNT);
181 | requestWrapper.writeShort(NSCOUNT);
182 | requestWrapper.writeShort(ARCOUNT);
183 |
184 | String[] domainParts = hostName.split("\\.");
185 |
186 | for (int i = 0; i < domainParts.length; i++) {
187 | byte[] domainBytes = domainParts[i].getBytes(StandardCharsets.UTF_8);
188 | requestWrapper.writeByte(domainBytes.length);
189 | requestWrapper.write(domainBytes);
190 | }
191 | requestWrapper.writeByte(0);
192 | requestWrapper.writeShort(recordType);
193 | requestWrapper.writeShort(1);
194 | byte[] dnsFrame = requestContentStream.toByteArray();
195 | DatagramPacket packet;
196 | byte[] response;
197 | try (DatagramSocket socket = new DatagramSocket()){
198 | DatagramPacket dnsReqPacket = new DatagramPacket(dnsFrame, dnsFrame.length, dNSServerIP, dNSServerPort);
199 | socket.send(dnsReqPacket);
200 | response = new byte[1024];
201 | packet = new DatagramPacket(response, response.length);
202 | socket.receive(packet);
203 | }
204 | return response;
205 | }
206 | }
207 |
208 | private Map parseResponse(byte[] responseContent) throws IOException {
209 | try (InputStream responseContentStream = new ByteArrayInputStream(responseContent);
210 | DataInputStream responseWrapper = new DataInputStream(responseContentStream)
211 | ) {
212 | responseWrapper.skip(6);
213 | short ANCOUNT = responseWrapper.readShort();
214 | responseWrapper.skip(4);
215 | int recLen;
216 | while ((recLen = responseWrapper.readByte()) > 0) {
217 | byte[] record = new byte[recLen];
218 | for (int i = 0; i < recLen; i++) {
219 | record[i] = responseWrapper.readByte();
220 | }
221 | }
222 | responseWrapper.skip(4);
223 | byte firstBytes = responseWrapper.readByte();
224 | int firstTwoBits = (firstBytes & 0b11000000) >>> 6;
225 | Map valueToDomainMap = new LinkedHashMap<>();
226 | try (ByteArrayOutputStream label = new ByteArrayOutputStream();) {
227 | for(int i = 0; i < ANCOUNT; i++) {
228 | if(firstTwoBits == 3) {
229 | byte currentByte = responseWrapper.readByte();
230 | boolean stop = false;
231 | byte[] newArray = Arrays.copyOfRange(responseContent, currentByte, responseContent.length);
232 | try (InputStream responseSectionContentStream = new ByteArrayInputStream(newArray);
233 | DataInputStream responseSectionWrapper = new DataInputStream(responseSectionContentStream);
234 | ) {
235 | List RDATA = new ArrayList<>();
236 | List DOMAINS = new ArrayList<>();
237 | List labels = new ArrayList<>();
238 | while(!stop) {
239 | byte nextByte = responseSectionWrapper.readByte();
240 | if(nextByte != 0) {
241 | byte[] currentLabel = new byte[nextByte];
242 | for(int j = 0; j < nextByte; j++) {
243 | currentLabel[j] = responseSectionWrapper.readByte();
244 | }
245 | label.write(currentLabel);
246 | labels.add(currentLabel);
247 | } else {
248 | stop = true;
249 | responseWrapper.skip(8);
250 | int RDLENGTH = responseWrapper.readShort();
251 | for(int s = 0; s < RDLENGTH; s++) {
252 | RDATA.add(responseWrapper.readByte());
253 | }
254 | }
255 | DOMAINS.add(new String( label.toByteArray(), StandardCharsets.UTF_8));
256 | label.reset();
257 | }
258 |
259 | StringBuilder domainSb = new StringBuilder();
260 | byte[] address = new byte[RDATA.size()];
261 | for(int j = 0; j < RDATA.size(); j++) {
262 | address[j] = RDATA.get(j);
263 | }
264 |
265 | for(String domainPart:DOMAINS) {
266 | if(!domainPart.equals("")) {
267 | domainSb.append(domainPart).append(".");
268 | }
269 | }
270 | String domainFinal = domainSb.toString();
271 | valueToDomainMap.put(
272 | address,
273 | domainFinal.substring(0, domainFinal.length()-1)
274 | );
275 | }
276 | }
277 | firstBytes = responseWrapper.readByte();
278 | firstTwoBits = (firstBytes & 0b11000000) >>> 6;
279 | }
280 | }
281 | return valueToDomainMap;
282 | }
283 | }
284 |
285 |
286 | @Override
287 | public Collection getAllHostNamesForHostAddress(Map argumentMap) {
288 | return resolveHostForAddress((byte[])getMethodArguments(argumentMap)[0]);
289 | }
290 |
291 | public Collection resolveHostForAddress(String iPAddress) {
292 | return resolveHostForAddress(IPAddressUtil.INSTANCE.textToNumericFormat(iPAddress));
293 | }
294 |
295 | public Collection resolveHostForAddress(byte[] iPAddressAsBytes) {
296 | Map iPToDomainMap = new LinkedHashMap<>();
297 | try {
298 | iPToDomainMap.putAll(parseResponse(sendRequest(iPAddressAsBytesToReversedString(iPAddressAsBytes), RECORD_TYPE_PTR)));
299 | } catch (IOException exc) {
300 | Driver.throwException(exc);
301 | }
302 | ArrayList domains = new ArrayList<>();
303 | iPToDomainMap.forEach((key, value) -> {
304 | domains.add(hostNameAsBytesToString(key));
305 | });
306 | return domains;
307 | }
308 |
309 | private String iPAddressAsBytesToReversedString(byte[] iPAddressAsByte) {
310 | if (iPAddressAsByte.length != 4 && iPAddressAsByte.length != 16) {
311 | throw new IllegalArgumentException("array must contain 4 or 16 elements");
312 | }
313 |
314 | StringBuilder sb = new StringBuilder();
315 | if (iPAddressAsByte.length == 4) {
316 | for (int i = iPAddressAsByte.length - 1; i >= 0; i--) {
317 | sb.append(iPAddressAsByte[i] & 0xFF);
318 | if (i > 0) {
319 | sb.append(".");
320 | }
321 | }
322 | } else {
323 | int[] pieces = new int[2];
324 | for (int i = iPAddressAsByte.length - 1; i >= 0; i--) {
325 | pieces[0] = (iPAddressAsByte[i] & 0xFF) >> 4;
326 | pieces[1] = iPAddressAsByte[i] & 0xF;
327 | for (int j = pieces.length - 1; j >= 0; j--) {
328 | sb.append(Integer.toHexString(pieces[j]));
329 | if (i > 0 || j > 0) {
330 | sb.append(".");
331 | }
332 | }
333 | }
334 | }
335 | if (iPAddressAsByte.length == 4) {
336 | return sb.toString() + "." + IPV4_DOMAIN;
337 | } else {
338 | return sb.toString() + "." + IPV6_DOMAIN;
339 | }
340 | }
341 |
342 | private String hostNameAsBytesToString(byte[] hostNameAsBytes) {
343 | StringBuilder sb = new StringBuilder();
344 | int position = 0;
345 | while (true) {
346 | int len = hostNameAsBytes[position++];
347 | for (int i = position; i < position + len; i++) {
348 | int b = hostNameAsBytes[i] & 0xFF;
349 | if (b <= 0x20 || b >= 0x7f) {
350 | sb.append('\\');
351 | if (b < 10) {
352 | sb.append("00");
353 | } else if (b < 100) {
354 | sb.append('0');
355 | }
356 | sb.append(b);
357 | } else if (b == '"' || b == '(' || b == ')' || b == '.' || b == ';' || b == '\\' || b == '@' || b == '$') {
358 | sb.append('\\');
359 | sb.append((char) b);
360 | } else {
361 | sb.append((char) b);
362 | }
363 | }
364 | position += len;
365 | if (position < hostNameAsBytes.length && hostNameAsBytes[position] != 0) {
366 | sb.append(".");
367 | } else {
368 | break;
369 | }
370 | }
371 | return sb.toString();
372 | }
373 | }
--------------------------------------------------------------------------------
/src/main/java/org/burningwave/tools/net/DefaultHostResolver.java:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of Burningwave Tools.
3 | *
4 | * Author: Roberto Gentili
5 | *
6 | * Hosted at: https://github.com/burningwave/tools
7 | *
8 | * --
9 | *
10 | * The MIT License (MIT)
11 | *
12 | * Copyright (c) 2021 Roberto Gentili
13 | *
14 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
15 | * documentation files (the "Software"), to deal in the Software without restriction, including without
16 | * limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
17 | * the Software, and to permit persons to whom the Software is furnished to do so, subject to the following
18 | * conditions:
19 | *
20 | * The above copyright notice and this permission notice shall be included in all copies or substantial
21 | * portions of the Software.
22 | *
23 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
24 | * LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
25 | * EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
26 | * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
27 | * OR OTHER DEALINGS IN THE SOFTWARE.
28 | */
29 | package org.burningwave.tools.net;
30 |
31 | import static org.burningwave.core.assembler.StaticComponentContainer.Driver;
32 | import static org.burningwave.core.assembler.StaticComponentContainer.Fields;
33 | import static org.burningwave.core.assembler.StaticComponentContainer.Methods;
34 | import static org.burningwave.core.assembler.StaticComponentContainer.Strings;
35 |
36 | import java.lang.reflect.Field;
37 | import java.lang.reflect.Method;
38 | import java.lang.reflect.ParameterizedType;
39 | import java.net.InetAddress;
40 | import java.net.UnknownHostException;
41 | import java.util.ArrayList;
42 | import java.util.Collection;
43 | import java.util.List;
44 | import java.util.Map;
45 | import java.util.concurrent.CopyOnWriteArrayList;
46 | import java.util.function.Function;
47 | import java.util.stream.Stream;
48 |
49 | import org.burningwave.core.classes.FieldCriteria;
50 | import org.burningwave.core.classes.MethodCriteria;
51 |
52 | import io.github.toolfactory.jvm.function.InitializeException;
53 |
54 | @SuppressWarnings("unchecked")
55 | public class DefaultHostResolver implements HostResolver {
56 | public final static HostResolver INSTANCE;
57 | static final Class> inetAddressClass;
58 | static final Field nameServiceField;
59 | static final Class> nameServiceFieldClass;
60 | static final Class> nameServiceClass;
61 | static final Method getAllAddressesForHostNameMethod;
62 | static final Method getAllHostNamesForHostAddressMethod;
63 | static final List