/**********************************************************************************
* $URL: https://source.sakaiproject.org/svn/mailarchive/trunk/mailarchive-james/james/src/java/org/sakaiproject/james/JamesServlet.java $
* $Id: JamesServlet.java 131945 2013-11-26 02:30:11Z matthew@longsight.com $
***********************************************************************************
*
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008 The Sakai Foundation
*
* Licensed under the Educational Community 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.opensource.org/licenses/ECL-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.sakaiproject.james;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.util.HashMap;
import javax.mail.internet.InternetAddress;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.sakaiproject.component.cover.ServerConfigurationService;
import org.apache.commons.lang.StringUtils;
import org.sakaiproject.util.Xml;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathFactory;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathException;
/**
* <p>
* JamesServlet starts James.
* </p>
*/
public class JamesServlet extends HttpServlet
{
/** Our log (commons). */
private static Log M_log = LogFactory.getLog(JamesServlet.class);
/** config variable and system property for the james / phoenix home. */
private final static String PHOENIX_HOME = "phoenix.home";
/** The james / phoenix home value. */
protected String m_phoenixHome = null;
/** The JamesRunner (Thread). */
protected JamesRunner m_runner = null;
/** James thread */
public class JamesRunner extends Thread
{
/**
* construct and start the init activity
*/
public JamesRunner()
{
start();
}
/**
* Run the James thread.
*/
public void run()
{
System.setProperty(PHOENIX_HOME, m_phoenixHome);
M_log.info("run: starting James service");
// start James / Avalon running in this VM.
try
{
// Set the log directory for the phoenix log
String[] args = new String[2];
args[0] = "-l";
args[1] = JamesServlet.getLogDirectory() + "phoenix.log";
int exitCode = PhoenixLauncherMain.startup(args, new HashMap(), true);
M_log.info("run: James service stopped: " + exitCode);
}
catch (Throwable e)
{
M_log.warn("run: exception:", e);
}
}
}
/**
* Access the Servlet's information display.
*
* @return servlet information.
*/
public String getServletInfo()
{
return "Sakai James Servlet";
}
/**
* Initialize the servlet.
*
* @param config
* The servlet config.
* @throws ServletException
*/
public void init(ServletConfig config) throws ServletException
{
super.init(config);
startJames(config);
}
/**
* Shutdown the servlet.
*/
public void destroy()
{
M_log.info("destroy()");
if (m_runner == null) return;
PhoenixLauncherMain.shutdown();
m_runner = null;
super.destroy();
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// do not return anything - SAK-23222
}
protected static String getLogDirectory()
{
String logDir = StringUtils.trimToNull(ServerConfigurationService.getString("smtp.logdir"));
if(logDir == null) {
M_log.info("init(): smtp.logdir not set, defaulting to {sakai.home}/logs/");
logDir = System.getProperty("sakai.home");
if(!logDir.endsWith("/")) {
logDir += "/";
}
logDir += "logs/";
} else {
if(!logDir.endsWith("/")) {
logDir += "/";
}
if(!logDir.startsWith("/")) {
// if the path is relative work from catalina.base/catalina.home
String catalinaDir = System.getProperty("catalina.base");
if(catalinaDir == null) {
catalinaDir = System.getProperty("catalina.home");
}
if(!catalinaDir.endsWith("/")) {
catalinaDir += "/";
}
logDir = catalinaDir + logDir;
}
}
M_log.debug("init(): using " + logDir + " as James log directory");
return logDir;
}
public class JamesConfigurationException extends Exception {}
protected void startJames(ServletConfig config)
{
// get config info
String logDir = JamesServlet.getLogDirectory();
String host = ServerConfigurationService.getServerName();
String dns1 = StringUtils.trimToNull(ServerConfigurationService.getString("smtp.dns.1"));
String dns2 = StringUtils.trimToNull(ServerConfigurationService.getString("smtp.dns.2"));
String smtpPort = StringUtils.trimToNull(ServerConfigurationService.getString("smtp.port"));
boolean enabled = ServerConfigurationService.getBoolean("smtp.enabled", false);
String postmasterAddress = null;
String postmasterLocalPart = StringUtils.trimToNull(ServerConfigurationService.getString("smtp.postmaster.address.local-part"));
String postmasterDomain = StringUtils.trimToNull(ServerConfigurationService.getString("smtp.postmaster.address.domain"));
if (postmasterDomain != null) {
if (postmasterLocalPart == null) {
postmasterLocalPart = "postmaster";
}
postmasterAddress = postmasterLocalPart + "@" + postmasterDomain;
try {
InternetAddress email = new InternetAddress(postmasterAddress);
email.validate();
} catch(Exception ex) {
M_log.warn("init(): '" + postmasterAddress + "' is not valid");
postmasterAddress = null;
}
}
// check for missing values
if(host == null) host = "127.0.0.1";
if(smtpPort == null) smtpPort = "25";
M_log.debug("init(): host: " + host + " enabled: " + enabled + " dns1: " + dns1 + " dns2: "
+ dns2 + " smtp.port: " + smtpPort + " logdir: " + logDir);
// if not enabled, don't start james
if (!enabled)
{
M_log.debug("init(): James not enabled, aborting");
return;
}
// set the home for james / phoenix, as configured
String homeRelative = config.getInitParameter(PHOENIX_HOME);
if (homeRelative == null)
{
// or pointing to the webapps root if not configured
homeRelative = "";
}
// expand to real path
m_phoenixHome = getServletContext().getRealPath(homeRelative);
try {
customizeConfig(host, dns1, dns2, smtpPort, logDir, postmasterAddress);
} catch(JamesConfigurationException e) {
M_log.error("init(): James could not be configured, aborting");
return;
}
// start the James thread
m_runner = new JamesRunner();
}
protected void customizeConfig(String host, String dns1, String dns2, String smtpPort, String logDir, String postmasterAddress)
throws JamesConfigurationException
{
String configPath = m_phoenixHome + "/apps/james/SAR-INF/config.xml";
String environmentPath = m_phoenixHome + "/apps/james/SAR-INF/environment.xml";
XPath xpath = XPathFactory.newInstance().newXPath();
Document doc;
try {
// process config.xml first
doc = Xml.readDocument(configPath);
if(doc == null) {
M_log.error("init(): James config file " + configPath + "could not be found.");
throw new JamesConfigurationException();
}
if (postmasterAddress == null) {
postmasterAddress = "postmaster@" + host;
}
// build a hashmap of node paths and values to set
HashMap<String, String> nodeValues = new HashMap<String, String>();
// WARNING!! in XPath, node-set indexes begin with 1, and NOT 0
nodeValues.put("/config/James/servernames/servername[1]", host);
nodeValues.put("/config/dnsserver/servers/server[2]", dns1);
nodeValues.put("/config/dnsserver/servers/server[3]", dns2);
nodeValues.put("/config/James/postmaster", postmasterAddress);
nodeValues.put("/config/smtpserver/port", smtpPort);
// loop through the hashmap, setting each value, or failing if one can't be found
for(String nodePath : nodeValues.keySet()) {
if(!(Boolean)xpath.evaluate(nodePath, doc, XPathConstants.BOOLEAN)) {
if(nodePath.startsWith("/config/dnsserver/servers/server")) {
// add node (only if we're dealing with DNS server entries)
Element element = doc.createElement("server");
element.appendChild(doc.createTextNode( nodeValues.get(nodePath) ));
Node parentNode = (Node) xpath.evaluate("/config/dnsserver/servers", doc, XPathConstants.NODE);
parentNode.appendChild(element);
}else{
// else, throw an exception
throw new JamesConfigurationException();
}
}else{
// change existing node (if value != null else remove it)
Node node = (Node) xpath.evaluate(nodePath, doc, XPathConstants.NODE);
if(nodeValues.get(nodePath) != null){
node.setTextContent(nodeValues.get(nodePath));
}else{
node.getParentNode().removeChild(node);
}
}
}
M_log.debug("init(): writing James configuration to " + configPath);
Xml.writeDocument(doc, configPath);
// now handle environment.xml
doc = Xml.readDocument(environmentPath);
if(doc == null) {
M_log.error("init(): James config file " + environmentPath + "could not be found.");
throw new JamesConfigurationException();
}
String nodePath = "/server/logs/targets/file/filename";
String nodeValue = logDir + "james";
if(!(Boolean)xpath.evaluate(nodePath, doc, XPathConstants.BOOLEAN)) {
M_log.error("init(): Could not find XPath '" + nodePath + "' in " + environmentPath + ".");
throw new JamesConfigurationException();
}
((Node)xpath.evaluate(nodePath, doc, XPathConstants.NODE)).setTextContent(nodeValue);
M_log.debug("init(): writing James configuration to " + environmentPath);
Xml.writeDocument(doc, environmentPath);
} catch(JamesConfigurationException e) {
throw e;
} catch(Exception e) {
M_log.warn("init(): An unhandled exception was encountered while configuring James: " + e.getMessage());
}
}
}