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