/* Copyright (c) 2001 - 2007 TOPP - www.openplans.org. All rights reserved. * This code is licensed under the GPL 2.0 license, availible at the root * application directory. */ package org.geoserver.rest; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.TreeMap; import org.geoserver.ows.util.ResponseUtils; import org.geoserver.rest.format.DataFormat; import org.geoserver.rest.format.MediaTypes; import org.restlet.Context; import org.restlet.data.MediaType; import org.restlet.data.Preference; import org.restlet.data.Request; import org.restlet.data.Response; import org.restlet.data.Status; import org.restlet.resource.Resource; /** * Abstract base class for resources. * * @author Justin Deoliveira, OpenGEO * */ public abstract class AbstractResource extends Resource { /** * media type setup */ static { MediaTypes.registerExtension( "html", MediaType.TEXT_HTML ); MediaTypes.registerExtension( "xml", MediaType.APPLICATION_XML ); MediaTypes.registerExtension( "json", MediaType.APPLICATION_JSON ); MediaTypes.registerSynonym( MediaType.APPLICATION_XML, MediaType.TEXT_XML ); MediaTypes.registerSynonym( MediaType.APPLICATION_JSON, new MediaType( "text/json") ); } /** * list of formats used to read and write representations of the resource. */ protected Map<MediaType,DataFormat> formats; /** * Constructs a new resource from context, request, and response. */ protected AbstractResource( Context context, Request request, Response response ) { super( context, request, response ); } /** * Constructs a new resource. * <p> * When using this constructor, {@link #init(Context, Request, Response)} needs to be * called before any other operations. * </p> */ protected AbstractResource() { } /** * Creates the list of formats used to create and read representations of the resource. * <p> * The first entry in the list is considered to be the default format. This format will * be used when the client does not explicitly specify another format. * </p> */ protected abstract List<DataFormat> createSupportedFormats(Request request,Response response); /** * Returns the format to use to serialize during a GET request. * <p> * To determine the format first the "Accepts" header is checked. If not set then the * "extension" of the resource being requested is used, which originates from the route * template under the {format} or {type} variable. IF neither the header or extension is * set the default format is used, ie the first format returned from * {@link #createSupportedFormats(Request, Response)}. * </p> */ protected DataFormat getFormatGet() { DataFormat df = null; //check if the client specified an extension String ext = (String) getRequest().getAttributes().get( "format" ); if ( ext == null ) { ext = (String) getRequest().getAttributes().get( "type" ); } if ( ext == null ) { //try from the resource uri String uri = getRequest().getResourceRef() != null ? getRequest().getResourceRef().getLastSegment() : null; if ( uri != null ) { ext = ResponseUtils.getExtension(uri); } } if ( ext != null ) { //lookup the media type matching the extension MediaType mt = MediaTypes.getMediaTypeForExtension( ext ); if ( mt != null ) { df = lookupFormat(mt); } } List<Preference<MediaType>> accepts = null; if ( df == null ) { //next check the Accepts header accepts = new ArrayList( getRequest().getClientInfo().getAcceptedMediaTypes() ); for ( Iterator<Preference<MediaType>> i = accepts.iterator(); i.hasNext(); ) { Preference<MediaType> pref = i.next(); if ( pref.getMetadata().equals( MediaType.ALL ) ) { i.remove(); continue; } df = lookupFormat( pref.getMetadata() ); if ( df != null ) { break; } } } if ( df == null && accepts.isEmpty() ) { //could not find suitable format, if client specifically did not specify // any accepted formats, just return the first df = getFormats().values().iterator().next(); } return df; } /** * returns the format to use to de-serialize during a POST or PUT request. * <p> * The format is located by looking up <pre>getRequest().getEntity().getMediaType()</pre> from * the list created by {@link #createSupportedFormats()}. * </p> */ protected DataFormat getFormatPostOrPut() { MediaType type = getRequest().getEntity().getMediaType(); if ( type != null ) { //DataFormat format = getFormats().get( type.toString() ); DataFormat format = lookupFormat( type ); /* if ( format == null ) { //check the sub type String sub = type.getSubType(); if ( sub != null ) { format = getFormats().get( sub ); if ( format == null ) { //check for sub type specified with '+' int plus = sub.indexOf( '+' ); if ( plus != -1 ) { sub = sub.substring(0,plus); format = getFormats().get( sub ); } } } } */ if ( format != null ) { return format; } } throw new RestletException( "Could not determine format. Try setting the Content-type header.", Status.CLIENT_ERROR_BAD_REQUEST ); } /** * Accessor for formats which lazily creates the format map. */ protected Map<MediaType,DataFormat> getFormats() { if ( formats == null ) { synchronized (this) { if ( formats == null ) { formats = new LinkedHashMap(); for ( DataFormat format : createSupportedFormats(getRequest(), getResponse()) ) { formats.put( format.getMediaType(), format ); } if ( formats.isEmpty() ) { throw new RuntimeException( "Empty format list" ); } } } } return formats; } /* * Helper method for looking up a format based on a media type. */ DataFormat lookupFormat( MediaType mt ) { //exact match DataFormat fmt = getFormats().get( mt ); if ( fmt == null ) { //check for the case of a media type being "contained" for ( MediaType mediaType : getFormats().keySet() ) { if ( mediaType.includes( mt ) || mt.includes( mediaType ) ) { fmt = getFormats().get( mediaType ); break; } } } if ( fmt == null ) { //do a check for synonyms for( MediaType syn : MediaTypes.getSynonyms( mt ) ) { fmt = getFormats().get( syn ); if ( fmt != null ) { break; } } } if ( fmt == null ) { //do a reverse check check for synonyms for ( MediaType mediaType : getFormats().keySet() ) { for( MediaType syn : MediaTypes.getSynonyms( mediaType ) ) { if ( mt.equals( syn ) ) { fmt = getFormats().get( mediaType ); } } } } if ( fmt == null ) { //do a "contains" check on synonyms for( MediaType syn : MediaTypes.getSynonyms( mt ) ) { for ( MediaType mediaType : getFormats().keySet() ) { if ( mediaType.includes( syn ) || syn.includes( mediaType )) { fmt = getFormats().get( mediaType ); break; } } } } //TODO: reverse contains check on synonyms return fmt; } /** * Returns the object which contains information about the page / resource bring requested. * <p> * This object is created by the rest dispatcher when a request comes in. * </p> */ protected PageInfo getPageInfo() { return (PageInfo) getRequest().getAttributes().get( PageInfo.KEY ); } }