blob: de411bf5b94e4df81fd19bd1942ae3de0031620d [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.oodt.grid;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.util.Iterator;
import java.util.List;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.oodt.product.HttpRedirectException;
import org.apache.oodt.product.LargeProductQueryHandler;
import org.apache.oodt.product.ProductException;
import org.apache.oodt.product.QueryHandler;
import org.apache.oodt.product.Retriever;
import org.apache.oodt.xmlquery.LargeResult;
import org.apache.oodt.xmlquery.Result;
import org.apache.oodt.xmlquery.XMLQuery;
/**
* Product query servlet handles product queries. It always returns the first matching
* product, if any. If no handler can provide a product, it returns 404 Not Found. If
* there are no query handlers, it returns 404 Not Found.
*
*/
public class ProductQueryServlet extends QueryServlet {
/** {@inheritDoc} */
protected List getServers(Configuration config) {
return config.getProductServers();
}
/** {@inheritDoc} */
protected void handleQuery(XMLQuery query, List handlers, HttpServletRequest req, HttpServletResponse res)
throws IOException, ServletException {
if (handlers.isEmpty()) {
res.sendError(HttpServletResponse.SC_NOT_FOUND, "no query handlers available to handle query");
return;
}
try { // OK, let's try
for (Iterator i = handlers.iterator(); i.hasNext();) { // Try each query handler
QueryHandler handler = (QueryHandler) i.next(); // Get the query handler
query = handler.query(query); // Give it the query
if (!query.getResults().isEmpty()) { // Did it give any result?
Result result = (Result) query.getResults().get(0); // Yes, get the result
deliverResult(handler, result, res); // And deliver it
return; // Done!
}
}
} catch (ProductException ex) {
if (ex instanceof HttpRedirectException) {
HttpRedirectException hre = (HttpRedirectException) ex;
res.sendRedirect(hre.getLocation());
} else {
res.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, ex.getMessage());
}
return;
}
res.sendError(HttpServletResponse.SC_NOT_FOUND, "no matching products from any query handler");
}
/**
* Deliver a result. This streams the product data.
*
* @param handler Which handler produced the result.
* @param result The result.
* @param res The HTTP response to which to send the result data.
* @throws IOException if an error occurs.
*/
private void deliverResult(QueryHandler handler, Result result, HttpServletResponse res) throws IOException {
characterize(handler, result, res); // First, describe it using HTTP headers
if (result instanceof LargeResult) { // Is it a large result?
LargeResult lr = (LargeResult) result; // Yes, this is gonna take some special work
LargeProductQueryHandler lpqh = (LargeProductQueryHandler) handler; // First treat 'em as large
ProductRetriever retriever = new ProductRetriever(lpqh); // Large ones need a retriever
lr.setRetriever(retriever); // Set the retriever
}
BufferedInputStream in = null; // Start with no input stream
try { // Then try ...
in = new BufferedInputStream(result.getInputStream()); // To open the input stream
byte[] buf = new byte[512]; // And a byte buffer for data
int num; // And a place to count data
while ((num = in.read(buf)) != -1) // While we read
res.getOutputStream().write(buf, 0, num); // We write
res.getOutputStream().flush(); // Flush to commit response
} finally { // And finally
if (in != null) try { // If we opened it
in.close(); // Close it
} catch (IOException ignore) {} // Ignoring any error during closing
} // Because come on, it's just closing!
} // For fsck's sake!
/**
* Characterize a result by using HTTP headers.
*
* @param result Result to characterize.
* @param res HTTP response to set headers in.
*/
private void characterize(QueryHandler handler, Result result, HttpServletResponse res) {
String contentType = result.getMimeType(); // Grab the content type
res.setContentType(contentType); // Set it
long size = result.getSize(); // Grab the size
if (size >= 0)
res.addHeader("Content-Length", String.valueOf(size)); // Don't use setContentLength(int)
if (!displayable(contentType)) // Finally, if a browser can't show it
this.suggestFilename(handler, result, res); // Then suggest a save-as filename
}
/**
* Tell if a result is displayable. This compares its MIME type to a list of MIME
* types commonly displayble by browsers.
*
* @param contentType MIME type.
* @return a <code>boolean</code> value.
*/
protected static boolean displayable(String contentType) {
for (int i = 0; i < DISPLAYABLE_TYPES.length; ++i) // For each displayable type
if (DISPLAYABLE_TYPES[i].equals(contentType)) return true; // Does it match?
return false; // None of 'em do, it's not displayable
}
/**
* We can suggest a filename (if the client happens to be a browser) using a
* content-disposition header.
*
* @param res a <code>HttpServletResponse</code> value.
*/
protected void suggestFilename(QueryHandler handler, Result result, HttpServletResponse res) {
String resource = result.getResourceID();
if (resource == null || resource.length() == 0) resource = "product.dat";
// suggest some names based on resource mime type
String contentType = res.getContentType();
// "zip" mime types
if (contentType!=null) {
if (contentType.equals("application/x-compressed") ||
contentType.equals("application/x-zip-compressed") ||
contentType.equals("application/zip") ||
contentType.equals("multipart/x-zip") ) {
// resource = resource.replaceAll("\\..+",".zip"); // replace extension with .zip
resource = "products_" + resource + ".zip";
}
}
// set "Content-disposition" header
res.addHeader("Content-disposition", "attachment; filename=\"" + resource + "\"");
}
/**
* MIME types commonly displayable by browsers.
*/
private static final String[] DISPLAYABLE_TYPES = {
"text/plain", "text/richtext", "text/enriched", "text/tab-separated-values", "text/html", "text/xml", "text/rtf",
"message/rfc822", "message/partial", "message/external-body", "message/news", "message/http",
"message/delivery-status", "message/disposition-notification", "message/s-http", "application/rtf",
"application/pdf", "image/jpeg", "image/gif", "image/tiff", "image/png", "audio/basic", "audio/32kadpcm",
"audio/mpeg", "video/mpeg", "video/quicktime"
};
/**
* Retriever that retrieves product data over a method call boundary to a large
* product query handler.
*/
private static class ProductRetriever implements Retriever {
/**
* Creates a new <code>ProductRetriever</code> instance.
*
* @param handler a <code>LargeProductQueryHandler</code> value.
*/
public ProductRetriever(LargeProductQueryHandler handler) {
this.handler = handler;
}
/** {@inheritDoc} */
public byte[] retrieveChunk(String id, long offset, int len) throws ProductException {
return handler.retrieveChunk(id, offset, len);
}
/** {@inheritDoc} */
public void close(String id) throws ProductException {
handler.close(id);
}
/** Handler to use. */
private LargeProductQueryHandler handler;
}
}