How to parse List<SomeEntity> in jersey response.readEntity? - java

I have the client that makes a rest call to a server like this:
Entity<RequestObject> entity = Entity.json(new RequestObject(Arrays.asList(1, 2, 3), Arrays.asList("xColumn", "yColumn")));
ClientConfig config = new ClientConfig();
config.register(JacksonJsonProvider.class);
Client client = ClientBuilder.newClient(config);
Response response = client.target("http://172.18.0.1:10000/getProductsData")
.request(MediaType.APPLICATION_JSON_TYPE)
.post(Entity.json(entity.getEntity()), Response.class);
List<WarehouseProductData> dwsData = response.readEntity(new GenericType<List<WarehouseProductData>>(){});
client.close();
The the other server is responding from the following method:
#PostMapping("/getProductsData")
public List<WarehouseProductData> greetingPost1(#RequestBody String json) {
WarehouseProductData wd = new WarehouseProductData(85654865);
wd.addData("xColumn", "value of product 85654865 for x column");
wd.addData("yColumn", "value of product 85654865 for y column");
wd.addData("the me column", "value of product 85654865 for 'the me column' column");
wd.addData("response for x", "1");
wd.addData("response for x", "2");
return Collections.singletonList(wd);
The WarehauseProductData class is like this:
public class WarehouseProductData {
private int agoId;
private Map<String, String> data;
public WarehouseProductData(int agoId, Map<String, String> data) {
this.agoId = agoId;
this.data = data;
}
public WarehouseProductData(int agoId) {
this.agoId = agoId;
this.data = new HashMap<>();
}
public int getAgoId() {
return agoId;
}
public void setAgoId(int agoId) {
this.agoId = agoId;
}
public Map<String, String> getData() {
return data;
}
public void setData(Map<String, String> data) {
this.data = data;
}
public void addData(String columnName, String value) {
data.put(columnName, value);
}
}
The problem is that when the
List<WarehouseProductData> dwsData = response.readEntity(new GenericType<List<WarehouseProductData>>(){});
is ran the dwsData is like this:
dwsData size = 1
|
--0 {WarehouseProductData}
|
-- agoId = 85654865
|
-- data = null (!!!which is incorect!!!)
So how to parse the response in order to get the data field correctly?

Related

compress and upload multiple files with progress using RxWorker

I try to upload multiple images to the server and before that, I want to compress my images. for compressing part I use AdvancedLuban Library in my project to compress selected images of users and then for uploading part I use Retrofit in RxJava way. I do all in my presenter class and all of this works properly. My problem is I want to do all of this in my UploadWorker class which is inherited from RxWorker and while doing uploading show progress in the notification.
The code I wrote does not work properly and notification progress did not update correctly.
Here is my UploadWorker.class
public class UploadWorker extends RxWorker {
private static final String TAG = UploadWorker.class.getSimpleName();
public static final String KEY_STRING_DATA = "string_data";
private static final int COMPRESS_MAX_SIZE = 200;
private static final int PROGRESS_MAX = 100;
private Context context;
private FileUploader uploader;
public UploadWorker(#NonNull Context context, #NonNull WorkerParameters workerParams) {
super(context, workerParams);
this.context = context;
ApiService apiService = ServiceBuilder.buildService(ApiService.class);
uploader = new FileUploader(apiService);
}
#NonNull
#Override
public Single<Result> createWork() {
Data data = getInputData();
String strData = data.getString(KEY_STRING_DATA);
List<String> stringList = deserializeFromJson(strData);
List<File> files = new ArrayList<>();
for (String path : stringList) {
File f = new File(path);
files.add(f);
}
return Single.fromObservable(Luban.compress(context,files)
.setMaxSize(COMPRESS_MAX_SIZE)
.putGear(Luban.CUSTOM_GEAR)
.setCompressFormat(Bitmap.CompressFormat.JPEG)
.asListObservable()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.map(new Function<List<File>, ArrayList<String>>() {
#Override
public ArrayList<String> apply(List<File> files) throws Exception {
ArrayList<String> filListPath = new ArrayList<>();
for (File file:files) {
filListPath.add(file.getAbsolutePath());
}
return filListPath ;
}
})
.map(new Function<ArrayList<String>, Disposable>() {
#Override
public Disposable apply(ArrayList<String> strings) throws Exception {
HashMap<String, RequestBody> map = new HashMap<>();
return getUploadObserver(map,strings);
}
}).map(new Function<Disposable, Result>() {
#Override
public Result apply(Disposable disposable) throws Exception {
return Result.success();
}
})
);
}
private Disposable getUploadObserver(HashMap<String, RequestBody> map, ArrayList<String> files) {
return uploader.uploadMultiImage(map, files)
.subscribeOn(Schedulers.computation())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<Double>() {
#Override
public void accept(Double progress) throws Exception {
notifyUpload((int) (100 * progress));
Log.d(TAG, "accept: " + (int) (100 * progress));
}
});
}
public void notifyUpload(int progress) {
NotificationManagerCompat notificationManagerCompat = NotificationManagerCompat.from(context);
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, Config.NOTIFICATION_CHANNEL);
builder.setSmallIcon(R.drawable.ic_notification_icon)
.setContentTitle("Upload")
.setContentText("Uploading in progress")
.setPriority(NotificationCompat.PRIORITY_LOW)
.setAutoCancel(true);
if (progress < PROGRESS_MAX) {
builder.setProgress(PROGRESS_MAX, progress, false);
}else {
builder.setContentText("Upload complete")
.setProgress(0,0,false);
}
notificationManagerCompat.notify(200, builder.build());
}
public static List<String> deserializeFromJson(String jsonString){
Gson gson = new Gson();
Type listOf = new TypeToken<ArrayList<String>>() {}.getType();
return gson.fromJson(jsonString,listOf);
}
}
FileUploader.class
public class FileUploader implements FileUploaderContract{
private final ApiService service;
private static final String TAG = FileUploaderModel.class.getSimpleName();
public FileUploaderModel(ApiService service) {
this.service = service;
}
#Override
public Flowable<Double> uploadMultiImage(HashMap<String,RequestBody> map, ArrayList<String> filePaths) {
return Flowable.create(new FlowableOnSubscribe<Double>() {
#Override
public void subscribe(FlowableEmitter<Double> emitter) throws Exception {
try {
List<MultipartBody.Part> myPart = new ArrayList<>();
for (String path:filePaths) {
myPart.add(createMultipartBody(path, emitter));
}
ResponseBody response = service.postMultipleImage(map,myPart).blockingGet();
Log.d(TAG, "subscribe: " + response);
emitter.onComplete();
} catch (Exception e) {
emitter.tryOnError(e);
}
}
}, BackpressureStrategy.LATEST);
}
#NonNull
private RequestBody createPartFromString(String descriptionString) {
return RequestBody.create(MultipartBody.FORM, descriptionString);
}
private MultipartBody.Part createMultipartBody(String filePath, FlowableEmitter<Double> emitter) {
File file = new File(filePath);
return MultipartBody.Part.createFormData("image", file.getName(), createCountingRequestBody(file, emitter));
}
private RequestBody createCountingRequestBody(File file, FlowableEmitter<Double> emitter) {
RequestBody requestBody = createRequestBody(file);
return new CountingRequestBody(requestBody, (bytesWritten, contentLength) -> {
double progress = (1.0 * bytesWritten) / contentLength;
emitter.onNext(progress);
});
}
}
And call my UploadWorker in my MainActivity like below
String strData = serializeToJson(mAdapter.getImageList());
Data data = new Data.Builder()
.putString(UploadWorker.KEY_STRING_DATA,strData)
.build();
WorkRequest mRequestWork = new OneTimeWorkRequest.Builder(UploadWorker.class)
.setInitialDelay(1, TimeUnit.SECONDS)
.setInputData(data)
.build();
WorkManager.getInstance(getContext()).enqueue(mRequestWork)

Check if item in text file in JSON format Android Studio

I'm trying to check if config1 exists in a text file, I'm using Google's Gson library.
My JSON file :
{
"maps":{
"config2":{
"component1":"url1",
"component2":"url1",
"component3":"url1"
},
"config1":{
"component1":"url1",
"component2":"url1",
"component3":"url1"
}
}
}
Loading :
public void load() throws IOException {
File file = getContext().getFileStreamPath("jsonfile.txt");
FileInputStream fis = getContext().openFileInput("jsonfile.txt");
InputStreamReader isr = new InputStreamReader(fis);
BufferedReader bufferedReader = new BufferedReader(isr);
StringBuilder sb = new StringBuilder();
String line;
while ((line = bufferedReader.readLine()) != null) {
sb.append(line);
}
String json = sb.toString();
Gson gson = new Gson();
Data data = gson.fromJson(json, Data.class);
componentURL= data.getMap().get("config1").get("component1");
Saving :
Gson gson = new Gson();
webViewActivity.Data data = gson.fromJson(json, webViewActivity.Data.class);
Map<String, String> configTest = data.getMap().get("config1");
data.getMap().get("config1").put(component, itemUrl);
String json = gson.toJson(data);
String filename = "jsonfile.txt";
FileOutputStream outputStream;
try {
outputStream = openFileOutput(filename, Context.MODE_PRIVATE);
outputStream.write(json.getBytes());
outputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
Data class :
public class Data {
private Map<String, Map<String, String>> map;
public Data() {
}
public Data(Map<String, Map<String, String>> map) {
this.map = map;
}
public Map<String, Map<String, String>> getMap() {
return map;
}
public void setMap(Map<String, Map<String, String>> map) {
this.map = map;
}
}
My problem is that I need to create the file once and then check if the file exists, if it does I need to check if config1 exists if it doesn't I need to put config1 in the file.
But I can't check if config1 exists because I get :
java.lang.NullPointerException: Attempt to invoke virtual method 'java.util.Map com.a.app.ui.app.appFragment$Data.getMap()
I check if it exists by doing :
Boolean configTest = data.getMap().containsKey("config1");
if(!configTest){}
How can I create the file and check the data without getting a NullPointerException ?
I think you should modify the way you're handling things.
First create POJO for Config1 each values as:
// file Config1.java
public class Config1
{
private String component1;
private String component2;
private String component3;
public String getComponent1 ()
{
return component1;
}
public void setComponent1 (String component1)
{
this.component1 = component1;
}
public String getComponent2 ()
{
return component2;
}
public void setComponent2 (String component2)
{
this.component2 = component2;
}
public String getComponent3 ()
{
return component3;
}
public void setComponent3 (String component3)
{
this.component3 = component3;
}
#Override
public String toString()
{
return "ClassPojo [component1 = "+component1+", component2 = "+component2+", component3 = "+component3+"]";
}
}
And then after that POJO for Config2
// file Config2.java
public class Config2
{
private String component1;
private String component2;
private String component3;
public String getComponent1 ()
{
return component1;
}
public void setComponent1 (String component1)
{
this.component1 = component1;
}
public String getComponent2 ()
{
return component2;
}
public void setComponent2 (String component2)
{
this.component2 = component2;
}
public String getComponent3 ()
{
return component3;
}
public void setComponent3 (String component3)
{
this.component3 = component3;
}
#Override
public String toString()
{
return "ClassPojo [component1 = "+component1+", component2 = "+component2+", component3 = "+component3+"]";
}
}
And then you need POJO for Maps
// file Maps.java
public class Maps
{
private Config2 config2;
private Config1 config1;
public Config2 getConfig2 ()
{
return config2;
}
public void setConfig2 (Config2 config2)
{
this.config2 = config2;
}
public Config1 getConfig1 ()
{
return config1;
}
public void setConfig1 (Config1 config1)
{
this.config1 = config1;
}
#Override
public String toString()
{
return "ClassPojo [config2 = "+config2+", config1 = "+config1+"]";
}
}
And finally the class which will wrap everything up MyJsonPojo. Though you can rename it to whatever you want.
// file MyJsonPojo.java
public class MyJsonPojo
{
private Maps maps;
public Maps getMaps ()
{
return maps;
}
public void setMaps (Maps maps)
{
this.maps = maps;
}
#Override
public String toString()
{
return "ClassPojo [maps = "+maps+"]";
}
}
Finally replace your code in the loadData() method as:
public void load() throws IOException {
File file = getContext().getFileStreamPath("jsonfile.txt");
FileInputStream fis = getContext().openFileInput("jsonfile.txt");
InputStreamReader isr = new InputStreamReader(fis);
BufferedReader bufferedReader = new BufferedReader(isr);
StringBuilder sb = new StringBuilder();
String line;
while ((line = bufferedReader.readLine()) != null) {
sb.append(line);
}
String json = sb.toString();
Gson gson = new Gson();
Data data = gson.fromJson(json, MyJsonPojo.class);
Maps maps = data.getMaps();
Config1 config1 = null;
if (maps != null) {
config1 = maps.getConfig1()
}
if (config1 != null) {
componentURL = config1.getComponent1();
}
}
For saving the values you can do this:
public void save() {
// set url here
Component1 component1 = new Component1();
component1.setComponent1(itemUrl);
// store it in maps
Maps maps = new Maps();
maps.setComponent1(component1);
// finally add it to the MyJsonPojo instance
MyJsonPojo myJsonPojo = new MyJsonPojo();
myJsonPojo.setMaps(maps);
Gson gson = new Gson();
String json = gson.toJson(maps);
String filename = "jsonfile.txt";
FileOutputStream outputStream;
try {
outputStream = openFileOutput(filename, Context.MODE_PRIVATE);
outputStream.write(json.getBytes());
outputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
}
Please note that you may have to modify the save() code as per your structure because I am quite unsure about how you have handled what in the code. I have provided the basic implementation without much proof reading my code.

initiallize the java bean using multiple values for ElasticSearch indexing

Am trying to create a java class where i want to create indexing in ElasticSearch. Actual data are available from REST API, but for testing my indexing code i have written a logic.
But now, i want to test my indexing code with few dummy data. For that i have created a bean class and using setter/getter i want to replicate the actual scenario for indexing documents in elasticsearch.
Please find my java code below :
public static void main(String args[]) throws IOException
{
System.out.println("Indexing via Java Code ....");
Product prod1=new Product("1001", 123172l, "Product", "VG3000");
Product prod2=new Product("1002", 123172l, "Series", "Valves, VG3000");
Product prod3=new Product("1003", 123172l, "Series", "Activa RoofTop, VG3000");
Product prod4=new Product("1004", 123172l, "Product", "Activa RoofTop VG3000, 3000");
Product prod=new Product();
Map<String, Object> jsonMap ;
for(int i=1;i<4;i++)
{
jsonMap = new HashMap<String, Object>();
jsonMap.put("id", prod.getId());
jsonMap.put("catalog_id", prod.getCatalog_id());
jsonMap.put("catalog_type", prod.getCatalog_type());
jsonMap.put("values", prod.getValues());
IndexRequest request = new IndexRequest(INDEX_NAME, "doc", prod.getId() )
.source(jsonMap);
try {
IndexResponse response = SearchEngineClient.getInstance3().index(request); // increased timeout
} catch(ElasticsearchException e) {
if (e.status() == RestStatus.CONFLICT) {
}
e.printStackTrace();
}
}
System.out.println("Indexing done....");
}
Please find my bean class :
public class Product {
public Product(String id, long catalog_id, String Catalog_type, String values)
{
this.id=id;
this.catalog_id=catalog_id;
this.catalog_type=catalog_type;
this.values=values;
}
public Product()
{
}
private String id;
private long catalog_id;
private String catalog_type;
private String values;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public Long getCatalog_id() {
return catalog_id;
}
public void setCatalog_id(Long catalog_id) {
this.catalog_id = catalog_id;
}
public String getCatalog_type() {
return catalog_type;
}
public void setCatalog_type(String catalog_type) {
this.catalog_type = catalog_type;
}
public String getValues() {
return values;
}
public void setValues(String values) {
this.values = values;
}
}
But, while indexing am getting the value from bean class which all the data coming as null.
**Update 1 :
I have modified the code in the below way :
public static void main(String args[]) throws IOException
{
System.out.println("Indexing via Java Code ....");
Product prod1=new Product("1001", 123172l, "Product", "VG3000");
Product prod2=new Product("1002", 123172l, "Series", "Valves, VG3000");
Product prod3=new Product("1003", 3536633, "Series", "Activa RoofTop, VG3000 abcd");
Product prod4=new Product("1004", 123172l, "Product", "Activa RoofTop VG3000, 3000");
Product prod=new Product();
IndexRequest request;
Map<String, Object> jsonMap ;
jsonMap = new HashMap<String, Object>();
jsonMap.put("id", prod1.getId());
jsonMap.put("catalog_id", prod1.getCatalog_id());
jsonMap.put("catalog_type", prod1.getCatalog_type());
jsonMap.put("values", prod1.getValues());
request = new IndexRequest(INDEX_NAME, "doc", prod1.getId() )
.source(jsonMap);
IndexResponse response1 = SearchEngineClient.getInstance3().index(request);
jsonMap = new HashMap<String, Object>();
jsonMap.put("id", prod2.getId());
jsonMap.put("catalog_id", prod2.getCatalog_id());
jsonMap.put("catalog_type", prod2.getCatalog_type());
jsonMap.put("values", prod2.getValues());
request = new IndexRequest(INDEX_NAME, "doc", prod2.getId() )
.source(jsonMap);
IndexResponse response2 = SearchEngineClient.getInstance3().index(request);
jsonMap = new HashMap<String, Object>();
jsonMap.put("id", prod3.getId());
jsonMap.put("catalog_id", prod3.getCatalog_id());
jsonMap.put("catalog_type", prod3.getCatalog_type());
jsonMap.put("values", prod3.getValues());
request = new IndexRequest(INDEX_NAME, "doc", prod3.getId() )
.source(jsonMap);
IndexResponse response3 = SearchEngineClient.getInstance3().index(request);
jsonMap = new HashMap<String, Object>();
jsonMap.put("id", prod4.getId());
jsonMap.put("catalog_id", prod4.getCatalog_id());
jsonMap.put("catalog_type", prod4.getCatalog_type());
jsonMap.put("values", prod4.getValues());
request = new IndexRequest(INDEX_NAME, "doc", prod4.getId() )
.source(jsonMap);
IndexResponse response4 = SearchEngineClient.getInstance3().index(request);
System.out.println("Indexing done....");
}
Is there any other way to simplify the same.?

Load very heavy stream with GSON

I'm trying to read a very heavy JSON (over than 6000 objects) and store them on a hash map to insert it into my database later.
But the problem is that I face with OOM and that's cause from my heavy JSON, however GSON library should rid me from this situation, but it is not !!!
Any ideas?
public Map<String,String> readJsonStream(InputStream in) throws IOException
{
JsonReader reader = new JsonReader(new InputStreamReader(in, "UTF-8"));
Map<String,String> contentMap = new HashMap<String,String>();
Gson mGson = new Gson();
contentMap = mGson.fromJson(reader, contentMap.getClass());
reader.close();
return contentMap;
}
From my experience, yes you can use google GSON to stream JSON data this is an example how to do it :
APIModel result = new APIModel();
try {
HttpResponse response;
HttpClient myClient = new DefaultHttpClient();
HttpPost myConnection = new HttpPost(APIParam.API_001_PRESENT(
serial_id, api_key));
try {
response = myClient.execute(myConnection);
Reader streamReader = new InputStreamReader(response
.getEntity().getContent());
JsonReader reader = new JsonReader(streamReader);
reader.beginObject();
while (reader.hasNext()) {
String name = reader.nextName();
if (name.equals("result")) {
if (reader.nextString() == "NG") {
result.setResult(Util.API_001_RESULT_NG);
break;
}
} else if (name.equals("items")) {
result = readItemsArray(reader);
} else {
reader.skipValue(); // avoid some unhandle events
}
}
reader.endObject();
reader.close();
} catch (Exception e) {
e.printStackTrace();
result.setResult(Util.API_001_RESULT_NG);
}
} catch (Exception e) {
e.printStackTrace();
result.setResult(Util.API_001_RESULT_NG);
}
readItemsArray function :
// read items array
private APIModel readItemsArray(JsonReader reader) throws IOException {
APIModel result = new APIModel();
String item_name, file_name, data;
result.setResult(Util.API_001_RESULT_OK);
reader.beginArray();
while (reader.hasNext()) {
item_name = "";
file_name = "";
data = "";
reader.beginObject();
while (reader.hasNext()) {
String name = reader.nextName();
if (name.equals("name")) {
item_name = reader.nextString();
} else if (name.equals("file")) {
file_name = reader.nextString();
} else if (name.equals("data")) {
data = reader.nextString();
} else {
reader.skipValue();
}
}
reader.endObject();
result.populateModel("null", item_name, file_name, data);
}
reader.endArray();
return result;
}
API Model Class :
public class APIModel {
private int result;
private String error_title;
private String error_message;
private ArrayList<String> type;
private ArrayList<String> item_name;
private ArrayList<String> file_name;
private ArrayList<String> data;
public APIModel() {
result = -1;
error_title = "";
error_message = "";
setType(new ArrayList<String>());
setItem_name(new ArrayList<String>());
setFile_name(new ArrayList<String>());
setData(new ArrayList<String>());
}
public void populateModel(String type, String item_name, String file_name, String data) {
this.type.add(type);
this.item_name.add(item_name);
this.file_name.add(file_name);
this.data.add(data);
}
public int getResult() {
return result;
}
public void setResult(int result) {
this.result = result;
}
public String getError_title() {
return error_title;
}
public void setError_title(String error_title) {
this.error_title = error_title;
}
public String getError_message() {
return error_message;
}
public void setError_message(String error_message) {
this.error_message = error_message;
}
public ArrayList<String> getType() {
return type;
}
public void setType(ArrayList<String> type) {
this.type = type;
}
public ArrayList<String> getItem_name() {
return item_name;
}
public void setItem_name(ArrayList<String> item_name) {
this.item_name = item_name;
}
public ArrayList<String> getFile_name() {
return file_name;
}
public void setFile_name(ArrayList<String> file_name) {
this.file_name = file_name;
}
public ArrayList<String> getData() {
return data;
}
public void setData(ArrayList<String> data) {
this.data = data;
}
}
before I use the streaming API from google GSON I also got OOM error because the JSON data I got is very big data (many images and sounds in Base64 encoding) but with GSON streaming I can overcome that error because it reads the data per token not all at once. And for Jackson JSON library I think it also have streaming API and how to use it almost same with my implementation with google GSON. I hope my answer can help you and if you have another question about my answer feel free to ask in the comment :)

How to enable resumable GET requests with Jersey?

I am creating a RESTful web service using Jersey. Some of the resources are binary files that I get from somewhere else on demand; such files are potentially big (hundreds of Mbytes).
I want browsers to GET those resources, so I have a #GET-annotated method returning a StreamingOutput, like in this answer.
I have two questions:
Is StreamingOutput the proper way of returning files?
What should I do in the server side to make it possible for browsers to resume an interrupted file transfer?
Just use the range-relevant HTTP headers, taking care w.r.t. caching. First, advertise that you can resume by setting the Accept-Ranges header. Secondly, check the Range and If-Range headers, and send the appropriate responses.
Note that you will probably need to construct your own Response and set the needed headers and result code by hand.
For the same purpose I've built a ContainerResponseFilter to intercept requests with a Range header and mangle the response accordingly[1].
This is the ContainerResponseFilter which will use encapsulate the output stream in a RangedOutputStream (see below):
public class RangeResponseFilter implements ContainerResponseFilter {
private static final String RANGE = "Range";
private static final String ACCEPT_RANGES = "Accept-Ranges";
#Override
public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext)
throws IOException {
if (requestContext.getHeaders().containsKey(RANGE)) {
String rangeHeader = requestContext.getHeaderString(RANGE);
String contentType = responseContext.getMediaType().toString();
OutputStream originOutputStream = responseContext.getEntityStream();
RangedOutputStream rangedOutputStream = new RangedOutputStream(originOutputStream, rangeHeader, contentType, responseContext.getHeaders());
responseContext.setStatus(Status.PARTIAL_CONTENT.getStatusCode());
responseContext.getHeaders().putSingle(ACCEPT_RANGES, rangedOutputStream.getAcceptRanges());
responseContext.setEntityStream(rangedOutputStream);
}
}
}
And here's the RangedOutputStream:
public class RangedOutputStream extends OutputStream {
public class Range extends OutputStream {
private ByteArrayOutputStream outputStream;
private Integer from;
private Integer to;
public Range(Integer from, Integer to) {
this.outputStream = new ByteArrayOutputStream();
this.from = from;
this.to = to;
}
public boolean contains(Integer i) {
if (this.to == null) {
return (this.from <= i);
}
return (this.from <= i && i <= this.to);
}
public byte[] getBytes() {
return this.outputStream.toByteArray();
}
public Integer getFrom() {
return this.from;
}
public Integer getTo(Integer ifNull) {
return this.to == null ? ifNull : this.to;
}
#Override
public void write(int b) throws IOException {
this.outputStream.write(b);
}
}
private final static char[] MULTIPART_CHARS = "-_1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
.toCharArray();
private static final String BOUNDARY_LINE_FORMAT = "--%s";
private static final String CONTENT_TYPE_LINE_FORMAT = "Content-Type: %s";
private static final String CONTENT_RANGE_FORMAT = "%s %d-%d/%d";
private static final String CONTENT_RANGE_LINE_FORMAT = "Content-Range: " + CONTENT_RANGE_FORMAT;
private static final String EMPTY_LINE = "\r\n";
private OutputStream outputStream;
private String boundary;
private String accept;
private String contentType;
private boolean multipart;
private boolean flushed = false;
private int pos = 0;
List<Range> ranges;
MultivaluedMap<String, Object> headers;
public RangedOutputStream(OutputStream outputStream, String ranges, String contentType, MultivaluedMap<String, Object> headers) {
this.outputStream = outputStream;
this.ranges = new ArrayList<>();
String[] acceptRanges = ranges.split("=");
this.accept = acceptRanges[0];
for (String range : acceptRanges[1].split(",")) {
String[] bounds = range.split("-");
this.ranges.add(new Range(Integer.valueOf(bounds[0]), bounds.length == 2 ? Integer.valueOf(bounds[1]) : null ));
}
this.headers = headers;
this.contentType = contentType;
this.multipart = this.ranges.size() > 1;
this.boundary = this.generateBoundary();
}
private String generateBoundary() {
StringBuilder buffer = new StringBuilder();
Random rand = new Random();
int count = rand.nextInt(11) + 30;
for (int i = 0; i < count; i++) {
buffer.append(MULTIPART_CHARS[rand.nextInt(MULTIPART_CHARS.length)]);
}
return buffer.toString();
}
public boolean isMultipart() {
return this.multipart;
}
public String getBoundary() {
return this.boundary;
}
public String getAcceptRanges() {
return this.accept;
}
public String getContentRange(int index) {
Range range = this.ranges.get(index);
return String.format(CONTENT_RANGE_LINE_FORMAT, this.accept, range.getFrom(), range.getTo(this.pos), this.pos);
}
#Override
public void write(int b) throws IOException {
for (Range range : this.ranges) {
if (range.contains(this.pos)) {
range.write(b);
}
}
this.pos++;
}
#Override
public void flush() throws IOException {
if (this.flushed) {
return;
}
if (this.multipart) {
this.headers.putSingle(HttpHeaders.CONTENT_TYPE, String.format("multipart/byteranges; boundary=%s", this.boundary));
for (Range range : this.ranges) {
this.outputStream.write(String.format(BOUNDARY_LINE_FORMAT + EMPTY_LINE, this.boundary).getBytes());
this.outputStream.write(String.format(CONTENT_TYPE_LINE_FORMAT + EMPTY_LINE, this.contentType).getBytes());
this.outputStream.write(
String.format(CONTENT_RANGE_LINE_FORMAT + EMPTY_LINE, this.accept, range.getFrom(), range.getTo(this.pos), this.pos)
.getBytes());
this.outputStream.write(EMPTY_LINE.getBytes());
this.outputStream.write(range.getBytes());
this.outputStream.write(EMPTY_LINE.getBytes());
}
this.outputStream.write(String.format(BOUNDARY_LINE_FORMAT, this.boundary + "--").getBytes());
} else {
Range range = this.ranges.get(0);
this.headers.putSingle("Content-Range", String.format(CONTENT_RANGE_FORMAT, this.accept, range.getFrom(), range.getTo(this.pos), this.pos));
this.outputStream.write(range.getBytes());
}
this.flushed = true;
}
}
[1] https://github.com/heruan/jersey-range-filter

Categories

Resources