/** * Copyright (c) 2003-2005, David A. Czarnecki * All rights reserved. * * Portions Copyright (c) 2003-2005 by Mark Lussier * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the "David A. Czarnecki" and "blojsom" nor the names of * its contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * Products derived from this software may not be called "blojsom", * nor may "blojsom" appear in their name, without prior written permission of * David A. Czarnecki. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO * EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.blojsom.extension.xmlrpc; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.xmlrpc.XmlRpc; import org.apache.xmlrpc.XmlRpcException; import org.apache.xmlrpc.XmlRpcServer; import org.blojsom.BlojsomException; import org.blojsom.authorization.AuthorizationProvider; import org.blojsom.blog.Blog; import org.blojsom.blog.BlogUser; import org.blojsom.blog.BlojsomConfigurationException; import org.blojsom.extension.xmlrpc.handlers.AbstractBlojsomAPIHandler; import org.blojsom.fetcher.BlojsomFetcherException; import org.blojsom.servlet.BlojsomBaseServlet; import org.blojsom.util.BlojsomProperties; import org.blojsom.util.BlojsomUtils; import javax.servlet.ServletConfig; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.*; import java.util.Iterator; import java.util.Properties; import java.util.Map; import java.util.HashMap; /** * Blojsom XML-RPC Servlet *
* This servlet uses the Jakarta XML-RPC Library (http://ws.apache.org/xmlrpc) * * @author Mark Lussier * @author David Czarnecki * @version $Id: BlojsomXMLRPCServlet.java,v 1.3.2.1 2005/07/21 04:30:22 johnan Exp $ */ public class BlojsomXMLRPCServlet extends BlojsomBaseServlet implements BlojsomXMLRPCConstants { private Log _logger = LogFactory.getLog(BlojsomXMLRPCServlet.class); protected AuthorizationProvider _authorizationProvider; protected ServletConfig _servletConfig; public static final int XMLRPC_DISABLED = 4000; public static final String XMLRPC_DISABLED_MESSAGE = "XML-RPC disabled for the requested blog"; /** * Construct a new Blojsom XML-RPC servlet instance */ public BlojsomXMLRPCServlet() { } /** * Configure the authorization provider * * @throws ServletException If there is an error instantiating and/or initializing the authorization provider */ protected void configureAuthorization() throws ServletException { try { Class authorizationProviderClass = Class.forName(_blojsomConfiguration.getAuthorizationProvider()); _authorizationProvider = (AuthorizationProvider) authorizationProviderClass.newInstance(); _authorizationProvider.init(_servletConfig, _blojsomConfiguration); } catch (ClassNotFoundException e) { throw new ServletException(e); } catch (InstantiationException e) { throw new ServletException(e); } catch (IllegalAccessException e) { throw new ServletException(e); } catch (BlojsomConfigurationException e) { throw new ServletException(e); } } /** * Configure the flavors for the blog which map flavor values like "html" and "rss" to * the proper template and content type * * @param servletConfig Servlet configuration information * @param blogUser {@link BlogUser} information * @since blojsom 2.22 */ protected void configureFlavorsForUser(ServletConfig servletConfig, BlogUser blogUser) throws ServletException { String flavorConfiguration = servletConfig.getInitParameter(BLOJSOM_FLAVOR_CONFIGURATION_IP); if (BlojsomUtils.checkNullOrBlank(flavorConfiguration)) { flavorConfiguration = DEFAULT_FLAVOR_CONFIGURATION_FILE; } Map flavors = new HashMap(); Map flavorToTemplateMap = new HashMap(); Map flavorToContentTypeMap = new HashMap(); String user = blogUser.getId(); Properties flavorProperties = new Properties(); InputStream is = servletConfig.getServletContext().getResourceAsStream(_baseConfigurationDirectory + user + '/' + flavorConfiguration); try { flavorProperties.load(is); is.close(); _logger.debug("Loaded flavor information for user: " + user); Iterator flavorIterator = flavorProperties.keySet().iterator(); while (flavorIterator.hasNext()) { String flavor = (String) flavorIterator.next(); String[] flavorMapping = BlojsomUtils.parseCommaList(flavorProperties.getProperty(flavor)); flavors.put(flavor, flavor); flavorToTemplateMap.put(flavor, flavorMapping[0]); flavorToContentTypeMap.put(flavor, flavorMapping[1]); } blogUser.setFlavors(flavors); blogUser.setFlavorToTemplate(flavorToTemplateMap); blogUser.setFlavorToContentType(flavorToContentTypeMap); } catch (IOException e) { _logger.error(e); throw new ServletException(e); } } /** * Configure the XML-RPC API Handlers * * @param httpServletRequest Request * @param httpServletResponse Response * @param userID User ID * @return {@link XmlRpcServer} configured for the given user ornull
if the configuration failed
*/
protected XmlRpcServer configureXMLRPCServer(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, String userID) throws ServletException {
XmlRpcServer xmlRpcServer = new XmlRpcServer();
String xmlrpcConfigurationFile = _servletConfig.getInitParameter(BLOG_XMLRPC_CONFIGURATION_IP);
Properties handlerMapProperties = new Properties();
InputStream is = _servletConfig.getServletContext().getResourceAsStream(xmlrpcConfigurationFile);
try {
handlerMapProperties.load(is);
is.close();
// Check for the default XML-RPC handler
String defaultXMLRPCHandler = handlerMapProperties.getProperty(DEFAULT_XMLRPC_HANDLER_KEY);
handlerMapProperties.remove(DEFAULT_XMLRPC_HANDLER_KEY);
// Instantiate an XML-RPC server and separate handler instances for the user ID
BlogUser blogUser = new BlogUser();
blogUser.setId(userID);
// Load the user's blog properties
Properties blogProperties = new BlojsomProperties();
is = _servletConfig.getServletContext().getResourceAsStream(_baseConfigurationDirectory + userID + '/' + BLOG_DEFAULT_PROPERTIES);
if (is == null) {
return null;
}
try {
blogProperties.load(is);
is.close();
} catch (IOException e) {
_logger.error(e);
throw new BlojsomConfigurationException(e);
}
// If a global blog-home directory has been defined, use it for each user
if (!BlojsomUtils.checkNullOrBlank(_blojsomConfiguration.getGlobalBlogHome()) &&
!blogProperties.containsKey(BLOG_HOME_IP)) {
String usersBlogHome = _blojsomConfiguration.getGlobalBlogHome() + userID + "/";
File blogHomeDirectory = new File(usersBlogHome);
if (!blogHomeDirectory.exists()) {
_logger.error("Unable to use blog-home directory for user: " + blogHomeDirectory.toString());
throw new BlojsomConfigurationException("Unable to use blog-home directory for user: " + blogHomeDirectory.toString());
}
blogProperties.setProperty(BLOG_HOME_IP, usersBlogHome);
_logger.debug("Setting user blog-home directory: " + usersBlogHome);
}
Blog blog = null;
try {
blog = new Blog(blogProperties);
// Check to see if we need to dynamically determine blog-base-url and blog-url?
BlojsomUtils.resolveDynamicBaseAndBlogURL(httpServletRequest, blog, userID);
blogUser.setBlog(blog);
_logger.debug("Configured blojsom user: " + blogUser.getId());
} catch (BlojsomConfigurationException e) {
_logger.error(e);
throw new BlojsomConfigurationException(e);
}
// Check to see if XML-RPC is enabled for the blog
if (!blog.getXmlrpcEnabled().booleanValue()) {
_logger.error(XMLRPC_DISABLED_MESSAGE);
throw new ServletException(new XmlRpcException(XMLRPC_DISABLED, XMLRPC_DISABLED_MESSAGE));
}
// Load the authentication credentials for the user
_authorizationProvider.loadAuthenticationCredentials(blogUser);
// Load the flavors
configureFlavorsForUser(_servletConfig, blogUser);
// Instantiate and initialize the XML-RPC handlers
Iterator handlerIterator = handlerMapProperties.keySet().iterator();
while (handlerIterator.hasNext()) {
String handlerName = (String) handlerIterator.next();
String handlerClassName = handlerMapProperties.getProperty(handlerName);
Class handlerClass = Class.forName(handlerClassName);
AbstractBlojsomAPIHandler handler = (AbstractBlojsomAPIHandler) handlerClass.newInstance();
handler.setFetcher(_fetcher);
handler.setConfiguration(_blojsomConfiguration);
handler.setBlogUser(blogUser);
handler.setAuthorizationProvider(_authorizationProvider);
handler.setServletConfig(_servletConfig);
handler.setHttpServletRequest(httpServletRequest);
handler.setHttpServletResponse(httpServletResponse);
xmlRpcServer.addHandler(handler.getName(), handler);
if (defaultXMLRPCHandler != null && defaultXMLRPCHandler.equals(handlerName)) {
xmlRpcServer.addHandler(DEFAULT_XMLRPC_HANDLER_KEY, handler);
_logger.debug("Added default XML-RPC handler: " + handlerClass + " for user: " + userID);
}
_logger.debug("Added [" + handler.getName() + "] API Handler : " + handlerClass + " for user: " + userID);
}
return xmlRpcServer;
} catch (InstantiationException e) {
throw new ServletException(e);
} catch (IllegalAccessException e) {
throw new ServletException(e);
} catch (ClassNotFoundException e) {
throw new ServletException(e);
} catch (IOException e) {
throw new ServletException(e);
} catch (BlojsomException e) {
throw new ServletException(e);
}
}
/**
* Initialize the blojsom XML-RPC servlet
*
* @param servletConfig Servlet configuration information
* @throws ServletException If there is an error initializing the servlet
*/
public void init(ServletConfig servletConfig) throws ServletException {
super.init(servletConfig);
_servletConfig = servletConfig;
// Set the default encoding for the XmlRpc classes to UTF-8
XmlRpc.setEncoding(UTF8);
configureBlojsom(_servletConfig);
configureAuthorization();
_logger.debug("blojsom XML-RPC: All Your Blog Are Belong To Us");
}
/**
* Service an XML-RPC request by passing the request to the proper handler
*
* @param httpServletRequest Request
* @param httpServletResponse Response
* @throws ServletException If there is an error processing the request
* @throws IOException If there is an error during I/O
*/
protected void service(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws
ServletException, IOException {
try {
httpServletRequest.setCharacterEncoding(UTF8);
} catch (UnsupportedEncodingException e) {
_logger.error(e);
}
// Determine the appropriate user from the URL
String user = BlojsomUtils.getUserFromPath(httpServletRequest.getPathInfo());
if (BlojsomUtils.checkNullOrBlank(user) || "/".equals(user)) {
httpServletResponse.sendError(HttpServletResponse.SC_NOT_FOUND, "Requested user not found: " + user);
return;
}
// Make sure that the user exists in the system
XmlRpcServer xmlRpcServer = configureXMLRPCServer(httpServletRequest, httpServletResponse, user);
if (xmlRpcServer == null) {
httpServletResponse.sendError(HttpServletResponse.SC_NOT_FOUND, "Requested user not found: " + user);
return;
}
byte[] result = xmlRpcServer.execute(httpServletRequest.getInputStream());
httpServletResponse.setContentType("text/xml; charset=UTF-8");
httpServletResponse.setContentLength(result.length);
OutputStream os = httpServletResponse.getOutputStream();
os.write(result);
os.flush();
}
/**
* Called when removing the servlet from the servlet container
*/
public void destroy() {
try {
_fetcher.destroy();
} catch (BlojsomFetcherException e) {
_logger.error(e);
}
}
}