Skip to content
/ JARiS Public template

Various utilities that complement those found in Guava.

License

Notifications You must be signed in to change notification settings

oliviercailloux/JARiS

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

JARiS

Maven CI Artifact on Maven Central Javadocs

Various utilities that complement those found in Guava.

This library focuses on code and API quality, not on performance. Open an issue if this creates a problem for some of your use cases.

This project adheres to the Zero Bugs Commitment.

The API is not stable. Write to me if you are interested in using this library and would like its API to be stable, or want more example code. The library also uses parts of the Guava API that are marked @Beta (meaning it is not stable). I will keep the dependencies of this library up-to-date in order to work around any disagreement that this can cause. This means that you need to be committed to update your dependency on this library if Guava’s Beta API changes in order to ensure that you do not run into version conflicts.

The following sections list some of the utilities that this library provides (by package).

Compiler bug

Bug about inference reported on the 17th of Jan.

$ sudo update-alternatives --set java /usr/lib/jvm/java-19-openjdk-amd64/bin/java $ sudo update-alternatives --set javac /usr/lib/jvm/java-19-openjdk-amd64/bin/javac $ javac --version javac 19.0.2 $ git clean -xfd $ javac -d "out/" somebug/*.java $ javac -classpath "out/" "someuser/UserClass.java" someuser/UserClass.java:10: error: incompatible types: MyInterface<Random> cannot be converted to MyImpl

$ sudo update-alternatives --set java /usr/lib/jvm/java-17-openjdk-amd64/bin/java $ sudo update-alternatives --set javac /usr/lib/jvm/java-17-openjdk-amd64/bin/javac $ javac --version javac 17.0.9 $ git clean -xfd $ javac -d "out/" somebug/*.java $ javac -classpath "out/" "someuser/UserClass.java" someuser/UserClass.java:10: error: incompatible types: MyInterface<Random> cannot be converted to MyImpl

Credentials

CredentialsReader permits to read user’s credentials (authentication information) from the system properties, then if not found there, from the environment, then if not found there, from a file.

The focus is on ease of use.

Consider the following code.

CredentialsReader<Credentials> reader = CredentialsReader.classicalReader();
Credentials myAuth = reader.getCredentials();
String username = myAuth.API_USERNAME();
String password = myAuth.API_PASSWORD();

The first line returns a reader configured to read using the API_USERNAME and API_PASSWORD system properties or environment variables, or from a file.

Start the software with the system properties API_USERNAME set to the value my username and API_PASSWORD to the value my password (for example, using the -D command line argument when starting java), and these values will be passed to the variables username and password.

Assuming that these system properties are not set, but the environment variables API_USERNAME and API_PASSWORD are set, these variables will then hold the corresponding values.

Assume that neither these system properties nor these environment variables are set, but a file API_credentials.txt exists, containing two lines of text: in this case, the returned myAuth instance will contain as username the first line of the file, and as password, the second line.

This mechanism is particularly appropriate for developing open source projects, where you do not want the credentials (for example, those used for testing the software) to be committed in the version control system. In such a situation, a classical problem is that such private credentials will also not be directly available from the continuous integration server. To solve this, put the credentials in a file on your own machine, so that they are available while you develop (it is easier and cleaner than configuring environment variables), and store the credentials in environment variables on the continuous integration server (which is usually made easy by the continuous integration web interface). The code above will make your application adapt automatically and find the credentials in both situations. You can also override them manually when starting the application, using system properties, if necessary.

CredentialsReader also allows for using more or less than two keys, other keys than API_USERNAME and API_PASSWORD, and configuring the path to read from.

Exceptions

Objects to deal with code that declare checked exceptions; or that throw exceptions.

The Try class permits to call code that you suspect may throw an exception when you’re only interested in storing the result of the call or the exception, rather than desiring that the exception propagates and the control be lost. This is mostly useful when running some code whose correctness you want to test, for example, when grading student’s code. It is a simplified version of, and is heavily inspired by, its Vavr counterpart. (I do not use Vavr personnally because it uses sneaky-throws, but this is only a personal preference.)

For example, having a method in the class SomeClass declared as public static Something getSomething(), you can use the instruction Try<Something> t = Try.of(SomeClass::getSomething);, and t will contain either the instance of Something that was returned by getSomething, or the exception it threw, if it threw an exception.

The Unchecker class (inspired by Durian) permits to reduce some annoyances coming with code that throws checked exceptions, in particular, when trying to use such code in lambda expressions. See the following example, where the method someRunnable is declared as follows: void someRunnable() throws SQLException.

void testRunnableDoesNotThrow() {
	Unchecker<SQLException, IllegalStateException> unchecker = Unchecker.wrappingWith(IllegalStateException::new);
	unchecker.call(this::someRunnable);
}

Note that calling someRunnable normally requires to deal with the (possible) SQLException, as it is a checked exception declared by someRunnable. We see that the method testRunnableDoesNotThrow, however, does nothing to deal with it, and does not declare it either. This is because the unchecker will transform the SQLException, if thrown when calling someRunnable, into an unchecked exception, here, an IllegalStateException. The transformation is done thanks to the wrapper given as a parameter to the wrappingWith factory method.

Use directly Unchecker.IO_UNCHECKER when desiring to transform IOException instances to UncheckedIOException ones.

GraphUtils

A few static methods to create graphs from other structures such as a set of root nodes and a successor function, and to obtain a set of the nodes of a graph that iterates following a topological sort (thanks to jbduncan).

Xml

A few helper classes to deal with XML, using the DOM (Document Object Model), XSLT, or schema based validation. See the package documentation.

DomHelper

DomHelper helps with W3C DOM manipulation, in particular, serialization and deserialization, and provides static methods for manipulating lists of nodes. For example, it provides a method to deal with a list of nodes as a Java List rather than the more cumbersome NodeList provided by the standard.

I like to use Java’s W3C library for manipulating the DOM because this API is a W3C standard (hence, will be similar in other languages and rests on sounds and clear principles); and it is included in the JRE. That said, its Java implementation is now quite old, as the need for the method DomHelper#toList illustrates. So for non-trivial manipulations of Xml or Html documents, you’d probably better use a more recent library.

SchemaHelper

SchemaHelper helps dealing with XML schema. It provides a method to obtain a Schema instance representing a given XML Schema (which may be useful for interacting with the Java XML classes); and one to obtain a document validator for a given XML Schema.

The following example validates a DocBook instance against its schema (note that for DocBook you’d better not use an XSD schema, this is just a silly example).

StreamSource docBook = new StreamSource("https://raw.githubusercontent.com/oliviercailloux/JARiS/master/src/test/resources/io/github/oliviercailloux/jaris/xml/docbook%20simple%20article.xml");
StreamSource docBookSchemaSource = new StreamSource("https://cdn.docbook.org/schema/5.0.1/xsd/docbook.xsd");

SchemaHelper schemaHelper = SchemaHelper.schemaHelper();
Schema docBookSchema = schemaHelper.asSchema(docBookSchemaSource);
ConformityChecker conformityChecker = schemaHelper.conformityChecker(docBookSchema);

conformityChecker.verifyValid(docBook);

The last line will throw an exception iff the document is invalid (it’s not, in this example).

XmlTransformer

XmlTransformer helps transforming XML Documents (including, transforming them to string form).

The following example transforms a DocBook document into a FO document using the DocBook to FO stylesheet. For this to work you need to have an xml library such as Xalan or Saxon in your class path: this is too complex for the default one provided with the JDK (see this example POM where they both appear as test dependencies).

StreamSource docBook = new StreamSource("https://raw.githubusercontent.com/oliviercailloux/JARiS/master/src/test/resources/io/github/oliviercailloux/jaris/xml/docbook%20simple%20article.xml");
StreamSource docBookStyle = new StreamSource("https://cdn.docbook.org/release/xsl/1.79.2/fo/docbook.xsl");
String transformed = XmlTransformer.usingFoundFactory().forSource(docBookStyle).transform(docBook);

About

Various utilities that complement those found in Guava.

Resources

License

Stars

Watchers

Forks

Languages