Skip to content

Commit 2e710fd

Browse files
committed
Merge branch 'master' of github.com:joyent/java-manta
2 parents 5760faa + 3ad1860 commit 2e710fd

File tree

21 files changed

+466
-57
lines changed

21 files changed

+466
-57
lines changed

.travis.yml

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,36 @@ dist: trusty
22
language: java
33
# We now cache the Maven dependencies directory for faster builds
44
cache:
5+
apt: true
56
directories:
67
- $HOME/.m2
7-
sudo: false
8+
- $HOME/.downloads
9+
sudo: true
810

911
addons:
1012
apt:
13+
sources:
14+
- sourceline: 'deb http://repos.azulsystems.com/ubuntu stable main'
15+
key_url: 'http://repos.azulsystems.com/RPM-GPG-KEY-azulsystems'
1116
packages:
12-
- oracle-java8-installer
13-
- oracle-java8-unlimited-jce-policy
17+
- zulu-8
18+
- zulu-9
19+
20+
# Oracle JDK 8 and the OpenJDK 8 are both installed by default by TravisCI
21+
# Additionally, they both have the unlimited JCE policy installed as well.
22+
# This is why we do not explicitly installed them in our Travis CI
23+
# configuration.
1424

1525
before_install:
1626
- echo 'MAVEN_OPTS="-Dorg.slf4j.simpleLogger.defaultLogLevel=warn"' >~/.mavenrc
27+
- mkdir -p $HOME/.downloads
28+
- wget -N -q -O $HOME/.downloads/ZuluJCEPolicies.zip 'https://cdn.azul.com/zcek/bin/ZuluJCEPolicies.zip'
29+
# The SHA256 checksum for JCE for Azul will break if Azul updates their archive.
30+
# If so, you will need to update the fingerprint below after verifying the
31+
# authenticity of the archive.
32+
- echo "8021a28b8cac41b44f1421fd210a0a0822fcaf88d62d2e70a35b2ff628a8675a $HOME/.downloads/ZuluJCEPolicies.zip" | sha256sum -c
33+
- sudo unzip -o -j $HOME/.downloads/ZuluJCEPolicies.zip ZuluJCEPolicies/local_policy.jar ZuluJCEPolicies/US_export_policy.jar -d /usr/lib/jvm/zulu-8-amd64/jre/lib/security
34+
- sudo unzip -o -j $HOME/.downloads/ZuluJCEPolicies.zip ZuluJCEPolicies/local_policy.jar ZuluJCEPolicies/US_export_policy.jar -d /usr/lib/jvm/zulu-9-amd64/lib/security
1735

1836
matrix:
1937
fast_finish: true
@@ -36,5 +54,22 @@ matrix:
3654
- DESC="openjdk8 unit tests"
3755
- CMD="mvn clean test -Dcheckstyle.skip=true"
3856
- LANG=en_US.utf8
57+
# unit tests (zulu-8)
58+
- env:
59+
- JAVA_HOME=/usr/lib/jvm/zulu-8-amd64
60+
- DESC="zulu-8 unit tests"
61+
- CMD="mvn clean test -Dcheckstyle.skip=true"
62+
- LANG=en_US.utf8
63+
- jdk: oraclejdk9
64+
env:
65+
- DESC="oraclejdk9 unit tests"
66+
- CMD="mvn clean test -Dcheckstyle.skip=true"
67+
- LANG=en_US.utf8
68+
# unit tests (zulu-9)
69+
- env:
70+
- JAVA_HOME=/usr/lib/jvm/zulu-9-amd64
71+
- DESC="zulu-9 unit tests"
72+
- CMD="mvn clean test -Dcheckstyle.skip=true"
73+
- LANG=en_US.utf8
3974

4075
script: echo ${CMD}; ${CMD}

USAGE.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ Below is a table of available configuration parameters followed by detailed desc
9393
| manta.tcp_socket_timeout | MANTA_TCP_SOCKET_TIMEOUT | 10000 | |
9494
| manta.connection_request_timeout | MANTA_CONNECTION_REQUEST_TIMEOUT | 1000 | |
9595
| manta.upload_buffer_size | MANTA_UPLOAD_BUFFER_SIZE | 16384 | |
96+
| manta.skip_directory_depth | MANTA_SKIP_DIRECTORY_DEPTH | | |
9697
| manta.client_encryption | MANTA_CLIENT_ENCRYPTION | false | |
9798
| manta.encryption_key_id | MANTA_CLIENT_ENCRYPTION_KEY_ID | | |
9899
| manta.encryption_algorithm | MANTA_ENCRYPTION_ALGORITHM | AES128/CTR/NoPadding | |
@@ -148,6 +149,12 @@ Note: Dynamic Updates marked with an asterisk (*) are enabled by the `AuthAwareC
148149
The initial amount of bytes to attempt to load into memory when uploading a stream. If the
149150
entirety of the stream fits within the number of bytes of this value, then the
150151
contents of the buffer are directly uploaded to Manta in a retryable form.
152+
* `manta.skip_directory_depth` (**MANTA_SKIP_DIRECTORY_DEPTH**)
153+
Integer indicating the number of directory levels to attempt to skip when performing a recursive `putDirectory`
154+
operation. Set to 0 to disable the optimization entirely. Irrelevant when the depth of the recursive `putDirectory`
155+
call is less than the setting. When creating a directory with more levels than the setting, the client will attempt
156+
to skip this many non-system directories from the root. Will return to normal directory creation procedure if
157+
the skipped `PUT` fails or proceed creating all directories between the skip depth and the child on success.
151158
* `manta.client_encryption` (**MANTA_CLIENT_ENCRYPTION**)
152159
Boolean indicating if client-side encryption is enabled.
153160
* `manta.encryption_key_id` (**MANTA_CLIENT_ENCRYPTION_KEY_ID**)

checkstyle.xml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,9 @@
8282
<property name="excludeScope" value="package"/>
8383
</module>
8484
<module name="JavadocType" />
85-
<module name="JavadocVariable"/>
85+
<module name="JavadocVariable">
86+
<property name="ignoreNamePattern" value="LOG|LOGGER"/>
87+
</module>
8688
<module name="JavadocStyle" />
8789

8890
<!-- Checks for Naming Conventions. -->

java-manta-client-kryo-serialization/src/main/java/com/joyent/manta/serialization/BaseBlockCipherSerializer.java

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,14 @@
2525
import org.bouncycastle.jcajce.provider.symmetric.util.BlockCipherProvider;
2626
import org.bouncycastle.jcajce.spec.GOST28147ParameterSpec;
2727
import org.bouncycastle.jcajce.util.BCJcaJceHelper;
28-
import org.objenesis.instantiator.sun.MagicInstantiator;
28+
import org.objenesis.instantiator.sun.UnsafeFactoryInstantiator;
2929

3030
import javax.crypto.spec.GCMParameterSpec;
3131
import javax.crypto.spec.IvParameterSpec;
3232
import javax.crypto.spec.PBEParameterSpec;
3333
import javax.crypto.spec.RC2ParameterSpec;
3434
import javax.crypto.spec.RC5ParameterSpec;
35+
3536
import java.lang.reflect.Field;
3637
import java.security.AlgorithmParameters;
3738

@@ -141,25 +142,25 @@ private void registerClasses(final Kryo kryo) {
141142
kryo.register(BufferedBlockCipher.class);
142143
kryo.register(bufferedGenericBlockCipherClass,
143144
new CompatibleFieldSerializer<>(kryo, bufferedGenericBlockCipherClass))
144-
.setInstantiator(new MagicInstantiator<>(bufferedGenericBlockCipherClass));
145+
.setInstantiator(new UnsafeFactoryInstantiator<>(bufferedGenericBlockCipherClass));
145146
kryo.register(SICBlockCipher.class)
146-
.setInstantiator(new MagicInstantiator<>(SICBlockCipher.class));
147+
.setInstantiator(new UnsafeFactoryInstantiator<>(SICBlockCipher.class));
147148
kryo.register(AlgorithmParameters.class);
148149
kryo.register(BCJcaJceHelper.class);
149150
kryo.register(ParametersWithIV.class)
150-
.setInstantiator(new MagicInstantiator<>(ParametersWithIV.class));
151+
.setInstantiator(new UnsafeFactoryInstantiator<>(ParametersWithIV.class));
151152
kryo.register(KeyParameter.class)
152-
.setInstantiator(new MagicInstantiator<>(KeyParameter.class));
153+
.setInstantiator(new UnsafeFactoryInstantiator<>(KeyParameter.class));
153154
kryo.register(paddedBufferedBlockCipherClass)
154-
.setInstantiator(new MagicInstantiator<>(paddedBufferedBlockCipherClass));
155+
.setInstantiator(new UnsafeFactoryInstantiator<>(paddedBufferedBlockCipherClass));
155156
kryo.register(CBCBlockCipher.class)
156-
.setInstantiator(new MagicInstantiator<>(CBCBlockCipher.class));
157+
.setInstantiator(new UnsafeFactoryInstantiator<>(CBCBlockCipher.class));
157158
kryo.register(AEADParameters.class, new CompatibleFieldSerializer<>(kryo, AEADParameters.class))
158-
.setInstantiator(new MagicInstantiator<>(AEADParameters.class));
159+
.setInstantiator(new UnsafeFactoryInstantiator<>(AEADParameters.class));
159160
kryo.register(aeadGenericBlockCipherClass, new CompatibleFieldSerializer<>(kryo, aeadGenericBlockCipherClass))
160-
.setInstantiator(new MagicInstantiator<>(aeadGenericBlockCipherClass));
161+
.setInstantiator(new UnsafeFactoryInstantiator<>(aeadGenericBlockCipherClass));
161162
kryo.register(GCMBlockCipher.class, new CompatibleFieldSerializer<>(kryo, GCMBlockCipher.class))
162-
.setInstantiator(new MagicInstantiator<>(GCMBlockCipher.class));
163+
.setInstantiator(new UnsafeFactoryInstantiator<>(GCMBlockCipher.class));
163164
kryo.register(RC2ParameterSpec.class);
164165
kryo.register(RC5ParameterSpec.class);
165166
kryo.register(GCMParameterSpec.class);

java-manta-client-kryo-serialization/src/main/java/com/joyent/manta/serialization/CipherSerializer.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import com.esotericsoftware.kryo.serializers.JavaSerializer;
1515
import org.bouncycastle.jcajce.provider.symmetric.AES;
1616
import org.objenesis.instantiator.sun.MagicInstantiator;
17+
import org.objenesis.instantiator.sun.UnsafeFactoryInstantiator;
1718

1819
import javax.crypto.Cipher;
1920
import javax.crypto.spec.SecretKeySpec;
@@ -94,10 +95,10 @@ private void registerClasses(final Kryo kryo) {
9495
kryo.register(IdentityHashMap.class, new JavaSerializer());
9596

9697
kryo.register(java.lang.ref.WeakReference.class)
97-
.setInstantiator(new MagicInstantiator<>(java.lang.ref.WeakReference.class));
98+
.setInstantiator(new UnsafeFactoryInstantiator<>(java.lang.ref.WeakReference.class));
9899

99100
kryo.register(java.security.AlgorithmParameters.class)
100-
.setInstantiator(new MagicInstantiator<>(java.security.AlgorithmParameters.class));
101+
.setInstantiator(new UnsafeFactoryInstantiator<>(java.security.AlgorithmParameters.class));
101102

102103
kryo.register(SecretKeySpec.class, new JavaSerializer());
103104

@@ -142,7 +143,7 @@ private void registerWithCompatSerializerMagicInstantiator(final Kryo kryo, fina
142143

143144
if (clazz != null) {
144145
kryo.register(clazz, new CompatibleFieldSerializer<>(kryo, clazz))
145-
.setInstantiator(new MagicInstantiator<>(clazz));
146+
.setInstantiator(new UnsafeFactoryInstantiator<>(clazz));
146147
}
147148
}
148149

@@ -170,7 +171,7 @@ private void registerWithMagicInstantiator(final Kryo kryo, final String classNa
170171
Class<?> clazz = findClass(className);
171172

172173
if (clazz != null) {
173-
kryo.register(clazz).setInstantiator(new MagicInstantiator<>(clazz));
174+
kryo.register(clazz).setInstantiator(new UnsafeFactoryInstantiator<>(clazz));
174175
}
175176
}
176177

java-manta-client-kryo-serialization/src/main/java/com/joyent/manta/serialization/EncryptionStateSerializer.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
import com.joyent.manta.util.HmacOutputStream;
2222
import org.bouncycastle.crypto.macs.HMac;
2323
import org.bouncycastle.jcajce.io.CipherOutputStream;
24-
import org.objenesis.instantiator.sun.MagicInstantiator;
24+
import org.objenesis.instantiator.sun.UnsafeFactoryInstantiator;
2525

2626
import javax.crypto.Cipher;
2727
import javax.crypto.SecretKey;
@@ -72,7 +72,7 @@ public EncryptionStateSerializer(final Kryo kryo,
7272
*/
7373
private void registerClasses(final Kryo kryo) {
7474
kryo.register(EncryptionContext.class)
75-
.setInstantiator(new MagicInstantiator<>(EncryptionContext.class));
75+
.setInstantiator(new UnsafeFactoryInstantiator<>(EncryptionContext.class));
7676
kryo.register(SupportedCipherDetails.class, new SupportedCipherDetailsSerializer());
7777
kryo.register(AesCtrCipherDetails.class, new SupportedCipherDetailsSerializer());
7878
kryo.register(AesCbcCipherDetails.class, new SupportedCipherDetailsSerializer());
@@ -85,9 +85,9 @@ private void registerClasses(final Kryo kryo) {
8585
"CloseShieldOutputStream reference must not be null");
8686

8787
kryo.register(closeShieldStreamClass)
88-
.setInstantiator(new MagicInstantiator<>(closeShieldStreamClass));
88+
.setInstantiator(new UnsafeFactoryInstantiator<>(closeShieldStreamClass));
8989
kryo.register(CipherOutputStream.class)
90-
.setInstantiator(new MagicInstantiator<>(CipherOutputStream.class));
90+
.setInstantiator(new UnsafeFactoryInstantiator<>(CipherOutputStream.class));
9191
}
9292

9393
/**

java-manta-client-kryo-serialization/src/main/java/com/joyent/manta/serialization/ReflectionUtils.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ static Field getField(final Class<?> clazz, final String name) {
115115
* @param object object to read from
116116
* @return field's value
117117
*/
118+
@SuppressWarnings("deprecation")
118119
static Object readField(final Field field, final Object object) {
119120
Objects.requireNonNull(field, "Field must not be null");
120121
Objects.requireNonNull(object, "Object must not be null");
@@ -142,6 +143,7 @@ static Object readField(final Field field, final Object object) {
142143
* @param target target object
143144
* @param value object to write
144145
*/
146+
@SuppressWarnings("deprecation")
145147
static void writeField(final Field field, final Object target, final Object value) {
146148
Objects.requireNonNull(field, "Field must not be null");
147149
Objects.requireNonNull(target, "Target must not be null");

java-manta-client-unshaded/src/main/java/com/joyent/manta/client/MantaClient.java

Lines changed: 11 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,6 @@
7979
import java.nio.charset.StandardCharsets;
8080
import java.nio.file.Files;
8181
import java.nio.file.Path;
82-
import java.nio.file.Paths;
8382
import java.nio.file.StandardCopyOption;
8483
import java.time.Instant;
8584
import java.time.temporal.TemporalAmount;
@@ -1576,13 +1575,14 @@ public void putDirectory(final String path, final boolean recursive)
15761575
/**
15771576
* Creates a directory in Manta.
15781577
*
1579-
* @param rawPath The fully qualified path of the Manta directory.
1578+
* @param rawPath The fully qualified path of the Manta directory.
15801579
* @param recursive recursive create all of the directories specified in the path
1581-
* @param headers Optional {@link MantaHttpHeaders}. Consult the Manta api for more header information.
1582-
* @throws IOException If an IO exception has occurred.
1580+
* @param headers Optional {@link MantaHttpHeaders}. Consult the Manta api for more header information.
1581+
* @throws IOException If an IO exception has occurred.
15831582
* @throws MantaClientHttpResponseException If a http status code {@literal > 300} is returned.
15841583
*/
1585-
public void putDirectory(final String rawPath, final boolean recursive,
1584+
public void putDirectory(final String rawPath,
1585+
final boolean recursive,
15861586
final MantaHttpHeaders headers)
15871587
throws IOException {
15881588
Validate.notBlank(rawPath, "rawPath must not be blank");
@@ -1592,23 +1592,12 @@ public void putDirectory(final String rawPath, final boolean recursive,
15921592
return;
15931593
}
15941594

1595-
final String[] parts = rawPath.split(SEPARATOR);
1596-
final Iterator<Path> itr = Paths.get("", parts).iterator();
1597-
final StringBuilder sb = new StringBuilder(SEPARATOR);
1598-
1599-
for (int i = 0; itr.hasNext(); i++) {
1600-
final String part = itr.next().toString();
1601-
sb.append(part);
1602-
1603-
// This means we aren't in the home nor in the reserved
1604-
// directory path (stor, public, jobs, etc)
1605-
if (i > 1) {
1606-
putDirectory(sb.toString(), headers);
1607-
}
1608-
1609-
if (itr.hasNext()) {
1610-
sb.append(SEPARATOR);
1611-
}
1595+
final Integer skipDepth = config.getSkipDirectoryDepth();
1596+
final RecursiveDirectoryCreationStrategy directoryCreationStrategy;
1597+
if (skipDepth != null && 0 < skipDepth) {
1598+
RecursiveDirectoryCreationStrategy.createWithSkipDepth(this, rawPath, headers, skipDepth);
1599+
} else {
1600+
RecursiveDirectoryCreationStrategy.createCompletely(this, rawPath, headers);
16121601
}
16131602
}
16141603

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
/*
2+
* Copyright (c) 2017, Joyent, Inc. All rights reserved.
3+
*
4+
* This Source Code Form is subject to the terms of the Mozilla Public
5+
* License, v. 2.0. If a copy of the MPL was not distributed with this
6+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
7+
*/
8+
package com.joyent.manta.client;
9+
10+
import com.joyent.manta.exception.MantaClientHttpResponseException;
11+
import com.joyent.manta.exception.MantaErrorCode;
12+
import com.joyent.manta.http.MantaHttpHeaders;
13+
import org.slf4j.Logger;
14+
import org.slf4j.LoggerFactory;
15+
16+
import java.io.IOException;
17+
18+
import static com.joyent.manta.util.MantaUtils.writeablePrefixPaths;
19+
20+
/**
21+
* Utility class for recursive directory creation strategies.
22+
*
23+
* @author <a href="https://github.com/tjcelaya">Tomas Celayac</a>
24+
* @since 3.1.7
25+
*/
26+
final class RecursiveDirectoryCreationStrategy {
27+
28+
private static final Logger LOG = LoggerFactory.getLogger(RecursiveDirectoryCreationStrategy.class);
29+
30+
private RecursiveDirectoryCreationStrategy() {
31+
}
32+
33+
static long createWithSkipDepth(final MantaClient client,
34+
final String rawPath,
35+
final MantaHttpHeaders headers,
36+
final int skipDepth) throws IOException {
37+
final String[] paths = writeablePrefixPaths(rawPath);
38+
39+
if (paths.length <= skipDepth) {
40+
return createCompletely(client, rawPath, headers);
41+
}
42+
43+
final String assumedExistingDirectory = paths[skipDepth - 1];
44+
final String maybeNewDirectory = paths[skipDepth];
45+
46+
LOG.debug("ASSUME {}", assumedExistingDirectory);
47+
48+
final Boolean redundantPut = createNewDirectory(client, maybeNewDirectory, headers, rawPath);
49+
long ops = 1;
50+
51+
if (redundantPut == null) {
52+
LOG.debug("FAILED {}", maybeNewDirectory);
53+
54+
// failed to create directory at the skip depth, proceed normally
55+
return ops + createCompletely(client, rawPath, headers);
56+
}
57+
58+
for (int idx = skipDepth + 1; idx < paths.length; idx++) {
59+
client.putDirectory(paths[idx], headers);
60+
ops++;
61+
}
62+
63+
return ops;
64+
}
65+
66+
static long createCompletely(final MantaClient client,
67+
final String rawPath,
68+
final MantaHttpHeaders headers) throws IOException {
69+
long ops = 0;
70+
for (final String path : writeablePrefixPaths(rawPath)) {
71+
client.putDirectory(path, headers);
72+
ops++;
73+
}
74+
75+
return ops;
76+
}
77+
78+
/**
79+
* Try to create a directory and unpack the error. The Boolean is intentional and acts as a tri-state variable.
80+
*
81+
* NULL = creation failed.
82+
* TRUE = a new directory was actually created
83+
* FALSE = the directory already existed
84+
*
85+
* @return whether or not the directory was actually new, or null if it failed to be created
86+
*/
87+
private static Boolean createNewDirectory(final MantaClient client,
88+
final String path,
89+
final MantaHttpHeaders headers,
90+
final String targetPath) throws IOException {
91+
try {
92+
return client.putDirectory(path, headers);
93+
} catch (final MantaClientHttpResponseException mchre) {
94+
if (mchre.getServerCode().equals(MantaErrorCode.DIRECTORY_DOES_NOT_EXIST_ERROR)) {
95+
return null;
96+
} else {
97+
mchre.setContextValue("recursiveDirectoryCreationTarget", targetPath);
98+
throw mchre;
99+
}
100+
}
101+
}
102+
}

0 commit comments

Comments
 (0)