commit
2b1b096fac
@ -0,0 +1,78 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2020 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.boot.loader.jar;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.net.MalformedURLException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.security.Permission;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base class for extended variants of {@link java.util.jar.JarFile}.
|
||||||
|
*
|
||||||
|
* @author Phillip Webb
|
||||||
|
*/
|
||||||
|
abstract class AbstractJarFile extends java.util.jar.JarFile {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new {@link AbstractJarFile}.
|
||||||
|
* @param file the root jar file.
|
||||||
|
* @throws IOException on IO error
|
||||||
|
*/
|
||||||
|
AbstractJarFile(File file) throws IOException {
|
||||||
|
super(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a URL that can be used to access this JAR file. NOTE: the specified URL
|
||||||
|
* cannot be serialized and or cloned.
|
||||||
|
* @return the URL
|
||||||
|
* @throws MalformedURLException if the URL is malformed
|
||||||
|
*/
|
||||||
|
protected abstract URL getUrl() throws MalformedURLException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the {@link JarFileType} of this instance.
|
||||||
|
* @return the jar file type
|
||||||
|
*/
|
||||||
|
protected abstract JarFileType getType();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the security permission for this JAR.
|
||||||
|
* @return the security permission.
|
||||||
|
*/
|
||||||
|
protected abstract Permission getPermission();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return an {@link InputStream} for the entire jar contents.
|
||||||
|
* @return the contents input stream
|
||||||
|
* @throws IOException on IO error
|
||||||
|
*/
|
||||||
|
protected abstract InputStream getInputStream() throws IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The type of a {@link JarFile}.
|
||||||
|
*/
|
||||||
|
enum JarFileType {
|
||||||
|
|
||||||
|
DIRECT, NESTED_DIRECTORY, NESTED_JAR
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,120 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2020 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.boot.loader.jar;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.net.MalformedURLException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.security.Permission;
|
||||||
|
import java.util.Enumeration;
|
||||||
|
import java.util.jar.JarEntry;
|
||||||
|
import java.util.jar.Manifest;
|
||||||
|
import java.util.zip.ZipEntry;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A wrapper used to create a copy of a {@link JarFile} so that it can be safely closed
|
||||||
|
* without closing the original.
|
||||||
|
*
|
||||||
|
* @author Phillip Webb
|
||||||
|
*/
|
||||||
|
class JarFileWrapper extends AbstractJarFile {
|
||||||
|
|
||||||
|
private final JarFile parent;
|
||||||
|
|
||||||
|
JarFileWrapper(JarFile parent) throws IOException {
|
||||||
|
super(parent.getRootJarFile().getFile());
|
||||||
|
this.parent = parent;
|
||||||
|
super.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected URL getUrl() throws MalformedURLException {
|
||||||
|
return this.parent.getUrl();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected JarFileType getType() {
|
||||||
|
return this.parent.getType();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Permission getPermission() {
|
||||||
|
return this.parent.getPermission();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Manifest getManifest() throws IOException {
|
||||||
|
return this.parent.getManifest();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Enumeration<JarEntry> entries() {
|
||||||
|
return this.parent.entries();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JarEntry getJarEntry(String name) {
|
||||||
|
return this.parent.getJarEntry(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ZipEntry getEntry(String name) {
|
||||||
|
return this.parent.getEntry(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected InputStream getInputStream() throws IOException {
|
||||||
|
return this.parent.getInputStream();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized InputStream getInputStream(ZipEntry ze) throws IOException {
|
||||||
|
return this.parent.getInputStream(ze);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getComment() {
|
||||||
|
return this.parent.getComment();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int size() {
|
||||||
|
return this.parent.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return this.parent.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return this.parent.getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
static JarFile unwrap(java.util.jar.JarFile jarFile) {
|
||||||
|
if (jarFile instanceof JarFile) {
|
||||||
|
return (JarFile) jarFile;
|
||||||
|
}
|
||||||
|
if (jarFile instanceof JarFileWrapper) {
|
||||||
|
return unwrap(((JarFileWrapper) jarFile).parent);
|
||||||
|
}
|
||||||
|
throw new IllegalStateException("Not a JarFile or Wrapper");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,140 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2020 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.boot.loader.jar;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.MalformedURLException;
|
||||||
|
import java.util.jar.JarOutputStream;
|
||||||
|
import java.util.zip.ZipEntry;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.io.TempDir;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||||
|
import static org.mockito.Mockito.spy;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link JarFileWrapper}.
|
||||||
|
*
|
||||||
|
* @author Phillip Webb
|
||||||
|
*/
|
||||||
|
class JarFileWrapperTests {
|
||||||
|
|
||||||
|
private JarFile parent;
|
||||||
|
|
||||||
|
private JarFileWrapper wrapper;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setup(@TempDir File temp) throws IOException {
|
||||||
|
this.parent = spy(new JarFile(createTempJar(temp)));
|
||||||
|
this.wrapper = new JarFileWrapper(this.parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
private File createTempJar(File temp) throws IOException {
|
||||||
|
File file = new File(temp, "temp.jar");
|
||||||
|
new JarOutputStream(new FileOutputStream(file)).close();
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getUrlDelegatesToParent() throws MalformedURLException {
|
||||||
|
this.wrapper.getUrl();
|
||||||
|
verify(this.parent).getUrl();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getTypeDelegatesToParent() {
|
||||||
|
this.wrapper.getType();
|
||||||
|
verify(this.parent).getType();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getPermissionDelegatesToParent() {
|
||||||
|
this.wrapper.getPermission();
|
||||||
|
verify(this.parent).getPermission();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getManifestDelegatesToParent() throws IOException {
|
||||||
|
this.wrapper.getManifest();
|
||||||
|
verify(this.parent).getManifest();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void entriesDelegatesToParent() {
|
||||||
|
this.wrapper.entries();
|
||||||
|
verify(this.parent).entries();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getJarEntryDelegatesToParent() {
|
||||||
|
this.wrapper.getJarEntry("test");
|
||||||
|
verify(this.parent).getJarEntry("test");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getEntryDelegatesToParent() {
|
||||||
|
this.wrapper.getEntry("test");
|
||||||
|
verify(this.parent).getEntry("test");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getInputStreamDelegatesToParent() throws IOException {
|
||||||
|
this.wrapper.getInputStream();
|
||||||
|
verify(this.parent).getInputStream();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getEntryInputStreamDelegatesToParent() throws IOException {
|
||||||
|
ZipEntry entry = new ZipEntry("test");
|
||||||
|
this.wrapper.getInputStream(entry);
|
||||||
|
verify(this.parent).getInputStream(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getCommentDelegatesToParent() {
|
||||||
|
this.wrapper.getComment();
|
||||||
|
verify(this.parent).getComment();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void sizeDelegatesToParent() {
|
||||||
|
this.wrapper.size();
|
||||||
|
verify(this.parent).size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void toStringDelegatesToParent() {
|
||||||
|
assertThat(this.wrapper.toString()).endsWith("/temp.jar");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test // gh-22991
|
||||||
|
void wrapperMustNotImplementClose() {
|
||||||
|
// If the wrapper overrides close then on Java 11 a FinalizableResource
|
||||||
|
// instance will be used to perform cleanup. This can result in a lot
|
||||||
|
// of additional memory being used since cleanup only occurs when the
|
||||||
|
// finalizer thread runs. See gh-22991
|
||||||
|
assertThatExceptionOfType(NoSuchMethodException.class)
|
||||||
|
.isThrownBy(() -> JarFileWrapper.class.getDeclaredMethod("close"));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue