KivaKit Resources – Java Code Geeks

[ad_1]

A resource is a flow of data that can be opened, read or written, and then closed. KivaKit provides a mini resource framework that allows easy and consistent access to many types of resources, and makes it easy to create new resources. Examples of KivaKit resources:

  • Files
  • Taken
  • Zip or JAR file entries
  • S3 objects
  • Package resources
  • HDFS files
  • HTTP responses
  • Input flow
  • Output stream

Examples of use cases

Some short examples of resource use cases:

Read the lines of a .csv file from a package, reporting the progress:

var resource = PackageResource.of(getClass(), "target-planets.csv");
try (var line : listenTo(new CsvReader(resource, schema, ',', reporter)).lines())
{
    [...]
}

Note that if this code is in a KivaKit Making up, then the first row can be reduced to:

var resource = packageResource("target-planets.csv");

Write a string to a file on S3:

var file = listenTo(File.parse("s3://mybucket/myobject.txt"));    
try (var out = file.writer().printWriter())
{
    out.println("Start Operation Impending Doom III in 10 seconds");
}

Safely extract an entry (ensuring no partial results) from a .zip file:

var file = listenTo(File.parse("/users/jonathan/input.zip"));
var folder = listenTo(Folder.parse("/users/jonathan"));
try (var zip = ZipArchive.open(file, reporter, READ))
{
    listenTo(zip.entry("data.txt")).safeCopyTo(folder, OVERWRITE);
}

In each case, the code is assumed to be present in a class implementing Repeater. The To listen() calls add this as the argument object’s listener, creating a chain of listeners. If anything noticeable happens in a Resource (for example, an attempt to open the resource when it does not exist), it will broadcast a message in the listen channel.

Resource and messaging issues

All Resources inherit and use the fatal() method for reporting unrecoverable opening, reading, and writing problems (other methods may have different semantics, such as those with a Boolean return value). The fatal() method in Streamerthe basic interface of Transmitter receiver does two things:

  1. Broadcast a Fatal Problem message to listeners
  2. Throw a IllegalStateException

This design decouples the broadcast of a FatalProblem message to listeners from the change in control flow that occurs as a result of throwing an exception.. The result is that, in most cases, exceptions can only be caught when an operation is recoverable, and the information in the exception can usually be ignored because it has already been broadcast (and probably logged, depending on the or terminal listeners).

For example, in this common (but seemingly unfortunate) idiom, error information is propagated to the caller with an exception that is caught, qualified with a cause, and logged:

class Launcher
{
    void doDangerousStuff()
    {
        [...]
        
        throw new DangerousStuffException("Whoops.");
    }
}
 
class AttackPlanet
{
    boolean prepareMissileLauncher()
    {
        try
        {
            doDangerousStuff();
            return true;
        }
        catch (DangerousStuffException e)
        {
            LOGGER.problem(e, "Unable to do dangerous stuff");
            return false;
        }
    }
}

A KivaKit alternative to this idiom is as follows:

class Launcher extends BaseRepeater
{
    void doDangerousStuff()
    {
        [...]
 
        fatal("Unable to do dangerous stuff: Whoops.");
    }
}

class AttackPlanet extends BaseRepeater
{
    boolean prepareMissileLauncher()
    {    
        listenTo(new Launcher()).doDangerousStuff();
        return true;
    }
}

After the Fatal Problem message in doStuffDangerous () is broadcast by the fatal() method, the control flow propagates separately via a IllegalStateException thrown by the same fatal() method to any caller on the call stack who might be able to substantially answer the problem (as opposed to just logging it). For more information, see KivaKit messaging.

Design

Okay, so how do KivaKit resources work?

The design of the KivaKit resource module is quite complex, so we will focus on the most important and high level aspects in this article.

A simplified UML diagram:

The Resource the class in this diagram is central. This class:

  • Has a ResourcePath (of ResourcePathed)
  • Has a size in bytes (of Size in bytes)
  • At a time of last modification (from Time-stamped modification)
  • Is a Readable resource

Since all resources are Readable resources, they can be opened with Readable.openForReading (), or read with the convenience methods in ResourceReader (which is accessible with ReadableResource.reader ()).

In addition, some resources are Writable resources. These can be opened with Write.openForWriting (), and written with methods in the convenience class Resource writer

The Resource the class itself can determine whether the resource exist() and if he isRemote (). Remote resources can be materialized to a temporary file on the local filesystem before reading them (using methods that are not in the UML diagram). Resources can also make a secure copy of their content to a destination To file Where Case with both safeCopyTo () methods. Secure copy involves 3 steps:

  1. Write to a temporary file
  2. Delete the destination file
  3. Rename the temporary file with the destination file name

Ultimately, BaseWritableResource extends BaseReadResource add the possibility of wipe off a resource, and save a Input flow to the resource, by reporting on the progress made.

To give an idea of โ€‹โ€‹the resources provided by KivaKit, here is an overview of the hierarchy of readable and writable resource classes:

Implementing a resource

Now let’s take a quick look at one Resource Implementation. The implementation of a simple Readable resource only requires one onOpenForReading method and a sizeInBytes () method. A default value for everything else will be provided by BaseReadResource. The String resource class is a good example. It looks like this:

public class StringResource extends BaseReadableResource
{
    private final String value;

    public StringResource(final ResourcePath path, final String value)
    {
        super(path);
        this.value = value;
    }

    @Override
    public InputStream onOpenForReading()
    {
        return new StringInput(value);
    }

        @Override
    public Bytes sizeInBytes()
    {
        return Bytes.bytes(value.length());
    }
}

Conclusion

A few things we haven’t talked about:

  • All resources transparently implement different types of compression and decompression through the Codec interface
  • The Progress report interface and I / O progress
  • Generic resource identifiers and their resolution
  • The Service Provider Interface (SPI) for To file and Case

Coded

The resource module covered above is available in kivakit-resource in the KivaKit project.

<dependency>
    <groupId>com.telenav.kivakit</groupId>
    <artifactId>kivakit-resource</artifactId>
    <version>${kivakit.version}</version>
</dependency>

Posted on Java Code Geeks with the permission of Jonathan Locke, partner of our JCG program. See the original article here: KivaKit Resources

The opinions expressed by contributors to Java Code Geeks are their own.

[ad_2]

Source link