Java tutorial
/******************************************************************************* * Copyright (c) 2011 IBM Corporation. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * and Eclipse Distribution License v. 1.0 which accompanies this distribution. * * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html * and the Eclipse Distribution License is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * Contributors: * * Jim Conallen - initial API and implementation *******************************************************************************/ package org.eclipse.lyo.oslc.am.resource; import java.awt.Color; import java.awt.Dimension; import java.awt.Graphics2D; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.net.URI; import java.net.URLEncoder; import java.util.Date; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.StringTokenizer; import javax.servlet.RequestDispatcher; import javax.servlet.ServletException; import javax.servlet.ServletInputStream; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import org.apache.commons.fileupload.FileItem; import org.apache.commons.fileupload.FileItemFactory; import org.apache.commons.fileupload.disk.DiskFileItemFactory; import org.apache.commons.fileupload.servlet.ServletFileUpload; import org.apache.poi.hslf.model.Slide; import org.apache.poi.hslf.usermodel.SlideShow; import org.eclipse.lyo.oslc.am.common.IAmConstants; import org.eclipse.lyo.rio.core.IConstants; import org.eclipse.lyo.rio.l10n.Messages; import org.eclipse.lyo.rio.query.PName; import org.eclipse.lyo.rio.query.SimpleQueryBuilder; import org.eclipse.lyo.rio.services.RioBaseService; import org.eclipse.lyo.rio.services.RioServiceException; import org.eclipse.lyo.rio.store.JsonFormatter2; import org.eclipse.lyo.rio.store.OslcResource; import org.eclipse.lyo.rio.store.RioServerException; import org.eclipse.lyo.rio.store.RioStatement; import org.eclipse.lyo.rio.store.RioStore; import org.eclipse.lyo.rio.store.RioValue; import org.eclipse.lyo.rio.store.JsonFormatter2.IMultiValueResolver; import org.eclipse.lyo.rio.store.RioValue.RioValueType; import org.eclipse.lyo.rio.store.UnrecognizedValueTypeException; import org.eclipse.lyo.rio.store.XmlFormatter; import org.eclipse.lyo.rio.util.StringUtils; import org.eclipse.lyo.rio.util.XmlUtils; import org.w3c.dom.Document; import org.w3c.dom.Element; public class ResourceService extends RioBaseService { private static final long serialVersionUID = -5280734755943517104L; protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { try { RioStore store = this.getStore(); URI reqUri = URI.create(request.getRequestURI()); String path = reqUri.getPath(); StringTokenizer st = new StringTokenizer(path, "/"); if (st.countTokens() > 2) { // /rio-am/resource/{resId} OslcResource resource = store.getOslcResource(request.getRequestURL().toString()); if (resource == null) { throw new RioServiceException(IConstants.SC_NOT_FOUND); } String accept = request.getHeader(IConstants.HDR_ACCEPT); if (this.willAccept(IConstants.CT_HTML, request)) { Resource amResource = new Resource(resource); request.setAttribute("resource", amResource); //$NON-NLS-1$ if (resource.isRdfType(IAmConstants.RIO_AM_PPT_DECK)) { RequestDispatcher rd = request.getRequestDispatcher("/am/resource_deck.jsp"); //$NON-NLS-1$ rd.forward(request, response); return; } else if (resource.isRdfType(IAmConstants.RIO_AM_PPT_SLIDE)) { RequestDispatcher rd = request.getRequestDispatcher("/am/resource_slide.jsp"); //$NON-NLS-1$ rd.forward(request, response); return; } RequestDispatcher rd = request.getRequestDispatcher("/am/resource_view.jsp"); //$NON-NLS-1$ rd.forward(request, response); } else if (this.willAccept(IConstants.CT_APP_N_TRIPLES, request)) { response.setContentType(IConstants.CT_JSON); response.getWriter().write(resource.dumpNTriples()); response.setStatus(IConstants.SC_OK); } else if (this.willAccept(IConstants.CT_JSON, request)) { JsonFormatter2 formatter = new JsonFormatter2(new IMultiValueResolver() { @Override public boolean isMultiValued(String property) { if (IConstants.DCTERMS_CREATOR.equals(property) || IConstants.DCTERMS_CONTRIBUTOR.equals(property) || IConstants.OSLC_SERVICEPROVIDER.equals(property)) { return true; } return false; } }); formatter.addNamespacePrefix(IAmConstants.OSLC_AM_NAMESPACE, IAmConstants.OSLC_AM_PREFIX); String json = formatter.format(resource); response.setContentType(IConstants.CT_JSON); response.getWriter().write(json); response.setStatus(IConstants.SC_OK); } else if (accept == null || this.willAccept(IConstants.CT_RDF_XML, request) || this.willAccept(IConstants.CT_XML, request)) { String content = XmlFormatter.formatResource(resource, IAmConstants.OSLC_AM_TYPE_RESOURCE); response.setHeader(IConstants.HDR_ETAG, resource.getETag()); String lm = StringUtils.rfc2822(resource.getModified()); response.setHeader(IConstants.HDR_LAST_MODIFIED, lm); response.setStatus(IConstants.SC_OK); response.setContentType(IConstants.CT_RDF_XML); response.setContentLength(content.getBytes().length); response.getWriter().write(content); } else if (this.willAccept(IConstants.CT_OSLC_COMPACT, request)) { String content = compactDocument(resource); response.setContentType(IConstants.CT_OSLC_COMPACT); response.setContentLength(content.getBytes().length); response.setStatus(IConstants.SC_OK); response.getWriter().write(content); } else { throw new RioServiceException(IConstants.SC_UNSUPPORTED_MEDIA_TYPE, Messages.getString("Resource.UnableToAccept") + accept); //$NON-NLS-1$ } } else { processQuery(request, response); } } catch (RioServerException e) { throw new RioServiceException(IConstants.SC_INTERNAL_ERROR, e); } } /** * @see HttpServlet#doPut(HttpServletRequest, HttpServletResponse) */ protected void doPut(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { try { RioStore store = this.getStore(); OslcResource resource = store.getOslcResource(request.getRequestURL().toString()); if (resource == null) { throw new RioServiceException(IConstants.SC_NOT_FOUND); } checkConditionalHeaders(request, resource); // cache the created and creator Date created = resource.getCreated(); String creator = resource.getCreator(); // ok, then we update this resource String contentType = request.getContentType(); if (!contentType.startsWith(IConstants.CT_RDF_XML)) { throw new RioServiceException(IConstants.SC_UNSUPPORTED_MEDIA_TYPE); } ServletInputStream content = request.getInputStream(); OslcResource updatedResource = new OslcResource(resource.getUri()); List<RioStatement> statements = store.parse(resource.getUri(), content, contentType); updatedResource.addStatements(statements); updatedResource.setCreated(created); updatedResource.setCreator(creator); String userId = request.getRemoteUser(); String userUri = this.getUserUri(userId); store.update(updatedResource, userUri); updatedResource = store.getOslcResource(resource.getUri()); response.setStatus(IConstants.SC_OK); response.addHeader(IConstants.HDR_ETAG, updatedResource.getETag()); response.addHeader(IConstants.HDR_LOCATION, updatedResource.getUri()); String lastModified = StringUtils.rfc2822(updatedResource.getModified()); response.addHeader(IConstants.HDR_LAST_MODIFIED, lastModified); } catch (RioServerException e) { throw new RioServiceException(IConstants.SC_BAD, e); } } /** * @see HttpServlet#doDelete(HttpServletRequest, HttpServletResponse) */ protected void doDelete(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { try { RioStore store = this.getStore(); OslcResource resource = store.getOslcResource(request.getRequestURL().toString()); if (resource == null) { throw new RioServiceException(IConstants.SC_NOT_FOUND); } checkConditionalHeaders(request, resource); store.remove(resource); } catch (RioServerException e) { throw new RioServiceException(IConstants.SC_INTERNAL_ERROR, e); } } @SuppressWarnings("nls") private String compactDocument(OslcResource resource) throws RioServiceException { String title = XmlUtils.encode(resource.getTitle()); String shortTitle = "Resource " + resource.getIdentifier(); String resourceUri = resource.getUri(); try { String resUri = URLEncoder.encode(resourceUri, IConstants.TEXT_ENCODING); String iconUrl = this.getBaseUrl() + "/oslc.png"; String smUrl = this.getBaseUrl() + "/compact/resource?uri=" + resUri + "&type=small"; String lgUrl = this.getBaseUrl() + "/compact/resource?uri=" + resUri + "&type=large"; String doc = "<?xml version=\"1.0\" encoding=\"UTF-8\"?> \n" + "<rdf:RDF \n" + " xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\" \n" + " xmlns:dcterms=\"http://purl.org/dc/terms/\" \n" + " xmlns:oslc=\"http://open-services.net/ns/core#\"> \n" + " <oslc:Compact \n" + " rdf:about=\"" + resourceUri + "\"> \n" + " <dcterms:title>" + title + "</dcterms:title> \n" + " <oslc:shortTitle>" + shortTitle + "</oslc:shortTitle> \n" + " <oslc:icon rdf:resource=\"" + iconUrl + "\" /> \n" + " <oslc:smallPreview> \n" + " <oslc:Preview> \n" + " <oslc:document rdf:resource=\"" + smUrl + "\" /> \n" + " <oslc:hintWidth>500px</oslc:hintWidth> \n" + " <oslc:hintHeight>120px</oslc:hintHeight> \n" + " </oslc:Preview> \n" + " </oslc:smallPreview> \n" + " <oslc:largePreview> \n" + " <oslc:Preview> \n" + " <oslc:document rdf:resource=\"" + lgUrl + "\" /> \n" + " <oslc:hintWidth>500px</oslc:hintWidth> \n" + " <oslc:hintHeight>500px</oslc:hintHeight> \n" + " </oslc:Preview> \n" + " </oslc:largePreview> \n" + " </oslc:Compact> \n" + "</rdf:RDF>"; return doc; } catch (UnsupportedEncodingException ex) { throw new RioServiceException(ex); } } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { boolean isFileUpload = ServletFileUpload.isMultipartContent(request); String contentType = request.getContentType(); if (!isFileUpload && (RioStore.rdfFormatFromContentType(contentType) == null)) { throw new RioServiceException(IConstants.SC_UNSUPPORTED_MEDIA_TYPE); } InputStream content = request.getInputStream(); if (isFileUpload) { // being uploaded from a web page try { FileItemFactory factory = new DiskFileItemFactory(); ServletFileUpload upload = new ServletFileUpload(factory); @SuppressWarnings("unchecked") List<FileItem> items = upload.parseRequest(request); // find the first (and only) file resource in the post Iterator<FileItem> iter = items.iterator(); while (iter.hasNext()) { FileItem item = iter.next(); if (item.isFormField()) { // this is a form field, maybe we can accept a title or descr? } else { content = item.getInputStream(); contentType = item.getContentType(); } } } catch (Exception e) { throw new RioServiceException(e); } } RioStore store = this.getStore(); if (RioStore.rdfFormatFromContentType(contentType) != null) { try { String resUri = store.nextAvailableUri(IAmConstants.SERVICE_RESOURCE); Resource resource = new Resource(resUri); List<RioStatement> statements = store.parse(resUri, content, contentType); resource.addStatements(statements); String userUri = getUserUri(request.getRemoteUser()); // if it parsed, then add it to the store. store.update(resource, userUri); // now get it back, to find OslcResource returnedResource = store.getOslcResource(resource.getUri()); Date created = returnedResource.getCreated(); String eTag = returnedResource.getETag(); response.setStatus(IConstants.SC_CREATED); response.setHeader(IConstants.HDR_LOCATION, resource.getUri()); response.setHeader(IConstants.HDR_LAST_MODIFIED, StringUtils.rfc2822(created)); response.setHeader(IConstants.HDR_ETAG, eTag); } catch (RioServerException e) { throw new RioServiceException(IConstants.SC_BAD, e); } } else if (IAmConstants.CT_APP_X_VND_MSPPT.equals(contentType) || isFileUpload) { try { ByteArrayInputStream bais = isToBais(content); String uri = store.nextAvailableUri(IAmConstants.SERVICE_RESOURCE); Resource resource = new Resource(uri); resource.addRdfType(IAmConstants.OSLC_AM_TYPE_RESOURCE); resource.addRdfType(IAmConstants.RIO_AM_PPT_DECK); String id = resource.getIdentifier(); String deckTitle = "PPT Deck " + id; resource.setTitle(deckTitle); resource.setDescription("A Power Point Deck"); String sourceUri = getBaseUrl() + '/' + IAmConstants.SERVICE_SOURCE + '/' + id; resource.setSource(sourceUri); resource.setSourceContentType(contentType); String userUri = getUserUri(request.getRemoteUser()); store.storeBinaryResource(bais, id); bais.reset(); SlideShow ppt = new SlideShow(bais); Dimension pgsize = ppt.getPageSize(); Slide[] slide = ppt.getSlides(); for (int i = 0; i < slide.length; i++) { String slideTitle = extractTitle(slide[i]); String slideUri = store.nextAvailableUri(IAmConstants.SERVICE_RESOURCE); Resource slideResource = new Resource(slideUri); slideResource.addRdfType(IAmConstants.OSLC_AM_TYPE_RESOURCE); slideResource.addRdfType(IAmConstants.RIO_AM_PPT_SLIDE); String slideId = slideResource.getIdentifier(); slideResource.setTitle(slideTitle); sourceUri = getBaseUrl() + '/' + IAmConstants.SERVICE_SOURCE + '/' + slideId; slideResource.setSource(sourceUri); slideResource.setSourceContentType(IConstants.CT_IMAGE_PNG); store.update(slideResource, userUri); BufferedImage img = new BufferedImage(pgsize.width, pgsize.height, BufferedImage.TYPE_INT_RGB); Graphics2D graphics = img.createGraphics(); graphics.setPaint(Color.white); graphics.fill(new Rectangle2D.Float(0, 0, pgsize.width, pgsize.height)); slide[i].draw(graphics); ByteArrayOutputStream out = new ByteArrayOutputStream(); javax.imageio.ImageIO.write(img, "png", out); ByteArrayInputStream is = new ByteArrayInputStream(out.toByteArray()); store.storeBinaryResource(is, slideId); out.close(); is.close(); try { RioValue v = new RioValue(RioValueType.URI, slideResource.getUri()); resource.appendToSeq(IConstants.RIO_NAMESPACE + "slides", v); } catch (UnrecognizedValueTypeException e) { // log this? don't want to throw away everything, since this should never happen } } store.update(resource, userUri); // now get it back, to find eTag and creator stuff OslcResource returnedResource = store.getOslcResource(resource.getUri()); Date created = returnedResource.getCreated(); String eTag = returnedResource.getETag(); response.setStatus(IConstants.SC_CREATED); response.setHeader(IConstants.HDR_LOCATION, resource.getUri()); response.setHeader(IConstants.HDR_LAST_MODIFIED, StringUtils.rfc2822(created)); response.setHeader(IConstants.HDR_ETAG, eTag); } catch (RioServerException e) { throw new RioServiceException(IConstants.SC_BAD, e); } } else { // must be a binary or unknown format, treat as black box // normally a service provider will understand this and parse it appropriately // however this server will accept any blank box resource try { String uri = store.nextAvailableUri(IAmConstants.SERVICE_RESOURCE); Resource resource = new Resource(uri); String id = resource.getIdentifier(); resource.setTitle("Resource " + id); resource.setDescription("A binary resource"); String sourceUri = getBaseUrl() + IAmConstants.SERVICE_SOURCE + '/' + id; resource.setSource(sourceUri); resource.setSourceContentType(contentType); String userUri = getUserUri(request.getRemoteUser()); store.update(resource, userUri); store.storeBinaryResource(content, id); // now get it back, to find eTag and creator stuff OslcResource returnedResource = store.getOslcResource(resource.getUri()); Date created = returnedResource.getCreated(); String eTag = returnedResource.getETag(); response.setStatus(IConstants.SC_CREATED); response.setHeader(IConstants.HDR_LOCATION, resource.getUri()); response.setHeader(IConstants.HDR_LAST_MODIFIED, StringUtils.rfc2822(created)); response.setHeader(IConstants.HDR_ETAG, eTag); } catch (RioServerException e) { throw new RioServiceException(IConstants.SC_BAD, e); } } } public static ByteArrayInputStream isToBais(InputStream is) throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); // byte[] buf = new byte[1024]; // int len = is.read(buf); // while( (len = is.read(buf)) > 0 ) { // baos.write(buf, 0, len); // } int b = is.read(); while (b >= 0) { baos.write(b); b = is.read(); } is.close(); return new ByteArrayInputStream(baos.toByteArray()); } private String filterCharacters(String str) { if (str == null || str.length() == 0) return ""; StringBuffer inBuf = new StringBuffer(str); StringBuffer outBuf = new StringBuffer(); int len = inBuf.length(); for (int i = 0; i < len; i++) { char ch = inBuf.charAt(i); if (Character.isWhitespace(ch)) { outBuf.append(' '); } else if (Character.isLetterOrDigit(ch) || ch == '\'') { outBuf.append(ch); } } return outBuf.toString(); } private String extractTitle(Slide slide) { String title = slide.getTitle(); if (title == null) { title = "(untitled)"; } else { title = filterCharacters(title); } return title; } private void processQuery(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { try { String prefix = req.getParameter("oslc.prefix"); //$NON-NLS-1$ String select = req.getParameter("oslc.select"); //$NON-NLS-1$ String where = req.getParameter("oslc.where"); //$NON-NLS-1$ String orderBy = req.getParameter("oslc.orderBy"); //$NON-NLS-1$ String searchTerms = req.getParameter("oslc.searchTerms"); //$NON-NLS-1$ SimpleQueryBuilder queryBuilder = new SimpleQueryBuilder(); queryBuilder.parsePrefix(prefix); queryBuilder.parseSelect(select); queryBuilder.parseWhere("uri", where); //$NON-NLS-1$ queryBuilder.parseSelect(orderBy); queryBuilder.parseSearchTerms(searchTerms); String sparql = queryBuilder.getQueryString(IAmConstants.OSLC_AM_TYPE_RESOURCE); RioStore store = this.getStore(); //don't add ?null if the the query is for queryBase String queryUri = (req.getQueryString() == null) ? req.getRequestURL().toString() : req.getRequestURL().toString() + '?' + req.getQueryString(); List<Map<String, RioValue>> results = store.query(IConstants.SPARQL, sparql, 100); // TODO: create propnames Map<String, PName> propNames = queryBuilder.getPropertyNames(); String responseResource = buildResponseResource(results, propNames, queryUri); resp.setContentType(IConstants.CT_RDF_XML); resp.getWriter().write(responseResource); resp.setStatus(IConstants.SC_OK); } catch (Exception e) { throw new RioServiceException(IConstants.SC_INTERNAL_ERROR, e); } } private String buildResponseResource(List<Map<String, RioValue>> results, Map<String, PName> propNames, String reqUri) throws RioServiceException { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); factory.setValidating(true); factory.setNamespaceAware(true); try { DocumentBuilder builder = factory.newDocumentBuilder(); Document doc = builder.newDocument(); Element rdf = doc.createElementNS(IConstants.RDF_NAMESPACE, IConstants.RDF_TYPE_PTERM_RDF); doc.appendChild(rdf); Set<String> namespaces = namespacePrefixes.keySet(); for (String namespace : namespaces) { rdf.setAttribute("xmlns:" + namespacePrefixes.get(namespace), namespace); //$NON-NLS-1$ } //If there is are parameters on the request (e.g. ?oslc.where=...), create 2 subject URIs, one for the //results and one to describe the query. If this query was for the queryBase itself all predicates //go under the same subject. //TODO: Refactor the services for all RIO artifacts to eliminate duplicated code. Element resultDescr = doc.createElementNS(IConstants.RDF_NAMESPACE, IConstants.RDF_TYPE_PTERM_DESCRIPTION); rdf.appendChild(resultDescr); //check for oslc query parameters String uriSplit[] = reqUri.split("\\?", 2); String baseUri = uriSplit[0]; boolean isOslcQuery = false; if (uriSplit.length > 1) isOslcQuery = hasOSLCQuery(uriSplit[1]); resultDescr.setAttributeNS(IConstants.RDF_NAMESPACE, IConstants.RDF_PTERM_ABOUT, baseUri); //if there are oslc query parameters, put the predicates under the query subject Element queryDescrElement = null; if (isOslcQuery) { queryDescrElement = doc.createElementNS(IConstants.RDF_NAMESPACE, IConstants.RDF_TYPE_PTERM_DESCRIPTION); queryDescrElement.setAttributeNS(IConstants.RDF_NAMESPACE, IConstants.RDF_PTERM_ABOUT, reqUri); rdf.appendChild(queryDescrElement); } else { //no oslc query parameters, everything goes under the queryBase subject queryDescrElement = resultDescr; } Element title = doc.createElementNS(IConstants.DCTERMS_NAMESPACE, IConstants.DCTERMS_PTERM_TITLE); queryDescrElement.appendChild(title); title.setTextContent(Messages.getString("ResourceQuery.Title")); Element count = doc.createElementNS(IConstants.OSLC_NAMESPACE, IConstants.OSLC_PTERM_TOTALCOUNT); queryDescrElement.appendChild(count); count.setTextContent(Integer.toString(results.size())); Element rdfType = doc.createElementNS(IConstants.RDF_NAMESPACE, IConstants.RDF_PTERM_TYPE); rdfType.setAttributeNS(IConstants.RDF_NAMESPACE, IConstants.RDF_PTERM_RESOURCE, IConstants.OSLC_RESPONSEINFO); queryDescrElement.appendChild(rdfType); Iterator<Map<String, RioValue>> iterator = results.iterator(); while (iterator.hasNext()) { Element rdfMem = doc.createElementNS(IConstants.RDFS_NAMESPACE, IConstants.RDFS_PTERM_MEMBER); resultDescr.appendChild(rdfMem); Map<String, RioValue> map = iterator.next(); RioValue uri = map.get("uri"); //$NON-NLS-1$ rdfMem.setAttributeNS(IConstants.RDF_NAMESPACE, IConstants.RDF_PTERM_RESOURCE, uri.stringValue()); } return XmlUtils.prettyPrint(doc); } catch (ParserConfigurationException e) { throw new RioServiceException(e); } finally { } } private boolean hasOSLCQuery(String parms) { boolean containsOslcParm = false; // not perfect - could have "oslc." in some random part of the parameters, but don't // have access to the request at this point to use getPararmeterNames String[] uriParts = parms.toLowerCase().split("oslc\\.", 2); if (uriParts.length > 1) { containsOslcParm = true; } return containsOslcParm; } static private Map<String, String> namespacePrefixes = new HashMap<String, String>(); static { namespacePrefixes.put(IConstants.RDF_NAMESPACE, IConstants.RDF_PREFIX); namespacePrefixes.put(IConstants.RDFS_NAMESPACE, IConstants.RDFS_PREFIX); namespacePrefixes.put(IConstants.OSLC_NAMESPACE, IConstants.OSLC_PREFIX); namespacePrefixes.put(IConstants.DCTERMS_NAMESPACE, IConstants.DCTERMS_PREFIX); } }