/* (c) 2014 Open Source Geospatial Foundation - all rights reserved
* (c) 2001 - 2013 OpenPlans
* This code is licensed under the GPL 2.0 license, available at the root
* application directory.
*/
package org.geoserver.wcs2_0.response;
import java.io.IOException;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.geoserver.catalog.Catalog;
import org.geoserver.catalog.CatalogVisitorAdapter;
import org.geoserver.catalog.CoverageInfo;
import org.geoserver.catalog.event.CatalogAddEvent;
import org.geoserver.catalog.event.CatalogListener;
import org.geoserver.catalog.event.CatalogModifyEvent;
import org.geoserver.catalog.event.CatalogPostModifyEvent;
import org.geoserver.catalog.event.CatalogRemoveEvent;
import org.geoserver.platform.GeoServerExtensions;
import org.geoserver.wcs.responses.CoverageResponseDelegate;
import org.geoserver.wcs.responses.CoverageResponseDelegateFinder;
import org.geoserver.wcs.responses.GeoTIFFCoverageResponseDelegate;
import org.geotools.util.SoftValueHashMap;
import org.geotools.util.Utilities;
import org.geotools.util.logging.Logging;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
/**
* Simple mapping utility to map native formats to Mime Types using ImageIO reader capabilities.
*
* It does perform caching of the mappings. Tha cache should be very small, hence it uses hard
* references.
*
* @author Simone Giannechini, GeoSolutions
*
*/
public class MIMETypeMapper implements ApplicationContextAware {
private static final String NO_MIME_TYPE = "NoMimeType";
public static final String DEFAULT_FORMAT = GeoTIFFCoverageResponseDelegate.GEOTIFF_CONTENT_TYPE;
private Logger LOGGER = Logging.getLogger(MIMETypeMapper.class);
private final SoftValueHashMap<String, String> mimeTypeCache = new SoftValueHashMap<String, String>(
100);
private final Set<String> outputMimeTypes = new HashSet<String>();
private List<CoverageMimeTypeMapper> mappers;
/**
* Constructor.
*
*/
private MIMETypeMapper(CoverageResponseDelegateFinder finder, Catalog catalog) {
// collect all of the output mime types
for (String of : finder.getOutputFormats()) {
CoverageResponseDelegate delegate = finder.encoderFor(of);
String mime = delegate.getMimeType(of);
outputMimeTypes.add(mime);
}
catalog.addListener(new MimeTypeCacheClearingListener());
}
/**
* Returns a mime types for the provided {@link CoverageInfo} using the
* {@link CoverageInfo#getNativeFormat()} as its key. In case none was found,
* the DEFAULT_FORMAT format is returned.
*
* @param cInfo the {@link CoverageInfo} to find a mime type for
* @return a mime types or null for the provided {@link CoverageInfo} using the
* {@link CoverageInfo#getNativeFormat()} as its key.
* @throws IOException in case we don't manage to open the underlying file
*/
public String mapNativeFormat(final CoverageInfo cInfo) throws IOException {
// checks
Utilities.ensureNonNull("cInfo", cInfo);
String mime = mimeTypeCache.get(cInfo.getId());
if(mime != null) {
if(NO_MIME_TYPE.equals(mime)) {
return DEFAULT_FORMAT;
} else {
return mime;
}
}
for (CoverageMimeTypeMapper mapper : mappers) {
mime = mapper.getMimeType(cInfo);
if(mime != null) {
break;
}
}
// the native format must be encodable
if (mime != null && outputMimeTypes.contains(mime)) {
mimeTypeCache.put(cInfo.getId(), mime);
if (LOGGER.isLoggable(Level.FINE)) {
LOGGER.fine("Added mapping for mime: " + mime);
}
return mime;
} else {
// we either don't have a clue about the mime, or we don't have an encoder,
// save the response as null
mimeTypeCache.put(cInfo.getId(), NO_MIME_TYPE);
return DEFAULT_FORMAT;
}
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
mappers = GeoServerExtensions.extensions(CoverageMimeTypeMapper.class, applicationContext);
}
/**
* Cleans the mime type cache contents on reload
* @author Andrea Aime - GeoSolutions
*/
public class MimeTypeCacheClearingListener extends CatalogVisitorAdapter implements CatalogListener {
public void handleAddEvent(CatalogAddEvent event) {
}
public void handleModifyEvent(CatalogModifyEvent event) {
}
public void handlePostModifyEvent(CatalogPostModifyEvent event) {
event.getSource().accept( this );
}
public void handleRemoveEvent(CatalogRemoveEvent event) {
event.getSource().accept( this );
}
public void reloaded() {
outputMimeTypes.clear();
}
@Override
public void visit(CoverageInfo coverage) {
outputMimeTypes.remove(coverage.getId());
}
}
}