Java tutorial
/* * Copyright 2007 Sun Microsystems, Inc. * * 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 com.rometools.propono.atom.server.impl; import java.io.File; import java.util.StringTokenizer; import javax.servlet.http.HttpServletRequest; import org.apache.commons.codec.binary.Base64; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.rometools.propono.atom.common.AtomService; import com.rometools.propono.atom.common.Categories; import com.rometools.propono.atom.server.AtomException; import com.rometools.propono.atom.server.AtomHandler; import com.rometools.propono.atom.server.AtomMediaResource; import com.rometools.propono.atom.server.AtomRequest; import com.rometools.propono.atom.server.AtomServlet; import com.rometools.rome.feed.atom.Entry; import com.rometools.rome.feed.atom.Feed; /** * File-based {@link com.rometools.rome.propono.atom.server.AtomHandler} implementation that stores * entries and media-entries to disk. Implemented using * {@link com.rometools.rome.propono.atom.server.impl.FileBasedAtomService}. */ public class FileBasedAtomHandler implements AtomHandler { private static final Logger LOG = LoggerFactory.getLogger(FileBasedAtomHandler.class); private String userName = null; private String atomProtocolURL = null; private String contextURI = null; private FileBasedAtomService service = null; /** * Construct handler to handle one request. * * @param req Request to be handled. */ public FileBasedAtomHandler(final HttpServletRequest req) { this(req, AtomServlet.getContextDirPath()); } /** * Contruct handler for one request, using specified file storage directory. * * @param req Request to be handled. * @param uploaddir File storage upload dir. */ public FileBasedAtomHandler(final HttpServletRequest req, final String uploaddir) { LOG.debug("ctor"); userName = authenticateBASIC(req); atomProtocolURL = req.getScheme() + "://" + req.getServerName() + ":" + req.getServerPort() + req.getContextPath() + req.getServletPath(); contextURI = req.getScheme() + "://" + req.getServerName() + ":" + req.getServerPort() + req.getContextPath(); try { service = new FileBasedAtomService(userName, uploaddir, contextURI, req.getContextPath(), req.getServletPath()); } catch (final Throwable t) { throw new RuntimeException("ERROR creating FileBasedAtomService", t); } } /** * Method used for validating user. Developers can overwrite this method and use credentials * stored in Database or LDAP to confirm if the user is allowed to access this service. * * @param login user submitted login id * @param password user submitted password */ public boolean validateUser(final String login, final String password) { return true; } /** * Get username of authenticated user * * @return User name. */ @Override public String getAuthenticatedUsername() { // For now return userName as the login id entered for authorization return userName; } /** * Get base URI of Atom protocol implementation. * * @return Base URI of Atom protocol implemenation. */ public String getAtomProtocolURL() { if (atomProtocolURL == null) { return "app"; } else { return atomProtocolURL; } } /** * Return introspection document * * @throws com.rometools.rome.propono.atom.server.AtomException Unexpected exception. * @return AtomService object with workspaces and collections. */ @Override public AtomService getAtomService(final AtomRequest areq) throws AtomException { return service; } /** * Returns null because we use in-line categories. * * @throws com.rometools.rome.propono.atom.server.AtomException Unexpected exception. * @return Categories object */ @Override public Categories getCategories(final AtomRequest areq) throws AtomException { LOG.debug("getCollection"); final String[] pathInfo = StringUtils.split(areq.getPathInfo(), "/"); final String handle = pathInfo[0]; final String collection = pathInfo[1]; final FileBasedCollection col = service.findCollectionByHandle(handle, collection); return col.getCategories(true).get(0); } /** * Get collection specified by pathinfo. * * @param areq Details of HTTP request * @return ROME feed representing collection. * @throws com.rometools.rome.propono.atom.server.AtomException Invalid collection or other * exception. */ @Override public Feed getCollection(final AtomRequest areq) throws AtomException { LOG.debug("getCollection"); final String[] pathInfo = StringUtils.split(areq.getPathInfo(), "/"); final String handle = pathInfo[0]; final String collection = pathInfo[1]; final FileBasedCollection col = service.findCollectionByHandle(handle, collection); return col.getFeedDocument(); } /** * Create a new entry specified by pathInfo and posted entry. We save the submitted Atom entry * verbatim, but we do set the id and reset the update time. * * @param entry Entry to be added to collection. * @param areq Details of HTTP request * @throws com.rometools.rome.propono.atom.server.AtomException On invalid collection or other * error. * @return Entry as represented on server. */ @Override public Entry postEntry(final AtomRequest areq, final Entry entry) throws AtomException { LOG.debug("postEntry"); final String[] pathInfo = StringUtils.split(areq.getPathInfo(), "/"); final String handle = pathInfo[0]; final String collection = pathInfo[1]; final FileBasedCollection col = service.findCollectionByHandle(handle, collection); try { return col.addEntry(entry); } catch (final Exception fe) { fe.printStackTrace(); throw new AtomException(fe); } } /** * Get entry specified by pathInfo. * * @param areq Details of HTTP request * @throws com.rometools.rome.propono.atom.server.AtomException On invalid pathinfo or other * error. * @return ROME Entry object. */ @Override public Entry getEntry(final AtomRequest areq) throws AtomException { LOG.debug("getEntry"); final String[] pathInfo = StringUtils.split(areq.getPathInfo(), "/"); final String handle = pathInfo[0]; final String collection = pathInfo[1]; final String fileName = pathInfo[2]; final FileBasedCollection col = service.findCollectionByHandle(handle, collection); try { return col.getEntry(fileName); } catch (final Exception re) { if (re instanceof AtomException) { throw (AtomException) re; } throw new AtomException("ERROR: getting entry", re); } } /** * Update entry specified by pathInfo and posted entry. * * @param entry * @param areq Details of HTTP request * @throws com.rometools.rome.propono.atom.server.AtomException */ @Override public void putEntry(final AtomRequest areq, final Entry entry) throws AtomException { LOG.debug("putEntry"); final String[] pathInfo = StringUtils.split(areq.getPathInfo(), "/"); final String handle = pathInfo[0]; final String collection = pathInfo[1]; final String fileName = pathInfo[2]; final FileBasedCollection col = service.findCollectionByHandle(handle, collection); try { col.updateEntry(entry, fileName); } catch (final Exception fe) { throw new AtomException(fe); } } /** * Delete entry specified by pathInfo. * * @param areq Details of HTTP request */ @Override public void deleteEntry(final AtomRequest areq) throws AtomException { LOG.debug("deleteEntry"); final String[] pathInfo = StringUtils.split(areq.getPathInfo(), "/"); final String handle = pathInfo[0]; final String collection = pathInfo[1]; final String fileName = pathInfo[2]; final FileBasedCollection col = service.findCollectionByHandle(handle, collection); try { col.deleteEntry(fileName); } catch (final Exception e) { final String msg = "ERROR in atom.deleteResource"; LOG.error(msg, e); throw new AtomException(msg); } } /** * Store media data in collection specified by pathInfo, create an Atom media-link entry to * store metadata for the new media file and return that entry to the caller. * * @param areq Details of HTTP request * @param entry New entry initialzied with only title and content type * @return Location URL of new media entry */ @Override public Entry postMedia(final AtomRequest areq, final Entry entry) throws AtomException { // get incoming slug from HTTP header final String slug = areq.getHeader("Slug"); if (LOG.isDebugEnabled()) { LOG.debug("postMedia - title: " + entry.getTitle() + " slug:" + slug); } try { final File tempFile = null; final String[] pathInfo = StringUtils.split(areq.getPathInfo(), "/"); final String handle = pathInfo[0]; final String collection = pathInfo[1]; final FileBasedCollection col = service.findCollectionByHandle(handle, collection); try { col.addMediaEntry(entry, slug, areq.getInputStream()); } catch (final Exception e) { e.printStackTrace(); final String msg = "ERROR reading posted file"; LOG.error(msg, e); throw new AtomException(msg, e); } finally { if (tempFile != null) { tempFile.delete(); } } } catch (final Exception re) { throw new AtomException("ERROR: posting media"); } return entry; } /** * Update the media file part of a media-link entry. * * @param areq Details of HTTP request Assuming pathInfo of form /user-name/resource/name */ @Override public void putMedia(final AtomRequest areq) throws AtomException { LOG.debug("putMedia"); final String[] pathInfo = StringUtils.split(areq.getPathInfo(), "/"); final String handle = pathInfo[0]; final String collection = pathInfo[1]; final String fileName = pathInfo[3]; final FileBasedCollection col = service.findCollectionByHandle(handle, collection); try { col.updateMediaEntry(fileName, areq.getContentType(), areq.getInputStream()); } catch (final Exception re) { throw new AtomException("ERROR: posting media"); } } @Override public AtomMediaResource getMediaResource(final AtomRequest areq) throws AtomException { LOG.debug("putMedia"); final String[] pathInfo = StringUtils.split(areq.getPathInfo(), "/"); final String handle = pathInfo[0]; final String collection = pathInfo[1]; final String fileName = pathInfo[3]; final FileBasedCollection col = service.findCollectionByHandle(handle, collection); try { return col.getMediaResource(fileName); } catch (final Exception re) { throw new AtomException("ERROR: posting media"); } } /** * Return true if specified pathinfo represents URI of service doc. */ @Override public boolean isAtomServiceURI(final AtomRequest areq) { final String[] pathInfo = StringUtils.split(areq.getPathInfo(), "/"); if (pathInfo.length == 0) { return true; } return false; } /** * Return true if specified pathinfo represents URI of category doc. */ @Override public boolean isCategoriesURI(final AtomRequest areq) { LOG.debug("isCategoriesDocumentURI"); final String[] pathInfo = StringUtils.split(areq.getPathInfo(), "/"); if (pathInfo.length == 3 && "categories".equals(pathInfo[2])) { return true; } return false; } /** * Return true if specified pathinfo represents URI of a collection. */ @Override public boolean isCollectionURI(final AtomRequest areq) { LOG.debug("isCollectionURI"); // workspace/collection-plural // if length is 2 and points to a valid collection then YES final String[] pathInfo = StringUtils.split(areq.getPathInfo(), "/"); if (pathInfo.length == 2) { final String handle = pathInfo[0]; final String collection = pathInfo[1]; if (service.findCollectionByHandle(handle, collection) != null) { return true; } } return false; } /** * Return true if specified pathinfo represents URI of an Atom entry. */ @Override public boolean isEntryURI(final AtomRequest areq) { LOG.debug("isEntryURI"); // workspace/collection-singular/fsid // if length is 3 and points to a valid collection then YES final String[] pathInfo = StringUtils.split(areq.getPathInfo(), "/"); if (pathInfo.length == 3) { final String handle = pathInfo[0]; final String collection = pathInfo[1]; if (service.findCollectionByHandle(handle, collection) != null) { return true; } } return false; } /** * Return true if specified pathinfo represents media-edit URI. */ @Override public boolean isMediaEditURI(final AtomRequest areq) { LOG.debug("isMediaEditURI"); // workspace/collection-singular/fsid/media/fsid // if length is 4, points to a valid collection and fsid is mentioned // twice then YES final String[] pathInfo = StringUtils.split(areq.getPathInfo(), "/"); if (pathInfo.length == 4) { final String handle = pathInfo[0]; final String collection = pathInfo[1]; final String media = pathInfo[2]; // final String fsid = pathInfo[3]; if (service.findCollectionByHandle(handle, collection) != null && media.equals("media")) { return true; } } return false; } /** * BASIC authentication. */ public String authenticateBASIC(final HttpServletRequest request) { LOG.debug("authenticateBASIC"); boolean valid = false; String userID = null; String password = null; try { final String authHeader = request.getHeader("Authorization"); if (authHeader != null) { final StringTokenizer st = new StringTokenizer(authHeader); if (st.hasMoreTokens()) { final String basic = st.nextToken(); if (basic.equalsIgnoreCase("Basic")) { final String credentials = st.nextToken(); final String userPass = new String(Base64.decodeBase64(credentials.getBytes())); final int p = userPass.indexOf(":"); if (p != -1) { userID = userPass.substring(0, p); password = userPass.substring(p + 1); // Validate the User. valid = validateUser(userID, password); } } } } } catch (final Exception e) { LOG.debug("An error occured while processing Basic authentication", e); } if (valid) { // For now assume userID as userName return userID; } return null; } }