Using Protobuf to process a config file - java

I'm relatively new to Protobufs and I was wondering how I would use it to process a list of structures.
Lets say I have a configuration file that looks like this:
Bucket{
name = "A";
path = "~/Document/...";
traffic = 5;
}
Bucket{
name = "B";
path = "~/Document/...";
traffic = 6;
}
Bucket{
name = "C";
path = "~/Document/...";
traffic = 7;
}
etc etc.
So I am using a protobuf to essentially structure this for ease of use later in Java. I'm essentially trying to make a map in a map (a protobuf to help find the correct bucket, and then another map to obtain member attributes inside the bucket).
option java_outer_classname = "Buckets";
message Bucket {
required string name = 1;
required string path = 2;
optional int32 traffic = 3;
}
message BucketList {
required Bucket bucket = 1;
}
I'm confused on how I would link the two, as in, how would I pass the configuration file into the protobuf methods (after it compiles into a java class) and use to to access the bucket member to do stuff like say get the path from the bucket with the name A?
Thank you!

It's perfectly acceptable to use Protobuf as a mechanism to declare and parse a text configuration file. However, one must keep in mind that Protobuf's purpose is the declare the format of the file, be it plain text or its binary wire format. Higher level semantic constraints need to be enforced in custom application code, once the configuration has been read.

You've got the wrong idea of what protobuf is used for, it is a data interchange library which means its used to encode and exchange data between programs. It was never meant to be used for configuration and doesn't have a way to read a text-based definition of the data since it deals with binary files.
Looking at your config format you have two options:
The format you've chosen looks a lot like HOCON, so https://github.com/typesafehub/config should be able to read it and provide a readable config object with a small amount of editing.
If you want a type-safe config (defining the structure of the config as an actual java object) you will need to use some other format which supports deserialization to object. A JSON configuration can be read into objects using libraries like https://github.com/google/gson or https://github.com/FasterXML/jackson

Related

Can I transform a JSON-LD to a Java object?

EDIT: I changed my mind. I would find a way to generate the Java class and load the JSON as an object of that class.
I just discovered that exists a variant of JSON called JSON-LD.
It seems to me a more structured way of defining JSON, that reminds me XML with an associated schema, like XSD.
Can I create a Java class from JSON-LD, load it at runtime and use it to convert JSON-LD to an instantiation of that class?
I read the documentation of both the implementations but I found nothing about it. Maybe I read them bad?
Doing a Google search brought me to a library that will decode the JSON-LD into an "undefined" Object.
// Open a valid json(-ld) input file
InputStream inputStream = new FileInputStream("input.json");
// Read the file into an Object (The type of this object will be a List, Map, String, Boolean,
// Number or null depending on the root object in the file).
Object jsonObject = JsonUtils.fromInputStream(inputStream);
// Create a context JSON map containing prefixes and definitions
Map context = new HashMap();
// Customise context...
// Create an instance of JsonLdOptions with the standard JSON-LD options
JsonLdOptions options = new JsonLdOptions();
// Customise options...
// Call whichever JSONLD function you want! (e.g. compact)
Object compact = JsonLdProcessor.compact(jsonObject, context, options);
// Print out the result (or don't, it's your call!)
System.out.println(JsonUtils.toPrettyString(compact));
https://github.com/jsonld-java/jsonld-java
Apparently, it can take it from just a string as well, as if reading it from a file or some other source. How you access the contents of the object, I can't tell. The documentation seems to be moderately decent, though.
It seems to be an active project, as the last commit was only 4 days ago and has 30 contributors. The license is BSD 3-Clause, if that makes any difference to you.
I'm not in any way associate with this project. I'm not an author nor have I made any pull requests. It's just something I found.
Good luck and I hope this helped!
see this page: JSON-LD Module for Jackson

Listing files in a specific "folder" of a AWS S3 bucket

I need to list all files contained in a certain folder contained in my S3 bucket.
The folder structure is the following
/my-bucket/users/<user-id>/contacts/<contact-id>
I have files related to users and files related to a certain user's contact.
I need to list both.
To list files I'm using this code:
ListObjectsRequest listObjectsRequest = new ListObjectsRequest().withBucketName("my-bucket")
.withPrefix("some-prefix").withDelimiter("/");
ObjectListing objects = transferManager.getAmazonS3Client().listObjects(listObjectsRequest);
To list a certain user's files I'm using this prefix:
users/<user-id>/
and I'm correctly getting all files in the directory excluding contacts subdirectory, for example:
users/<user-id>/file1.txt
users/<user-id>/file2.txt
users/<user-id>/file3.txt
To list a certain user contact's files instead I'm using this prefix:
users/<user-id>/contacts/<contact-id>/
but in this case I'm getting also the
directory itself as a returned object:
users/<user-id>/contacts/<contact-id>/file1.txt
users/<user-id>/contacts/<contact-id>/file2.txt
users/<user-id>/contacts/<contact-id>/
Why am I getting this behaviour? What's different beetween the two listing requests? I need to list only files in the directory, excluding sub-directories.
While everybody say that there are no directories and files in s3, but only objects (and buckets), which is absolutely true, I would suggest to take advantage of CommonPrefixes, described in this answer.
So, you can do following to get list of "folders" (commonPrefixes) and "files" (objectSummaries):
ListObjectsV2Request req = new ListObjectsV2Request().withBucketName(bucket.getName()).withPrefix(prefix).withDelimiter(DELIMITER);
ListObjectsV2Result listing = s3Client.listObjectsV2(req);
for (String commonPrefix : listing.getCommonPrefixes()) {
System.out.println(commonPrefix);
}
for (S3ObjectSummary summary: listing.getObjectSummaries()) {
System.out.println(summary.getKey());
}
In your case, for objectSummaries (files) it should return (in case of correct prefix):
users/user-id/contacts/contact-id/file1.txt
users/user-id/contacts/contact-id/file2.txt
for commonPrefixes:
users/user-id/contacts/contact-id/
Reference: https://docs.aws.amazon.com/AmazonS3/latest/API/API_ListObjectsV2.html
Everything in S3 is an object. To you, it may be files and folders. But to S3, they're just objects.
Objects that end with the delimiter (/ in most cases) are usually perceived as a folder, but it's not always the case. It depends on the application. Again, in your case, you're interpretting it as a folder. S3 is not. It's just another object.
In your case above, the object users/<user-id>/contacts/<contact-id>/ exists in S3 as a distinct object, but the object users/<user-id>/ does not. That's the difference in your responses. Why they're like that, we cannot tell you, but someone made the object in one case, and didn't in the other. You don't see it in the AWS Management Console because the console is interpreting it as a folder and hiding it from you.
Since S3 just sees these things as objects, it won't "exclude" certain things for you. It's up to the client to deal with the objects as they should be dealt with.
Your Solution
Since you're the one that doesn't want the folder objects, you can exclude it yourself by checking the last character for a /. If it is, then ignore the object from the response.
If your goal is only to take the files and not the folder, the approach I made was to use the file size as a filter. This property is the current size of the file hosted by AWS. All the folders return 0 in that property.
The following is a C# code using linq but it shouldn't be hard to translate to Java.
var amazonClient = new AmazonS3Client(key, secretKey, region);
var listObjectsRequest= new ListObjectsRequest
{
BucketName = 'someBucketName',
Delimiter = 'someDelimiter',
Prefix = 'somePrefix'
};
var objects = amazonClient.ListObjects(listObjectsRequest);
var objectsInFolder = objects.S3Objects.Where(file => file.Size > 0).ToList();
you can check the type. s3 has a special application/x-directory
bucket.objects({:delimiter=>"/", :prefix=>"f1/"}).each { |obj| p obj.object.content_type }
As other have already said, everything in S3 is an object. To you, it may be files and folders. But to S3, they're just objects.
If you don't need objects which end with a '/' you can safely delete them e.g. via REST api or AWS Java SDK (I assume you have write access). You will not lose "nested files" (there no files, so you will not lose objects whose names are prefixed with the key you delete)
AmazonS3 amazonS3 = AmazonS3ClientBuilder.standard().withCredentials(new ProfileCredentialsProvider()).withRegion("region").build();
amazonS3.deleteObject(new DeleteObjectRequest("my-bucket", "users/<user-id>/contacts/<contact-id>/"));
Please note that I'm using ProfileCredentialsProvider so that my requests are not anonymous. Otherwise, you will not be able to delete an object. I have my AWS keep key stored in ~/.aws/credentials file.
S3 does not have directories, while you can list files in a pseudo directory manner like you demonstrated, there is no directory "file" per-se.
You may of inadvertently created a data file called users/<user-id>/contacts/<contact-id>/.
Based on #davioooh answer.
This code is worked for me.
ListObjectsRequest listObjectsRequest = new ListObjectsRequest().withBucketName("your-bucket")
.withPrefix("your/folder/path/").withDelimiter("/");

Can camel generate File from java object

I need to generate a large file which is going to be done in Java, so a method is hit and the service goes off to the repository returns a list of type X as Java object. I then need to place this list in a file and send this off to an ftp server.
I know I can put files on ftp servers using camel, but wanted to know if it possible for camel to generate the file from the Java object and then place on the ftp server?
My code would look like this:
List<ObjectX> xList = someRepo.getListOfx();
So I need to write xList to a file and place on the ftp server.
Generally speaking, to convert your POJO messages to/from a text (or binary) based representation, you can use a Camel Dataformat. In your route, you will use the marshall and unmarshall keywords to perform the conversion.
There are several Camel dataformats available to marshall/unmarshal CSV, including the CSV dataformat or the Bindy dataformat (but there are a few others listed on the Dataformat page, under the "Flat data structure marshalling" header). One advantage of Bindy is that it can also support other unstructured formats (such as fixed width records)
Also note :
With Bindy, you will have to add annotations to your model class (ObjectX)
With CSV, you will have to convert you model objects (of type ObjectX) to Maps (or register an appropriate TypeConverter with Camel to do this conversion automatically)
If you check the other available dataformats, they may have different requirements too
Here is a very simple example with bindy:
package mypackage;
#CsvRecord(separator = ",")
public Class MyPojo {
#DataField(pos = 1) // first column in the CSV file
private int foo;
#DataField(pos = 2) // second column in the CSV file
private String bar;
// Plus constructors, accessors, etc.
}
// In the RouteBuilder :
DataFormat bindy = new BindyCsvDataFormat("mypackage");
from("...") // Message received as a List<MyPojo>
.marshal(bindy)
.to("ftp:...");

Is a .properties-file suitable for storing hierarchical settings-objects?

I have been tasked with an assignement at work, that needs persistance of client side settings. These settings are represented by a XML-file in the C# version of the framework I am implementing, and it represents objects with an hierarchical structure of up to six levels. Also, the number of objects in the different levels are sometimes arbitrary, like an ArrayList. Object serialization is unfortunately not an option, due to a requirement of being able to edit the file directly. It has been more than implied that I have to implement the persistance of these settings as a .properties-file, but I am having some concerns about if that is a good idea.
As far as I know, a .properties-file can only be used in the same way as a HashMap, with a key-value pairing of only Strings. The representation of complex objects seems only possible with long, complex key strings, representing the whole "path" of every single element, including it's individual index in possibly several lists.
filters.filter3.traces.rule4.add1.value="8"
I have not been able to find any decent ways to persist hierarchical objects in a .properties-file, but I have not been able to find any definite proof that it is not possible, either.
So, is a .properties-file suitable for storing hierarchical settings-objects? Or should I implement my properties as an XML-file or JSON instead?
You have a perfect example in the form of log4j: here is property sample from here.
log4j.appender.R = org.apache.log4j.DailyRollingFileAppender
log4j.appender.R.File = logs/bensApps.log
log4j.appender.R.Append = true
log4j.appender.R.DatePattern = '.'yyy-MM-dd
log4j.appender.R.layout = org.apache.log4j.PatternLayout
log4j.appender.R.layout.ConversionPattern = %d{yyyy-MM-dd HH:mm:ss} %c{1} [%p] %m%n
If you have a hierarchy in those keys, like:
Map<String,Object> root = new HashMap<>();
root.put("level1", "foobar");
root.put("level2", Collections.singletonMap("p", "foobar");
It is up to you to translate it into:
level1=foobar
level2.p=foobar
And when reading the file, to split . into submap:
Map<String, Object> root = new HashMap<>();
Properties propz = new Properties();
// load from your file
for (Map.Entry<String,Object> entry : propz.entrySet()) {
String[] path = entry.getKey().split('\\.');
Map<String,Object> parent = root;
int n = path.length - 1;
for (int i = 0; i < n; ++i) {
String p = path[i];
Object child = parent.get(p);
if (null == child) {
Map<String,Object> _p = new HashMap<>();
parent.put(p, _p);
parent = _p;
} else if (child instanceof Map) {
parent = (Map<String,Objext>) child;
} else {
// it is up to you to do something when the path extends beyond a "final" key
}
}
parent.put(path[n], entry.getValue());
}
However, this is a "reinvent the wheel" pattern, the other answers point you probably better solution than doing it by yourself. This example also show you one kind of problem:
p1=foobar
p1.p2=foobar
In the case of a machine generated properties, this won't happen, and an exception is perhaps the best answer. But in the case of human manipulated properties, this may have some meaning.
On a side note, since Java 7 come with JAXB, you can also do it in full XML, without requiring additional libraries. And using a hierarchy of some sort.
I would stick with an XML file. Nothing irritates users more than having different formats with different variants of an API framework.
You can use libraries such as Apache XMLBeans to simplify the process of reading and writing to XML files, using schema files to auto-generate appropriate Java classes.
Properties files can be used to store hierarchical data, but it's a real pain to edit that stuff by hand and it's easy to introduce errors. Some would argue that XML editing isn't a breeze either, but at least there exists editors that can make this easier when linked with an XSD.
I have always found this way of storing hierarchical configuration data quite cumbersome, but it is very well possible. For example, it is (used to be?) the most common way of configuring Log4j, but from my experience, more and more developers are switching to XML here as well.
I really fail to see the point of using properties files for this purpose.
I think you've already got the gist of it - Java properties files can express hierarchies, but awkwardly.
Take a look at the Typesafe Config library, which is what Play Framework uses. Its primary language is HOCON (human-optimized config notation) which is great for expressing hierarchical properties. The library also plays well with both properties files and JSON, so you can easily support any and all of these languages simultaneously.
In a Java properties file, as you've said, you're restricted to only using dotted keys
a.b.c = 42
In HOCON, you can choose to either do that or to nest sections with curly braces. So that example could be expressed in any of the following ways:
a.b.c : 42
a {
b.c : 42
}
a.b {
c : 42
}
a {
b {
c : 42
}
}

Access fields of returned Java Object

I made a simple client call to the XML-RPC WordPress API/Posts using a xml-rpc client and according to their documentation here it returns a struct. How can i access the return values.
Here is a look at my code:
XmlRpcClient client = new XmlRpcClient("http://www.mywebsite.net/xmlrpc.php", false);
String[] fields = new String[4];
fields[0] = "post_id";
fields[1] = "post_title";
fields[2] = "post_date";
fields[3] = "post_content";
Object token = client.invoke("wp.getPost", new Object[]{"0","myusername", "mypassword", 1545, fields });
System.out.println(token);
When I print use
System.out.println(token);
I get the following out put:
{item_one=I am item number one, item_two=I am Item two...}
How can I access item_one and item_two?
There's a bit of information missing (what's the fully qualified name of XmlRpcClient?), but assuming that client.invoke actually returns just an Object and not something more specific that has accessor methods, you can parse the response using something like this:
Object token = client.invoke("wp.getPost", new Object[]{"0","myusername", "mypassword", 1545, fields });
String[] items = token.toString().split(",");
for (String item : items) {
String[] parts = item.split("=");
String key = parts[0];
String value = parts[1];
// do stuff with your keys and values here
}
Of course this isn't perfect code -- you may need to check for nulls, use String.trim(), etc, but it should get you started.
You don't have a true Java representation of the data returned, in that you don't have an object on which you can access
token.item_one
rather you have a string containing a representation of a set - that is something that (in concept) from which you could retrieve an value by its name
token.get("item_one")
This string format is probably JSON, which pretty much looks like JavaScript, and hence can represent quite complex data. In general you can have arrays of objects and objects containing objects (for example, a Customer might contain an Address object)
So you have two possibilities:
1). parse the string into a true Java representation such as one of the standard Java collection classes. You then use the get-by-name style I show above.
2). define a Java class that mimics the structure of the data and then parse the string to fill out such an object, you can then use the "dot" form of access - you really have a Java Object representing the data.
In the first case there are suitable libraries such as quickJson
For the second you can use implementations of standards such as JAX/B, which tends to be more work as you may need to construct the target Java Class by hand. Enterprise Java runtimes will give you these facilities and perhaps tooling to help, or look at implementaitons such as Jackson. You will see that JAX/B hada focus on mapping from XML to Java, but tutorials such as this show how to work with JSON instead.
My guess is that the first option, simple parsing to a collection may be enough for you.

Categories