Skip to content

Commit d879cf4

Browse files
committed
Move Streams.copy into elasticsearch-core and make a multi-release jar (#29322)
* Move Streams.copy into elasticsearch-core and make a multi-release jar This moves the method `Streams.copy(InputStream in, OutputStream out)` into the `elasticsearch-core` project (inside the `o.e.core.internal.io` package). It also makes this class into a multi-release class where the Java 9 equivalent uses `InputStream#transferTo`. This is a followup from #29300 (comment)
1 parent a87c2f3 commit d879cf4

File tree

17 files changed

+278
-55
lines changed

17 files changed

+278
-55
lines changed

libs/elasticsearch-core/build.gradle

+53-10
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,45 @@ apply plugin: 'nebula.maven-scm'
2626

2727
archivesBaseName = 'elasticsearch-core'
2828

29+
// we want to keep the JDKs in our IDEs set to JDK 8 until minimum JDK is bumped to 9 so we do not include this source set in our IDEs
30+
if (!isEclipse && !isIdea) {
31+
sourceSets {
32+
java9 {
33+
java {
34+
srcDirs = ['src/main/java9']
35+
}
36+
}
37+
}
38+
39+
configurations {
40+
java9Compile.extendsFrom(compile)
41+
}
42+
43+
dependencies {
44+
java9Compile sourceSets.main.output
45+
}
46+
47+
compileJava9Java {
48+
sourceCompatibility = 9
49+
targetCompatibility = 9
50+
}
51+
52+
/* Enable this when forbiddenapis was updated to 2.6.
53+
* See: https://github.com/elastic/elasticsearch/issues/29292
54+
forbiddenApisJava9 {
55+
targetCompatibility = 9
56+
}
57+
*/
58+
59+
jar {
60+
metaInf {
61+
into 'versions/9'
62+
from sourceSets.java9.output
63+
}
64+
manifest.attributes('Multi-Release': 'true')
65+
}
66+
}
67+
2968
publishing {
3069
publications {
3170
nebula {
@@ -39,6 +78,10 @@ dependencies {
3978
testCompile "junit:junit:${versions.junit}"
4079
testCompile "org.hamcrest:hamcrest-all:${versions.hamcrest}"
4180

81+
if (!isEclipse && !isIdea) {
82+
java9Compile sourceSets.main.output
83+
}
84+
4285
if (isEclipse == false || project.path == ":libs:elasticsearch-core-tests") {
4386
testCompile("org.elasticsearch.test:framework:${version}") {
4487
exclude group: 'org.elasticsearch', module: 'elasticsearch-core'
@@ -66,14 +109,14 @@ if (isEclipse) {
66109
}
67110

68111
thirdPartyAudit.excludes = [
69-
// from log4j
70-
'org/osgi/framework/AdaptPermission',
71-
'org/osgi/framework/AdminPermission',
72-
'org/osgi/framework/Bundle',
73-
'org/osgi/framework/BundleActivator',
74-
'org/osgi/framework/BundleContext',
75-
'org/osgi/framework/BundleEvent',
76-
'org/osgi/framework/SynchronousBundleListener',
77-
'org/osgi/framework/wiring/BundleWire',
78-
'org/osgi/framework/wiring/BundleWiring'
112+
// from log4j
113+
'org/osgi/framework/AdaptPermission',
114+
'org/osgi/framework/AdminPermission',
115+
'org/osgi/framework/Bundle',
116+
'org/osgi/framework/BundleActivator',
117+
'org/osgi/framework/BundleContext',
118+
'org/osgi/framework/BundleEvent',
119+
'org/osgi/framework/SynchronousBundleListener',
120+
'org/osgi/framework/wiring/BundleWire',
121+
'org/osgi/framework/wiring/BundleWiring'
79122
]

libs/elasticsearch-core/src/main/java/org/elasticsearch/core/internal/io/IOUtils.java

+41-13
Original file line numberDiff line numberDiff line change
@@ -41,45 +41,73 @@ private IOUtils() {
4141
}
4242

4343
/**
44-
* Closes all given <tt>Closeable</tt>s. Some of the <tt>Closeable</tt>s may be null; they are ignored. After everything is closed, the
45-
* method either throws the first exception it hit while closing, or completes normally if there were no exceptions.
44+
* Closes all given <tt>Closeable</tt>s. Some of the <tt>Closeable</tt>s may be null; they are
45+
* ignored. After everything is closed, the method either throws the first exception it hit
46+
* while closing with other exceptions added as suppressed, or completes normally if there were
47+
* no exceptions.
4648
*
4749
* @param objects objects to close
4850
*/
4951
public static void close(final Closeable... objects) throws IOException {
50-
close(Arrays.asList(objects));
52+
close(null, Arrays.asList(objects));
5153
}
5254

5355
/**
54-
* Closes all given {@link Closeable}s.
56+
* Closes all given <tt>Closeable</tt>s. Some of the <tt>Closeable</tt>s may be null; they are
57+
* ignored. After everything is closed, the method adds any exceptions as suppressed to the
58+
* original exception, or throws the first exception it hit if {@code Exception} is null. If
59+
* no exceptions are encountered and the passed in exception is null, it completes normally.
5560
*
5661
* @param objects objects to close
62+
*/
63+
public static void close(final Exception e, final Closeable... objects) throws IOException {
64+
close(e, Arrays.asList(objects));
65+
}
66+
67+
/**
68+
* Closes all given <tt>Closeable</tt>s. Some of the <tt>Closeable</tt>s may be null; they are
69+
* ignored. After everything is closed, the method either throws the first exception it hit
70+
* while closing with other exceptions added as suppressed, or completes normally if there were
71+
* no exceptions.
5772
*
58-
* @see #close(Closeable...)
73+
* @param objects objects to close
5974
*/
6075
public static void close(final Iterable<? extends Closeable> objects) throws IOException {
61-
Exception ex = null;
76+
close(null, objects);
77+
}
6278

79+
/**
80+
* Closes all given {@link Closeable}s. If a non-null exception is passed in, or closing a
81+
* stream causes an exception, throws the exception with other {@link RuntimeException} or
82+
* {@link IOException} exceptions added as suppressed.
83+
*
84+
* @param ex existing Exception to add exceptions occurring during close to
85+
* @param objects objects to close
86+
*
87+
* @see #close(Closeable...)
88+
*/
89+
public static void close(final Exception ex, final Iterable<? extends Closeable> objects) throws IOException {
90+
Exception firstException = ex;
6391
for (final Closeable object : objects) {
6492
try {
6593
if (object != null) {
6694
object.close();
6795
}
6896
} catch (final IOException | RuntimeException e) {
69-
if (ex == null) {
70-
ex = e;
97+
if (firstException == null) {
98+
firstException = e;
7199
} else {
72-
ex.addSuppressed(e);
100+
firstException.addSuppressed(e);
73101
}
74102
}
75103
}
76104

77-
if (ex != null) {
78-
if (ex instanceof IOException) {
79-
throw (IOException) ex;
105+
if (firstException != null) {
106+
if (firstException instanceof IOException) {
107+
throw (IOException) firstException;
80108
} else {
81109
// since we only assigned an IOException or a RuntimeException to ex above, in this case ex must be a RuntimeException
82-
throw (RuntimeException) ex;
110+
throw (RuntimeException) firstException;
83111
}
84112
}
85113
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/*
2+
* Licensed to Elasticsearch under one or more contributor
3+
* license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright
5+
* ownership. Elasticsearch licenses this file to you under
6+
* the Apache License, Version 2.0 (the "License"); you may
7+
* not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
package org.elasticsearch.core.internal.io;
21+
22+
import java.io.IOException;
23+
import java.io.InputStream;
24+
import java.io.OutputStream;
25+
import java.util.Objects;
26+
27+
/**
28+
* Simple utility methods for file and stream copying.
29+
* All copy methods use a block size of 4096 bytes,
30+
* and close all affected streams when done.
31+
* <p>
32+
* Mainly for use within the framework,
33+
* but also useful for application code.
34+
*/
35+
public class Streams {
36+
37+
/**
38+
* Copy the contents of the given InputStream to the given OutputStream.
39+
* Closes both streams when done.
40+
*
41+
* @param in the stream to copy from
42+
* @param out the stream to copy to
43+
* @return the number of bytes copied
44+
* @throws IOException in case of I/O errors
45+
*/
46+
public static long copy(final InputStream in, final OutputStream out) throws IOException {
47+
Objects.requireNonNull(in, "No InputStream specified");
48+
Objects.requireNonNull(out, "No OutputStream specified");
49+
final byte[] buffer = new byte[8192];
50+
Exception err = null;
51+
try {
52+
long byteCount = 0;
53+
int bytesRead;
54+
while ((bytesRead = in.read(buffer)) != -1) {
55+
out.write(buffer, 0, bytesRead);
56+
byteCount += bytesRead;
57+
}
58+
out.flush();
59+
return byteCount;
60+
} catch (IOException | RuntimeException e) {
61+
err = e;
62+
throw e;
63+
} finally {
64+
IOUtils.close(err, in, out);
65+
}
66+
}
67+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/*
2+
* Licensed to Elasticsearch under one or more contributor
3+
* license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright
5+
* ownership. Elasticsearch licenses this file to you under
6+
* the Apache License, Version 2.0 (the "License"); you may
7+
* not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
package org.elasticsearch.core.internal.io;
21+
22+
import java.io.IOException;
23+
import java.io.InputStream;
24+
import java.io.OutputStream;
25+
26+
/**
27+
* Simple utility methods for file and stream copying.
28+
* All copy methods close all affected streams when done.
29+
* <p>
30+
* Mainly for use within the framework,
31+
* but also useful for application code.
32+
*/
33+
public abstract class Streams {
34+
35+
/**
36+
* Copy the contents of the given InputStream to the given OutputStream.
37+
* Closes both streams when done.
38+
*
39+
* @param in the stream to copy from
40+
* @param out the stream to copy to
41+
* @return the number of bytes copied
42+
* @throws IOException in case of I/O errors
43+
*/
44+
public static long copy(final InputStream in, final OutputStream out) throws IOException {
45+
Exception err = null;
46+
try {
47+
final long byteCount = in.transferTo(out);
48+
out.flush();
49+
return byteCount;
50+
} catch (IOException | RuntimeException e) {
51+
err = e;
52+
throw e;
53+
} finally {
54+
IOUtils.close(err, in, out);
55+
}
56+
}
57+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/*
2+
* Licensed to Elasticsearch under one or more contributor
3+
* license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright
5+
* ownership. Elasticsearch licenses this file to you under
6+
* the Apache License, Version 2.0 (the "License"); you may
7+
* not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
package org.elasticsearch.core.internal.io;
21+
22+
import org.elasticsearch.test.ESTestCase;
23+
24+
import java.io.ByteArrayInputStream;
25+
import java.io.ByteArrayOutputStream;
26+
import java.io.IOException;
27+
import java.nio.charset.StandardCharsets;
28+
import java.util.Arrays;
29+
30+
import static org.hamcrest.Matchers.equalTo;
31+
32+
public class StreamsTests extends ESTestCase {
33+
public void testCopyFromInputStream() throws IOException {
34+
byte[] content = "content".getBytes(StandardCharsets.UTF_8);
35+
ByteArrayInputStream in = new ByteArrayInputStream(content);
36+
ByteArrayOutputStream out = new ByteArrayOutputStream(content.length);
37+
long count = Streams.copy(in, out);
38+
39+
assertThat(count, equalTo((long) content.length));
40+
assertThat(Arrays.equals(content, out.toByteArray()), equalTo(true));
41+
}
42+
}

plugins/repository-azure/src/test/java/org/elasticsearch/cloud/azure/storage/AzureStorageServiceMock.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@
2525
import org.elasticsearch.common.blobstore.support.PlainBlobMetaData;
2626
import org.elasticsearch.common.collect.MapBuilder;
2727
import org.elasticsearch.common.component.AbstractComponent;
28-
import org.elasticsearch.common.io.Streams;
2928
import org.elasticsearch.common.settings.Settings;
29+
import org.elasticsearch.core.internal.io.Streams;
3030

3131
import java.io.ByteArrayInputStream;
3232
import java.io.ByteArrayOutputStream;

plugins/repository-gcs/src/test/java/org/elasticsearch/repositories/gcs/GoogleCloudStorageFixture.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
import com.sun.net.httpserver.HttpHandler;
2323
import com.sun.net.httpserver.HttpServer;
2424
import org.elasticsearch.common.SuppressForbidden;
25-
import org.elasticsearch.common.io.Streams;
25+
import org.elasticsearch.core.internal.io.Streams;
2626
import org.elasticsearch.mocksocket.MockHttpServer;
2727
import org.elasticsearch.repositories.gcs.GoogleCloudStorageTestServer.Response;
2828

server/src/main/java/org/elasticsearch/common/blobstore/fs/FsBlobContainer.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
import org.elasticsearch.common.blobstore.BlobPath;
2525
import org.elasticsearch.common.blobstore.support.AbstractBlobContainer;
2626
import org.elasticsearch.common.blobstore.support.PlainBlobMetaData;
27-
import org.elasticsearch.common.io.Streams;
27+
import org.elasticsearch.core.internal.io.Streams;
2828

2929
import java.io.BufferedInputStream;
3030
import java.io.FileNotFoundException;
@@ -128,7 +128,7 @@ public void writeBlob(String blobName, InputStream inputStream, long blobSize) t
128128
}
129129
final Path file = path.resolve(blobName);
130130
try (OutputStream outputStream = Files.newOutputStream(file, StandardOpenOption.CREATE_NEW)) {
131-
Streams.copy(inputStream, outputStream, new byte[blobStore.bufferSizeInBytes()]);
131+
Streams.copy(inputStream, outputStream);
132132
}
133133
IOUtils.fsync(file, false);
134134
IOUtils.fsync(path, true);

server/src/main/java/org/elasticsearch/common/compress/CompressorFactory.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,11 @@
2121

2222
import org.elasticsearch.common.Nullable;
2323
import org.elasticsearch.common.bytes.BytesReference;
24-
import org.elasticsearch.common.io.Streams;
2524
import org.elasticsearch.common.io.stream.BytesStreamOutput;
2625
import org.elasticsearch.common.io.stream.StreamInput;
2726
import org.elasticsearch.common.xcontent.XContentHelper;
2827
import org.elasticsearch.common.xcontent.XContentType;
28+
import org.elasticsearch.core.internal.io.Streams;
2929

3030
import java.io.IOException;
3131
import java.util.Objects;

0 commit comments

Comments
 (0)