blob: 0e66de68d014aeb5afff8e11f4afec676b79672a [file] [view]
<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
-->
# Local Caching ClassLoader
`LocalCachingContextClassLoaderFactory` implements Accumulo's
`ContextClassLoaderFactory` SPI. Given a context parameter, supplied as a
String containing a URL to a remote context definition file, it will produce
and return `ClassLoader` instances to load classes and resources for a Java
application, based on the list of resource URLs contained in the remote context
definition. It will also monitor the URL to the context definition file for any
changes, at the monitoring interval specified in the context definition file.
## Introduction
This factory creates `ClassLoader` instances that point to locally cached
copies of remote resource files. In this way, this factory allows placing
common resources in a remote location for use across many hosts, but without
many of the problems that can occur when loading resources from a remote
location.
This factory uses a storage cache in the local filesystem for any files it
downloads from a remote URL.
To use this factory, one must store resource files in a location that can be
specified by a supported URL, and then must create a JSON-formatted context
definition file that contains a monitoring interval (in seconds, greater than
0), and a list of resource URLs along with a checksum for each resource file.
This context definition file must then be stored somewhere where this factory
can download it, and use the URL to that context definition file as the
`context` parameter for this factory's `getClassLoader(String context)` method.
This factory can handle context and resource URLs that use the `file`, `hdfs`,
`http`, or `https` URL scheme.
Here is an example context definition file:
```json
{
"monitorIntervalSeconds": 5,
"resources": [
{
"location": "file:/home/user/ClassLoaderTestA/TestA.jar",
"checksum": "a10883244d70d971ec25cbfa69b6f08f"
},
{
"location": "hdfs://localhost:8020/contextB/TestB.jar",
"checksum": "a02a3b7026528156fb782dcdecaaa097"
},
{
"location": "http://localhost:80/TestC.jar",
"checksum": "f464e66f6d07a41c656e8f4679509215"
}
]
}
```
## How it Works
When this factory receives a request for a `ClassLoader` for a given URL, it
downloads a copy of the context definition file and parses it. If it has
recently acquired that context definition file, based on the monitoring
interval from a previous retrieval, it can skip this step and use the
definition from the earlier retrieval, which is kept up-to-date by the
background monitoring process that started when it was previously retrieved.
Once it has the context definition, it then returns a `ClassLoader` instance
containing the resources defined in that context definition file, first
downloading any missing resources and verifying them using the checksums in the
context definition file.
`ClassLoader` instances are stored in a de-deduplicating cache in memory with a
minimum lifetime of 24 hours. So, no two instances will ever exist in a process
for the same context definition.
If this context definition had not previously been downloaded, a background
monitoring task is set up to ensure the URL is watched for any changes to the
context definition. This monitoring continues for as long as there exists
`ClassLoader` instances in the system that were constructed from the definition
file at that URL (at least 24 hours, since that is the minimum time they will
exist in the de-duplicating cache).
## Local Storage Cache
The local storage cache location is configured by the user by setting the
Accumulo property named `general.custom.classloader.lcc.cache.dir` to a
directory on the local filesystem. This location may be specified as an
absolute path or as a URL representing an absolute path with the `file` scheme.
The selected location should be a persistent location with plenty of space to
store downloaded resources (usually jar files), and should be writable by all
the processes which use this factory to share the same resources.
Resources downloaded to this cache may be used by multiple contexts, threads,
and processes, so be very careful when removing old contents to ensure that
they are no longer needed. If a resource file is deleted from the local storage
cache while a `ClassLoader` exists that references it, that `ClassLoader` may,
and probably will, stop working correctly. Similarly, files that have been
downloaded should not be modified, because checksums are only verified on first
download, and any modification will likely cause unexpected behavior.
* Do **NOT** use a temporary directory for the local storage cache location.
* The local storage cache location **MUST** use a filesystem that supports
atomic moves.
## Creating a ContextDefinition file
Users may take advantage of the `ContextDefinition.create(int,URL[])` method to
construct a `ContextDefinition` object. This will calculate the checksums of
the classpath elements. `ContextDefinition.toJson()` can be used to serialize
the `ContextDefinition` to a `String` to store in a file.
## Updating a ContextDefinition file
This factory uses a background thread to fetch the context definition file at
its initial URL using the interval specified in the definition file the last
time it was retrieved. The definition file at a watched URL can be replaced at
any time with any changes, including to changes to the monitor interval and
resources. When the context definition is next retrieved, the new context
definition file will be used as though it were an entirely new context at that
URL. The next retrieval will occur after the monitor interval read from the
most recent retrieval elapses. Changes to the context resources in any way will
result in those new resources being downloaded, verified, and a new
`ClassLoader` instance created and ready to be returned the next time
`getClassLoader(String context)` is called with that context URL.
Note: if the contents of a context definition file change in only
inconsequential ways, such as JSON formatting changes, then those changes will
not trigger any new downloads or `ClassLoader` staging. The is because the
context definition JSON files are normalized prior to computing their checksum
to determine if any changes have occurred.
## Error Handling
If there is an exception in creating the initial `ClassLoader`, then a
`ContextClassLoaderException` is thrown. If there is an exception when updating
the classloader, then the exception is logged and the classloader is not
updated. Calls to `getClassLoader(String context)` will return the most recent
classloader with valid contents. If the checksum of a downloaded resource does
not match the checksum in the context definition file, then the downloaded
version of the file is deleted from the context cache directory so that it can
be retried at the next interval.
The property `general.custom.classloader.lcc.update.grace.minutes` determines
how long the update process continues to return the most recent valid
classloader when an exception occurs in the background update thread. A zero
value (default) will cause the most recent valid classloader to be returned.
Otherwise, if a non-zero number is configured, then monitoring will stop after
the update has failed for that number of minutes. Once monitoring has stopped,
any subsequent calls to `getClassLoader(String context)` will behave as it
would during an initial request, throwing a `ContextClassLoaderException` if
the context definition cannot be retrieved or a `ClassLoader` cannot be
constructed at that time.
## Cleanup
Because the cache directory is shared among multiple processes, and one process
can't know what the other processes are doing, this class cannot clean up the
shared cache directory of unused resources. It is left to the user to remove
unused files from the cache. While the context definition JSON files are always
safe to delete, it is not recommended to do so for any that are still in use,
because they can be useful for troubleshooting.
**IMPORTANT**: as mentioned earlier, it is not safe to delete resource files
that are still referenced by any `ClassLoader` instances. Each `ClassLoader`
instance assumes that the locally cached resources exist and can be read. They
will not attempt to download any files. Downloading and verifying files only
occurs when `ClassLoader` instances are initially created for a context
definition.
## Accumulo Configuration
To use this with Accumulo:
1. Set the following Accumulo properties:
* `general.context.class.loader.factory=org.apache.accumulo.classloader.lcc.LocalCachingContextClassLoaderFactory`
* `general.custom.classloader.lcc.cache.dir=file://path/to/some/directory`
2. Set the following table property:
* `table.class.loader.context=(file|hdfs|http|https)://path/to/context/definition.json`