org.eel.kitchen.jsonschema.uri.URIManager Maven / Gradle / Ivy
Show all versions of json-schema-validator Show documentation
/*
* Copyright (c) 2012, Francis Galiegue
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the Lesser GNU General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* Lesser GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package org.eel.kitchen.jsonschema.uri;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.base.Preconditions;
import com.google.common.collect.Maps;
import org.eel.kitchen.jsonschema.keyword.NumericKeywordValidator;
import org.eel.kitchen.jsonschema.main.JsonSchemaException;
import org.eel.kitchen.jsonschema.ref.JsonRef;
import org.eel.kitchen.jsonschema.report.Domain;
import org.eel.kitchen.jsonschema.report.Message;
import org.eel.kitchen.jsonschema.schema.SchemaRegistry;
import org.eel.kitchen.jsonschema.util.JsonLoader;
import org.eel.kitchen.jsonschema.util.jackson.JacksonUtils;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Map;
/**
* Class to fetch JSON documents
*
* This uses a map of {@link URIDownloader} instances to fetch the contents
* of a URI as an {@link InputStream}, then tries and turns this content into
* JSON using an {@link ObjectMapper}.
*
* Normally, you will never use this class directly.
*
* @see SchemaRegistry
* @see JsonLoader
*/
public final class URIManager
{
/**
* Our object mapper
*
* Note that it uses {@link
* DeserializationFeature#USE_BIG_DECIMAL_FOR_FLOATS} to deserialize, for
* accuracy reasons.
*
* @see NumericKeywordValidator
*/
private static final ObjectMapper MAPPER = JacksonUtils.getMapper();
/**
* Map of downloaders (schemes as keys, {@link URIDownloader} instances
* as values)
*/
private final Map downloaders = Maps.newHashMap();
/**
* Map for URI redirections
*
* This map will be used if you wish for an absolute URI to be redirected
* to another URI of your choice, which must also be absolute.
*
* Note that this map requires URIs to be exact. Currently, you
* cannot, for example, redirect a whole namespace this way.
*/
private final Map URIRedirections = Maps.newHashMap();
public URIManager()
{
downloaders.put("http", DefaultURIDownloader.getInstance());
downloaders.put("ftp", DefaultURIDownloader.getInstance());
downloaders.put("file", DefaultURIDownloader.getInstance());
downloaders.put("jar", DefaultURIDownloader.getInstance());
downloaders.put("resource", ResourceURIDownloader.getInstance());
}
/**
* Register a new downloader for a given URI scheme
*
* @param scheme the scheme
* @param downloader the {@link URIDownloader} instance
* @throws NullPointerException scheme is null
* @throws IllegalArgumentException scheme is empty, or is already
* registered
*/
public void registerScheme(final String scheme,
final URIDownloader downloader)
{
Preconditions.checkNotNull(scheme, "scheme is null");
Preconditions.checkArgument(!scheme.isEmpty(), "scheme is empty");
try {
new URI(scheme, "x", "y");
} catch (URISyntaxException ignored) {
throw new IllegalArgumentException("illegal scheme \"" + scheme
+ '"');
}
downloaders.put(scheme, downloader);
}
/**
* Unregister a downloader for a given scheme
*
* @param scheme the scheme
* @throws NullPointerException scheme is null
*/
public void unregisterScheme(final String scheme)
{
Preconditions.checkNotNull(scheme, "scheme is null");
downloaders.remove(scheme);
}
/**
* Add a URI rediction
*
* The typical use case for this is if you have a local copy of a schema
* whose id is normally unreachable. You can transform all references to
* this schema's URI to another URI which is reachable by your application.
*
*
* Note that the given strings will be considered as JSON References, and
* that both arguments must be valid absolute JSON References. For the
* recall, there is more to it than URIs being absolute: their fragment part
* must also be empty or null.
*
* @param from the original URI
* @param to an URI which is reachable
* @throws NullPointerException {@code from} or {@code to} is null
* @throws IllegalArgumentException either {@code from} or {@code to} are
* invalid URIs, or are not absolute JSON References
*/
public void addRedirection(final String from, final String to)
{
Preconditions.checkNotNull(from, "source URI is null");
Preconditions.checkNotNull(to, "destination URI is null");
final URI sourceURI, destURI;
JsonRef ref;
String errmsg;
errmsg = "source URI " + from + " is not an absolute JSON reference";
try {
ref = JsonRef.fromString(from);
Preconditions.checkArgument(ref.isAbsolute(), errmsg);
sourceURI = ref.getLocator();
} catch (JsonSchemaException ignored) {
throw new IllegalArgumentException(errmsg);
}
errmsg = "destination URI " + to + " is not an absolute JSON reference";
try {
ref = JsonRef.fromString(to);
Preconditions.checkArgument(ref.isAbsolute(), errmsg);
destURI = ref.getLocator();
} catch (JsonSchemaException ignored) {
throw new IllegalArgumentException(errmsg);
}
URIRedirections.put(sourceURI, destURI);
}
/**
* Get the content at a given URI as a {@link JsonNode}
*
* @param uri the URI
* @return the content
* @throws JsonSchemaException scheme is not registered, failed to get
* content, or content is not JSON
*/
public JsonNode getContent(final URI uri)
throws JsonSchemaException
{
Preconditions.checkNotNull(uri, "null URI");
final URI target = URIRedirections.containsKey(uri)
? URIRedirections.get(uri) : uri;
/*
* All errors at this level are fatal
*/
final Message.Builder msg = Domain.REF_RESOLVING.newMessage()
.setKeyword("N/A").addInfo("uri", target).setFatal(true);
if (!target.isAbsolute())
throw new JsonSchemaException(msg.setMessage("URI is not absolute")
.build());
final String scheme = target.getScheme();
final URIDownloader downloader = downloaders.get(scheme);
if (downloader == null) {
msg.setMessage("cannot handle scheme").addInfo("scheme", scheme);
throw new JsonSchemaException(msg.build());
}
final InputStream in;
try {
in = downloader.fetch(target);
} catch (IOException e) {
msg.setMessage("cannot fetch content from URI");
throw new JsonSchemaException(msg.build(), e);
}
try {
// Note: ObjectMapper's .readTree() closes the InputStream after it
// is done with it!
return MAPPER.readTree(in);
} catch (IOException e) {
msg.setMessage("content fetched from URI is not valid JSON");
throw new JsonSchemaException(msg.build(), e);
}
}
}