FileResourceFactory to generate stable file names for the same request Id and entity tag
This commit is contained in:
parent
d787637e6e
commit
f4f5f73be2
|
@ -1,90 +0,0 @@
|
||||||
/*
|
|
||||||
* ====================================================================
|
|
||||||
* Licensed to the Apache Software Foundation (ASF) under one
|
|
||||||
* or more contributor license agreements. See the NOTICE file
|
|
||||||
* distributed with this work for additional information
|
|
||||||
* regarding copyright ownership. The ASF licenses this file
|
|
||||||
* to you 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
|
|
||||||
*
|
|
||||||
* http://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.
|
|
||||||
* ====================================================================
|
|
||||||
*
|
|
||||||
* This software consists of voluntary contributions made by many
|
|
||||||
* individuals on behalf of the Apache Software Foundation. For more
|
|
||||||
* information on the Apache Software Foundation, please see
|
|
||||||
* <http://www.apache.org/>.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
package org.apache.hc.client5.http.impl.cache;
|
|
||||||
|
|
||||||
import java.net.InetAddress;
|
|
||||||
import java.net.UnknownHostException;
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
import java.security.SecureRandom;
|
|
||||||
import java.util.Formatter;
|
|
||||||
import java.util.Locale;
|
|
||||||
import java.util.concurrent.locks.ReentrantLock;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Should produce reasonably unique tokens.
|
|
||||||
*/
|
|
||||||
class BasicIdGenerator {
|
|
||||||
|
|
||||||
private final String hostname;
|
|
||||||
private final SecureRandom rnd;
|
|
||||||
|
|
||||||
private long count;
|
|
||||||
|
|
||||||
private final ReentrantLock lock;
|
|
||||||
|
|
||||||
public BasicIdGenerator() {
|
|
||||||
super();
|
|
||||||
String hostname;
|
|
||||||
try {
|
|
||||||
hostname = InetAddress.getLocalHost().getHostName();
|
|
||||||
} catch (final UnknownHostException ex) {
|
|
||||||
hostname = "localhost";
|
|
||||||
}
|
|
||||||
this.hostname = hostname;
|
|
||||||
try {
|
|
||||||
this.rnd = SecureRandom.getInstance("SHA1PRNG");
|
|
||||||
} catch (final NoSuchAlgorithmException ex) {
|
|
||||||
throw new Error(ex);
|
|
||||||
}
|
|
||||||
this.rnd.setSeed(System.currentTimeMillis());
|
|
||||||
this.lock = new ReentrantLock();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void generate(final StringBuilder buffer) {
|
|
||||||
lock.lock();
|
|
||||||
try {
|
|
||||||
this.count++;
|
|
||||||
final int rndnum = this.rnd.nextInt();
|
|
||||||
buffer.append(System.currentTimeMillis());
|
|
||||||
buffer.append('.');
|
|
||||||
try (Formatter formatter = new Formatter(buffer, Locale.ROOT)) {
|
|
||||||
formatter.format("%1$016x-%2$08x", this.count, rndnum);
|
|
||||||
}
|
|
||||||
buffer.append('.');
|
|
||||||
buffer.append(this.hostname);
|
|
||||||
} finally {
|
|
||||||
lock.unlock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public String generate() {
|
|
||||||
final StringBuilder buffer = new StringBuilder();
|
|
||||||
generate(buffer);
|
|
||||||
return buffer.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -29,13 +29,17 @@ package org.apache.hc.client5.http.impl.cache;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.security.MessageDigest;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
|
||||||
import org.apache.hc.client5.http.cache.Resource;
|
import org.apache.hc.client5.http.cache.Resource;
|
||||||
import org.apache.hc.client5.http.cache.ResourceFactory;
|
import org.apache.hc.client5.http.cache.ResourceFactory;
|
||||||
import org.apache.hc.client5.http.cache.ResourceIOException;
|
import org.apache.hc.client5.http.cache.ResourceIOException;
|
||||||
import org.apache.hc.core5.annotation.Contract;
|
import org.apache.hc.core5.annotation.Contract;
|
||||||
import org.apache.hc.core5.annotation.ThreadingBehavior;
|
import org.apache.hc.core5.annotation.ThreadingBehavior;
|
||||||
|
import org.apache.hc.core5.net.PercentCodec;
|
||||||
import org.apache.hc.core5.util.Args;
|
import org.apache.hc.core5.util.Args;
|
||||||
|
import org.apache.hc.core5.util.TextUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates {@link Resource} instances whose body is stored in a temporary file.
|
* Generates {@link Resource} instances whose body is stored in a temporary file.
|
||||||
|
@ -46,37 +50,44 @@ import org.apache.hc.core5.util.Args;
|
||||||
public class FileResourceFactory implements ResourceFactory {
|
public class FileResourceFactory implements ResourceFactory {
|
||||||
|
|
||||||
private final File cacheDir;
|
private final File cacheDir;
|
||||||
private final BasicIdGenerator idgen;
|
|
||||||
|
|
||||||
public FileResourceFactory(final File cacheDir) {
|
public FileResourceFactory(final File cacheDir) {
|
||||||
super();
|
super();
|
||||||
this.cacheDir = cacheDir;
|
this.cacheDir = cacheDir;
|
||||||
this.idgen = new BasicIdGenerator();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private File generateUniqueCacheFile(final String requestId) {
|
static String generateUniqueCacheFileName(final String requestId, final String eTag, final byte[] content, final int off, final int len) {
|
||||||
final StringBuilder buffer = new StringBuilder();
|
final StringBuilder buf = new StringBuilder();
|
||||||
this.idgen.generate(buffer);
|
if (eTag != null) {
|
||||||
buffer.append('.');
|
PercentCodec.RFC3986.encode(buf, eTag);
|
||||||
final int len = Math.min(requestId.length(), 100);
|
buf.append('@');
|
||||||
for (int i = 0; i < len; i++) {
|
} else if (content != null) {
|
||||||
final char ch = requestId.charAt(i);
|
final MessageDigest sha256;
|
||||||
if (Character.isLetterOrDigit(ch) || ch == '.') {
|
try {
|
||||||
buffer.append(ch);
|
sha256 = MessageDigest.getInstance("SHA-256");
|
||||||
} else {
|
} catch (final NoSuchAlgorithmException ex) {
|
||||||
buffer.append('-');
|
throw new IllegalStateException(ex);
|
||||||
}
|
}
|
||||||
|
sha256.update(content, off, len);
|
||||||
|
buf.append(TextUtils.toHexString(sha256.digest()));
|
||||||
|
buf.append('@');
|
||||||
}
|
}
|
||||||
return new File(this.cacheDir, buffer.toString());
|
PercentCodec.RFC3986.encode(buf, requestId);
|
||||||
|
return buf.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 5.4
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public Resource generate(
|
public Resource generate(
|
||||||
final String requestId,
|
final String requestId,
|
||||||
|
final String eTag,
|
||||||
final byte[] content, final int off, final int len) throws ResourceIOException {
|
final byte[] content, final int off, final int len) throws ResourceIOException {
|
||||||
Args.notNull(requestId, "Request id");
|
Args.notNull(requestId, "Request id");
|
||||||
final File file = generateUniqueCacheFile(requestId);
|
final String filename = generateUniqueCacheFileName(requestId, eTag, content, off, len);
|
||||||
try (FileOutputStream outStream = new FileOutputStream(file)) {
|
final File file = new File(cacheDir, filename);
|
||||||
|
try (FileOutputStream outStream = new FileOutputStream(file, false)) {
|
||||||
if (content != null) {
|
if (content != null) {
|
||||||
outStream.write(content, off, len);
|
outStream.write(content, off, len);
|
||||||
}
|
}
|
||||||
|
@ -86,10 +97,22 @@ public class FileResourceFactory implements ResourceFactory {
|
||||||
return new FileResource(file);
|
return new FileResource(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Resource generate(final String requestId, final byte[] content, final int off, final int len) throws ResourceIOException {
|
||||||
|
if (content != null) {
|
||||||
|
return generate(requestId, null, content, off, len);
|
||||||
|
} else {
|
||||||
|
return generate(requestId, null, null, 0, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Resource generate(final String requestId, final byte[] content) throws ResourceIOException {
|
public Resource generate(final String requestId, final byte[] content) throws ResourceIOException {
|
||||||
Args.notNull(content, "Content");
|
if (content != null) {
|
||||||
return generate(requestId, content, 0, content.length);
|
return generate(requestId, null, content, 0, content.length);
|
||||||
|
} else {
|
||||||
|
return generate(requestId, null, null, 0, 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -0,0 +1,69 @@
|
||||||
|
/*
|
||||||
|
* ====================================================================
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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
|
||||||
|
*
|
||||||
|
* http://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.
|
||||||
|
* ====================================================================
|
||||||
|
*
|
||||||
|
* This software consists of voluntary contributions made by many
|
||||||
|
* individuals on behalf of the Apache Software Foundation. For more
|
||||||
|
* information on the Apache Software Foundation, please see
|
||||||
|
* <http://www.apache.org/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package org.apache.hc.client5.http.impl.cache;
|
||||||
|
|
||||||
|
|
||||||
|
import java.net.URI;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Assertions;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
public class TestFileResourceFactory {
|
||||||
|
|
||||||
|
CacheKeyGenerator keyGenerator;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
public void setUp() {
|
||||||
|
keyGenerator = new CacheKeyGenerator();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testViaValueLookup() throws Exception {
|
||||||
|
final String requestId = keyGenerator.generateKey(new URI("http://localhost/stuff"));
|
||||||
|
|
||||||
|
Assertions.assertEquals(
|
||||||
|
"blah%20blah@http%3A%2F%2Flocalhost%3A80%2Fstuff",
|
||||||
|
FileResourceFactory.generateUniqueCacheFileName(requestId, "blah blah", null, 0, 0));
|
||||||
|
Assertions.assertEquals(
|
||||||
|
"blah-blah@http%3A%2F%2Flocalhost%3A80%2Fstuff",
|
||||||
|
FileResourceFactory.generateUniqueCacheFileName(requestId, "blah-blah", null, 0, 0));
|
||||||
|
Assertions.assertEquals(
|
||||||
|
"blah%40blah@http%3A%2F%2Flocalhost%3A80%2Fstuff",
|
||||||
|
FileResourceFactory.generateUniqueCacheFileName(requestId, "blah@blah", null, 0, 0));
|
||||||
|
Assertions.assertEquals(
|
||||||
|
"039058c6f2c0cb492c533b0a4d14ef77cc0f78abccced5287d84a1a2011cfb81@http%3A%2F%2Flocalhost%3A80%2Fstuff",
|
||||||
|
FileResourceFactory.generateUniqueCacheFileName(requestId, null, new byte[]{1, 2, 3}, 0, 3));
|
||||||
|
Assertions.assertEquals(
|
||||||
|
"039058c6f2c0cb492c533b0a4d14ef77cc0f78abccced5287d84a1a2011cfb81@http%3A%2F%2Flocalhost%3A80%2Fstuff",
|
||||||
|
FileResourceFactory.generateUniqueCacheFileName(requestId, null, new byte[]{1, 2, 3, 4, 5}, 0, 3));
|
||||||
|
Assertions.assertEquals(
|
||||||
|
"http%3A%2F%2Flocalhost%3A80%2Fstuff",
|
||||||
|
FileResourceFactory.generateUniqueCacheFileName(requestId, null, null, 0, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue