Skip to content

Commit 5bdbc7b

Browse files
committed
binary stream method returns with range and content-length info
1 parent 836e0b9 commit 5bdbc7b

File tree

8 files changed

+587
-36
lines changed

8 files changed

+587
-36
lines changed

dsf-bpe/dsf-bpe-process-api-v2-impl/src/main/java/dev/dsf/bpe/v2/client/dsf/BasicDsfClientWithRetryImpl.java

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,17 +76,32 @@ public Bundle search(Class<? extends Resource> resourceType, Map<String, List<St
7676
}
7777

7878
@Override
79-
public InputStream readBinary(String id, String version, MediaType mediaType)
79+
public BinaryInputStream readBinary(String id, String version, MediaType mediaType)
8080
{
8181
return retry(() -> delegate.readBinary(id, version, mediaType));
8282
}
8383

8484
@Override
85-
public InputStream readBinary(String id, MediaType mediaType)
85+
public BinaryInputStream readBinary(String id, String version, MediaType mediaType, Long rangeStart,
86+
Long rangeEndInclusive, Map<String, String> additionalHeaders)
87+
{
88+
return retry(
89+
() -> delegate.readBinary(id, version, mediaType, rangeStart, rangeEndInclusive, additionalHeaders));
90+
}
91+
92+
@Override
93+
public BinaryInputStream readBinary(String id, MediaType mediaType)
8694
{
8795
return retry(() -> delegate.readBinary(id, mediaType));
8896
}
8997

98+
@Override
99+
public BinaryInputStream readBinary(String id, MediaType mediaType, Long rangeStart, Long rangeEndInclusive,
100+
Map<String, String> additionalHeaders)
101+
{
102+
return retry(() -> delegate.readBinary(id, mediaType, rangeStart, rangeEndInclusive, additionalHeaders));
103+
}
104+
90105
@Override
91106
public <R extends Resource> R read(Class<R> resourceType, String id, String version)
92107
{

dsf-bpe/dsf-bpe-process-api-v2-impl/src/main/java/dev/dsf/bpe/v2/client/dsf/DsfClientJersey.java

Lines changed: 138 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
import java.util.TimeZone;
1414
import java.util.concurrent.TimeUnit;
1515
import java.util.logging.Level;
16+
import java.util.regex.Matcher;
17+
import java.util.regex.Pattern;
1618
import java.util.stream.Collectors;
1719
import java.util.stream.Stream;
1820

@@ -42,6 +44,7 @@
4244
import ca.uhn.fhir.context.FhirContext;
4345
import ca.uhn.fhir.model.api.annotation.ResourceDef;
4446
import ca.uhn.fhir.rest.api.Constants;
47+
import dev.dsf.bpe.v2.client.dsf.BinaryInputStream.Range;
4548
import jakarta.ws.rs.ProcessingException;
4649
import jakarta.ws.rs.WebApplicationException;
4750
import jakarta.ws.rs.client.Client;
@@ -72,6 +75,8 @@ public class DsfClientJersey implements DsfClient
7275
private static final Map<String, Class<?>> RESOURCE_TYPES_BY_NAME = Stream.of(ResourceType.values())
7376
.filter(type -> !ResourceType.List.equals(type))
7477
.collect(Collectors.toMap(ResourceType::name, DsfClientJersey::getFhirClass));
78+
private static final String CONTENT_RANGE_PATTERN_TEXT = "bytes (?<start>\\d+)-(?<end>\\d+)\\/(?<size>\\d+)";
79+
private static final Pattern CONTENT_RANGE_PATTERN = Pattern.compile(CONTENT_RANGE_PATTERN_TEXT);
7580

7681
private static Class<?> getFhirClass(ResourceType type)
7782
{
@@ -570,7 +575,7 @@ else if (Status.NOT_FOUND.getStatusCode() == response.getStatus())
570575
}
571576

572577
@Override
573-
public InputStream readBinary(String id, MediaType mediaType)
578+
public BinaryInputStream readBinary(String id, MediaType mediaType)
574579
{
575580
Objects.requireNonNull(id, "id");
576581
Objects.requireNonNull(mediaType, "mediaType");
@@ -580,11 +585,108 @@ public InputStream readBinary(String id, MediaType mediaType)
580585
logger.debug("HTTP {}: {}", response.getStatusInfo().getStatusCode(),
581586
response.getStatusInfo().getReasonPhrase());
582587
if (Status.OK.getStatusCode() == response.getStatus())
583-
return response.readEntity(InputStream.class);
588+
return toBinaryInputStream(response);
584589
else
585590
throw handleError(response);
586591
}
587592

593+
@Override
594+
public BinaryInputStream readBinary(String id, MediaType mediaType, Long rangeStart, Long rangeEndInclusive,
595+
Map<String, String> additionalHeaders)
596+
{
597+
Objects.requireNonNull(id, "id");
598+
Objects.requireNonNull(mediaType, "mediaType");
599+
600+
Builder builder = getResource().path("Binary").path(id).request().accept(mediaType);
601+
602+
String range = getRangeHeader(rangeStart, rangeEndInclusive);
603+
if (range != null)
604+
builder = builder.header("Range", range);
605+
606+
if (additionalHeaders != null)
607+
{
608+
for (Entry<String, String> e : additionalHeaders.entrySet())
609+
builder = builder.header(e.getKey(), e.getValue());
610+
}
611+
612+
Response response = builder.get();
613+
614+
logger.debug("HTTP {}: {}", response.getStatusInfo().getStatusCode(),
615+
response.getStatusInfo().getReasonPhrase());
616+
if (Status.OK.getStatusCode() == response.getStatus()
617+
|| Status.PARTIAL_CONTENT.getStatusCode() == response.getStatus())
618+
return toBinaryInputStream(response);
619+
else
620+
throw handleError(response);
621+
}
622+
623+
private String getRangeHeader(Long rangeStart, Long rangeEndInclusive)
624+
{
625+
// from given start to end of file
626+
if (rangeStart != null && rangeStart >= 0 && rangeEndInclusive == null)
627+
{
628+
return "bytes=" + rangeStart + "-";
629+
}
630+
// from given start to given end (inclusive)
631+
else if (rangeStart != null && rangeStart >= 0 && rangeEndInclusive != null && rangeEndInclusive > rangeStart)
632+
{
633+
return "bytes=" + rangeStart + "-" + rangeEndInclusive;
634+
}
635+
// from length + end to end of file
636+
else if (rangeStart == null && rangeEndInclusive != null && rangeEndInclusive < 0)
637+
{
638+
return "bytes=" + rangeEndInclusive;
639+
}
640+
else
641+
return null;
642+
}
643+
644+
private BinaryInputStream toBinaryInputStream(Response response)
645+
{
646+
long contentLength = getContentLength(response);
647+
Range range = getRange(response);
648+
InputStream input = response.readEntity(InputStream.class);
649+
650+
return new BinaryInputStream(input, contentLength, range);
651+
}
652+
653+
private long getContentLength(Response response)
654+
{
655+
try
656+
{
657+
return Long.parseLong(response.getHeaderString("Content-Length"));
658+
}
659+
catch (NumberFormatException e)
660+
{
661+
return Long.MIN_VALUE;
662+
}
663+
}
664+
665+
private Range getRange(Response response)
666+
{
667+
String contentRange = response.getHeaderString("Content-Range");
668+
if (contentRange == null)
669+
return null;
670+
671+
Matcher matcher = CONTENT_RANGE_PATTERN.matcher(contentRange);
672+
if (matcher.matches())
673+
{
674+
try
675+
{
676+
long start = Long.parseLong(matcher.group("start"));
677+
long end = Long.parseLong(matcher.group("end"));
678+
long size = Long.parseLong(matcher.group("size"));
679+
680+
return new Range(size, start, end);
681+
}
682+
catch (NumberFormatException e)
683+
{
684+
}
685+
}
686+
687+
return null;
688+
}
689+
588690
@Override
589691
public Resource read(String resourceTypeName, String id, String version)
590692
{
@@ -644,7 +746,7 @@ else if (Status.NOT_FOUND.getStatusCode() == response.getStatus())
644746
}
645747

646748
@Override
647-
public InputStream readBinary(String id, String version, MediaType mediaType)
749+
public BinaryInputStream readBinary(String id, String version, MediaType mediaType)
648750
{
649751
Objects.requireNonNull(id, "id");
650752
Objects.requireNonNull(version, "version");
@@ -656,7 +758,39 @@ public InputStream readBinary(String id, String version, MediaType mediaType)
656758
logger.debug("HTTP {}: {}", response.getStatusInfo().getStatusCode(),
657759
response.getStatusInfo().getReasonPhrase());
658760
if (Status.OK.getStatusCode() == response.getStatus())
659-
return response.readEntity(InputStream.class);
761+
return toBinaryInputStream(response);
762+
else
763+
throw handleError(response);
764+
}
765+
766+
@Override
767+
public BinaryInputStream readBinary(String id, String version, MediaType mediaType, Long rangeStart,
768+
Long rangeEndInclusive, Map<String, String> additionalHeaders)
769+
{
770+
Objects.requireNonNull(id, "id");
771+
Objects.requireNonNull(version, "version");
772+
Objects.requireNonNull(mediaType, "mediaType");
773+
774+
Builder builder = getResource().path("Binary").path(id).path("_history").path(version).request()
775+
.accept(mediaType);
776+
777+
String range = getRangeHeader(rangeStart, rangeEndInclusive);
778+
if (range != null)
779+
builder = builder.header("Range", range);
780+
781+
if (additionalHeaders != null)
782+
{
783+
for (Entry<String, String> e : additionalHeaders.entrySet())
784+
builder = builder.header(e.getKey(), e.getValue());
785+
}
786+
787+
Response response = builder.get();
788+
789+
logger.debug("HTTP {}: {}", response.getStatusInfo().getStatusCode(),
790+
response.getStatusInfo().getReasonPhrase());
791+
if (Status.OK.getStatusCode() == response.getStatus()
792+
|| Status.PARTIAL_CONTENT.getStatusCode() == response.getStatus())
793+
return toBinaryInputStream(response);
660794
else
661795
throw handleError(response);
662796
}

dsf-bpe/dsf-bpe-process-api-v2/src/main/java/dev/dsf/bpe/v2/client/dsf/BasicDsfClient.java

Lines changed: 79 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package dev.dsf.bpe.v2.client.dsf;
22

3-
import java.io.InputStream;
43
import java.util.List;
54
import java.util.Map;
65

@@ -50,9 +49,44 @@ public interface BasicDsfClient extends PreferReturnResource
5049
* not <code>null</code>
5150
* @param mediaType
5251
* not <code>null</code>
53-
* @return {@link InputStream} needs to be closed
52+
* @return {@link BinaryInputStream} needs to be closed
5453
*/
55-
InputStream readBinary(String id, MediaType mediaType);
54+
BinaryInputStream readBinary(String id, MediaType mediaType);
55+
56+
/**
57+
* @param id
58+
* not <code>null</code>
59+
* @param mediaType
60+
* not <code>null</code>
61+
* @param rangeStart
62+
* <code>null</code> if suffix range (<b>rangeEndInclusive</b> <code>&lt;0</code>), else <code>>=0</code>
63+
* @param rangeEndInclusive
64+
* <code>null</code> if range from <b>rangeStart</b> to end of file, <code>&lt;0</code> if suffix range
65+
* (<b>rangeStart</b> <code>null</code>), <code>>=rangeStart</code> for range end
66+
* @return {@link BinaryInputStream} needs to be closed
67+
*/
68+
default BinaryInputStream readBinary(String id, MediaType mediaType, Long rangeStart, Long rangeEndInclusive)
69+
{
70+
return readBinary(id, mediaType, rangeStart, rangeEndInclusive, null);
71+
}
72+
73+
/**
74+
* @param id
75+
* not <code>null</code>
76+
* @param mediaType
77+
* not <code>null</code>
78+
* @param rangeStart
79+
* <code>null</code> if suffix range (<b>rangeEndInclusive</b> <code>&lt;0</code>), else <code>>=0</code>
80+
* @param rangeEndInclusive
81+
* <code>null</code> if range from <b>rangeStart</b> to end of file, <code>&lt;0</code> if suffix range
82+
* (<b>rangeStart</b> <code>null</code>), <code>>=rangeStart</code> for range end
83+
* @param additionalHeaders
84+
* may be <code>null</code>, use to set values of headers like "If-Unmodified-Since", "If-Match" and
85+
* "If-Range"
86+
* @return {@link BinaryInputStream} needs to be closed
87+
*/
88+
BinaryInputStream readBinary(String id, MediaType mediaType, Long rangeStart, Long rangeEndInclusive,
89+
Map<String, String> additionalHeaders);
5690

5791
/**
5892
* @param resourceTypeName
@@ -76,9 +110,49 @@ public interface BasicDsfClient extends PreferReturnResource
76110
* not <code>null</code>
77111
* @param mediaType
78112
* not <code>null</code>
79-
* @return {@link InputStream} needs to be closed
113+
* @return {@link BinaryInputStream} needs to be closed
114+
*/
115+
BinaryInputStream readBinary(String id, String version, MediaType mediaType);
116+
117+
/**
118+
* @param id
119+
* not <code>null</code>
120+
* @param version
121+
* not <code>null</code>
122+
* @param mediaType
123+
* not <code>null</code>
124+
* @param rangeStart
125+
* <code>null</code> if suffix range (<b>rangeEndInclusive</b> <code>&lt;0</code>), else <code>>=0</code>
126+
* @param rangeEndInclusive
127+
* <code>null</code> if range from <b>rangeStart</b> to end of file, <code>&lt;0</code> if suffix range
128+
* (<b>rangeStart</b> <code>null</code>), <code>>=rangeStart</code> for range end
129+
* @return {@link BinaryInputStream} needs to be closed
130+
*/
131+
default BinaryInputStream readBinary(String id, String version, MediaType mediaType, Long rangeStart,
132+
Long rangeEndInclusive)
133+
{
134+
return readBinary(id, version, mediaType, rangeStart, rangeEndInclusive, null);
135+
}
136+
137+
/**
138+
* @param id
139+
* not <code>null</code>
140+
* @param version
141+
* not <code>null</code>
142+
* @param mediaType
143+
* not <code>null</code>
144+
* @param rangeStart
145+
* <code>null</code> if suffix range (<b>rangeEndInclusive</b> <code>&lt;0</code>), else <code>>=0</code>
146+
* @param rangeEndInclusive
147+
* <code>null</code> if range from <b>rangeStart</b> to end of file, <code>&lt;0</code> if suffix range
148+
* (<b>rangeStart</b> <code>null</code>), <code>>=rangeStart</code> for range end
149+
* @param additionalHeaders
150+
* may be <code>null</code>, use to set values of headers like "If-Unmodified-Since", "If-Match" and
151+
* "If-Range"
152+
* @return {@link BinaryInputStream} needs to be closed
80153
*/
81-
InputStream readBinary(String id, String version, MediaType mediaType);
154+
BinaryInputStream readBinary(String id, String version, MediaType mediaType, Long rangeStart,
155+
Long rangeEndInclusive, Map<String, String> additionalHeaders);
82156

83157
boolean exists(IdType resourceTypeIdVersion);
84158

0 commit comments

Comments
 (0)