yacy_search_server/source/de/anomic/soap/httpdSoapService.java
2006-05-24 04:40:30 +00:00

351 lines
14 KiB
Java

package de.anomic.soap;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.StringReader;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.apache.axis.AxisFault;
import org.apache.axis.Message;
import org.apache.axis.MessageContext;
import org.apache.axis.message.SOAPEnvelope;
import org.apache.axis.message.SOAPHeaderElement;
import org.w3c.dom.Document;
import org.xml.sax.InputSource;
import de.anomic.http.httpHeader;
import de.anomic.http.httpTemplate;
import de.anomic.server.serverClassLoader;
import de.anomic.server.serverObjects;
import de.anomic.server.serverSwitch;
/**
* SOAP Service Class that will be invoked by the httpdSoapHandler
*
* @author Martin Thelian
*/
public class httpdSoapService
{
/* ================================================================
* Constants needed to set the template that should be used to
* fullfil the request
* ================================================================ */
/**
* Constant: template for searching
*/
private static final String TEMPLATE_SEARCH = "yacysearch.soap";
/**
* Constant: template for the network status page
*/
private static final String TEMPLATE_NETWORK_XML = "Network.xml";
/**
* Constant: template for crawling
*/
private static final String TEMPLATE_CRAWLING = "IndexCreate_p.html";
/* ================================================================
* Other Object fields
* ================================================================ */
/**
* A hashset containing all templates that requires user authentication
*/
private static final HashSet SERVICE_NEEDS_AUTHENTICATION = new HashSet(Arrays.asList(new String[]
{TEMPLATE_CRAWLING}));
private String rootPath;
private serverClassLoader provider;
private HashMap templates;
private serverSwitch switchboard;
private httpHeader requestHeader;
private MessageContext messageContext;
/**
* Constructor of this class
*/
public httpdSoapService() {
super();
// nothing special todo here at the moment
}
/**
* Service for doing a simple search with the standard settings
*
* @param searchString the search string that should be used
* @return an rss document containing the search results.
*
* @throws AxisFault if the service could not be executed propery.
*/
public Document search(
String searchString,
String searchMode,
String searchOrder,
int maxSearchCount,
int maxSearchTime,
String urlMaskFilter
)
throws AxisFault {
try {
// extracting the message context
extractMessageContext();
if ((searchMode == null) || !(searchMode.equalsIgnoreCase("global") || searchMode.equalsIgnoreCase("locale"))) {
searchMode = "global";
}
if (urlMaskFilter == null) urlMaskFilter = ".*";
// setting the searching properties
serverObjects args = new serverObjects();
args.put("order","Quality-Date");
args.put("Enter","Search");
args.put("count",Integer.toString(maxSearchCount));
args.put("resource","global");
args.put("time",Integer.toString(maxSearchTime));
args.put("urlmaskfilter",urlMaskFilter);
args.put("search",searchString);
// generating the template containing the search result
String result = writeTemplate(TEMPLATE_SEARCH, args);
// sending back the result to the client
return this.convertContentToXML(result);
} catch (Exception e) {
throw new AxisFault(e.getMessage());
}
}
/**
* Service used to query the network properties
* @throws AxisFault if the service could not be executed propery.
*/
public String network() throws AxisFault {
try {
// extracting the message context
extractMessageContext();
// generating the template containing the network status information
String result = writeTemplate(TEMPLATE_NETWORK_XML, new serverObjects());
// sending back the result to the client
return result;
} catch (Exception e) {
throw new AxisFault(e.getMessage());
}
}
/**
* Service used start a new crawling job using the default settings for crawling
*
* @return returns the http status page containing the crawling properties to the user
* TODO: creating an extra xml template that can be send back to the client.
*
* @throws AxisFault if the service could not be executed propery.
*/
public String crawling(String crawlingURL) throws AxisFault {
try {
// extracting the message context
extractMessageContext();
// setting the crawling properties
serverObjects args = new serverObjects();
args.put("crawlingQ","on");
args.put("xsstopw","on");
args.put("crawlOrder","on");
args.put("crawlingstart","Start New Crawl");
args.put("crawlingDepth","2");
args.put("crawlingFilter",".*");
args.put("storeHTCache","on");
args.put("localIndexing","on");
args.put("crawlingURL",crawlingURL);
// triggering the crawling
String result = writeTemplate(TEMPLATE_CRAWLING, args);
// sending back the crawling status page to the user
return result;
} catch (Exception e) {
throw new AxisFault(e.getMessage());
}
}
/**
* This function is called by the available service functions to
* extract all needed informations from the SOAP message context.
*/
private void extractMessageContext() {
this.messageContext = MessageContext.getCurrentContext();
this.rootPath = (String) this.messageContext.getProperty(httpdSoapHandler.MESSAGE_CONTEXT_HTTP_ROOT_PATH);
this.provider = (serverClassLoader) this.messageContext.getProperty(httpdSoapHandler.MESSAGE_CONTEXT_SERVER_CLASSLOADER);
this.templates = (HashMap) this.messageContext.getProperty(httpdSoapHandler.MESSAGE_CONTEXT_TEMPLATES);
this.switchboard = (serverSwitch) this.messageContext.getProperty(httpdSoapHandler.MESSAGE_CONTEXT_SERVER_SWITCH);
this.requestHeader = (httpHeader) this.messageContext.getProperty(httpdSoapHandler.MESSAGE_CONTEXT_HTTP_HEADER);
}
/**
* This function is called by the service functions to
* invoke the desired server-internal method and to generate
* a output document using one of the available templates.
*
* @param templateName
* @param args
* @return
* @throws AxisFault
*/
private String writeTemplate(String templateName, serverObjects args)
throws AxisFault {
try {
// determining the proper class that should be invoked
File file = new File(this.rootPath, templateName);
File rc = rewriteClassFile(file);
if (SERVICE_NEEDS_AUTHENTICATION.contains(templateName)) {
this.doAuthentication();
}
// invoke the desired method
serverObjects tp = (serverObjects) rewriteMethod(rc).invoke(null, new Object[] {this.requestHeader, args, this.switchboard});
// testing if a authentication was needed by the invoked method
validateAuthentication(tp);
// adding all available templates
tp.putAll(this.templates);
// generating the output document
ByteArrayOutputStream o = new ByteArrayOutputStream();
FileInputStream fis = new FileInputStream(file);
httpTemplate.writeTemplate(fis, o, tp, "-UNRESOLVED_PATTERN-".getBytes());
o.close();
fis.close();
// convert it into a byte array and send it back as result
byte[] result = o.toByteArray();
return new String(result, "UTF-8");
} catch (Exception e) {
throw new AxisFault(e.getMessage());
}
}
/**
* This function is used to test if an invoked method requires authentication
*
* @param tp the properties returned by a previous method invocation
*
* @throws AxisFault if an authentication was required.
*/
private void validateAuthentication(serverObjects tp)
throws AxisFault
{
// check if the servlets requests authentification
if (tp.containsKey("AUTHENTICATE")) {
throw new AxisFault("log-in required");
}
}
/**
* Doing the user authentication. To improve security, this client
* accepts the base64 encoded and md5 hashed password directly.
*
* @throws AxisFault if the authentication could not be done successfully
*/
private void doAuthentication()
throws AxisFault {
// accessing the SOAP request message
Message message = this.messageContext.getRequestMessage();
// getting the contained soap envelope
SOAPEnvelope envelope = message.getSOAPEnvelope();
// getting the proper soap header containing the authorization field
SOAPHeaderElement authElement = envelope.getHeaderByName(httpdSoapHandler.serviceHeaderNamespace, "Authorization");
if (authElement != null) {
// the base64 encoded and md5 hashed authentication string
String authString = authElement.getValue();
String adminAccountBase64MD5 = this.switchboard.getConfig("adminAccountBase64MD5","");
if (adminAccountBase64MD5.length() == 0) {
throw new AxisFault("log-in required");
} else if (!(adminAccountBase64MD5.equals(authString))) {
throw new AxisFault("log-in required");
}
}
else throw new AxisFault("log-in required");
}
/**
* This method was copied from the {@link httpdFileHandler httpdFileHandler-class}
* @param template
* @return
*/
private File rewriteClassFile(File template) {
try {
String f = template.getCanonicalPath();
int p = f.lastIndexOf(".");
if (p < 0) return null;
f = f.substring(0, p) + ".class";
//System.out.println("constructed class path " + f);
File cf = new File(f);
if (cf.exists()) return cf;
return null;
} catch (IOException e) {
return null;
}
}
/**
* This method was copied from the {@link httpdFileHandler httpdFileHandler-class}
* @param classFile
* @return
*/
private Method rewriteMethod(File classFile) {
Method m = null;
// now make a class out of the stream
try {
//System.out.println("**DEBUG** loading class file " + classFile);
Class c = provider.loadClass(classFile);
Class[] params = new Class[] {
Class.forName("de.anomic.http.httpHeader"),
Class.forName("de.anomic.server.serverObjects"),
Class.forName("de.anomic.server.serverSwitch")};
m = c.getMethod("respond", params);
} catch (ClassNotFoundException e) {
System.out.println("INTERNAL ERROR: class " + classFile + " is missing:" + e.getMessage());
} catch (NoSuchMethodException e) {
System.out.println("INTERNAL ERROR: method respond not found in class " + classFile + ": " + e.getMessage());
}
//System.out.println("found method: " + m.toString());
return m;
}
private Document convertContentToXML(String contentString)
throws Exception
{
Document doc = null;
try {
DocumentBuilderFactory newDocBuilderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder newDocBuilder = newDocBuilderFactory.newDocumentBuilder();
InputSource is = new InputSource(new StringReader(contentString));
doc = newDocBuilder.parse(is);
} catch (Exception e) {
String errorMessage = "Unable to parse the search result XML data. " + e.getClass().getName() + ". " + e.getMessage();
throw new Exception(errorMessage);
}
return doc;
}
}