Java tutorial
/** * Copyright 2004 The Apache Software Foundation * * Licensed 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 * * http://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. */ package org.apache.lucene.gdata.server; import java.io.IOException; import java.io.Reader; import java.util.Enumeration; import java.util.Map; import java.util.StringTokenizer; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.lucene.gdata.search.config.IndexSchema; import org.apache.lucene.gdata.search.query.QueryTranslator; import org.apache.lucene.gdata.server.authentication.AuthenticationController; import org.apache.lucene.gdata.server.registry.GDataServerRegistry; import org.apache.lucene.gdata.server.registry.ProvidedService; /** * The GDataRequest Class wraps the incoming HttpServletRequest. Needed * information coming with the HttpServletRequest can be accessed directly. It * represents an abstraction on the plain HttpServletRequest. Every GData * specific data coming from the client will be available and can be accessed * via the GDataRequest. * <p> * GDataRequest instances will be passed to any action requested by the client. * This class also holds the logic to retrieve important information like * response format, the requested feed instance and query parameters. * * </p> * * @author Simon Willnauer * */ /* this class might be extracted as an interface in later development */ public class GDataRequest { private static final Log LOG = LogFactory.getLog(GDataRequest.class); private static final String RESPONSE_FORMAT_PARAMETER = "alt"; private static final String RESPONSE_FORMAT_PARAMETER_RSS = "rss"; private static final String RESPONSE_FORMAT_PARAMETER_HTML = "html"; private static final int DEFAULT_ITEMS_PER_PAGE = 25; private static final int DEFAULT_START_INDEX = 1; private static final String START_INDEX_NEXT_PAGE_PARAMETER = "start-index"; private static final String ITEMS_PER_PAGE_PARAMETER = "max-results"; private String contextPath; @SuppressWarnings("unused") private static final String RESPONSE_FORMAT_PARAMETER_ATOM = "atom"; private static final String HTTP_HEADER_IF_MODIFIED_SINCE = "If-Modified-Since"; private static final String HTTP_HEADER_AUTH = "Authorization"; private static final Object CATEGORY_QUERY_INDICATOR = "-"; // Atom is the default response format private OutputFormat responseFormat = OutputFormat.ATOM; private final HttpServletRequest request; private String feedId = null; private String entryId = null; private String service = null; private ProvidedService configurator = null; private boolean isSearchRequest = false; private String entryVersion = null; private GDataRequestType type; private String categoryQuery; private String translatedSearchQuery; private boolean isFeedRequest = false; /** * Creates a new FeedRequest * * @param requst - * the incoming HttpServletReqeust * @param type - * the request type * */ public GDataRequest(final HttpServletRequest requst, final GDataRequestType type) { if (requst == null) throw new IllegalArgumentException("request must not be null "); if (type == null) throw new IllegalArgumentException("request type must not be null "); this.request = requst; this.type = type; } /** * Initialize the GDataRequest. This will initialize all needed values / * attributes in this request. * * @throws GDataRequestException */ public void initializeRequest() throws GDataRequestException { generateIdentificationProperties(); setOutputFormat(); try { /* * ExtensionProfile and the type is used for building the Entry / * Feed Instances from an input stream or reader * */ this.configurator = GDataServerRegistry.getRegistry().getProvidedService(this.service); if (this.configurator == null) throw new GDataRequestException("no Provided Service found for service id: " + this.service, GDataResponse.NOT_FOUND); applyRequestParameter(); if (this.translatedSearchQuery != null) this.isSearchRequest = true; } catch (GDataRequestException ex) { throw ex; } catch (Exception e) { throw new GDataRequestException("failed to initialize GDataRequest -- " + e.getMessage(), e, GDataResponse.SERVER_ERROR); } } @SuppressWarnings("unchecked") private void applyRequestParameter() throws GDataRequestException { IndexSchema schema = this.configurator.getIndexSchema(); try { this.translatedSearchQuery = QueryTranslator.translateHttpSearchRequest(schema, this.request.getParameterMap(), this.categoryQuery); } catch (Exception e) { throw new GDataRequestException("Can not translate user query to search query", e, GDataResponse.BAD_REQUEST); } } /** * @return - the id of the requested feed */ public String getFeedId() { return this.feedId; } /** * @return - the entry id of the requested Entry if specified, otherwise * <code>null</code> */ public String getEntryId() { return this.entryId; } /** * @return the version Id of the requested Entry if specified, otherwise * <code>null</code> */ public String getEntryVersion() { return this.entryVersion; } /** * A Reader instance to read form the client input stream * * @return - the HttpServletRequest {@link Reader} * @throws IOException - * if an I/O Exception occurs */ public Reader getReader() throws IOException { return this.request.getReader(); } /** * Returns the {@link HttpServletRequest} parameter map containing all * <i>GET</i> request parameters. * * @return the parameter map */ @SuppressWarnings("unchecked") public Map<String, String[]> getQueryParameter() { return this.request.getParameterMap(); } /** * The {@link HttpServletRequest} request parameter names * * @return parameter names enumeration */ @SuppressWarnings("unchecked") public Enumeration<String> getQueryParameterNames() { return this.request.getParameterNames(); } /** * Either <i>Atom</i> or <i>RSS</i> * * @return - The output format requested by the client */ public OutputFormat getRequestedResponseFormat() { return this.responseFormat; } private void generateIdentificationProperties() throws GDataRequestException { /* generate all needed data to identify the requested feed/entry */ String pathInfo = this.request.getPathInfo(); if (pathInfo.length() <= 1) throw new GDataRequestException("No feed or entry specified for this request", GDataResponse.BAD_REQUEST); StringTokenizer tokenizer = new StringTokenizer(pathInfo, "/"); this.service = tokenizer.nextToken(); if (!tokenizer.hasMoreTokens()) throw new GDataRequestException("Can not find feed id in requested path " + pathInfo, GDataResponse.BAD_REQUEST); this.feedId = tokenizer.nextToken(); String appendix = tokenizer.hasMoreTokens() ? tokenizer.nextToken() : null; if (appendix == null) { this.isFeedRequest = true; return; } if (appendix.equals(CATEGORY_QUERY_INDICATOR)) { StringBuilder builder = new StringBuilder(); while (tokenizer.hasMoreTokens()) builder.append("/").append(tokenizer.nextToken()); this.categoryQuery = builder.toString(); } else { this.entryId = appendix; this.entryVersion = tokenizer.hasMoreTokens() ? tokenizer.nextToken() : ""; } this.isFeedRequest = (this.type == GDataRequestType.GET && (this.entryId == null || this.entryId.length() == 0 || (this.entryId.equals('/')))); } private void setOutputFormat() { String formatParameter = this.request.getParameter(RESPONSE_FORMAT_PARAMETER); if (formatParameter == null) return; if (formatParameter.equalsIgnoreCase(RESPONSE_FORMAT_PARAMETER_RSS)) this.responseFormat = OutputFormat.RSS; if (formatParameter.equalsIgnoreCase(RESPONSE_FORMAT_PARAMETER_HTML)) this.responseFormat = OutputFormat.HTML; } /** * @return - the number of returned items per page */ public int getItemsPerPage() { if (this.request.getParameter(ITEMS_PER_PAGE_PARAMETER) == null) return DEFAULT_ITEMS_PER_PAGE; int retval = -1; try { retval = new Integer(this.request.getParameter(ITEMS_PER_PAGE_PARAMETER)).intValue(); } catch (Exception e) { LOG.warn("Items per page could not be parsed - " + e.getMessage(), e); } return retval < 0 ? DEFAULT_ITEMS_PER_PAGE : retval; } /** * Start index represents the number of the first entry of the query - * result. The order depends on the query. Is the query a search query the * this value will be assigned to the score in a common feed query the value * will be assigned to the update time of the entries. * * @return - the requested start index */ public int getStartIndex() { String startIndex = this.request.getParameter(START_INDEX_NEXT_PAGE_PARAMETER); if (startIndex == null) return DEFAULT_START_INDEX; int retval = -1; try { retval = new Integer(startIndex).intValue(); } catch (Exception e) { LOG.warn("Start-index could not be parsed - not an integer - " + e.getMessage()); } return retval < 0 ? DEFAULT_START_INDEX : retval; } /** * The self id is the feeds <i>href</i> pointing to the requested resource * * @return - the self id */ public String getSelfId() { StringBuilder builder = new StringBuilder(); builder.append(buildRequestIDString(false)); builder.append("?"); builder.append(getQueryString()); return builder.toString(); } /** * The previous id is the feeds <i>href</i> pointing to the previous result of the requested resource * * @return - the self id */ public String getPreviousId() { int startIndex = getStartIndex(); if (startIndex == DEFAULT_START_INDEX) return null; StringBuilder builder = new StringBuilder(); builder.append(buildRequestIDString(false)); startIndex = startIndex - getItemsPerPage(); builder.append(getPreparedQueryString(startIndex < 1 ? DEFAULT_START_INDEX : startIndex)); return builder.toString(); } private String getPreparedQueryString(int startIndex) { String queryString = this.request.getQueryString(); String startIndexValue = this.request.getParameter(START_INDEX_NEXT_PAGE_PARAMETER); String maxResultsValue = this.request.getParameter(ITEMS_PER_PAGE_PARAMETER); StringBuilder builder = new StringBuilder("?"); if (maxResultsValue == null) { builder.append(ITEMS_PER_PAGE_PARAMETER).append("=").append(DEFAULT_ITEMS_PER_PAGE); builder.append("&"); } if (startIndexValue == null) { builder.append(START_INDEX_NEXT_PAGE_PARAMETER).append("="); builder.append(Integer.toString(startIndex)); if (queryString != null) { builder.append("&"); builder.append(queryString); } } else { builder.append(queryString.replaceAll("start-index=[\\d]*", START_INDEX_NEXT_PAGE_PARAMETER + "=" + Integer.toString(startIndex))); } return builder.toString(); } /** * The <i>href</i> id of the next page of the requested resource. * * @return the id of the next page */ public String getNextId() { int startIndex = getStartIndex(); StringBuilder builder = new StringBuilder(); builder.append(buildRequestIDString(false)); startIndex = startIndex + getItemsPerPage(); builder.append(getPreparedQueryString(startIndex)); return builder.toString(); } private String buildRequestIDString(boolean endingSlash) { StringBuilder builder = new StringBuilder("http://"); builder.append(this.request.getHeader("Host")); builder.append(this.request.getRequestURI()); if (!endingSlash && builder.charAt(builder.length() - 1) == '/') builder.setLength(builder.length() - 1); if (endingSlash && builder.charAt(builder.length() - 1) != '/') builder.append("/"); return builder.toString(); } /** * This will return the current query string including all parameters. * Additionally the <code>max-resul</code> parameter will be added if not * specified. * <p> * <code>max-resul</code> indicates the number of results returned to the * client. The default value is 25. * </p> * * @return - the query string including all parameters */ public String getQueryString() { String retVal = this.request.getQueryString(); if (this.request.getParameter(ITEMS_PER_PAGE_PARAMETER) != null) return retVal; String tempString = (retVal == null ? ITEMS_PER_PAGE_PARAMETER + "=" + DEFAULT_ITEMS_PER_PAGE : "&" + ITEMS_PER_PAGE_PARAMETER + "=" + DEFAULT_ITEMS_PER_PAGE); return retVal == null ? tempString : retVal + tempString; } /** * This enum represents the OutputFormat of the GDATA Server * * @author Simon Willnauer * */ public static enum OutputFormat { /** * Output format ATOM. ATOM is the default response format. */ ATOM, /** * Output format RSS */ RSS, /** * Output format html if user defined xsl style sheet is present */ HTML } /** * Returns the requested path including the domain name and the requested * resource <i>http://www.apache.org/path/resource/</i> * * @return the context path */ public String getContextPath() { if (this.contextPath == null) this.contextPath = buildRequestIDString(true); return this.contextPath; } /** * Indicates the request type * * @author Simon Willnauer * */ public enum GDataRequestType { /** * Type FeedRequest */ GET, /** * Type UpdateRequest */ UPDATE, /** * Type DeleteRequest */ DELETE, /** * Type InsertRequest */ INSERT } /** * {@link GDataRequestType} * * @return the current request type */ public GDataRequestType getType() { return this.type; } /** * If the request is a {@link GDataRequestType#GET} request and there is no * entry id specified, the requested resource is a feed. * * @return - <code>true</code> if an only if the requested resource is a * feed */ public boolean isFeedRequested() { return this.isFeedRequest; } /** * * If the request is a {@link GDataRequestType#GET} request and there is * an entry id specified, the requested resource is an entry. * * @return - <code>true</code> if an only if the requested resource is an * entry */ public boolean isEntryRequested() { return !this.isFeedRequested(); } /** * @return - <code>true</code> if an only if the user request is a search request, otherwise <code>false</code> */ public boolean isSearchRequested() { return this.isSearchRequest; } /** * @return the configuration for this request */ public ProvidedService getConfigurator() { return this.configurator; } /** * @return - Returns the Internet Protocol (IP) address of the client or * last proxy that sent the request. */ public String getRemoteAddress() { return this.request.getRemoteAddr(); } /** * @return - the value for the send auth token. The auth token will be send * as a request <tt>Authentication</tt> header. */ public String getAuthToken() { String token = this.request.getHeader(HTTP_HEADER_AUTH); if (token == null) return null; token = token.substring(token.indexOf("=") + 1); return token; } /** * @return - Returns an array containing all of the Cookie objects the * client sent with underlying HttpServletRequest. */ public Cookie[] getCookies() { return this.request.getCookies(); } /** * @return - the cookie set instead of the authentication token or * <code>null</code> if no auth cookie is set */ public Cookie getAuthCookie() { Cookie[] cookies = this.request.getCookies(); if (cookies == null) return null; for (int i = 0; i < cookies.length; i++) { if (cookies[i].getName().equals(AuthenticationController.TOKEN_KEY)) return cookies[i]; } return null; } /** * @return - the date string of the <tt>If-Modified-Since</tt> HTTP * request header, or null if header is not set */ public String getModifiedSince() { return this.request.getHeader(HTTP_HEADER_IF_MODIFIED_SINCE); } /** * @return - the underlying HttpServletRequest */ public HttpServletRequest getHttpServletRequest() { return this.request; } protected String getTranslatedQuery() { return this.translatedSearchQuery; } }