/*
* Copyright (c) 2015 Data Harmonisation Panel
*
* All rights reserved. This program and the accompanying materials are made
* available under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of the License,
* or (at your option) any later version.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution. If not, see <http://www.gnu.org/licenses/>.
*
* Contributors:
* Data Harmonisation Panel <http://www.dhpanel.eu>
*/
package eu.esdihumboldt.hale.io.geoserver;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.apache.http.entity.ContentType;
import eu.esdihumboldt.hale.io.geoserver.template.Templates;
/**
* Base class for classes representing GeoServer resources.
*
* <p>
* The basic idea is that a resource has a name, a set of attributes (the actual
* set of attributes depends on the resource type) and a content, which is
* either read from a template file, or directly from an input stream.
* </p>
*
* @author Stefano Costa, GeoSolutions
*/
public abstract class AbstractResource implements Resource {
/**
* Default resource content type.
*/
public static final ContentType DEF_CONTENT_TYPE = ContentType.APPLICATION_XML
.withCharset("UTF-8");
/**
* Zipped archive content type.
*/
public static final ContentType ZIP_CONTENT_TYPE = ContentType.create("application/zip");
/**
* Resource attributes.
*/
protected Map<String, Object> attributes;
/**
* The set of allowed attribute names for this particular resource type.
*/
protected Set<String> allowedAttributes;
/**
* Default constructor.
*
* <p>
* Should be invoked by subclasses.
* </p>
*/
public AbstractResource() {
this.attributes = new HashMap<String, Object>();
this.allowedAttributes = new HashSet<String>();
this.allowedAttributes.addAll(allowedAttributes());
}
/**
* @see eu.esdihumboldt.hale.io.geoserver.Resource#contentType()
*/
@Override
public ContentType contentType() {
return DEF_CONTENT_TYPE;
}
/**
* @see eu.esdihumboldt.hale.io.geoserver.Resource#getAttribute(java.lang.String)
*/
@Override
public Object getAttribute(String name) {
if (name == null || name.isEmpty()) {
throw new IllegalArgumentException("name must be set");
}
return this.attributes.get(name);
}
/**
* @see eu.esdihumboldt.hale.io.geoserver.Resource#setAttribute(java.lang.String,
* java.lang.Object)
*/
@Override
public void setAttribute(String name, Object value) {
if (name == null || name.isEmpty()) {
throw new IllegalArgumentException("name must be set");
}
if (!this.allowedAttributes.contains(name)) {
throw new IllegalArgumentException(String.format(
"Variable \"%s\" not allowed in this resource", name));
}
this.attributes.put(name, value);
}
/**
* @see eu.esdihumboldt.hale.io.geoserver.Resource#write(java.io.OutputStream)
*/
@Override
public void write(OutputStream out) throws IOException {
// unset unspecified variables by setting their value to null
for (String var : this.allowedAttributes) {
if (!this.attributes.containsKey(var)) {
this.attributes.put(var, null);
}
}
InputStream resourceStream = locateResource();
if (resourceStream != null) {
BufferedInputStream input = new BufferedInputStream(resourceStream);
BufferedOutputStream output = new BufferedOutputStream(out);
try {
for (int b = input.read(); b >= 0; b = input.read()) {
output.write(b);
}
} finally {
try {
input.close();
} catch (IOException e) {
// ignore exception on close
}
try {
output.close();
} catch (IOException e) {
// ignore exception on close
}
}
}
}
/**
* Grabs an input stream from which resource content can be read.
*
* <p>
* If a non-null template location is returned by the
* {@link #templateLocation()} method, the returned input stream points to
* the result of the merging of the template with the resource; otherwise,
* the result of {@link #resourceStream()} is returned.
* </p>
*
* @return the input stream from which resource content is read
* @throws IOException if an I/O error occurs
*/
protected InputStream locateResource() throws IOException {
if (templateLocation() != null && !templateLocation().isEmpty()) {
// resource is loaded from a template and attributes map is used to
// interpolate variables in it
return Templates.getInstance().loadTemplate(templateLocation(), this.attributes);
}
else {
// load resource straight from the classpath
return resourceStream();
}
}
/**
* @see eu.esdihumboldt.hale.io.geoserver.Resource#asStream()
*/
@Override
public InputStream asStream() throws IOException {
ByteArrayInputStream bis = new ByteArrayInputStream(asByteArray());
return bis;
}
/**
* @see eu.esdihumboldt.hale.io.geoserver.Resource#asByteArray()
*/
@Override
public byte[] asByteArray() throws IOException {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
try {
write(bos);
return bos.toByteArray();
} finally {
try {
bos.close();
} catch (IOException e) {
// ignore exception on close
}
}
}
/**
* Should be overridden by sublcasses loading resource content from
* template.
*
* @return the template location (relative to classpath)
*/
protected String templateLocation() {
return null;
}
/**
* Should be overridden by subclasses loading resource content from an
* {@link InputStream}, e.g. file resources.
*
* @return the {@link InputStream} from which resource content should be
* read
*/
protected InputStream resourceStream() {
return null;
}
/**
* Return the set of allowed attributes.
*
* <p>
* The method must be implemented by subclasses and is invoked by the
* default constructor to populate the {@link #allowedAttributes} set.
* </p>
*
* @return the set of allowed attributes
*/
protected abstract Set<String> allowedAttributes();
}