gdata dev update from Simon 2006-06-06

git-svn-id: https://svn.apache.org/repos/asf/lucene/java/trunk@417265 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Yonik Seeley 2006-06-26 18:14:53 +00:00
parent 6e86efb527
commit 4707eea96c
45 changed files with 6168 additions and 4386 deletions

View File

@ -16,7 +16,11 @@
<pathelement location="lib/gdata-client-1.0.jar" /> <pathelement location="lib/gdata-client-1.0.jar" />
<pathelement location="lib/commons-logging-1.1.jar" /> <pathelement location="lib/commons-logging-1.1.jar" />
<pathelement location="lib/commons-jxpath-1.2.jar" /> <pathelement location="lib/commons-jxpath-1.2.jar" />
<pathelement location="lib/je.jar" /> <pathelement location="lib/commons-digester-1.7.jar" />
<pathelement location="lib/commons-beanutils.jar" />
<pathelement location="lib/commons-collections-3.2.jar" />
</path> </path>
@ -36,12 +40,16 @@
<target name="war-gdata" depends="prepare-dist"> <target name="war-gdata" depends="prepare-dist">
<echo>Distributing GData War </echo> <echo>Distributing GData War </echo>
<war destfile="${dist.dir}/${gdata.war.name}.war" <war destfile="${dist.dir}/${gdata.war.name}.war"
webxml="webroot/WEB-INF/web.xml"> webxml="webroot/WEB-INF/web.xml" >
<fileset dir="webroot" excludes="WEB-INF/web.xml"/> <metainf dir="webroot/meta-inf"/>
<fileset dir="webroot" excludes="WEB-INF/web.xml"/>
<lib dir="${gdata.lib.dir}" includes="commons-logging-1.1.jar"/> <lib dir="${gdata.lib.dir}" includes="commons-logging-1.1.jar"/>
<lib dir="${gdata.lib.dir}" includes="gdata-client-1.0.jar"/> <lib dir="${gdata.lib.dir}" includes="gdata-client-1.0.jar"/>
<lib dir="${gdata.lib.dir}" includes="commons-digester-1.7.jar" />
<lib dir="${gdata.lib.dir}" includes="commons-beanutils.jar" />
<lib dir="${gdata.lib.dir}" includes="commons-collections-3.2.jar" />
<lib dir="${build.dir}" includes="${final.name}.jar"/> <lib dir="${build.dir}" includes="${final.name}.jar"/>
<lib file="${lucene.jar}" /> <lib file="${lucene.jar}" />
</war> </war>

View File

@ -0,0 +1,2 @@
AnyObjectId[b1b89c9c921f16af22a88db3ff28975a8e40d886] was removed in git history.
Apache SVN contains full history.

View File

@ -0,0 +1,2 @@
AnyObjectId[75580be255065727b20b41c2d338b14792bb35cd] was removed in git history.
Apache SVN contains full history.

View File

@ -0,0 +1,2 @@
AnyObjectId[1783dbea232ced6db122268f8faa5ce773c7ea42] was removed in git history.
Apache SVN contains full history.

View File

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
<comment>Lucene Storage Properties</comment>
<entry key="gdata.server.storage.lucene.buffersize">20</entry>
<entry key="gdata.server.storage.lucene.optimizeInterval">20</entry>
<entry key="gdata.server.storage.lucene.persistFactor">20</entry>
<entry key="gdata.server.storage.lucene.directory">/tmp/storage/</entry>
<entry key="gdata.server.storage.lucene.recover">true</entry>
<entry key="gdata.server.storage.lucene.recover.keepFiles">false</entry>
</properties>

View File

@ -18,13 +18,10 @@ package org.apache.lucene.gdata.server;
import java.io.IOException; import java.io.IOException;
import java.io.Reader; import java.io.Reader;
import org.apache.lucene.gdata.server.registry.DataBuilderException; import org.apache.lucene.gdata.server.registry.ProvidedService;
import org.apache.lucene.gdata.server.registry.FeedInstanceConfigurator;
import org.apache.lucene.gdata.server.registry.GDataServerRegistry;
import com.google.gdata.data.BaseEntry; import com.google.gdata.data.BaseEntry;
import com.google.gdata.data.BaseFeed; import com.google.gdata.data.BaseFeed;
import com.google.gdata.data.ExtensionProfile;
import com.google.gdata.util.ParseException; import com.google.gdata.util.ParseException;
/** /**
@ -36,15 +33,17 @@ import com.google.gdata.util.ParseException;
* To provide a generic builder class the {@link GDataEntityBuilder} requests * To provide a generic builder class the {@link GDataEntityBuilder} requests
* the type of the feed / entry and the corresponding * the type of the feed / entry and the corresponding
* {@link com.google.gdata.data.ExtensionProfile} form the global * {@link com.google.gdata.data.ExtensionProfile} form the global
* {@link org.apache.lucene.gdata.server.registry.GDataServerRegistry} and builds the * {@link org.apache.lucene.gdata.server.registry.GDataServerRegistry} and
* instances from the provided reader. * builds the instances from the provided reader.
* </p>
* <p>
* This build will not returne the abstract base classes.
* </p> * </p>
* *
* @author Simon Willnauer * @author Simon Willnauer
* *
*/ */
public class GDataEntityBuilder { public class GDataEntityBuilder {
private static final GDataServerRegistry REGISTRY = GDataServerRegistry.getRegistry(); // TODO find another way for getting the registered feeds
/** /**
* Builds a {@link BaseFeed} instance from the {@link Reader} provided by * Builds a {@link BaseFeed} instance from the {@link Reader} provided by
@ -53,51 +52,42 @@ public class GDataEntityBuilder {
* @param request - * @param request -
* the request to build the instance from * the request to build the instance from
* @return - a BaseFeed instance * @return - a BaseFeed instance
* @throws FeedNotFoundException - *
* if the feed is not registered
* @throws IOException - * @throws IOException -
* if an I/O Exception occures on the provided reader * if an I/O Exception occures on the provided reader
* @throws ParseException - * @throws ParseException -
* if the feed could not be parsed * if the feed could not be parsed
*/ */
public static BaseFeed buildFeed(final GDataRequest request) public static BaseFeed buildFeed(final GDataRequest request)
throws FeedNotFoundException, IOException, ParseException { throws IOException, ParseException {
if (request == null) if (request == null)
throw new IllegalArgumentException("request must not be null"); throw new IllegalArgumentException("request must not be null");
return buildFeed(request.getFeedId(), request.getReader(),request.getExtensionProfile()); ProvidedService config = request.getConfigurator();
return buildFeed(request.getReader(), config);
} }
/** /**
* Builds a {@link BaseFeed} from the provided {@link Reader} * Builds a {@link BaseFeed} from the provided {@link Reader}
* *
* @param feedId - *
* the feed ID to request the feed type from the registry
* @param reader - * @param reader -
* the reader to build the feed from * the reader to build the feed from
* @param profile - extension profile to parse the resource * @param config -
* the feed instance config containing the extension profile to
* parse the resource
* @return - a BaseFeed instance * @return - a BaseFeed instance
* @throws FeedNotFoundException - *
* if the feed is not registered
* @throws IOException - * @throws IOException -
* if an I/O Exception occures on the provided reader * if an I/O Exception occures on the provided reader
* @throws ParseException - * @throws ParseException -
* if the feed could not be parsed * if the feed could not be parsed
*/ */
public static BaseFeed buildFeed(final String feedId, final Reader reader,final ExtensionProfile profile) public static BaseFeed buildFeed(final Reader reader,
throws FeedNotFoundException, ParseException, IOException { final ProvidedService config) throws ParseException, IOException {
BaseFeed retVal = null; BaseFeed retVal = null;
try { retVal = createEntityInstance(config);
retVal = (BaseFeed) createEntityInstance(feedId); retVal.parseAtom(config.getExtensionProfile(), reader);
} catch (FeedNotFoundException e) {
throw e;
} catch (Exception e) {
DataBuilderException ex = new DataBuilderException(
"Could not build Feed for Feed class ", e);
ex.setStackTrace(e.getStackTrace());
throw ex;
}
retVal.parseAtom(profile, reader);
return retVal; return retVal;
} }
@ -109,63 +99,73 @@ public class GDataEntityBuilder {
* @param request - * @param request -
* the request to build the instance from * the request to build the instance from
* @return - a BaseEntry instance * @return - a BaseEntry instance
* @throws FeedNotFoundException - *
* if the feed, requested by the client is not registered
* @throws IOException - * @throws IOException -
* if an I/O Exception occures on the provided reader * if an I/O Exception occures on the provided reader
* @throws ParseException - * @throws ParseException -
* if the entry could not be parsed * if the entry could not be parsed
*/ */
public static BaseEntry buildEntry(final GDataRequest request) public static BaseEntry buildEntry(final GDataRequest request)
throws FeedNotFoundException, IOException, ParseException { throws IOException, ParseException {
if (request == null) if (request == null)
throw new IllegalArgumentException("request must not be null"); throw new IllegalArgumentException("request must not be null");
return buildEntry(request.getFeedId(), request.getReader(),request.getExtensionProfile()); ProvidedService config = request.getConfigurator();
return buildEntry(request.getReader(), config);
} }
/** /**
* Builds a {@link BaseFeed} instance from the {@link Reader} provided by * Builds a {@link BaseFeed} instance from the {@link Reader} provided by
* the {@link GDataRequest} * the {@link GDataRequest}
* @param feedId - *
* the feed ID to request the feed type from the registry
* @param reader - * @param reader -
* the reader to build the feed from * the reader to build the feed from
* @param profile - extension profile to parse the resource * @param config -
* the instance config containing the extension profile to parse
* the resource
* @return - a BaseFeed instance * @return - a BaseFeed instance
* @throws FeedNotFoundException - *
* if the feed is not registered
* @throws IOException - * @throws IOException -
* if an I/O Exception occures on the provided reader * if an I/O Exception occures on the provided reader
* @throws ParseException - * @throws ParseException -
* if the entry could not be parsed * if the entry could not be parsed
*/ */
public static BaseEntry buildEntry(final String feedId, final Reader reader,final ExtensionProfile profile) public static BaseEntry buildEntry(final Reader reader,
throws FeedNotFoundException, ParseException, IOException { final ProvidedService config) throws ParseException, IOException {
BaseEntry retVal = null; BaseEntry e = createEntityInstance(config).createEntry();
e.parseAtom(config.getExtensionProfile(), reader);
return e;
}
private static BaseFeed createEntityInstance(
final ProvidedService config) {
if(config.getFeedType() == null)
throw new IllegalArgumentException("feedtype is null in ProvidedService");
BaseFeed retVal = null;
try { try {
retVal = ((BaseFeed) createEntityInstance(feedId)).createEntry(); retVal = (BaseFeed) config.getFeedType().newInstance();
} catch (FeedNotFoundException e) {
throw e;
} catch (Exception e) { } catch (Exception e) {
DataBuilderException ex = new DataBuilderException( throw new EntityBuilderException("Can't instanciate Feed for feedType "+config.getFeedType().getName(),e);
"Could not build Entry for Entry class ", e);
ex.setStackTrace(e.getStackTrace());
throw ex;
} }
retVal.parseAtom(new ExtensionProfile(), reader);
return retVal; return retVal;
} }
static class EntityBuilderException extends RuntimeException{
/**
*
*/
private static final long serialVersionUID = 7224011324202237951L;
EntityBuilderException(String arg0) {
super(arg0);
}
EntityBuilderException(String arg0, Throwable arg1) {
super(arg0, arg1);
}
private static Object createEntityInstance(String feedId)
throws FeedNotFoundException, InstantiationException,
IllegalAccessException {
FeedInstanceConfigurator config = REGISTRY.getFeedConfigurator(feedId);
if (config == null)
throw new FeedNotFoundException(
"No feed for requested feed ID found - " + feedId);
Class feedClass = config.getFeedType();
return feedClass.newInstance();
} }
} }

View File

@ -22,13 +22,17 @@ import java.util.Enumeration;
import java.util.Map; import java.util.Map;
import java.util.StringTokenizer; import java.util.StringTokenizer;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.apache.lucene.gdata.server.authentication.AuthenticationController;
import org.apache.lucene.gdata.server.registry.ComponentType;
import org.apache.lucene.gdata.server.registry.ProvidedService;
import org.apache.lucene.gdata.server.registry.GDataServerRegistry; import org.apache.lucene.gdata.server.registry.GDataServerRegistry;
import org.apache.lucene.gdata.storage.Storage;
import com.google.gdata.data.ExtensionProfile; import org.apache.lucene.gdata.storage.StorageController;
/** /**
* The GDataRequest Class wraps the incoming HttpServletRequest. Needed * The GDataRequest Class wraps the incoming HttpServletRequest. Needed
@ -68,6 +72,10 @@ public class GDataRequest {
@SuppressWarnings("unused") @SuppressWarnings("unused")
private static final String RESPONSE_FORMAT_PARAMETER_ATOM = "atom"; private static final String RESPONSE_FORMAT_PARAMETER_ATOM = "atom";
private static final String HTTP_HEADER_IF_MODIFIED_SINCE = "If-Modified-Since";
private static final String HTTP_HEADER_AUTH = "Authorization";
// Atom is the default resopnse format // Atom is the default resopnse format
private OutputFormat responseFormat = OutputFormat.ATOM; private OutputFormat responseFormat = OutputFormat.ATOM;
@ -77,7 +85,7 @@ public class GDataRequest {
private String entryId = null; private String entryId = null;
private ExtensionProfile extensionProfile= null; private ProvidedService configurator = null;
private String entryVersion = null; private String entryVersion = null;
@ -112,12 +120,31 @@ public class GDataRequest {
public void initializeRequest() throws GDataRequestException { public void initializeRequest() throws GDataRequestException {
generateIdentificationProperties(); generateIdentificationProperties();
setOutputFormat(); setOutputFormat();
/* // TODO remove this dependency
* ExtensionProfile is used for building the Entry / Feed Instances from an inputstream or reader StorageController controller = GDataServerRegistry.getRegistry()
*/ .lookup(StorageController.class,
this.extensionProfile = GDataServerRegistry.getRegistry().getExtensionProfile(this.feedId); ComponentType.STORAGECONTROLLER);
if(this.extensionProfile == null) try {
throw new GDataRequestException("feed is not registered or extension profile could not be created");
Storage storage = controller.getStorage();
String service = storage.getServiceForFeed(this.feedId);
/*
* ExtensionProfile and the type is used for building the Entry /
* Feed Instances from an inputstream or reader
*
*/
this.configurator = GDataServerRegistry.getRegistry()
.getProvidedService(service);
if (this.configurator == null)
throw new GDataRequestException(
"feed is not registered or extension profile could not be created");
} catch (Exception e) {
throw new GDataRequestException(
"feed is not registered or extension profile could not be created");
}
} }
/** /**
@ -231,6 +258,7 @@ public class GDataRequest {
LOG.warn("Intems per page could not be parsed - " + e.getMessage(), LOG.warn("Intems per page could not be parsed - " + e.getMessage(),
e); e);
} }
return retval < 0 ? DEFAULT_ITEMS_PER_PAGE : retval; return retval < 0 ? DEFAULT_ITEMS_PER_PAGE : retval;
} }
@ -263,7 +291,7 @@ public class GDataRequest {
public String getSelfId() { public String getSelfId() {
StringBuilder builder = new StringBuilder(); StringBuilder builder = new StringBuilder();
builder.append(buildRequestIDString(false)); builder.append(buildRequestIDString(false));
builder.append("?");
builder.append(getQueryString()); builder.append(getQueryString());
return builder.toString(); return builder.toString();
@ -275,42 +303,48 @@ public class GDataRequest {
* @return the id of the next page * @return the id of the next page
*/ */
public String getNextId() { public String getNextId() {
// StringBuilder builder = new StringBuilder(); StringBuilder builder = new StringBuilder();
// builder.append(buildRequestIDString()); builder.append(buildRequestIDString(false));
// builder.append("?");
// builder.append(getQueryString());
// Enumeration parameters = this.request.getParameterNames();
// if(this.request.getParameter(START_INDEX_NEXT_PAGE_PARAMETER)== while (parameters.hasMoreElements()) {
// null){ String element = (String) parameters.nextElement();
// builder.append("&").append(START_INDEX_NEXT_PAGE_PARAMETER).append("="); String[] values = this.request.getParameterValues(element);
// builder.append(DEFAULT_ITEMS_PER_PAGE+1); for (int i = 0; i < values.length; i++) {
// }
// else{ builder.append(element).append("=");
// if (element.equals(START_INDEX_NEXT_PAGE_PARAMETER)) {
// int next = 0; int tempVal = DEFAULT_START_INDEX;
// try{ try {
// next = tempVal = Integer.parseInt(values[i]);
// Integer.parseInt(this.request.getParameter(START_INDEX_NEXT_PAGE_PARAMETER)); } catch (Exception e) {
// }catch (Exception e) { LOG.info("Can not parse StartIndex -- use defaut");
// // }
// } builder.append(tempVal + getItemsPerPage());
// break;
// if(next < 0) }
// builder.append(DEFAULT_ITEMS_PER_PAGE+1);
// else builder.append(values[i]);
// builder.append(next+DEFAULT_ITEMS_PER_PAGE);
// int pos = builder.indexOf(START_INDEX_NEXT_PAGE_PARAMETER); }
// boolean end = builder.lastIndexOf("&",pos) < pos; if (parameters.hasMoreElements())
// builder.replace(pos+START_INDEX_NEXT_PAGE_PARAMETER.length()+1,pos+START_INDEX_NEXT_PAGE_PARAMETER.length()+3,""+next); builder.append("&");
//
// }
// System.out.println(end); if (this.request.getParameter(ITEMS_PER_PAGE_PARAMETER) == null) {
// } if (builder.charAt(builder.length() - 1) != '?')
// builder.append('&');
// builder.append(ITEMS_PER_PAGE_PARAMETER).append("=").append(
// DEFAULT_ITEMS_PER_PAGE);
// return builder.toString(); }
return buildRequestIDString(false); if (this.request.getParameter(START_INDEX_NEXT_PAGE_PARAMETER) == null) {
builder.append('&');
builder.append(START_INDEX_NEXT_PAGE_PARAMETER).append("=");
builder.append(DEFAULT_ITEMS_PER_PAGE + 1);
}
return builder.toString();
} }
@ -340,9 +374,9 @@ public class GDataRequest {
if (this.request.getParameter(ITEMS_PER_PAGE_PARAMETER) != null) if (this.request.getParameter(ITEMS_PER_PAGE_PARAMETER) != null)
return retVal; return retVal;
String tempString = (retVal == null ? "?" + ITEMS_PER_PAGE_PARAMETER String tempString = (retVal == null ? ITEMS_PER_PAGE_PARAMETER + "="
+ "=" + DEFAULT_ITEMS_PER_PAGE : "&" + ITEMS_PER_PAGE_PARAMETER + DEFAULT_ITEMS_PER_PAGE : "&" + ITEMS_PER_PAGE_PARAMETER + "="
+ "=" + DEFAULT_ITEMS_PER_PAGE); + DEFAULT_ITEMS_PER_PAGE);
return retVal == null ? tempString : retVal + tempString; return retVal == null ? tempString : retVal + tempString;
@ -412,31 +446,92 @@ public class GDataRequest {
} }
/** /**
* If the reuquest is a {@link GDataRequestType#GET} request and there is * If the reuquest is a {@link GDataRequestType#GET} request and there is no
* no entry id specified, the requested resource is a feed. * entry id specified, the requested resource is a feed.
* *
* @return - <code>true</code> if an only if the requested resource is a feed * @return - <code>true</code> if an only if the requested resource is a
* feed
*/ */
public boolean isFeedRequested() { public boolean isFeedRequested() {
return (this.type.equals(GDataRequestType.GET) && (this.entryId == null|| this.entryId.length() == 0|| (this.entryId.equals('/')))); return (this.type.equals(GDataRequestType.GET) && (this.entryId == null
|| this.entryId.length() == 0 || (this.entryId.equals('/'))));
} }
/** /**
* * If the reuquest is a {@link GDataRequestType#GET} request and there is * * If the reuquest is a {@link GDataRequestType#GET} request and there is
* an entry id specified, the requested resource is an entry. * an entry id specified, the requested resource is an entry.
* *
* @return - <code>true</code> if an only if the requested resource is an entry * @return - <code>true</code> if an only if the requested resource is an
* entry
*/ */
public boolean isEntryRequested() { public boolean isEntryRequested() {
return !this.isFeedRequested(); return !this.isFeedRequested();
} }
/** /**
* @return - the extensionProfile for the requested resource * @return the configuration for this request
*/ */
public ExtensionProfile getExtensionProfile() { public ProvidedService getConfigurator() {
return this.extensionProfile; return this.configurator;
} }
/**
* @return - Returns the Internet Protocol (IP) address of the client or
* last proxy that sent the request.
*/
public String getRemoteAddress() {
return this.request.getRemoteAddr();
}
/**
* @return - the value for the send auth token. The auth token will be send
* as a request <tt>Authentication</tt> header.
*/
public String getAuthToken() {
String token = this.request.getHeader(HTTP_HEADER_AUTH);
if (token == null)
return null;
token = token.substring(token.indexOf("=") + 1);
return token;
}
/**
* @return - Returns an array containing all of the Cookie objects the
* client sent with underlaying HttpServletRequest.
*/
public Cookie[] getCookies() {
return this.request.getCookies();
}
/**
* @return - the cookie set instead of the authentication token or
* <code>null</code> if not auth cookie is set
*/
public Cookie getAuthCookie() {
Cookie[] cookies = this.request.getCookies();
if (cookies == null)
return null;
for (int i = 0; i < cookies.length; i++) {
if (cookies[i].getName().equals(AuthenticationController.TOKEN_KEY))
return cookies[i];
}
return null;
}
/**
* @return - the date string of the <tt>If-Modified-Since</tt> HTTP
* request header, or null if header is not set
*/
public String getModifiedSince() {
return this.request.getHeader(HTTP_HEADER_IF_MODIFIED_SINCE);
}
/**
* @return - the underlaying HttpServletRequest
*/
public HttpServletRequest getHttpServletRequest() {
return this.request;
}
} }

View File

@ -18,13 +18,16 @@ package org.apache.lucene.gdata.server;
import java.io.IOException; import java.io.IOException;
import java.io.Writer; import java.io.Writer;
import java.util.Date;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import org.apache.lucene.gdata.server.GDataRequest.OutputFormat; import org.apache.lucene.gdata.server.GDataRequest.OutputFormat;
import org.apache.lucene.gdata.utils.DateFormater;
import com.google.gdata.data.BaseEntry; import com.google.gdata.data.BaseEntry;
import com.google.gdata.data.BaseFeed; import com.google.gdata.data.BaseFeed;
import com.google.gdata.data.DateTime;
import com.google.gdata.data.ExtensionProfile; import com.google.gdata.data.ExtensionProfile;
import com.google.gdata.util.common.xml.XmlWriter; import com.google.gdata.util.common.xml.XmlWriter;
import com.google.gdata.util.common.xml.XmlWriter.Namespace; import com.google.gdata.util.common.xml.XmlWriter.Namespace;
@ -50,6 +53,13 @@ import com.google.gdata.util.common.xml.XmlWriter.Namespace;
* {@link org.apache.lucene.gdata.server.GDataResponse#sendResponse(BaseEntry, ExtensionProfile)} * {@link org.apache.lucene.gdata.server.GDataResponse#sendResponse(BaseEntry, ExtensionProfile)}
* which sends the entry e.g feed to the output stream. * which sends the entry e.g feed to the output stream.
* </p> * </p>
* <p>
* This class will set the HTTP <tt>Last-Modified</tt> Header to enable
* clients to send <tt>If-Modified-Since</tt> request header to avoid
* retrieving the content again if it hasn't changed. If the content hasn't
* changed since the If-Modified-Since time, then the GData service returns a
* 304 (Not Modified) HTTP response.
* </p>
* *
* *
* *
@ -68,11 +78,17 @@ public class GDataResponse {
private final HttpServletResponse response; private final HttpServletResponse response;
protected static final String XMLMIME_ATOM = "text/xml";
protected static final String XMLMIME_RSS = "text/xml";
private static final String DEFAUL_NAMESPACE_URI = "http://www.w3.org/2005/Atom"; private static final String DEFAUL_NAMESPACE_URI = "http://www.w3.org/2005/Atom";
private static final Namespace DEFAULT_NAMESPACE = new Namespace("", private static final Namespace DEFAULT_NAMESPACE = new Namespace("",
DEFAUL_NAMESPACE_URI); DEFAUL_NAMESPACE_URI);
private static final String HEADER_LASTMODIFIED = "Last-Modified";
/** /**
* Creates a new GDataResponse * Creates a new GDataResponse
* *
@ -83,7 +99,7 @@ public class GDataResponse {
if (response == null) if (response == null)
throw new IllegalArgumentException("response must not be null"); throw new IllegalArgumentException("response must not be null");
this.response = response; this.response = response;
this.response.setContentType("text/xml");
} }
/** /**
@ -96,14 +112,18 @@ public class GDataResponse {
this.isError = true; this.isError = true;
this.error = errorCode; this.error = errorCode;
} }
/** /**
* Sets the status of the underlaying response * Sets the status of the underlaying response
*
* @see HttpServletResponse * @see HttpServletResponse
* @param responseCode - the status of the response * @param responseCode -
* the status of the response
*/ */
public void setResponseCode(int responseCode){ public void setResponseCode(int responseCode) {
this.response.setStatus(responseCode); this.response.setStatus(responseCode);
} }
/** /**
* This method sends the specified error to the user if set * This method sends the specified error to the user if set
* *
@ -113,6 +133,7 @@ public class GDataResponse {
public void sendError() throws IOException { public void sendError() throws IOException {
if (this.isError) if (this.isError)
this.response.sendError(this.error); this.response.sendError(this.error);
} }
/** /**
@ -141,22 +162,31 @@ public class GDataResponse {
throws IOException { throws IOException {
if (feed == null) if (feed == null)
throw new IllegalArgumentException("feed must not be null"); throw new IllegalArgumentException("feed must not be null");
if(profile == null) if (profile == null)
throw new IllegalArgumentException("extension profil must not be null"); throw new IllegalArgumentException(
"extension profil must not be null");
DateTime time = feed.getUpdated();
if (time != null)
setLastModifiedHeader(time.getValue());
XmlWriter writer = createWriter(); XmlWriter writer = createWriter();
if (this.outputFormat.equals(OutputFormat.ATOM)) if (this.outputFormat.equals(OutputFormat.ATOM)) {
this.response.setContentType(XMLMIME_ATOM);
feed.generateAtom(writer, profile); feed.generateAtom(writer, profile);
else } else {
this.response.setContentType(XMLMIME_RSS);
feed.generateRss(writer, profile); feed.generateRss(writer, profile);
}
} }
/** /**
* *
* Sends a response for an update, insert or delete request. This method * Sends a response for an update, insert or delete request. This method
* must not invoked in a case of an error performing the requeste action. * must not invoked in a case of an error performing the requeste action. If
* If the specified response format is ATOM the default namespace will be set to ATOM. * the specified response format is ATOM the default namespace will be set
* to ATOM.
*
* @param entry - * @param entry -
* the modified / created entry to send * the modified / created entry to send
* @param profile - * @param profile -
@ -169,8 +199,12 @@ public class GDataResponse {
throws IOException { throws IOException {
if (entry == null) if (entry == null)
throw new IllegalArgumentException("entry must not be null"); throw new IllegalArgumentException("entry must not be null");
if(profile == null) if (profile == null)
throw new IllegalArgumentException("extension profil must not be null"); throw new IllegalArgumentException(
"extension profil must not be null");
DateTime time = entry.getUpdated();
if (time != null)
setLastModifiedHeader(time.getValue());
XmlWriter writer = createWriter(); XmlWriter writer = createWriter();
if (this.outputFormat.equals(OutputFormat.ATOM)) if (this.outputFormat.equals(OutputFormat.ATOM))
entry.generateAtom(writer, profile); entry.generateAtom(writer, profile);
@ -181,7 +215,7 @@ public class GDataResponse {
private XmlWriter createWriter() throws IOException { private XmlWriter createWriter() throws IOException {
XmlWriter writer = new XmlWriter(getWriter(), this.encoding); XmlWriter writer = new XmlWriter(getWriter(), this.encoding);
// set the default namespace to Atom if Atom is the response format // set the default namespace to Atom if Atom is the response format
if(this.outputFormat.equals(OutputFormat.ATOM)) if (this.outputFormat.equals(OutputFormat.ATOM))
writer.setDefaultNamespace(DEFAULT_NAMESPACE); writer.setDefaultNamespace(DEFAULT_NAMESPACE);
return writer; return writer;
} }
@ -224,11 +258,12 @@ public class GDataResponse {
public void setOutputFormat(OutputFormat outputFormat) { public void setOutputFormat(OutputFormat outputFormat) {
this.outputFormat = outputFormat; this.outputFormat = outputFormat;
} }
/** /**
* @see Object#toString() * @see Object#toString()
*/ */
@Override @Override
public String toString(){ public String toString() {
StringBuilder builder = new StringBuilder(" GDataResponse: "); StringBuilder builder = new StringBuilder(" GDataResponse: ");
builder.append("Error: ").append(this.error); builder.append("Error: ").append(this.error);
builder.append(" outputFormat: ").append(getOutputFormat()); builder.append(" outputFormat: ").append(getOutputFormat());
@ -236,7 +271,20 @@ public class GDataResponse {
return builder.toString(); return builder.toString();
}
protected void setLastModifiedHeader(long lastModified) {
String lastMod = DateFormater.formatDate(new Date(lastModified),
DateFormater.HTTP_HEADER_DATE_FORMAT);
this.response.setHeader(HEADER_LASTMODIFIED, lastMod);
}
/**
* @see HttpServletResponse#setStatus(int)
* @param status - the request status code
*/
public void setStatus(int status){
this.response.setStatus(status);
} }
} }

View File

@ -16,14 +16,21 @@
package org.apache.lucene.gdata.server; package org.apache.lucene.gdata.server;
import java.io.IOException; import java.io.IOException;
import java.util.Date;
import java.util.List; import java.util.List;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.apache.lucene.gdata.data.ServerBaseEntry;
import org.apache.lucene.gdata.data.ServerBaseFeed;
import org.apache.lucene.gdata.server.registry.ComponentType;
import org.apache.lucene.gdata.server.registry.GDataServerRegistry; import org.apache.lucene.gdata.server.registry.GDataServerRegistry;
import org.apache.lucene.gdata.storage.ResourceNotFoundException;
import org.apache.lucene.gdata.storage.Storage; import org.apache.lucene.gdata.storage.Storage;
import org.apache.lucene.gdata.storage.StorageController;
import org.apache.lucene.gdata.storage.StorageException; import org.apache.lucene.gdata.storage.StorageException;
import org.apache.lucene.gdata.storage.StorageFactory;
import com.google.gdata.data.BaseEntry; import com.google.gdata.data.BaseEntry;
import com.google.gdata.data.BaseFeed; import com.google.gdata.data.BaseFeed;
@ -33,21 +40,26 @@ import com.google.gdata.data.Link;
import com.google.gdata.util.ParseException; import com.google.gdata.util.ParseException;
/** /**
* default implementation of the {@link org.apache.lucene.gdata.server.Service}
* interface.
*
* @author Simon Willnauer * @author Simon Willnauer
* *
*/ */
public class GDataService extends Service { public class GDataService implements Service {
private static final Log LOGGER = LogFactory.getLog(GDataService.class); private static final Log LOGGER = LogFactory.getLog(GDataService.class);
private Storage storage; protected Storage storage;
private GDataServerRegistry registry = GDataServerRegistry.getRegistry(); protected GDataServerRegistry registry = GDataServerRegistry.getRegistry();
private static final Generator generator; private static final Generator generator;
private static final String generatorName = "Lucene GData-Server"; private static final String generatorName = "Lucene GData-Server";
private static final String generatorURI = "http://lucene.apache.org"; private static final String generatorURI = "http://lucene.apache.org";
private static final String XMLMIME = "application/atom+xml";
static { static {
generator = new Generator(); generator = new Generator();
generator.setName(generatorName); generator.setName(generatorName);
@ -57,7 +69,13 @@ public class GDataService extends Service {
protected GDataService() throws ServiceException { protected GDataService() throws ServiceException {
try { try {
this.storage = StorageFactory.getStorage(); StorageController controller = GDataServerRegistry.getRegistry()
.lookup(StorageController.class,
ComponentType.STORAGECONTROLLER);
if (controller == null)
throw new StorageException(
"StorageController is not registered");
this.storage = controller.getStorage();
} catch (StorageException e) { } catch (StorageException e) {
LOGGER LOGGER
@ -75,40 +93,55 @@ public class GDataService extends Service {
* @see org.apache.lucene.gdata.server.Service#createEntry(org.apache.lucene.gdata.server.GDataRequest, * @see org.apache.lucene.gdata.server.Service#createEntry(org.apache.lucene.gdata.server.GDataRequest,
* org.apache.lucene.gdata.server.GDataResponse) * org.apache.lucene.gdata.server.GDataResponse)
*/ */
@Override
public BaseEntry createEntry(GDataRequest request, GDataResponse response) public BaseEntry createEntry(GDataRequest request, GDataResponse response)
throws ServiceException { throws ServiceException {
checkFeedIsRegisterd(request);
if (LOGGER.isInfoEnabled()) if (LOGGER.isInfoEnabled())
LOGGER.info("create Entry for feedId: " + request.getFeedId()); LOGGER.info("create Entry for feedId: " + request.getFeedId());
BaseEntry entry = buildEntry(request);
setUpdateTime(entry);
try {
this.storage.storeEntry(entry, request.getFeedId()); ServerBaseEntry entry = buildEntry(request, response);
entry.setFeedId(request.getFeedId());
entry.setServiceConfig(request.getConfigurator());
setTimeStamps(entry.getEntry());
BaseEntry retVal = null;
try {
retVal = this.storage.storeEntry(entry);
} catch (Exception e) { } catch (Exception e) {
response.setError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
ServiceException ex = new ServiceException("Could not store entry", ServiceException ex = new ServiceException("Could not store entry",
e); e);
ex.setStackTrace(e.getStackTrace()); ex.setStackTrace(e.getStackTrace());
throw ex; throw ex;
} }
return entry; return retVal;
} }
/** /**
* @see org.apache.lucene.gdata.server.Service#deleteEntry(org.apache.lucene.gdata.server.GDataRequest, * @see org.apache.lucene.gdata.server.Service#deleteEntry(org.apache.lucene.gdata.server.GDataRequest,
* org.apache.lucene.gdata.server.GDataResponse) * org.apache.lucene.gdata.server.GDataResponse)
*/ */
@Override
public BaseEntry deleteEntry(GDataRequest request, GDataResponse response) public BaseEntry deleteEntry(GDataRequest request, GDataResponse response)
throws ServiceException { throws ServiceException {
checkFeedIsRegisterd(request);
String entryid = request.getEntryId(); ServerBaseEntry entry = new ServerBaseEntry();
String feedid = request.getFeedId(); entry.setServiceConfig(request.getConfigurator());
entry.setFeedId(request.getFeedId());
entry.setId(request.getEntryId());
if (entry.getId() == null)
throw new ServiceException(
"entry id is null -- can not delete null entry");
try { try {
this.storage.deleteEntry(entryid, feedid); this.storage.deleteEntry(entry);
} catch (ResourceNotFoundException e) {
response.setError(HttpServletResponse.SC_BAD_REQUEST);
ServiceException ex = new ServiceException(
"Could not delete entry", e);
ex.setStackTrace(e.getStackTrace());
throw ex;
} catch (Exception e) { } catch (Exception e) {
response.setError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
ServiceException ex = new ServiceException( ServiceException ex = new ServiceException(
"Could not delete entry", e); "Could not delete entry", e);
ex.setStackTrace(e.getStackTrace()); ex.setStackTrace(e.getStackTrace());
@ -121,26 +154,50 @@ public class GDataService extends Service {
* @see org.apache.lucene.gdata.server.Service#updateEntry(org.apache.lucene.gdata.server.GDataRequest, * @see org.apache.lucene.gdata.server.Service#updateEntry(org.apache.lucene.gdata.server.GDataRequest,
* org.apache.lucene.gdata.server.GDataResponse) * org.apache.lucene.gdata.server.GDataResponse)
*/ */
@Override
public BaseEntry updateEntry(GDataRequest request, GDataResponse response) public BaseEntry updateEntry(GDataRequest request, GDataResponse response)
throws ServiceException { throws ServiceException {
checkFeedIsRegisterd(request);
BaseEntry entry = buildEntry(request); ServerBaseEntry entry = buildEntry(request, response);
String feedid = request.getFeedId(); entry.setFeedId(request.getFeedId());
entry.setServiceConfig(request.getConfigurator());
if (LOGGER.isInfoEnabled()) if (LOGGER.isInfoEnabled())
LOGGER.info("update Entry" + entry.getId() + " for feedId: " LOGGER.info("update Entry" + entry.getId() + " for feedId: "
+ feedid); + request.getFeedId());
setUpdateTime(entry); if (entry.getId() == null) {
response.setError(HttpServletResponse.SC_BAD_REQUEST);
throw new ServiceException("Entry id is null can not update entry");
}
if (!entry.getId().equals(request.getEntryId())) {
if (LOGGER.isInfoEnabled())
LOGGER
.info("Entry id in the entry xml does not match the requested resource -- XML-ID:"
+ entry.getId()
+ "; Requested resource: "
+ request.getEntryId());
response.setError(HttpServletResponse.SC_BAD_REQUEST);
throw new ServiceException(
"Entry id in the entry xml does not match the requested resource");
}
setTimeStamps(entry.getEntry());
BaseEntry retVal = null;
try { try {
this.storage.updateEntry(entry, feedid); retVal = this.storage.updateEntry(entry);
} catch (ResourceNotFoundException e) {
response.setError(HttpServletResponse.SC_BAD_REQUEST);
ServiceException ex = new ServiceException(
"Could not update entry", e);
ex.setStackTrace(e.getStackTrace());
throw ex;
} catch (StorageException e) { } catch (StorageException e) {
response.setError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
ServiceException ex = new ServiceException( ServiceException ex = new ServiceException(
"Could not update entry", e); "Could not update entry", e);
ex.setStackTrace(e.getStackTrace()); ex.setStackTrace(e.getStackTrace());
throw ex; throw ex;
} }
return entry; return retVal;
} }
/** /**
@ -148,21 +205,25 @@ public class GDataService extends Service {
* org.apache.lucene.gdata.server.GDataResponse) * org.apache.lucene.gdata.server.GDataResponse)
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Override
public BaseFeed getFeed(GDataRequest request, GDataResponse response) public BaseFeed getFeed(GDataRequest request, GDataResponse response)
throws ServiceException { throws ServiceException {
checkFeedIsRegisterd(request);
ServerBaseFeed feed = new ServerBaseFeed();
feed.setId(request.getFeedId());
feed.setStartIndex(request.getStartIndex());
feed.setItemsPerPage(request.getItemsPerPage());
feed.setServiceConfig(request.getConfigurator());
try { try {
// TODO remove when storing feeds is implemented just for BaseFeed retVal = this.storage.getFeed(feed);
// development dynamicElementFeedStragey(retVal, request);
BaseFeed feed = this.storage.getFeed(request.getFeedId(), request
.getStartIndex(), request.getItemsPerPage()); return retVal;
buildDynamicFeedElements(request, feed); /*
List<BaseEntry> list = feed.getEntries(); * resouce not found will be detected in Gdata request.
addContextPath(list, request.getContextPath()); * the request queries the storage for the feed to get the serivce for the feed
return feed; */
} catch (StorageException e) { } catch (StorageException e) {
response.setError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
ServiceException ex = new ServiceException("Could not get feed", e); ServiceException ex = new ServiceException("Could not get feed", e);
ex.setStackTrace(e.getStackTrace()); ex.setStackTrace(e.getStackTrace());
throw ex; throw ex;
@ -170,18 +231,6 @@ public class GDataService extends Service {
} }
/*
* build the dynamic elements like self link and next link
*/
private void buildDynamicFeedElements(final GDataRequest request,
final BaseFeed feed) {
feed.setGenerator(generator);
feed.setItemsPerPage(request.getItemsPerPage());
feed.getLinks().add(
buildLink(Link.Rel.SELF, Link.Type.ATOM, request.getSelfId()));
// TODO add next link
}
private Link buildLink(String rel, String type, String href) { private Link buildLink(String rel, String type, String href) {
Link retVal = new Link(); Link retVal = new Link();
retVal.setHref(href); retVal.setHref(href);
@ -190,41 +239,21 @@ public class GDataService extends Service {
return retVal; return retVal;
} }
/* private ServerBaseEntry buildEntry(final GDataRequest request,
* every entry has an ID which has to have a prefix. The prefix is the final GDataResponse response) throws ServiceException {
* context path of the requested feed. This will be used to request the
* entry directly
*/
private void addContextPath(List<BaseEntry> list, final String contextPath) {
for (BaseEntry entry : list) {
addcontextPath(entry, contextPath);
}
}
@SuppressWarnings("unchecked")
private BaseEntry addcontextPath(final BaseEntry entry,
final String contextPath) {
String id = contextPath + entry.getId();
entry.setId(id);
Link self = new Link();
self.setRel("self");
self.setHref(id);
self.setType("application/atom+xml");
entry.getLinks().add(self);
return entry;
}
private BaseEntry buildEntry(final GDataRequest request)
throws ServiceException {
try { try {
return GDataEntityBuilder.buildEntry(request); ServerBaseEntry entry = new ServerBaseEntry(GDataEntityBuilder
.buildEntry(request));
return entry;
} catch (ParseException e) { } catch (ParseException e) {
response.setError(HttpServletResponse.SC_BAD_REQUEST);
ServiceException ex = new ServiceException( ServiceException ex = new ServiceException(
"Could not parse entry from incoming request", e); "Could not parse entry from incoming request", e);
ex.setStackTrace(e.getStackTrace()); ex.setStackTrace(e.getStackTrace());
throw ex; throw ex;
} catch (IOException e) { } catch (IOException e) {
response.setError(HttpServletResponse.SC_BAD_REQUEST);
ServiceException ex = new ServiceException( ServiceException ex = new ServiceException(
"Could not read or open input stream", e); "Could not read or open input stream", e);
ex.setStackTrace(e.getStackTrace()); ex.setStackTrace(e.getStackTrace());
@ -232,20 +261,11 @@ public class GDataService extends Service {
} }
} }
/* private BaseEntry setTimeStamps(final BaseEntry entry) {
* checks whether the reqeuested feed is registered if (entry.getUpdated() == null)
*/ entry.setUpdated(DateTime.now());
private void checkFeedIsRegisterd(final GDataRequest request) if (entry.getPublished() == null)
throws FeedNotFoundException { entry.setPublished(DateTime.now());
if (!this.registry.isFeedRegistered(request.getFeedId()))
throw new FeedNotFoundException(
"Feed could not be found - is not registed - Feed ID:"
+ request.getFeedId());
this.storage.setExtensionProfile(request.getExtensionProfile());
}
private BaseEntry setUpdateTime(final BaseEntry entry) {
entry.setUpdated(DateTime.now());
return entry; return entry;
} }
@ -253,18 +273,30 @@ public class GDataService extends Service {
* @see org.apache.lucene.gdata.server.Service#getSingleEntry(org.apache.lucene.gdata.server.GDataRequest, * @see org.apache.lucene.gdata.server.Service#getSingleEntry(org.apache.lucene.gdata.server.GDataRequest,
* org.apache.lucene.gdata.server.GDataResponse) * org.apache.lucene.gdata.server.GDataResponse)
*/ */
@Override
public BaseEntry getSingleEntry(GDataRequest request, GDataResponse response) public BaseEntry getSingleEntry(GDataRequest request, GDataResponse response)
throws ServiceException { throws ServiceException {
checkFeedIsRegisterd(request);
try { try {
BaseEntry entry = this.storage.getEntry(request.getEntryId(), ServerBaseEntry entry = new ServerBaseEntry();
request.getFeedId()); entry.setServiceConfig(request.getConfigurator());
if(entry == null) entry.setFeedId(request.getFeedId());
return null; entry.setId(request.getEntryId());
addcontextPath(entry, request.getContextPath()); if(entry.getId() == null){
return entry; response.setError(HttpServletResponse.SC_BAD_REQUEST);
throw new ServiceException("entry is null can't get entry");
}
BaseEntry retVal = null;
retVal = this.storage.getEntry(entry);
dynamicElementEntryStragey(retVal, request);
return retVal;
} catch (ResourceNotFoundException e) {
response.setError(HttpServletResponse.SC_BAD_REQUEST);
ServiceException ex = new ServiceException(
"Could not get entry", e);
ex.setStackTrace(e.getStackTrace());
throw ex;
} catch (StorageException e) { } catch (StorageException e) {
ServiceException ex = new ServiceException("Could not get feed", e); ServiceException ex = new ServiceException("Could not get feed", e);
ex.setStackTrace(e.getStackTrace()); ex.setStackTrace(e.getStackTrace());
@ -272,4 +304,95 @@ public class GDataService extends Service {
} }
} }
/*
* adds all dynamic element to the entry
*/
private void dynamicElementEntryStragey(final BaseEntry entry,
final GDataRequest request) {
setSelfLink(entry, request.getContextPath());
}
/*
* adds all dynamic element to the feed entries
*/
@SuppressWarnings("unchecked")
private void dynamicElementFeedStragey(final BaseFeed feed,
final GDataRequest request) {
buildDynamicFeedElements(request, feed);
List<BaseEntry> entryList = feed.getEntries();
for (BaseEntry entry : entryList) {
String id = request.getContextPath() + entry.getId();
setSelfLink(entry, id);
}
}
/*
* The selfLink is build from a prefix and the entry id. The prefix is the
* context path of the requested feed. This will be used to request the
* entry directly
*/@SuppressWarnings("unchecked")
private BaseEntry setSelfLink(final BaseEntry entry, String id) {
Link self = buildLink(Link.Rel.SELF, XMLMIME, id);
entry.getLinks().add(self);
return entry;
}
/*
* build the dynamic elements like self link and next link
*/
private void buildDynamicFeedElements(final GDataRequest request,
final BaseFeed feed) {
feed.setGenerator(generator);
feed.setItemsPerPage(request.getItemsPerPage());
feed.setStartIndex(request.getStartIndex());
feed.setId(request.getContextPath());
feed.getLinks().add(
buildLink(Link.Rel.SELF, Link.Type.ATOM, request.getSelfId()));
feed.getLinks().add(
buildLink(Link.Rel.NEXT, XMLMIME, request.getNextId()));
}
/**
* @see org.apache.lucene.gdata.server.Service#close()
*/
public void close() {
this.storage.close();
}
/**
* @see org.apache.lucene.gdata.server.Service#getFeedLastModified(java.lang.String)
*/
public Date getFeedLastModified(final String feedId) throws ServiceException {
try {
return new Date(this.storage.getFeedLastModified(feedId));
} catch (StorageException e) {
ServiceException ex = new ServiceException(
"Could not get Last update for feed -- "+feedId, e);
ex.setStackTrace(e.getStackTrace());
throw ex;
}
}
/**
* @see org.apache.lucene.gdata.server.Service#getEntryLastModified(java.lang.String, java.lang.String)
*/
public Date getEntryLastModified(final String entryId,final String feedId) throws ServiceException {
try {
return new Date(this.storage.getEntryLastModified(entryId, feedId));
} catch (StorageException e) {
ServiceException ex = new ServiceException(
"Could not get Last update for entry -- "+entryId, e);
ex.setStackTrace(e.getStackTrace());
throw ex;
}
}
} }

View File

@ -16,6 +16,8 @@
package org.apache.lucene.gdata.server; package org.apache.lucene.gdata.server;
import java.util.Date;
import com.google.gdata.data.BaseEntry; import com.google.gdata.data.BaseEntry;
import com.google.gdata.data.BaseFeed; import com.google.gdata.data.BaseFeed;
@ -39,7 +41,7 @@ import com.google.gdata.data.BaseFeed;
* *
* *
*/ */
public abstract class Service { public interface Service {
/** /**
* Service method to create an entry in an already created and existing * Service method to create an entry in an already created and existing
@ -133,6 +135,27 @@ public abstract class Service {
public abstract BaseEntry getSingleEntry(final GDataRequest request, final GDataResponse response) public abstract BaseEntry getSingleEntry(final GDataRequest request, final GDataResponse response)
throws ServiceException; throws ServiceException;
/**
* will close the Service - service should not be used after this method has been called
*/
public void close();
/**
* Retruns the date of the last modification for the given feed id
* @param feedId - the id of the feed
* @return - the last modified date or the current date if the date can not be retrieved
* @throws ServiceException - if the storage can not be accessed
*/
public abstract Date getFeedLastModified(String feedId)throws ServiceException;
/**
* Retruns the date of the last modification for the given entry id
* @param entryId - the id of the entry
* @param feedId - the feed id this entry belongs to
* @return - the last modified date or the current date if the date can not be retrieved
* @throws ServiceException - if the storage can not be accessed
*/
public abstract Date getEntryLastModified(String entryId, String feedId)throws ServiceException;

View File

@ -17,6 +17,10 @@
package org.apache.lucene.gdata.server; package org.apache.lucene.gdata.server;
/** /**
* The ServiceException is used to encapsulate all {@link java.lang.Exception}
* throw by underlaying layers of the
* {@link org.apache.lucene.gdata.server.Service} layer.
*
* @author Simon Willnauer * @author Simon Willnauer
* *
*/ */
@ -28,7 +32,7 @@ public class ServiceException extends Exception {
private static final long serialVersionUID = -7099825107871876584L; private static final long serialVersionUID = -7099825107871876584L;
/** /**
* * Constructs a new ServiceException
*/ */
public ServiceException() { public ServiceException() {
super(); super();
@ -36,7 +40,8 @@ public class ServiceException extends Exception {
} }
/** /**
* @param arg0 * Constructs a new ServiceException
* @param arg0 - the exception message
*/ */
public ServiceException(String arg0) { public ServiceException(String arg0) {
super(arg0); super(arg0);
@ -44,8 +49,9 @@ public class ServiceException extends Exception {
} }
/** /**
* @param arg0 * Constructs a new ServiceException
* @param arg1 * @param arg0 - the exceptin message
* @param arg1 - the exception cause
*/ */
public ServiceException(String arg0, Throwable arg1) { public ServiceException(String arg0, Throwable arg1) {
super(arg0, arg1); super(arg0, arg1);
@ -53,7 +59,8 @@ public class ServiceException extends Exception {
} }
/** /**
* @param arg0 * Constructs a new ServiceException
* @param arg0 - the exception cause
*/ */
public ServiceException(Throwable arg0) { public ServiceException(Throwable arg0) {
super(arg0); super(arg0);

View File

@ -15,43 +15,78 @@
*/ */
package org.apache.lucene.gdata.server; package org.apache.lucene.gdata.server;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.lucene.gdata.server.administration.AdminService;
import org.apache.lucene.gdata.server.administration.GDataAdminService;
import org.apache.lucene.gdata.server.registry.Component;
import org.apache.lucene.gdata.server.registry.ComponentType;
import org.apache.lucene.gdata.server.registry.ServerComponent;
/** /**
* The {@link ServiceFactory} creates {@link Service} implementations to access * The {@link ServiceFactory} creates {@link Service} implementations to access
* the GData - Server components. * the GData - Server components.
* This class should not be access directy. The class will be registered in the {@link org.apache.lucene.gdata.server.registry.GDataServerRegistry}.
* Use {@link org.apache.lucene.gdata.server.registry.GDataServerRegistry#lookup(Class, ComponentType)}
* *
* @author Simon Willnauer * @author Simon Willnauer
* *
*/ */
public class ServiceFactory { @Component(componentType=ComponentType.SERVICEFACTORY)
public class ServiceFactory implements ServerComponent {
private static ServiceFactory INSTANCE = null; private static final Log LOG = LogFactory.getLog(ServiceFactory.class);
/**
* @return - a Singleton Instance of the factory
*/
public static synchronized ServiceFactory getInstance() {
if (INSTANCE == null)
INSTANCE = new ServiceFactory();
return INSTANCE;
}
private ServiceFactory() { /**
// private constructor --> singleton * public constructor to enable loading via the registry
} * @see org.apache.lucene.gdata.server.registry.Component
* @see org.apache.lucene.gdata.server.registry.GDataServerRegistry
*/
public ServiceFactory() {
//
}
/** /**
* Creates a {@link Service} implementation. * Creates a {@link Service} instance.
* *
* @return a Service Implementation * @return a Service instance
*/ */
public Service getService() { public Service getService() {
try{ try{
return new GDataService(); return new GDataService();
}catch (Exception e) { }catch (Exception e) {
// //
} }
return null; return null;
}
/**
* Creates a {@link AdminService} instance
* @return a AdminService instance
*/
public AdminService getAdminService(){
try {
return new GDataAdminService();
} catch (ServiceException e) {
LOG.warn("Factory method can not create GDataAdminService returning null-- "+e.getMessage(),e);
}
return null;
}
/**
* @see org.apache.lucene.gdata.server.registry.ServerComponent#initialize()
*/
public void initialize() {
//
}
/**
* @see org.apache.lucene.gdata.server.registry.ServerComponent#destroy()
*/
public void destroy() {
//
} }
} }

View File

@ -1,62 +0,0 @@
/**
* Copyright 2004 The Apache Software Foundation
*
* 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 org.apache.lucene.gdata.server.registry;
/**
* @author Simon Willnauer
*
*/
public class DataBuilderException extends RuntimeException {
/**
*
*/
private static final long serialVersionUID = -3802958802500735198L;
/**
*
*/
public DataBuilderException() {
super();
// TODO Auto-generated constructor stub
}
/**
* @param message
*/
public DataBuilderException(String message) {
super(message);
// TODO Auto-generated constructor stub
}
/**
* @param message
* @param cause
*/
public DataBuilderException(String message, Throwable cause) {
super(message, cause);
// TODO Auto-generated constructor stub
}
/**
* @param cause
*/
public DataBuilderException(Throwable cause) {
super(cause);
// TODO Auto-generated constructor stub
}
}

View File

@ -1,66 +0,0 @@
/**
* Copyright 2004 The Apache Software Foundation
*
* 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 org.apache.lucene.gdata.server.registry;
/**
* @author Simon Willnauer
*
*/
public class FeedInstanceConfigurator {
private Class feedType;
private String feedId;
private Class extensionProfileClass;
/**
* @return Returns the feedType.
*/
public Class getFeedType() {
return this.feedType;
}
/**
* @param feedType The feedType to set.
*/
public void setFeedType(Class feedType) {
this.feedType = feedType;
}
/**
* @return Returns the feedURL.
*/
public String getFeedId() {
return this.feedId;
}
/**
* @param feedURL The feedURL to set.
*/
public void setFeedId(String feedURL) {
this.feedId = feedURL;
}
/**
* @return - the extension profile for this feed
*/
public Class getExtensionProfilClass(){
return this.extensionProfileClass;
}
/**
* @param extensionProfilClass
*/
public void setExtensionProfileClass(Class extensionProfilClass){
this.extensionProfileClass = extensionProfilClass;
}
}

View File

@ -20,23 +20,29 @@ import java.util.Map;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.apache.lucene.gdata.storage.StorageController;
import com.google.gdata.data.ExtensionProfile;
/** /**
* *
* The FeedRegistry represents the registry component of the GData Server. All * The GDataServerRegistry represents the registry component of the GData
* feed configurations will be registered here. Feed configurations contain * Server. All provided services and server components will be registered here.
* several informationsa about GData feed like: * The Gdata Server serves RSS / ATOM feeds for defined services. Each service
* <ol> * provides <i>n</i> feeds of a defined subclass of
* <li>the feed id - where the feed can be accessed via http methodes</li> * {@link com.google.gdata.data.BaseFeed}. Each feed contains <i>m</i> entries
* <li>the feed type - feed types are implementations of the abstract * of a defined subclass of {@link com.google.gdata.data.BaseEntry}. To
* {@link com.google.gdata.data.BaseFeed}</li> * generate RSS / ATOM formates a class of the type
* </ol> * {@link com.google.gdata.data.ExtensionProfile} is also defined for a service.
* The registry will be set up at start up of the server application and can be * <p>
* accessed from other components to get configurations according to incoming * The entry,feed and the ExtensionProfile classes are defined in the
* requests. * gdata-config.xml and will be loaded when the server starts up.
* </p>
* <p>
* The components defined in the gdata-config.xml will also be loaded and
* instanciated at startup. If a component can not be loaded or an Exception
* occures the server will not start up. To cause of the exception or error will
* be logged to the standart server output.
* </p>
* <p>The GDataServerRegistry is a Singleton</p>
*
* *
* @author Simon Willnauer * @author Simon Willnauer
* *
@ -44,12 +50,13 @@ import com.google.gdata.data.ExtensionProfile;
public class GDataServerRegistry { public class GDataServerRegistry {
private static GDataServerRegistry INSTANCE; private static GDataServerRegistry INSTANCE;
private StorageController storageInstance;
private static final Log LOGGER = LogFactory private static final Log LOGGER = LogFactory
.getLog(GDataServerRegistry.class); .getLog(GDataServerRegistry.class);
private final Map<String, FeedInstanceConfigurator> feedTypMap = new HashMap<String, FeedInstanceConfigurator>(); private final Map<String, ProvidedService> serviceTypeMap = new HashMap<String, ProvidedService>();
private final Map<ComponentType, ComponentBean> componentMap = new HashMap<ComponentType, ComponentBean>(
10);
private GDataServerRegistry() { private GDataServerRegistry() {
// private - singleton // private - singleton
@ -65,90 +72,183 @@ public class GDataServerRegistry {
} }
/** /**
* Registers a {@link FeedInstanceConfigurator} * Registers a {@link ProvidedService}
* *
* @param configurator - * @param configurator -
* the configurator to register in the registry * the configurator to register in the registry
*/ */
public void registerFeed(FeedInstanceConfigurator configurator) { public void registerService(ProvidedService configurator) {
if (configurator == null) { if (configurator == null) {
LOGGER.warn("Feedconfigurator is null -- skip registration"); LOGGER.warn("Feedconfigurator is null -- skip registration");
return; return;
} }
this.feedTypMap.put(configurator.getFeedId(), configurator); this.serviceTypeMap.put(configurator.getName(), configurator);
} }
/** /**
* Looks up the {@link FeedInstanceConfigurator} by the given feed id. * Looks up the {@link ProvidedServiceConfig} by the given service name.
* *
* @param feedId * @param service
* @return - the {@link FeedInstanceConfigurator} or <code>null</code> if * @return - the {@link ProvidedServiceConfig} or <code>null</code> if the
* the no configuration for this feed has been registered * no configuration for this service has been registered
*/ */
public FeedInstanceConfigurator getFeedConfigurator(String feedId) { public ProvidedService getProvidedService(String service) {
if (feedId == null) if (service == null)
throw new IllegalArgumentException( throw new IllegalArgumentException(
"Feed URL is null - must not be null to get registered feedtype"); "Service is null - must not be null to get registered feedtype");
return this.feedTypMap.get(feedId); return this.serviceTypeMap.get(service);
} }
protected void flushRegistry() { protected void flushRegistry() {
this.feedTypMap.clear(); this.serviceTypeMap.clear();
this.componentMap.clear();
} }
/** /**
* @param feedId - * @param service -
* the id of the feed as the feed is registered * the name of the service
* @return - <code>true</code> if and only if the feed is registered, * @return - <code>true</code> if and only if the service is registered,
* otherwise <code>false</code>. * otherwise <code>false</code>.
*/ */
public boolean isFeedRegistered(String feedId) { public boolean isServiceRegistered(String service) {
return this.feedTypMap.containsKey(feedId); return this.serviceTypeMap.containsKey(service);
} }
/**
* @param storage
*/
public void registerStorage(StorageController storage) {
if (this.storageInstance != null)
throw new IllegalStateException(
"Storage already registered -- Instance of "
+ this.storageInstance.getClass());
this.storageInstance = storage;
}
/** /**
* Destroys the registry and release all resources * Destroys the registry and release all resources
*/ */
public void destroy() { public void destroy() {
for (ComponentBean component : this.componentMap.values()) {
component.getObject().destroy();
}
flushRegistry(); flushRegistry();
this.storageInstance.destroy();
this.storageInstance = null;
} }
/** /**
* Creates the {@link ExtensionProfile} for a registered feed * This method is the main interface to the Component Lookup Service of the
* @param feedId - the feed id * registry. Every GDATA - Server component like STORAGE or the INDEXER
* @return - the extension profil for this feed of <code>null</code> if * component will be accessible via this method. To get a Component from the
* the feed is not registered or the extension profile could not be * lookup service specify the expected Class as an argument and the
* instanciated * component type of the component to return. For a lookup of the
* STORAGECONTORLER the code looks like:
* <p>
* <code> registryInstance.lookup(StorageController.class,ComponentType.STORAGECONTROLLER);</code>
* </p>
*
* @param <R>
* the type of the expected return value
* @param clazz -
* Class object of the expected return value
* @param compType -
* The component type
* @return the registered component or <code>null</code> if the component
* can not looked up.
*/ */
public ExtensionProfile getExtensionProfile(final String feedId) { @SuppressWarnings("unchecked")
FeedInstanceConfigurator configurator = this.feedTypMap.get(feedId); public <R> R lookup(Class<R> clazz, ComponentType compType) {
if (configurator == null) ComponentBean bean = this.componentMap.get(compType);
if (bean == null)
return null; return null;
Class clazz = configurator.getExtensionProfilClass(); if (bean.getSuperType().equals(clazz))
try { return (R) bean.getObject();
return (ExtensionProfile) clazz.newInstance();
} catch (Exception e) {
LOGGER
.error("Can not create instance of ExtensionProfil for class: "
+ clazz + " -- feedId: " + feedId);
}
return null; return null;
} }
/**
* @param <E>
* @param componentClass
* @throws RegistryException
*/
@SuppressWarnings("unchecked")
public <E extends ServerComponent> void registerComponent(final Class<E> componentClass)
throws RegistryException {
if (componentClass == null)
throw new IllegalArgumentException(
"component class must not be null");
if(!checkImplementsServerComponent(componentClass))
throw new RegistryException("can not register component. the given class does not implement ServerComponent interface -- "+componentClass.getName());
try {
Component annotation = componentClass.getAnnotation(Component.class);
if (annotation == null)
throw new RegistryException(
"can not register component. the given class is not a component -- "
+ componentClass.getName());
ComponentType type = annotation.componentType();
if (this.componentMap.containsKey(type))
throw new RegistryException("component already registered -- "
+ type.name());
Class superType = type.getClass().getField(type.name())
.getAnnotation(SuperType.class).superType();
if (!checkSuperType(componentClass, superType))
throw new RegistryException("Considered Supertype <"
+ superType.getName() + "> is not a super type of <"
+ componentClass + ">");
ServerComponent comp = componentClass.newInstance();
comp.initialize();
ComponentBean bean = new ComponentBean(comp, superType);
this.componentMap.put(type, bean);
} catch (Exception e) {
throw new RegistryException("Can not register component -- "
+ e.getMessage(), e);
}
}
private static boolean checkImplementsServerComponent(Class type){
if(type == null)
return false;
if(type.equals(Object.class))
return false;
if(type.equals(ServerComponent.class))
return true;
Class[] compInterfaces = type.getInterfaces();
for (int i = 0; i < compInterfaces.length; i++) {
if(checkImplementsServerComponent(compInterfaces[i]))
return true;
}
return checkImplementsServerComponent(type.getSuperclass());
}
private static boolean checkSuperType(Class type, Class consideredSuperType) {
if (type.equals(Object.class))
return false;
if (type.equals(consideredSuperType))
return true;
Class[] interfaces = type.getInterfaces();
for (int i = 0; i < interfaces.length; i++) {
if (interfaces[i].equals(consideredSuperType))
return true;
}
return checkSuperType(type.getSuperclass(), consideredSuperType);
}
private class ComponentBean {
private final Class superType;
private final ServerComponent object;
ComponentBean(final ServerComponent object, final Class superType) {
this.superType = superType;
this.object = object;
}
ServerComponent getObject() {
return this.object;
}
Class getSuperType() {
return this.superType;
}
}
} }

View File

@ -15,27 +15,56 @@
*/ */
package org.apache.lucene.gdata.server.registry; package org.apache.lucene.gdata.server.registry;
import com.google.gdata.data.ExtensionProfile; import java.io.IOException;
import com.google.gdata.data.Feed;
import org.apache.commons.digester.Digester;
import org.xml.sax.SAXException;
/** /**
* Reads the configuration file and creates the
* {@link org.apache.lucene.gdata.server.registry.GDataServerRegistry} singleton
* instance. All services and components will be instanciated and registered in
* the registry.
*
* @author Simon Willnauer * @author Simon Willnauer
* *
*/ */
public class RegistryBuilder { class RegistryBuilder {
/** /**
* builds the {@link GDataServerRegistry} accessible via the
* {@link GDataServerRegistry#getRegistry()} method
* *
* @throws IOException -
* if an IOException occures while reading the config file
* @throws SAXException -
* if the config file can not be parsed
*/ */
public static void buildRegistry(){ static void buildRegistry() throws IOException, SAXException {
// TODO Implement this!! -- just for develping purposes
GDataServerRegistry reg = GDataServerRegistry.getRegistry(); buildFromConfiguration(new Digester(), GDataServerRegistry
FeedInstanceConfigurator configurator = new FeedInstanceConfigurator(); .getRegistry());
configurator.setFeedType(Feed.class);
configurator.setFeedId("weblog");
configurator.setExtensionProfileClass(ExtensionProfile.class);
reg.registerFeed(configurator);
} }
private static void buildFromConfiguration(Digester digester,
GDataServerRegistry registry) throws IOException, SAXException {
digester.setValidating(false);
digester.push(registry);
digester.addCallMethod("gdata/server-components/component",
"registerComponent", 0, new Class[] { Class.class });
digester.addObjectCreate("gdata/service", ProvidedServiceConfig.class);
digester.addSetProperties("gdata/service");
digester.addSetNext("gdata/service", "registerService");
digester.addBeanPropertySetter("gdata/service/feed-class", "feedType");
digester.addBeanPropertySetter("gdata/service/entry-class", "entryType");
digester.addBeanPropertySetter("gdata/service/extension-profile",
"extensionProfileClass");
digester.parse(RegistryBuilder.class
.getResourceAsStream("/gdata-config.xml"));
}
} }

View File

@ -30,7 +30,14 @@ import org.apache.commons.logging.LogFactory;
* The Registry will be loaded and set up befor the REST interface is available. * The Registry will be loaded and set up befor the REST interface is available.
* <p> * <p>
* This ContextListener has to be configured in the <code>web.xml</code> * This ContextListener has to be configured in the <code>web.xml</code>
* deployment descriptor.</p> * deployment descriptor.
* </p>
* <p>
* When the
* {@link javax.servlet.ServletContextListener#contextDestroyed(javax.servlet.ServletContextEvent)}
* method is called the registry will be destroyed using
* {@link org.apache.lucene.gdata.server.registry.GDataServerRegistry#destroy()}
* method.
* *
* *
* @author Simon Willnauer * @author Simon Willnauer
@ -42,15 +49,23 @@ public class RegistryContextListener implements ServletContextListener {
private static final Log LOG = LogFactory private static final Log LOG = LogFactory
.getLog(RegistryContextListener.class); .getLog(RegistryContextListener.class);
/** /**
* @see javax.servlet.ServletContextListener#contextInitialized(javax.servlet.ServletContextEvent) * @see javax.servlet.ServletContextListener#contextInitialized(javax.servlet.ServletContextEvent)
*/ */
public void contextInitialized(ServletContextEvent arg0) { public void contextInitialized(ServletContextEvent arg0) {
LOG.info("RegistryContextListener has been loaded"); LOG.info("RegistryContextListener has been loaded");
RegistryBuilder.buildRegistry();
this.serverRegistry = GDataServerRegistry.getRegistry(); try {
RegistryBuilder.buildRegistry();
this.serverRegistry = GDataServerRegistry.getRegistry();
} catch (Exception e) {
this.serverRegistry.destroy();
LOG.error("can not register requiered components", e);
throw new RuntimeException("Can not register required components",
e);
}
} }
/** /**

View File

@ -5,6 +5,6 @@
<meta name="Author" content="Simon Willnauer"> <meta name="Author" content="Simon Willnauer">
</head> </head>
<body> <body>
Internal registry - registering feeds and configurations Internal registry - registering services and server components
</body> </body>
</html> </html>

View File

@ -18,11 +18,16 @@ package org.apache.lucene.gdata.servlet;
import java.io.IOException; import java.io.IOException;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException; import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import org.apache.lucene.gdata.server.registry.ComponentType;
import org.apache.lucene.gdata.server.registry.GDataServerRegistry;
import org.apache.lucene.gdata.servlet.handler.RequestHandlerFactory;
/** /**
* *
* Provides an abstract class to be subclassed to create an GDATA servlet * Provides an abstract class to be subclassed to create an GDATA servlet
@ -44,6 +49,8 @@ public abstract class AbstractGdataServlet extends HttpServlet {
private static final String METHOD_PUT = "PUT"; private static final String METHOD_PUT = "PUT";
protected static RequestHandlerFactory HANDLER_FACTORY = null;
/** /**
* This overwrites the protected <code>service</code> method to dispatch * This overwrites the protected <code>service</code> method to dispatch
* the request to the correponding <code>do</code> method. There is * the request to the correponding <code>do</code> method. There is
@ -94,4 +101,15 @@ public abstract class AbstractGdataServlet extends HttpServlet {
} }
/**
*
* @see javax.servlet.GenericServlet#init(javax.servlet.ServletConfig)
*/
public void init(ServletConfig arg0) throws ServletException {
HANDLER_FACTORY = GDataServerRegistry.getRegistry().lookup(RequestHandlerFactory.class,ComponentType.REQUESTHANDLERFACTORY);
if(HANDLER_FACTORY == null)
throw new ServletException("service not available");
}
} }

View File

@ -18,16 +18,13 @@ package org.apache.lucene.gdata.servlet;
import java.io.IOException; import java.io.IOException;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException; import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.apache.lucene.gdata.servlet.handler.DefaultRequestHandlerFactory;
import org.apache.lucene.gdata.servlet.handler.GDataRequestHandler; import org.apache.lucene.gdata.servlet.handler.GDataRequestHandler;
import org.apache.lucene.gdata.servlet.handler.RequestHandlerFactory;
/** /**
* Provides a clean basic interface for GDATA Client API and requests to the * Provides a clean basic interface for GDATA Client API and requests to the
@ -39,7 +36,6 @@ import org.apache.lucene.gdata.servlet.handler.RequestHandlerFactory;
* *
*/ */
public class RequestControllerServlet extends AbstractGdataServlet { public class RequestControllerServlet extends AbstractGdataServlet {
private static RequestHandlerFactory HANDLER_FACTORY = null;
private static final Log LOGGER = LogFactory.getLog(RequestControllerServlet.class); private static final Log LOGGER = LogFactory.getLog(RequestControllerServlet.class);
/** /**
@ -54,9 +50,9 @@ public class RequestControllerServlet extends AbstractGdataServlet {
* javax.servlet.http.HttpServletResponse) * javax.servlet.http.HttpServletResponse)
*/ */
@Override @Override
protected void doDelete(HttpServletRequest arg0, HttpServletResponse arg1) protected void doDelete(HttpServletRequest arg0, HttpServletResponse arg1)
throws ServletException, IOException { throws ServletException, IOException {
GDataRequestHandler hanlder = HANDLER_FACTORY.getDeleteHandler(); GDataRequestHandler hanlder = HANDLER_FACTORY.getEntryDeleteHandler();
if(LOGGER.isInfoEnabled()) if(LOGGER.isInfoEnabled())
LOGGER.info("Process DELETE request"); LOGGER.info("Process DELETE request");
@ -68,12 +64,11 @@ public class RequestControllerServlet extends AbstractGdataServlet {
* javax.servlet.http.HttpServletResponse) * javax.servlet.http.HttpServletResponse)
*/ */
@Override @Override
protected void doGet(HttpServletRequest arg0, HttpServletResponse arg1) protected void doGet(HttpServletRequest arg0, HttpServletResponse arg1)
throws ServletException, IOException { throws ServletException, IOException {
GDataRequestHandler hanlder = HANDLER_FACTORY.getQueryHandler(); GDataRequestHandler hanlder = HANDLER_FACTORY.getFeedQueryHandler();
if(LOGGER.isInfoEnabled()) if(LOGGER.isInfoEnabled())
LOGGER.info("Process GET request"); LOGGER.info("Process GET request");
hanlder.processRequest(arg0, arg1); hanlder.processRequest(arg0, arg1);
} }
@ -82,9 +77,9 @@ public class RequestControllerServlet extends AbstractGdataServlet {
* javax.servlet.http.HttpServletResponse) * javax.servlet.http.HttpServletResponse)
*/ */
@Override @Override
protected void doPost(HttpServletRequest arg0, HttpServletResponse arg1) protected void doPost(HttpServletRequest arg0, HttpServletResponse arg1)
throws ServletException, IOException { throws ServletException, IOException {
GDataRequestHandler hanlder = HANDLER_FACTORY.getInsertHandler(); GDataRequestHandler hanlder = HANDLER_FACTORY.getEntryInsertHandler();
if(LOGGER.isInfoEnabled()) if(LOGGER.isInfoEnabled())
LOGGER.info("Process POST request"); LOGGER.info("Process POST request");
hanlder.processRequest(arg0, arg1); hanlder.processRequest(arg0, arg1);
@ -95,28 +90,13 @@ public class RequestControllerServlet extends AbstractGdataServlet {
* javax.servlet.http.HttpServletResponse) * javax.servlet.http.HttpServletResponse)
*/ */
@Override @Override
protected void doPut(HttpServletRequest arg0, HttpServletResponse arg1) protected void doPut(HttpServletRequest arg0, HttpServletResponse arg1)
throws ServletException, IOException { throws ServletException, IOException {
GDataRequestHandler hanlder = HANDLER_FACTORY.getUpdateHandler(); GDataRequestHandler hanlder = HANDLER_FACTORY.getEntryUpdateHandler();
if(LOGGER.isInfoEnabled()) if(LOGGER.isInfoEnabled())
LOGGER.info("Process PUT request"); LOGGER.info("Process PUT request");
hanlder.processRequest(arg0, arg1); hanlder.processRequest(arg0, arg1);
} }
/**
* @see javax.servlet.GenericServlet#init(javax.servlet.ServletConfig)
*/
@Override
public void init(ServletConfig arg0) {
/*
* The Factory implementation could be configured as an initial
* parameter or by an external config file.
*
*/
HANDLER_FACTORY = RequestHandlerFactory
.getInstance(DefaultRequestHandlerFactory.class);
}
} }

View File

@ -30,17 +30,19 @@ import org.apache.lucene.gdata.server.GDataResponse;
import org.apache.lucene.gdata.server.Service; import org.apache.lucene.gdata.server.Service;
import org.apache.lucene.gdata.server.ServiceFactory; import org.apache.lucene.gdata.server.ServiceFactory;
import org.apache.lucene.gdata.server.GDataRequest.GDataRequestType; import org.apache.lucene.gdata.server.GDataRequest.GDataRequestType;
import org.apache.lucene.gdata.server.registry.ComponentType;
import org.apache.lucene.gdata.server.registry.GDataServerRegistry;
/** /**
* @author Simon Willnauer * @author Simon Willnauer
* *
*/ */
public abstract class AbstractGdataRequestHandler implements public abstract class AbstractGdataRequestHandler extends RequestAuthenticator implements
GDataRequestHandler { GDataRequestHandler {
private final static Log LOG = LogFactory private final static Log LOG = LogFactory
.getLog(AbstractGdataRequestHandler.class); .getLog(AbstractGdataRequestHandler.class);
protected Service service;
protected GDataRequest feedRequest; protected GDataRequest feedRequest;
protected GDataResponse feedResponse; protected GDataResponse feedResponse;
@ -52,9 +54,10 @@ public abstract class AbstractGdataRequestHandler implements
HttpServletResponse response) throws ServletException, IOException; HttpServletResponse response) throws ServletException, IOException;
protected void initializeRequestHandler(final HttpServletRequest request, final HttpServletResponse response, final GDataRequestType type) protected void initializeRequestHandler(final HttpServletRequest request, final HttpServletResponse response, final GDataRequestType type)
throws GDataRequestException { throws GDataRequestException, ServletException {
this.feedRequest = new GDataRequest(request, type); this.feedRequest = new GDataRequest(request, type);
this.feedResponse = new GDataResponse(response); this.feedResponse = new GDataResponse(response);
getService();
try { try {
this.feedRequest.initializeRequest(); this.feedRequest.initializeRequest();
} catch (GDataRequestException e) { } catch (GDataRequestException e) {
@ -71,24 +74,29 @@ public abstract class AbstractGdataRequestHandler implements
} }
protected void setFeedResponseFormat() { protected void setFeedResponseFormat() {
this.feedResponse.setOutputFormat(this.feedRequest.getRequestedResponseFormat()); this.feedResponse.setOutputFormat(this.feedRequest.getRequestedResponseFormat());
} }
protected void setFeedResponseStatus(int status) { protected void setFeedResponseStatus(int status) {
this.feedResponse.setResponseCode(status); this.feedResponse.setResponseCode(status);
} }
protected void setError(int error) { protected void setError(int error) {
this.feedResponse.setError(error); this.feedResponse.setError(error);
} }
protected Service getService() throws ServletException { private void getService() throws ServletException {
ServiceFactory serviceFactory = ServiceFactory.getInstance(); GDataServerRegistry registry = GDataServerRegistry.getRegistry();
Service service = serviceFactory.getService(); ServiceFactory serviceFactory = registry.lookup(ServiceFactory.class,ComponentType.SERVICEFACTORY);
if(service == null) this.service = serviceFactory.getService();
if(this.service == null)
throw new ServletException("Service not available"); throw new ServletException("Service not available");
return service;
}
protected void closeService(){
this.service.close();
} }

View File

@ -23,9 +23,8 @@ import javax.servlet.http.HttpServletResponse;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.apache.lucene.gdata.server.FeedNotFoundException; import org.apache.lucene.gdata.data.GDataAccount.AccountRole;
import org.apache.lucene.gdata.server.GDataRequestException; import org.apache.lucene.gdata.server.GDataRequestException;
import org.apache.lucene.gdata.server.Service;
import org.apache.lucene.gdata.server.ServiceException; import org.apache.lucene.gdata.server.ServiceException;
import org.apache.lucene.gdata.server.GDataRequest.GDataRequestType; import org.apache.lucene.gdata.server.GDataRequest.GDataRequestType;
@ -46,39 +45,39 @@ import org.apache.lucene.gdata.server.GDataRequest.GDataRequestType;
* *
*/ */
public class DefaultDeleteHandler extends AbstractGdataRequestHandler { public class DefaultDeleteHandler extends AbstractGdataRequestHandler {
private static final Log LOG = LogFactory private static final Log LOG = LogFactory
.getLog(DefaultDeleteHandler.class); .getLog(DefaultDeleteHandler.class);
/** /**
* @throws ServletException * @throws ServletException
* @see org.apache.lucene.gdata.servlet.handler.AbstractGdataRequestHandler#processRequest(javax.servlet.http.HttpServletRequest, * @see org.apache.lucene.gdata.servlet.handler.AbstractGdataRequestHandler#processRequest(javax.servlet.http.HttpServletRequest,
* javax.servlet.http.HttpServletResponse) * javax.servlet.http.HttpServletResponse)
*/ */
@Override @Override
public void processRequest(HttpServletRequest request, public void processRequest(HttpServletRequest request,
HttpServletResponse response) throws IOException, ServletException { HttpServletResponse response) throws IOException, ServletException {
try { try {
initializeRequestHandler(request, response,GDataRequestType.DELETE); initializeRequestHandler(request, response,GDataRequestType.DELETE);
} catch (GDataRequestException e) { } catch (GDataRequestException e) {
sendError();
return;
}
if(!authenticateAccount(request,AccountRole.ENTRYAMINISTRATOR)){
setError(HttpServletResponse.SC_UNAUTHORIZED);
sendError(); sendError();
return; return;
} }
Service service = getService(); try {
try { this.service.deleteEntry(this.feedRequest, this.feedResponse);
service.deleteEntry(this.feedRequest, this.feedResponse);
} catch (FeedNotFoundException e) {
LOG.error("Could not process DeleteFeed request Feed Not Found- "
+ e.getMessage(), e);
setError(HttpServletResponse.SC_NOT_FOUND);
sendError();
} catch (ServiceException e) {
LOG.error("Could not process DeleteFeed request - "
+ e.getMessage(), e);
setError(HttpServletResponse.SC_BAD_REQUEST);
sendError();
}
} } catch (ServiceException e) {
LOG.error("Could not process DeleteFeed request - "
+ e.getMessage(), e);
sendError();
}
closeService();
}
} }

View File

@ -17,6 +17,7 @@
package org.apache.lucene.gdata.servlet.handler; package org.apache.lucene.gdata.servlet.handler;
import java.io.IOException; import java.io.IOException;
import java.util.Date;
import javax.servlet.ServletException; import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
@ -28,6 +29,7 @@ import org.apache.lucene.gdata.server.GDataRequestException;
import org.apache.lucene.gdata.server.Service; import org.apache.lucene.gdata.server.Service;
import org.apache.lucene.gdata.server.ServiceException; import org.apache.lucene.gdata.server.ServiceException;
import org.apache.lucene.gdata.server.GDataRequest.GDataRequestType; import org.apache.lucene.gdata.server.GDataRequest.GDataRequestType;
import org.apache.lucene.gdata.utils.DateFormater;
import com.google.gdata.data.BaseEntry; import com.google.gdata.data.BaseEntry;
import com.google.gdata.data.BaseFeed; import com.google.gdata.data.BaseFeed;
@ -42,6 +44,13 @@ import com.google.gdata.data.BaseFeed;
* {@link org.apache.lucene.gdata.server.GDataRequest} instance passed to the * {@link org.apache.lucene.gdata.server.GDataRequest} instance passed to the
* {@link Service} class. * {@link Service} class.
* </p> * </p>
* <p>
* The DefaultGetHandler supports HTTP Conditional GET. It set the Last-Modified
* response header based upon the value of the <atom:updated> element in the
* returned feed or entry. A client can send this value back as the value of the
* If-Modified-Since request header to avoid retrieving the content again if it
* hasn't changed. If the content hasn't changed since the If-Modified-Since
* time, then the GData service returns a 304 (Not Modified) HTTP response.</p>
* *
* *
* @author Simon Willnauer * @author Simon Willnauer
@ -63,42 +72,67 @@ public class DefaultGetHandler extends AbstractGdataRequestHandler {
sendError(); sendError();
return; return;
} }
Service service = getService();
try { try {
String modifiedSince = this.feedRequest.getModifiedSince();
if (!checkIsModified(modifiedSince)) {
this.feedResponse
.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
return;
}
if (LOG.isInfoEnabled()) if (LOG.isInfoEnabled())
LOG.info("Requested output formate: " LOG.info("Requested output formate: "
+ this.feedRequest.getRequestedResponseFormat()); + this.feedRequest.getRequestedResponseFormat());
this.feedResponse.setOutputFormat(this.feedRequest this.feedResponse.setOutputFormat(this.feedRequest
.getRequestedResponseFormat()); .getRequestedResponseFormat());
if(this.feedRequest.isFeedRequested()){ if (this.feedRequest.isFeedRequested()) {
BaseFeed feed = service BaseFeed feed = this.service.getFeed(this.feedRequest,
.getFeed(this.feedRequest, this.feedResponse); this.feedResponse);
this.feedResponse.sendResponse(feed, this.feedRequest.getExtensionProfile()); this.feedResponse.sendResponse(feed, this.feedRequest
}else{ .getConfigurator().getExtensionProfile());
BaseEntry entry = service.getSingleEntry(this.feedRequest,this.feedResponse); } else {
if(entry == null){ BaseEntry entry = this.service.getSingleEntry(this.feedRequest,
this.feedResponse.setError(HttpServletResponse.SC_NOT_FOUND); this.feedResponse);
sendError(); this.feedResponse.sendResponse(entry, this.feedRequest
} .getConfigurator().getExtensionProfile());
this.feedResponse.sendResponse(entry, this.feedRequest.getExtensionProfile());
} }
} catch (ServiceException e) {
} catch (ServiceException e) { // TODO handle exceptions to send exact
// response
LOG.error("Could not process GetFeed request - " + e.getMessage(), LOG.error("Could not process GetFeed request - " + e.getMessage(),
e); e);
this.feedResponse.setError(HttpServletResponse.SC_BAD_REQUEST); // TODO
// change
// this
sendError(); sendError();
} }
closeService();
} }
/**
*
* returns true if the resource has been modified since the specified
* reqeust header value
*/
private boolean checkIsModified(String lastModified)
throws ServiceException {
if (lastModified == null)
return true;
try {
Date clientDate = DateFormater.parseDate(lastModified,DateFormater.HTTP_HEADER_DATE_FORMAT,DateFormater.HTTP_HEADER_DATE_FORMAT_TIME_OFFSET);
Date entityDate;
if (this.feedRequest.isFeedRequested())
entityDate = this.service.getFeedLastModified(this.feedRequest
.getFeedId());
else
entityDate = this.service.getEntryLastModified(this.feedRequest
.getEntryId(),this.feedRequest.getFeedId());
if(LOG.isInfoEnabled())
LOG.info("comparing date clientDate: "+clientDate+"; lastmodified: "+entityDate);
return (entityDate.getTime()-clientDate.getTime() > 1000);
} catch (java.text.ParseException e) {
LOG.info("Couldn't parse Last-Modified header -- "+lastModified,e);
}
return true;
}
} }

View File

@ -24,8 +24,8 @@ import javax.servlet.http.HttpServletResponse;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.apache.lucene.gdata.data.GDataAccount.AccountRole;
import org.apache.lucene.gdata.server.GDataRequestException; import org.apache.lucene.gdata.server.GDataRequestException;
import org.apache.lucene.gdata.server.Service;
import org.apache.lucene.gdata.server.ServiceException; import org.apache.lucene.gdata.server.ServiceException;
import org.apache.lucene.gdata.server.GDataRequest.GDataRequestType; import org.apache.lucene.gdata.server.GDataRequest.GDataRequestType;
@ -55,7 +55,7 @@ public class DefaultInsertHandler extends AbstractGdataRequestHandler {
* @see org.apache.lucene.gdata.servlet.handler.GDataRequestHandler#processRequest(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) * @see org.apache.lucene.gdata.servlet.handler.GDataRequestHandler#processRequest(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
*/ */
@Override @Override
public void processRequest(HttpServletRequest request, public void processRequest(HttpServletRequest request,
HttpServletResponse response) throws IOException, ServletException { HttpServletResponse response) throws IOException, ServletException {
try { try {
initializeRequestHandler(request,response,GDataRequestType.INSERT); initializeRequestHandler(request,response,GDataRequestType.INSERT);
@ -63,20 +63,23 @@ public class DefaultInsertHandler extends AbstractGdataRequestHandler {
sendError(); sendError();
return; return;
} }
if(!authenticateAccount(this.feedRequest,AccountRole.ENTRYAMINISTRATOR)){
setError(HttpServletResponse.SC_UNAUTHORIZED);
sendError();
return;
}
Service service = getService();
try{ try{
BaseEntry entry = service.createEntry(this.feedRequest,this.feedResponse); BaseEntry entry = this.service.createEntry(this.feedRequest,this.feedResponse);
setFeedResponseFormat(); setFeedResponseFormat();
setFeedResponseStatus(HttpServletResponse.SC_CREATED); setFeedResponseStatus(HttpServletResponse.SC_CREATED);
this.feedResponse.sendResponse(entry, this.feedRequest.getExtensionProfile()); this.feedResponse.sendResponse(entry, this.feedRequest.getConfigurator().getExtensionProfile());
}catch (ServiceException e) { }catch (ServiceException e) {
LOG.error("Could not process GetFeed request - "+e.getMessage(),e); LOG.error("Could not process GetFeed request - "+e.getMessage(),e);
setError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
this.feedResponse.sendError(); this.feedResponse.sendError();
} }
closeService();
} }

View File

@ -16,54 +16,134 @@
package org.apache.lucene.gdata.servlet.handler; package org.apache.lucene.gdata.servlet.handler;
import org.apache.lucene.gdata.server.registry.Component;
import org.apache.lucene.gdata.server.registry.ComponentType;
/** /**
* Default implementation for RequestHandlerFactory Builds the * Default implementation for RequestHandlerFactory Builds the
* {@link org.apache.lucene.gdata.servlet.handler.GDataRequestHandler} * {@link org.apache.lucene.gdata.servlet.handler.GDataRequestHandler}
* instances. * instances.
* This class should not be access directy. The class will be registered in the {@link org.apache.lucene.gdata.server.registry.GDataServerRegistry}.
* Use {@link org.apache.lucene.gdata.server.registry.GDataServerRegistry#lookup(Class, ComponentType)}
* *
* @author Simon Willnauer * @author Simon Willnauer
* *
*/ */
@Component(componentType=ComponentType.REQUESTHANDLERFACTORY)
public class DefaultRequestHandlerFactory extends RequestHandlerFactory { public class DefaultRequestHandlerFactory extends RequestHandlerFactory {
DefaultRequestHandlerFactory() {
/**
* public constructor to enable loading via the registry
* @see org.apache.lucene.gdata.server.registry.Component
* @see org.apache.lucene.gdata.server.registry.GDataServerRegistry
*/
public DefaultRequestHandlerFactory() {
//
}
/**
* @see org.apache.lucene.gdata.servlet.handler.RequestHandlerFactory#getEntryUpdateHandler()
*/
@Override
public GDataRequestHandler getEntryUpdateHandler() {
return new DefaultUpdateHandler();
}
/**
* @see org.apache.lucene.gdata.servlet.handler.RequestHandlerFactory#getEntryDeleteHandler()
*/
@Override
public GDataRequestHandler getEntryDeleteHandler() {
return new DefaultDeleteHandler();
}
/**
* @see org.apache.lucene.gdata.servlet.handler.RequestHandlerFactory#getFeedQueryHandler()
*/
@Override
public GDataRequestHandler getFeedQueryHandler() {
return new DefaultGetHandler();
}
/**
* @see org.apache.lucene.gdata.servlet.handler.RequestHandlerFactory#getEntryInsertHandler()
*/
@Override
public GDataRequestHandler getEntryInsertHandler() {
return new DefaultInsertHandler();
}
/**
* @see org.apache.lucene.gdata.servlet.handler.RequestHandlerFactory#getInsertAccountHandler()
*/
@Override
public GDataRequestHandler getInsertAccountHandler() {
return new InsertAccountStrategy();
}
/**
* @see org.apache.lucene.gdata.servlet.handler.RequestHandlerFactory#getDeleteAccountHandler()
*/
@Override
public GDataRequestHandler getDeleteAccountHandler() {
return new DeleteAccountStrategy();
}
/**
* @see org.apache.lucene.gdata.servlet.handler.RequestHandlerFactory#getUpdateAccountHandler()
*/
@Override
public GDataRequestHandler getUpdateAccountHandler() {
return new UpdataAccountStrategy();
}
/**
* @see org.apache.lucene.gdata.servlet.handler.RequestHandlerFactory#getInsertFeedHandler()
*/
@Override
public GDataRequestHandler getInsertFeedHandler() {
return new InsertFeedHandler();
}
/**
* @see org.apache.lucene.gdata.servlet.handler.RequestHandlerFactory#getUpdateFeedHandler()
*/
@Override
public GDataRequestHandler getUpdateFeedHandler() {
return new UpdateFeedHandler();
}
/**
* @see org.apache.lucene.gdata.servlet.handler.RequestHandlerFactory#getDeleteFeedHandler()
*/
@Override
public GDataRequestHandler getDeleteFeedHandler() {
return new DeleteFeedHandler();
}
/**
* @see org.apache.lucene.gdata.server.registry.ServerComponent#initialize()
*/
public void initialize() {
// //
} }
/** /**
* @see org.apache.lucene.gdata.servlet.handler.RequestHandlerFactory#getUpdateHandler() * @see org.apache.lucene.gdata.server.registry.ServerComponent#destroy()
*/ */
@Override public void destroy() {
public GDataRequestHandler getUpdateHandler() { //
return new DefaultUpdateHandler();
}
/**
* @see org.apache.lucene.gdata.servlet.handler.RequestHandlerFactory#getDeleteHandler()
*/
@Override
public GDataRequestHandler getDeleteHandler() {
return new DefaultDeleteHandler();
}
/**
* @see org.apache.lucene.gdata.servlet.handler.RequestHandlerFactory#getQueryHandler()
*/
@Override
public GDataRequestHandler getQueryHandler() {
return new DefaultGetHandler();
}
/**
* @see org.apache.lucene.gdata.servlet.handler.RequestHandlerFactory#getInsertHandler()
*/
@Override
public GDataRequestHandler getInsertHandler() {
return new DefaultInsertHandler();
} }
} }

View File

@ -23,9 +23,8 @@ import javax.servlet.http.HttpServletResponse;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.apache.lucene.gdata.server.FeedNotFoundException; import org.apache.lucene.gdata.data.GDataAccount.AccountRole;
import org.apache.lucene.gdata.server.GDataRequestException; import org.apache.lucene.gdata.server.GDataRequestException;
import org.apache.lucene.gdata.server.Service;
import org.apache.lucene.gdata.server.ServiceException; import org.apache.lucene.gdata.server.ServiceException;
import org.apache.lucene.gdata.server.GDataRequest.GDataRequestType; import org.apache.lucene.gdata.server.GDataRequest.GDataRequestType;
@ -48,47 +47,43 @@ import com.google.gdata.data.BaseEntry;
* *
*/ */
public class DefaultUpdateHandler extends AbstractGdataRequestHandler { public class DefaultUpdateHandler extends AbstractGdataRequestHandler {
private static final Log LOG = LogFactory private static final Log LOG = LogFactory
.getLog(DefaultUpdateHandler.class); .getLog(DefaultUpdateHandler.class);
/** /**
* @throws ServletException * @throws ServletException
* @see org.apache.lucene.gdata.servlet.handler.AbstractGdataRequestHandler#processRequest(javax.servlet.http.HttpServletRequest, * @see org.apache.lucene.gdata.servlet.handler.AbstractGdataRequestHandler#processRequest(javax.servlet.http.HttpServletRequest,
* javax.servlet.http.HttpServletResponse) * javax.servlet.http.HttpServletResponse)
*/ */
@Override @Override
public void processRequest(HttpServletRequest request, public void processRequest(HttpServletRequest request,
HttpServletResponse response) throws IOException, ServletException { HttpServletResponse response) throws IOException, ServletException {
try { try {
initializeRequestHandler(request, response,GDataRequestType.UPDATE); initializeRequestHandler(request, response,GDataRequestType.UPDATE);
} catch (GDataRequestException e) { } catch (GDataRequestException e) {
setError(HttpServletResponse.SC_UNAUTHORIZED);
sendError();
return;
}
if(!authenticateAccount(request,AccountRole.ENTRYAMINISTRATOR)){
sendError(); sendError();
return; return;
} }
Service service = getService(); try {
try { BaseEntry entry = this.service.updateEntry(this.feedRequest,
BaseEntry entry = service.updateEntry(this.feedRequest, this.feedResponse);
this.feedResponse); setFeedResponseFormat();
setFeedResponseFormat(); setFeedResponseStatus(HttpServletResponse.SC_OK);
setFeedResponseStatus(HttpServletResponse.SC_OK); this.feedResponse.sendResponse(entry, this.feedRequest.getConfigurator().getExtensionProfile());
this.feedResponse.sendResponse(entry, this.feedRequest.getExtensionProfile());
}catch (FeedNotFoundException e) {
LOG.error("Could not process UpdateFeed request - "
+ e.getMessage(), e);
setError(HttpServletResponse.SC_NOT_FOUND);
sendError(); }
} catch (ServiceException e) {
catch (ServiceException e) { LOG.error("Could not process UpdateFeed request - "
+ e.getMessage(), e);
LOG.error("Could not process UpdateFeed request - " sendError();
+ e.getMessage(), e); }
setError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); closeService();
}
sendError();
}
}
} }

View File

@ -16,107 +16,83 @@
package org.apache.lucene.gdata.servlet.handler; package org.apache.lucene.gdata.servlet.handler;
import org.apache.lucene.gdata.server.registry.ServerComponent;
/** /**
* Abstract Superclass for RequestHandlerFactories
* @author Simon Willnauer * @author Simon Willnauer
* *
*/ */
public abstract class RequestHandlerFactory { public abstract class RequestHandlerFactory implements ServerComponent {
private static RequestHandlerFactory INSTANCE = null;
/** /**
* This method creates a singleton instance of the given type. The fist call * public constructor to enable loading via the registry
* will create an instance of the given class which will be returned in * @see org.apache.lucene.gdata.server.registry.Component
* every subsequent call. Any subsequent call to this method will ignore the * @see org.apache.lucene.gdata.server.registry.GDataServerRegistry
* given class object.
*
* @param factoryImplementation -
* the factory implementation (must be a subtype of this Class)
*
* @return - a singleton instance of the given type
*
*/ */
public static synchronized RequestHandlerFactory getInstance( public RequestHandlerFactory() {
Class factoryImplementation) {
if (INSTANCE == null) {
INSTANCE = createInstance(factoryImplementation);
}
return INSTANCE;
}
/**
* Singleton - Pattern using private constructor
*
*/
RequestHandlerFactory() {
super(); super();
} }
private static RequestHandlerFactory createInstance(
final Class qualifiedClass) {
if (qualifiedClass == null)
throw new IllegalArgumentException(
"Factory class is null -- must be a implementation of org.apache.lucene.gdata.servlet.handler.RequestHandlerFactory");
try {
return (RequestHandlerFactory) qualifiedClass.newInstance();
} catch (Exception e) {
FactoryImplementationException ex = new FactoryImplementationException(
"Factory implementation could not be created", e.getCause());
ex.setStackTrace(e.getStackTrace());
throw ex;
}
}
/** /**
* Creates a UpdateHandler which processes a GDATA UPDATE request. * Creates a EntryUpdateHandler which processes a GDATA UPDATE request.
* @return - an RequestHandlerInstance * @return - a RequestHandlerInstance
*/ */
public abstract GDataRequestHandler getUpdateHandler(); public abstract GDataRequestHandler getEntryUpdateHandler();
/** /**
* Creates a DeleteHandler which processes a GDATA DELETE request. * Creates a EntryDeleteHandler which processes a GDATA DELETE request.
* @return - an RequestHandlerInstance * @return - a RequestHandlerInstance
*/ */
public abstract GDataRequestHandler getDeleteHandler(); public abstract GDataRequestHandler getEntryDeleteHandler();
/** /**
* Creates a QueryHandler which processes a GDATA Query / Get request. * Creates a FeedQueryHandler which processes a GDATA Query / Get request.
* @return - an RequestHandlerInstance * @return - a RequestHandlerInstance
*/ */
public abstract GDataRequestHandler getQueryHandler(); public abstract GDataRequestHandler getFeedQueryHandler();
/** /**
* Creates a InsertHandler which processes a GDATA Insert request. * Creates a EntryInsertHandler which processes a GDATA Insert request.
* @return - an RequestHandlerInstance * @return - a RequestHandlerInstance
*/ */
public abstract GDataRequestHandler getInsertHandler(); public abstract GDataRequestHandler getEntryInsertHandler();
/**
* Creates a InsertAccountHandler which processes a Account Insert request.
* @return - a RequestHandlerInstance
*/
public abstract GDataRequestHandler getInsertAccountHandler();
/**
* Creates a DeleteAccountHandler which processes a Account Delete request.
* @return - a RequestHandlerInstance
*/
public abstract GDataRequestHandler getDeleteAccountHandler();
/**
* Creates a UpdateAccountHandler which processes a Account Update request.
* @return - a RequestHandlerInstance
*/
public abstract GDataRequestHandler getUpdateAccountHandler();
/**
* Creates a InsertFeedHandler which processes a Feed Insert request.
* @return - a RequestHandlerInstance
*/
public abstract GDataRequestHandler getInsertFeedHandler();
/**
* Creates a UpdateFeedHandler which processes a Feed Insert request.
* @return - a RequestHandlerInstance
*/
public abstract GDataRequestHandler getUpdateFeedHandler();
/**
* Creates a DeleteFeedHandler which processes a Feed Insert request.
* @return - a RequestHandlerInstance
*/
public abstract GDataRequestHandler getDeleteFeedHandler();
private static class FactoryImplementationException extends
RuntimeException {
/**
*
*/
private static final long serialVersionUID = 3166033278825112569L;
/**
* Constructs a new FactoryImplementationException with the specified
* cause and message
*
* @param arg0 -
* the detail message
* @param arg1 -
* the throw cause
*/
public FactoryImplementationException(String arg0, Throwable arg1) {
super(arg0, arg1);
}
}
} }

View File

@ -53,6 +53,8 @@ public class IDGenerator {
protected static final Log LOGGER = LogFactory.getLog(IDGenerator.class); protected static final Log LOGGER = LogFactory.getLog(IDGenerator.class);
private static final String RUNNER_THREAD_NAME = "GDATA-ID Generator";
/** /**
* Constructs a new ID generator. with a fixed capacity of prebuild ids. The * Constructs a new ID generator. with a fixed capacity of prebuild ids. The
* default capacity is 10. Every given parameter less than 10 will be * default capacity is 10. Every given parameter less than 10 will be
@ -92,6 +94,8 @@ public class IDGenerator {
UIDProducer producer = new UIDProducer(this.blockingQueue, UIDProducer producer = new UIDProducer(this.blockingQueue,
this.secureRandom, this.mdigest); this.secureRandom, this.mdigest);
this.runner = new Thread(producer); this.runner = new Thread(producer);
this.runner.setDaemon(true);
this.runner.setName(RUNNER_THREAD_NAME);
this.runner.start(); this.runner.start();
} }
} }

View File

@ -15,86 +15,260 @@
*/ */
package org.apache.lucene.gdata.storage; package org.apache.lucene.gdata.storage;
import java.util.List; import org.apache.lucene.gdata.data.GDataAccount;
import org.apache.lucene.gdata.data.ServerBaseEntry;
import org.apache.lucene.gdata.data.ServerBaseFeed;
import com.google.gdata.data.BaseEntry; import com.google.gdata.data.BaseEntry;
import com.google.gdata.data.BaseFeed; import com.google.gdata.data.BaseFeed;
import com.google.gdata.data.ExtensionProfile;
/** /**
* This is the main storage interface. The Storage represents the internal * A interface every storage implementation must provide to access the
* server storage. It acts as a Database to persist the feed data. * <tt>Storage</tt>. It describes all access methodes needed to store,
* This inferface is not public yet!! * retrieve and look up data stored in the <tt>Storage</tt> component. This
* interface acts as a <tt>Facade</tt> to hide the storage implementation from
* the user.
* <p>
* This could also act as a proxy for a remote storage. It also removes any
* restrictions from custom storage implementations.
* </p>
*
* *
* @author Simon Willnauer * @author Simon Willnauer
* *
*/ */
/*
* not final yet
*/
public interface Storage { public interface Storage {
/** /**
* This stores an incoming entry for a later retrival. *
* The Entry will be associated with the feedid. * Stores the given entry. The ServerBaseEntry must provide a feed id and
* @param entry - the entry * the service type. configuration for the entry.
* @param feedId - the feedID *
* @return - the stored Entry * @param entry -
* @throws StorageException * the entry to store
*
* @return - the stored Entry for the server response
* @throws StorageException -
* if the entry can not be stored or required field are not set.
*/ */
public abstract BaseEntry storeEntry(BaseEntry entry, String feedId) public abstract BaseEntry storeEntry(ServerBaseEntry entry)
throws StorageException; throws StorageException;
/** /**
* @param entryId * Deletes the given entry. The ServerBaseEntry just hase to provide the
* @param feedId * entry id to be deleted.
* @throws StorageException *
* @param entry -
* the entry to delete from the storage
* @throws StorageException -
* if the entry can not be deleted or the entry does not exist
* or required field are not set.
*/ */
public abstract void deleteEntry(String entryId, String feedId) public abstract void deleteEntry(ServerBaseEntry entry)
throws StorageException; throws StorageException;
/** /**
* @param entry * Updates the given entry. The ServerBaseEntry must provide a feed id,
* @param feedId * service id and the
* @return * {@link org.apache.lucene.gdata.server.registry.ProvidedService}
* @throws StorageException *
* @param entry -
* the entry to update
*
* @return - the updated entry for server response.
* @throws StorageException -
* if the entry can not be updated or does not exist or required
* field are not set.
*/ */
public abstract BaseEntry updateEntry(BaseEntry entry, String feedId) public abstract BaseEntry updateEntry(ServerBaseEntry entry)
throws StorageException; throws StorageException;
/** /**
* @param feedId * Retrieves the requested feed from the storage. The given ServerBaseFeed
* @param startIndex * must provide information about the feed id, max-result count and the
* @param resultCount * start index. To create feeds and entries also the service type must be
* @return * provided.
* @throws StorageException *
* @param feed -
* the to retieve from the storage
* @return the requested feed
* @throws StorageException -
* the feed does not exist or can not be retrieved or required
* field are not set.
*/ */
public abstract BaseFeed getFeed(String feedId, int startIndex, public abstract BaseFeed getFeed(ServerBaseFeed feed)
int resultCount) throws StorageException;
/**
* @param entryId
* @param feedId
* @return
* @throws StorageException
*/
public abstract BaseEntry getEntry(String entryId, String feedId)
throws StorageException; throws StorageException;
/** /**
* @param entryIdList * Retrieves the requested entry from the storage. The given entry must
* @param feedId * provide information about the entry id and service type.
* @return *
* @throws StorageException * @param entry -
* the entry to retrieve
* @return - the requested entry
* @throws StorageException -
* if the entry does not exist or can not be created or required
* field are not set.
*/ */
public abstract List<BaseEntry> getEntries(List<String> entryIdList, public abstract BaseEntry getEntry(ServerBaseEntry entry)
String feedId) throws StorageException; throws StorageException;
/** /**
* @param profile * Saves a new account. Required attributes to set are <tt>password</tt>
* and <tt>accountname</tt>
*
* @param account -
* the account to save
* @throws StorageException -
* if the account can not be stored or the account already
* exists or required field are not set.
*/ */
public abstract void setExtensionProfile(final ExtensionProfile profile); public abstract void storeAccount(final GDataAccount account)
throws StorageException;
/** /**
* close this storage instance * Updates an existing account. Required attributes to set are
* <tt>password</tt> and <tt>accountname</tt>
*
* @param account -
* the account to update
* @throws StorageException -
* if the account does not exist or required field are not set.
*/
public abstract void updateAccount(final GDataAccount account)
throws StorageException;
/**
* Deletes the account for the given account name. All feeds and entries
* referencing this account will be deleted as well!
*
* @param accountname -
* the name of the account to delete
* @throws StorageException -
* if the account does not exist
*/
public abstract void deleteAccount(final String accountname)
throws StorageException;
/**
* Stores a new feed for a existing account. The Feed must provide
* information about the service type to store the feed for and the feed id
* used for accessing and retrieving the feed from the storage. Each feed is
* associated with a provided service. This method does check wheather a
* feed with the same feed id as the given feed does already exists.
*
* @see org.apache.lucene.gdata.server.registry.ProvidedService
* @param feed -
* the feed to create
* @param accountname -
* the account name belongs to the feed
* @throws StorageException -
* if the feed already exists or the feed can not be stored
*/
public abstract void storeFeed(final ServerBaseFeed feed, String accountname)
throws StorageException;
/**
* Deletes the feed for the given feed id. All Entries referencing the given
* feed id will be deleted as well.
*
* @param feedId -
* the feed id for the feed to delete.
* @throws StorageException -
* if the feed for the feed id does not exist or the feed can
* not be deleted
*/
public abstract void deleteFeed(final String feedId)
throws StorageException;
/**
* Updates a stored feed. The Feed must provide information about the
* service type to store the feed for and the feed id used for accessing and
* retrieving the feed from the storage.
*
* @param feed -
* the feed to update
* @param accountname -
* the account name belongs to the feed
* @throws StorageException -
* if the feed does not exist or the feed can not be updated
*/
public abstract void updateFeed(final ServerBaseFeed feed,
String accountname) throws StorageException;
/**
* Retrieves the service name for a stored feed
*
* @param feedId -
* the feed id
* @return - the name of the service
* @throws StorageException -
* if no feed for the provided id is stored
*/
public abstract String getServiceForFeed(String feedId)
throws StorageException;
/**
* @param accountName -
* the name of the requested account
* @return - a {@link GDataAccount} instance for the requested account name
* @throws StorageException -
* if no account for the account name is stored
*
*/
public abstract GDataAccount getAccount(String accountName)
throws StorageException;
/**
* close this storage instance. This method will be called by clients after
* use.
*/ */
public abstract void close(); public abstract void close();
/**
* Each feed belongs to one specific account. This method retrieves the
* account name for
*
* @param feedId -
* the id of the feed to retrieve the accountname
* @return - the name / id of the account associated with the feed for the
* given feed id
* @throws StorageException -
* if the feed is not stored or the storage can not be accessed
*/
public String getAccountNameForFeedId(String feedId)
throws StorageException;
/**
* Retrieves the date of the last modification for the given id
*
* @param entryId -
* the entry Id
* @param feedId -
* the feed which contains the entry
* @return - The date of the last modifiaction in milliseconds or
* <code>new Long(0)</code> if the resource can not be found eg.
* the time can not be accessed
* @throws StorageException -
* if the storage can not be accessed
*/
public Long getEntryLastModified(String entryId, String feedId)
throws StorageException;
/**
* Retrieves the date of the last modification for the given id
*
* @param feedId -
* the feed Id
* @return - The date of the last modifiaction in milliseconds or
* <code>new Long(0)</code> if the resource can not be found eg.
* the time can not be accessed
* @throws StorageException -
* if the storage can not be accessed
*/
public Long getFeedLastModified(String feedId) throws StorageException;
} }

View File

@ -15,13 +15,47 @@
*/ */
package org.apache.lucene.gdata.storage; package org.apache.lucene.gdata.storage;
import org.apache.lucene.gdata.server.registry.ServerComponent;
/** /**
* An interface to define a central storage controller acting as a
* <tt>Stroage</tt> Factory. The <tt>StroageController</tt> manages the
* storage logic. Subclasses of {@link StorageController} can be registered as
* {@link org.apache.lucene.gdata.server.registry.Component} in the
* {@link org.apache.lucene.gdata.server.registry.GDataServerRegistry}. A
* single instance of the contorller will be loaded and passed to clients via
* the lookup service.
* <p>
* This instances, registered in the registry must be thread save as they are
* shared between several clients
* </p>
* <p>
* Each StroageController implementation must provide a super user
* {@link org.apache.lucene.gdata.data.GDataAccount} with all
* {@link org.apache.lucene.gdata.data.GDataAccount.AccountRole} set. This
* account must have the defined name <i>administrator</i> and a default
* password <i>password</i>. The password has to be updated by the server
* administrator before production use.
* To get the predefinded GDataAccount use {@link org.apache.lucene.gdata.data.GDataAccount#createAdminAccount()}
* </p>
*
*
* @author Simon Willnauer * @author Simon Willnauer
* *
*/ */
public interface StorageController { public interface StorageController extends ServerComponent {
/** /**
* Destroys the controller * Destroys the controller - this method is called by the registry when the
*/ * context will be destroyed
public abstract void destroy(); */
public abstract void destroy();
/**
* Creates Storage instances to access the underlaying storage component
*
* @return a storage instance
* @throws StorageException -
* if the storage instance can not be created
*/
public abstract Storage getStorage() throws StorageException;
} }

View File

@ -1,44 +0,0 @@
/**
* Copyright 2004 The Apache Software Foundation
*
* 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 org.apache.lucene.gdata.storage;
import java.io.IOException;
import org.apache.lucene.gdata.storage.lucenestorage.StorageImplementation;
/**
*TODO document me
* @author Simon Willnauer
*
*/
public class StorageFactory {
/**
* Creates a {@link Storage} instance
* @return - a storage instance
* @throws StorageException - if the storage can not be created
*/
public static Storage getStorage()throws StorageException{
try {
return new StorageImplementation();
} catch (IOException e) {
StorageException ex = new StorageException("Can't create Storage instance -- "
+ e.getMessage(), e);
ex.setStackTrace(e.getStackTrace());
throw ex;
}
}
}

View File

@ -20,8 +20,11 @@ import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;
import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock;
@ -30,6 +33,10 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.apache.lucene.gdata.storage.lucenestorage.StorageEntryWrapper.StorageOperation; import org.apache.lucene.gdata.storage.lucenestorage.StorageEntryWrapper.StorageOperation;
import com.google.gdata.data.BaseEntry;
import com.google.gdata.data.ExtensionProfile;
import com.google.gdata.data.Link;
/** /**
* The StorageBuffer is used to buffer incoming updates, deletes and inserts to * The StorageBuffer is used to buffer incoming updates, deletes and inserts to
* the storage. The storage uses an lucene index to store the enries. As * the storage. The storage uses an lucene index to store the enries. As
@ -57,6 +64,8 @@ public class StorageBuffer {
private final Map<String, Map<String, StorageEntryWrapper>> bufferMap; private final Map<String, Map<String, StorageEntryWrapper>> bufferMap;
private final Map<String, Long> modifiyMap;
private final List<String> excludeList; private final List<String> excludeList;
private final ReadWriteLock lock = new ReentrantReadWriteLock(true); private final ReadWriteLock lock = new ReentrantReadWriteLock(true);
@ -86,6 +95,9 @@ public class StorageBuffer {
this.excludeList = new ArrayList<String>( this.excludeList = new ArrayList<String>(
expectedBufferCount < DEFAULT_BUFFER_COUNT ? DEFAULT_BUFFER_COUNT expectedBufferCount < DEFAULT_BUFFER_COUNT ? DEFAULT_BUFFER_COUNT
: expectedBufferCount); : expectedBufferCount);
this.modifiyMap = new HashMap<String, Long>(
expectedBufferCount < DEFAULT_BUFFER_COUNT ? DEFAULT_BUFFER_COUNT
: expectedBufferCount);
} }
/** /**
@ -114,7 +126,9 @@ public class StorageBuffer {
20); 20);
newFeedMap.put(wrapper.getEntryId(), wrapper); newFeedMap.put(wrapper.getEntryId(), wrapper);
this.bufferMap.put(feedId, newFeedMap); this.bufferMap.put(feedId, newFeedMap);
} }
addLastModified(wrapper.getFeedId(),wrapper.getTimestamp());
} finally { } finally {
/* /*
* add all to exclude from searches doc will be available via the * add all to exclude from searches doc will be available via the
@ -125,6 +139,22 @@ public class StorageBuffer {
} }
} }
private void addLastModified(final String feedId,Long timestamp){
if(this.modifiyMap.containsKey(feedId))
this.modifiyMap.remove(feedId);
this.modifiyMap.put(feedId,timestamp);
}
protected Long getFeedLastModified(final String feedId){
return this.modifiyMap.get(feedId);
}
protected Set<Entry<String,Long>> getLastModified(){
return this.modifiyMap.entrySet();
}
/** /**
* Returns all entries for the given feed id sorted by the update timestamp * Returns all entries for the given feed id sorted by the update timestamp
* desc. * desc.
@ -173,6 +203,7 @@ public class StorageBuffer {
if (tempMap == null) if (tempMap == null)
return; return;
tempMap.remove(entryId); tempMap.remove(entryId);
this.addLastModified(feedId,new Long(System.currentTimeMillis()));
} finally { } finally {
this.writeLock.unlock(); this.writeLock.unlock();
@ -230,6 +261,7 @@ public class StorageBuffer {
private void clearBuffer() { private void clearBuffer() {
this.bufferMap.clear(); this.bufferMap.clear();
this.excludeList.clear(); this.excludeList.clear();
this.modifiyMap.clear();
} }
@ -246,4 +278,35 @@ public class StorageBuffer {
} }
static class BufferableEntry extends BaseEntry{
/**
*
*/
@SuppressWarnings("unchecked")
public BufferableEntry() {
super();
this.links = new LinkedList<Link>();
}
/**
* @param arg0
*/
@SuppressWarnings("unchecked")
public BufferableEntry(BaseEntry arg0) {
super(arg0);
this.links = new LinkedList<Link>();
}
/**
* @see com.google.gdata.data.BaseEntry#declareExtensions(com.google.gdata.data.ExtensionProfile)
*/
@Override
public void declareExtensions(ExtensionProfile arg0) {
//
}
}
} }

View File

@ -6,161 +6,194 @@ import java.io.IOException;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.apache.lucene.analysis.standard.StandardAnalyzer; import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.gdata.server.registry.GDataServerRegistry; import org.apache.lucene.gdata.data.GDataAccount;
import org.apache.lucene.gdata.server.registry.Component;
import org.apache.lucene.gdata.server.registry.ComponentType;
import org.apache.lucene.gdata.storage.IDGenerator; import org.apache.lucene.gdata.storage.IDGenerator;
import org.apache.lucene.gdata.storage.Storage;
import org.apache.lucene.gdata.storage.StorageController; import org.apache.lucene.gdata.storage.StorageController;
import org.apache.lucene.gdata.storage.StorageException; import org.apache.lucene.gdata.storage.StorageException;
import org.apache.lucene.gdata.storage.lucenestorage.configuration.StorageConfigurator; import org.apache.lucene.gdata.storage.lucenestorage.configuration.StorageConfigurator;
import org.apache.lucene.gdata.storage.lucenestorage.util.ReferenceCounter; import org.apache.lucene.gdata.storage.lucenestorage.util.ReferenceCounter;
import org.apache.lucene.index.IndexModifier; import org.apache.lucene.index.IndexModifier;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.store.Directory; import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory; import org.apache.lucene.store.FSDirectory;
import org.apache.lucene.store.RAMDirectory;
/** /**
* TODO document this *
*
* @author Simon Willnauer * @author Simon Willnauer
* *
*/ */
public class StorageCoreController implements StorageController{ @Component(componentType = ComponentType.STORAGECONTROLLER)
protected static final Log LOG = LogFactory.getLog(StorageCoreController.class); public class StorageCoreController implements StorageController {
protected static final Log LOG = LogFactory
.getLog(StorageCoreController.class);
private IndexSearcher searcher; private IndexSearcher searcher;
private static StorageCoreController coreController;
private final Directory storageDir; private final Directory storageDir;
private final StorageModifier modifier; private final StorageModifier modifier;
private ReferenceCounter<StorageQuery> storageQuery; private ReferenceCounter<StorageQuery> storageQuery;
private StorageBuffer currentBuffer; private StorageBuffer currentBuffer;
private Object storageControllerLock = new Object(); private Object storageControllerLock = new Object();
private static final int DEFAULT_STORAGE_BUFFER_SIZE = 10; private static final int DEFAULT_STORAGE_BUFFER_SIZE = 10;
private static final int DEFAULT_STORAGE_PERSIST_FACTOR = 10; private static final int DEFAULT_STORAGE_PERSIST_FACTOR = 10;
private static final String STORAGELOG = ".lucenestorage"; private static final String STORAGELOG = ".lucenestorage";
private int storageBufferSize; private int storageBufferSize;
private int storagePersistFactor; private int storagePersistFactor;
private StorageConfigurator configurator; private StorageConfigurator configurator;
private IDGenerator idGenerator; private IDGenerator idGenerator;
private int indexOptimizeInterval; private int indexOptimizeInterval;
private StorageCoreController()throws IOException, StorageException{ // private RecoverController recoverController;
this(null);
}
/**
* Creates a new <tt>StoragCoreController</tt> and sets up the storage
* environment reading the configuration file.
private StorageCoreController(final Directory dir) throws IOException, StorageException { *
*
*
* @throws IOException -
* if an IOException occures
* @throws StorageException -
* if the storage lock can not be created or the
* {@link IDGenerator} can not be loaded
*/
public StorageCoreController() throws IOException, StorageException {
synchronized (StorageCoreController.class) { synchronized (StorageCoreController.class) {
try{ try {
this.idGenerator = new IDGenerator(10); this.idGenerator = new IDGenerator(10);
}catch (Exception e) { } catch (Exception e) {
throw new StorageException("Can't create ID Generator",e); throw new StorageException("Can't create ID Generator", e);
} }
boolean createNewStorage = false; boolean createNewStorage = false;
if(dir == null){
this.configurator = StorageConfigurator.getStorageConfigurator(); this.configurator = StorageConfigurator.getStorageConfigurator();
String storageDirPath = this.configurator.getStorageDirectory(); if (!this.configurator.isRamDirectory()) {
File storeDir = new File(storageDirPath);
File storageLog = new File(storeDir.getAbsolutePath()+System.getProperty("file.separator")+STORAGELOG);
try{
if(storeDir.isDirectory() && !storageLog.exists()){
if(createLuceneStorageLog(storeDir)){ String storageDirPath = this.configurator.getStorageDirectory();
this.storageDir = FSDirectory.getDirectory(storeDir,true); File storeDir = new File(storageDirPath);
createNewStorage = true; File storageLog = new File(storeDir.getAbsolutePath()
+ System.getProperty("file.separator") + STORAGELOG);
try {
if (storeDir.isDirectory() && !storageLog.exists()) {
if (createLuceneStorageLog(storeDir)) {
this.storageDir = FSDirectory.getDirectory(
storeDir, true);
createNewStorage = true;
} else
throw new StorageException(
"could not create storage lock file in "
+ storageDirPath);
} else
this.storageDir = FSDirectory.getDirectory(storeDir,
false);
} catch (IOException e) {
storageLog.delete();
throw e;
} }
else this.indexOptimizeInterval = this.configurator
throw new StorageException("could not create storage log file in "+storageDirPath); .getIndexOptimizeInterval();
this.storageBufferSize = this.configurator
.getStorageBufferSize() < DEFAULT_STORAGE_BUFFER_SIZE ? DEFAULT_STORAGE_BUFFER_SIZE
: this.configurator.getStorageBufferSize();
this.storagePersistFactor = this.configurator
.getStoragepersistFactor() < DEFAULT_STORAGE_PERSIST_FACTOR ? DEFAULT_STORAGE_PERSIST_FACTOR
: this.configurator.getStoragepersistFactor();
}else } else
this.storageDir = FSDirectory.getDirectory(storeDir,false); this.storageDir = getRamDirectory();
}catch (IOException e) {
storageLog.delete();
throw e;
}
this.indexOptimizeInterval = this.configurator.getIndexOptimizeInterval();
this.storageBufferSize = this.configurator.getStorageBufferSize() < DEFAULT_STORAGE_BUFFER_SIZE?DEFAULT_STORAGE_BUFFER_SIZE:this.configurator.getStorageBufferSize();
this.storagePersistFactor = this.configurator.getStoragepersistFactor() < DEFAULT_STORAGE_PERSIST_FACTOR? DEFAULT_STORAGE_PERSIST_FACTOR:this.configurator.getStoragepersistFactor();
}
else
this.storageDir = dir;
this.currentBuffer = new StorageBuffer(this.storageBufferSize); this.currentBuffer = new StorageBuffer(this.storageBufferSize);
this.modifier = createStorageModifier(createNewStorage); this.modifier = createStorageModifier(createNewStorage);
this.searcher = new IndexSearcher(this.storageDir); this.searcher = new IndexSearcher(this.storageDir);
// this.recoverController = new RecoverController(null,this.configurator.isRecover(),this.configurator.isKeepRecoveredFiles());
if(createNewStorage)
createAdminAccount();
}
GDataServerRegistry.getRegistry().registerStorage(this);// TODO reverse dependency here
}
} }
private StorageModifier createStorageModifier(boolean create) throws IOException{
IndexModifier indexModifier = new IndexModifier(this.storageDir,new StandardAnalyzer(),create); private StorageModifier createStorageModifier(boolean create)
return new StorageModifier(indexModifier,this.currentBuffer,this.storagePersistFactor,this.indexOptimizeInterval); throws IOException {
IndexModifier indexModifier = new IndexModifier(this.storageDir,
new StandardAnalyzer(), create);
return new StorageModifier(this, indexModifier, this.currentBuffer,
this.storagePersistFactor, this.indexOptimizeInterval);
} }
/**TODO document this
* @return /**
* returns the current storage modifier
*
* @return - the current storage modifier
*/ */
public StorageModifier getStorageModifier(){ protected StorageModifier getStorageModifier() {
return this.modifier; return this.modifier;
} }
/**TODO document this /**
* @return * returns a <tt>StorageQuery</tt> to query the storage index. The
* @throws IOException * returned object is a reference counter to keep track of the references to
* @throws StorageException * the <tt>StorageQuery</tt>. The reference is already incremented before
* returned from this method.
* <p>
* if the reference counter has no remaining references the resource e.g.
* the <tt>StorageQuery</tt> will be closed. This ensures that a
* <tt>StorageQuery</tt> instance will be arround as long as needed and
* the resources will be released. The reference counter should be
* decremented by clients after finished using the query instance.
* </p>
*
* @return a {@link ReferenceCounter} instance holding the StorageQuery as a
* resource.
*
*/ */
public static StorageCoreController getStorageCoreController() throws IOException, StorageException{ protected ReferenceCounter<StorageQuery> getStorageQuery() {
synchronized (StorageCoreController.class) {
if(coreController == null)
coreController = new StorageCoreController();
return coreController;
}
}
/**TODO document this
* @param dir
* @return
* @throws IOException
* @throws StorageException
*/
protected static StorageCoreController getStorageCoreController(final Directory dir) throws IOException, StorageException{
synchronized (StorageCoreController.class) {
if(coreController == null)
coreController = new StorageCoreController(dir);
return coreController;
}
}
/**TODO document this
* @return
* @throws IOException
*/
public ReferenceCounter<StorageQuery> getStorageQuery() throws IOException {
synchronized (this.storageControllerLock) { synchronized (this.storageControllerLock) {
if(this.storageQuery == null){ if (this.storageQuery == null) {
this.storageQuery = getNewStorageQueryHolder(new StorageQuery(this.currentBuffer,this.searcher)); this.storageQuery = getNewStorageQueryHolder(new StorageQuery(
if(LOG.isInfoEnabled()) this.currentBuffer, this.searcher));
LOG.info("Relese new StorageQuery"); if (LOG.isInfoEnabled())
} LOG.info("Relese new StorageQuery");
this.storageQuery.increamentReference(); }
return this.storageQuery; this.storageQuery.increamentReference();
return this.storageQuery;
} }
} }
private ReferenceCounter<StorageQuery> getNewStorageQueryHolder(final StorageQuery query){ private ReferenceCounter<StorageQuery> getNewStorageQueryHolder(
ReferenceCounter<StorageQuery> holder = new ReferenceCounter<StorageQuery>(query){ final StorageQuery query) {
public void close(){ ReferenceCounter<StorageQuery> holder = new ReferenceCounter<StorageQuery>(
try{ query) {
if(LOG.isInfoEnabled()) public void close() {
LOG.info("close StorageQuery -- zero references remaining"); try {
if (LOG.isInfoEnabled())
LOG
.info("close StorageQuery -- zero references remaining");
this.resource.close(); this.resource.close();
}catch (IOException e) { } catch (IOException e) {
LOG.warn("Error during close call on StorageQuery"+e.getMessage(),e); LOG.warn("Error during close call on StorageQuery"
+ e.getMessage(), e);
} }
} }
}; };
@ -168,13 +201,19 @@ public class StorageCoreController implements StorageController{
return holder; return holder;
} }
/**
* Forces the controller to register a new <tt>StorageQuery</tt> instance.
protected void registerNewStorageQuery() throws IOException{ * This method will be called after an index has been modified to make the
if(LOG.isInfoEnabled()) * changes available for searching.
*
* @throws IOException -
* if an IO exception occures
*/
protected void registerNewStorageQuery() throws IOException {
if (LOG.isInfoEnabled())
LOG.info("new StorageQuery requested -- create new storage buffer"); LOG.info("new StorageQuery requested -- create new storage buffer");
synchronized (this.storageControllerLock) { synchronized (this.storageControllerLock) {
if(this.storageQuery != null) if (this.storageQuery != null)
this.storageQuery.decrementRef(); this.storageQuery.decrementRef();
this.searcher = new IndexSearcher(this.storageDir); this.searcher = new IndexSearcher(this.storageDir);
this.storageQuery = null; this.storageQuery = null;
@ -184,103 +223,176 @@ public class StorageCoreController implements StorageController{
} }
/**
* Creates a new StorageBuffer
*
* @return the new StorageBuffer
*/
protected StorageBuffer releaseNewStorageBuffer() { protected StorageBuffer releaseNewStorageBuffer() {
synchronized (this.storageControllerLock) { synchronized (this.storageControllerLock) {
return this.currentBuffer; return this.currentBuffer;
} }
} }
/**TODO document this /**
* @return * Creates a new IndexModifier on the storage index
* @throws IOException *
* @return - a new modifier
* @throws IOException -
* if an IO exception occures
*/ */
public IndexModifier createIndexModifier() throws IOException { protected IndexModifier createIndexModifier() throws IOException {
if(LOG.isInfoEnabled()) if (LOG.isInfoEnabled())
LOG.info("new IndexModifier created - release to StorageModifier"); LOG.info("new IndexModifier created - release to StorageModifier");
synchronized (this.storageControllerLock) { synchronized (this.storageControllerLock) {
return new IndexModifier(this.storageDir,new StandardAnalyzer(),false); return new IndexModifier(this.storageDir, new StandardAnalyzer(),
false);
} }
} }
private void close() throws IOException{ private void close() throws IOException {
synchronized (this.storageControllerLock) { synchronized (this.storageControllerLock) {
if(LOG.isInfoEnabled()) if (LOG.isInfoEnabled())
LOG.info("StorageController has been closed -- server is shutting down -- release all resources"); LOG
if(this.storageQuery != null) .info("StorageController has been closed -- server is shutting down -- release all resources");
this.storageQuery.decrementRef(); if (this.storageQuery != null)
coreController = null; this.storageQuery.decrementRef();
this.modifier.close(); this.modifier.close();
//TODO make sure all resources will be released // TODO make sure all resources will be released
} }
} }
/**TODO document this
* @return /**
* The size of the <tt>StorageBuffer</tt>.
*
* @return - storage buffer size
*/ */
public int getStorageBufferSize() { public int getStorageBufferSize() {
return this.storageBufferSize; return this.storageBufferSize;
} }
/** /**
* The size of the <tt>StorageBuffer</tt>. This size should be at least
* as big as the persist factor to prevent the <tt>StorageBuffer</tt> from
* resizing
*
* @param storageBufferSize * @param storageBufferSize
*/ */
public void setStorageBufferSize(int storageBufferSize) { public void setStorageBufferSize(int storageBufferSize) {
this.storageBufferSize = storageBufferSize; this.storageBufferSize = storageBufferSize;
} }
/**TODO document this
* @return /**
* An integer value after how many changes to the StorageModifier the
* buffered changes will be persisted / wirtten to the index
*
* @return - the persist factor
*/ */
public int getStoragePersistFactor() { public int getStoragePersistFactor() {
return this.storagePersistFactor; return this.storagePersistFactor;
} }
/** /**
* @param storagePersistFactor * @param storagePersistFactor
*/ */
public void setStoragePersistFactor(int storagePersistFactor) { public void setStoragePersistFactor(int storagePersistFactor) {
this.storagePersistFactor = storagePersistFactor; this.storagePersistFactor = storagePersistFactor;
} }
/** /**
* @throws IOException * Forces the StorageModifier to write all buffered changes.
* @throws StorageException *
* @throws IOException -
* if an IO exception occures
*
*/ */
public void forceWrite()throws IOException, StorageException{ public void forceWrite() throws IOException {
this.modifier.forceWrite(); this.modifier.forceWrite();
} }
private boolean createLuceneStorageLog(File storageDirectory)
private boolean createLuceneStorageLog(File storageDirectory) throws IOException{ throws IOException {
if(storageDirectory.isDirectory() && !storageDirectory.exists()){ if (storageDirectory.isDirectory() && !storageDirectory.exists()) {
storageDirectory.createNewFile(); storageDirectory.createNewFile();
} }
File file = new File(storageDirectory.getAbsolutePath()+System.getProperty("file.separator")+STORAGELOG); File file = new File(storageDirectory.getAbsolutePath()
+ System.getProperty("file.separator") + STORAGELOG);
return file.createNewFile(); return file.createNewFile();
} }
/**
/**TODO document this * Creates a unique ID to store as an id for
* @return * {@link org.apache.lucene.gdata.data.ServerBaseEntry} instances
* @throws StorageException *
* @return - a unique id
* @throws StorageException -
* if no id can be released
*/ */
public synchronized String releaseID() throws StorageException{ public synchronized String releaseID() throws StorageException {
try{ try {
return this.idGenerator.getUID(); return this.idGenerator.getUID();
}catch (InterruptedException e) { } catch (InterruptedException e) {
throw new StorageException("Can't release new ID",e); throw new StorageException("Can't release new ID", e);
} }
} }
/** /**
* @see org.apache.lucene.gdata.storage.StorageController#destroy() * @see org.apache.lucene.gdata.storage.StorageController#destroy()
*/ */
public void destroy() { public void destroy() {
try{ try {
close(); close();
}catch (Exception e) { } catch (Exception e) {
LOG.error("Closing StorageCoreController failed -- "+e.getMessage(),e); LOG.error("Closing StorageCoreController failed -- "
+ e.getMessage(), e);
} }
} }
/**
*
* @return - the lucene directory used as a storage
*/
protected Directory getDirectory() {
return this.storageDir;
}
/**
* @see org.apache.lucene.gdata.storage.StorageController#getStorage()
*/
public Storage getStorage() throws StorageException {
try {
return new StorageImplementation();
} catch (StorageException e) {
StorageException ex = new StorageException(
"Can't create Storage instance -- " + e.getMessage(), e);
ex.setStackTrace(e.getStackTrace());
throw ex;
}
}
// TODO Try to remove this --> testcases
private RAMDirectory getRamDirectory() throws IOException {
IndexWriter writer;
RAMDirectory retVal = new RAMDirectory();
writer = new IndexWriter(retVal, new StandardAnalyzer(), true);
writer.close();
return retVal;
}
/**
* @see org.apache.lucene.gdata.server.registry.ServerComponent#initialize()
*/
public void initialize() {
//
}
private void createAdminAccount() throws StorageException{
GDataAccount adminAccount = GDataAccount.createAdminAccount();
StorageAccountWrapper wrapper = new StorageAccountWrapper(adminAccount);
this.getStorageModifier().createAccount(wrapper);
}
} }

View File

@ -21,20 +21,27 @@ import java.io.StringWriter;
import org.apache.lucene.document.Document; import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field; import org.apache.lucene.document.Field;
import org.apache.lucene.gdata.data.ServerBaseEntry;
import org.apache.lucene.gdata.server.registry.ProvidedService;
import org.apache.lucene.gdata.server.registry.ProvidedServiceConfig;
import org.apache.lucene.gdata.storage.lucenestorage.StorageBuffer.BufferableEntry;
import com.google.gdata.data.BaseEntry; import com.google.gdata.data.BaseEntry;
import com.google.gdata.data.ExtensionProfile;
import com.google.gdata.util.common.xml.XmlWriter; import com.google.gdata.util.common.xml.XmlWriter;
/** /**
* This immutable class wrapps Entries for an internal Storage representation of * This immutable class wrapps <tt>ServerBaseEntry</tt> for an internal
* an entry. This class also acts as a Documentfactory for lucene documents to * Storage representation of an entry. This class also acts as a Documentfactory
* be stored inside the index. * for lucene documents to be stored inside the index.
* *
* @author Simon Willnauer * @author Simon Willnauer
* *
*/ */
public class StorageEntryWrapper implements Comparable<StorageEntryWrapper> { public class StorageEntryWrapper implements Comparable<StorageEntryWrapper>,
StorageWrapper {
private static final long serialVersionUID = -4619985652059888526L;
private static final String INTERNAL_ENCODING = "UTF-8"; private static final String INTERNAL_ENCODING = "UTF-8";
/** /**
@ -45,7 +52,7 @@ public class StorageEntryWrapper implements Comparable<StorageEntryWrapper> {
/** /**
* lucene field name feed id * lucene field name feed id
*/ */
public final static String FIELD_FEED_ID = "feedId"; public final static String FIELD_FEED_REFERENCE = "feedReference";
/** /**
* lucene field name entry content * lucene field name entry content
@ -61,67 +68,73 @@ public class StorageEntryWrapper implements Comparable<StorageEntryWrapper> {
private final String feedId; private final String feedId;
private final String content; private String content;
private final transient BaseEntry entry; private final ServerBaseEntry entry;
private final Long timestamp; private Long timestamp;
private transient Document document; private transient Document document;
private StorageOperation operation; private StorageOperation operation;
private final ExtensionProfile profile; private ProvidedService config;
/** /**
* Creates a new StorageEntryWrapper. * Creates a new StorageEntryWrapper.
* *
* @param entry - * @param entry -
* the entry to wrap * the entry to wrap
* @param feedId - *
* the feed id
* @param operation - * @param operation -
* the StorageOperation * the StorageOperation
* @param profile - *
* the ExtensionProfil for the given entry
* @throws IOException - * @throws IOException -
* if the entry content can not be generated * if the entry content can not be generated
*/ */
protected StorageEntryWrapper(final BaseEntry entry, final String feedId, public StorageEntryWrapper(final ServerBaseEntry entry,
StorageOperation operation, final ExtensionProfile profile) StorageOperation operation) throws IOException {
throws IOException {
this.entry = entry; this.entry = entry;
this.operation = operation; this.operation = operation;
this.entryId = entry.getId(); this.entryId = entry.getId();
this.feedId = feedId; this.feedId = entry.getFeedId();
this.profile = profile; if (operation != StorageOperation.DELETE) {
this.content = buildContent(); this.config = entry.getServiceConfig();
this.timestamp = new Long(System.currentTimeMillis()); this.content = buildContent();
}
this.timestamp = new Long(
this.entry.getUpdated() != null ? this.entry.getUpdated()
.getValue() : System.currentTimeMillis());
} }
private String buildContent() throws IOException { private String buildContent() throws IOException {
StringWriter writer = new StringWriter(); StringWriter writer = new StringWriter();
XmlWriter xmlWriter = new XmlWriter(writer, INTERNAL_ENCODING); XmlWriter xmlWriter = new XmlWriter(writer, INTERNAL_ENCODING);
this.entry.generateAtom(xmlWriter, this.profile); this.entry.generateAtom(xmlWriter, this.config.getExtensionProfile());
return writer.toString(); return writer.toString();
} }
/** /**
* @return - the lucene document representing the entry * @see org.apache.lucene.gdata.storage.lucenestorage.StorageWrapper#getLuceneDocument()
*/ */
public Document getLuceneDocument() { public Document getLuceneDocument() {
if(this.operation == StorageOperation.DELETE)
return null;
if (this.document != null) if (this.document != null)
return this.document; return this.document;
this.document = new Document(); this.document = new Document();
this.document.add(new Field("entryId", this.entryId, Field.Store.YES, this.document.add(new Field(FIELD_ENTRY_ID, this.entryId,
Field.Index.UN_TOKENIZED)); Field.Store.YES, Field.Index.UN_TOKENIZED));
this.document.add(new Field("feedId", this.feedId, Field.Store.YES, this.document.add(new Field(FIELD_FEED_REFERENCE, this.feedId,
Field.Index.UN_TOKENIZED)); Field.Store.YES, Field.Index.UN_TOKENIZED));
this.document.add(new Field("content", this.content, this.document.add(new Field(FIELD_CONTENT, this.content,
Field.Store.COMPRESS, Field.Index.UN_TOKENIZED)); Field.Store.COMPRESS, Field.Index.NO));
this.document.add(new Field("timestamp", this.timestamp.toString(), this.document.add(new Field(FIELD_TIMESTAMP, this.timestamp.toString(),
Field.Store.YES, Field.Index.UN_TOKENIZED)); Field.Store.YES, Field.Index.UN_TOKENIZED));
return this.document; return this.document;
@ -132,7 +145,11 @@ public class StorageEntryWrapper implements Comparable<StorageEntryWrapper> {
* @return - the wrapped entry * @return - the wrapped entry
*/ */
public BaseEntry getEntry() { public BaseEntry getEntry() {
return this.entry; /*
* this wrapps the entry again. BufferableEntry creates a new instance
* for the dynamic element like links.
*/
return new BufferableEntry(this.entry.getEntry());
} }
/** /**
@ -178,11 +195,32 @@ public class StorageEntryWrapper implements Comparable<StorageEntryWrapper> {
} }
/** /**
* @see java.lang.Comparable#compareTo(T) * This compare method compares the timestamps of the wrapper instances.
*
* @param arg0 -
* the wrapper to compare
* @par
* @return - 0 if the wrappers timestamp are the same, an integer > 0 if the
* given wrapper is after this wrapper
*
*/ */
public int compareTo(StorageEntryWrapper arg0) { public int compareTo(StorageEntryWrapper arg0) {
return arg0.timestamp == this.timestamp ? 0 return arg0.timestamp == this.timestamp ? 0
: (arg0.timestamp > this.timestamp ? 1 : -1); : (arg0.timestamp > this.timestamp ? 1 : -1);
} }
/**
* @return - the specified {@link ProvidedServiceConfig}
*/
public ProvidedService getConfigurator() {
return this.config;
}
/**
* @return Returns the timestamp.
*/
public Long getTimestamp() {
return this.timestamp;
}
} }

View File

@ -17,19 +17,23 @@
package org.apache.lucene.gdata.storage.lucenestorage; package org.apache.lucene.gdata.storage.lucenestorage;
import java.io.IOException; import java.io.IOException;
import java.util.List;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.apache.lucene.gdata.data.GDataAccount;
import org.apache.lucene.gdata.data.ServerBaseEntry;
import org.apache.lucene.gdata.data.ServerBaseFeed;
import org.apache.lucene.gdata.server.registry.ComponentType;
import org.apache.lucene.gdata.server.registry.GDataServerRegistry;
import org.apache.lucene.gdata.storage.ResourceNotFoundException;
import org.apache.lucene.gdata.storage.Storage; import org.apache.lucene.gdata.storage.Storage;
import org.apache.lucene.gdata.storage.StorageController;
import org.apache.lucene.gdata.storage.StorageException; import org.apache.lucene.gdata.storage.StorageException;
import org.apache.lucene.gdata.storage.lucenestorage.StorageEntryWrapper.StorageOperation; import org.apache.lucene.gdata.storage.lucenestorage.StorageEntryWrapper.StorageOperation;
import org.apache.lucene.gdata.storage.lucenestorage.util.ReferenceCounter; import org.apache.lucene.gdata.storage.lucenestorage.util.ReferenceCounter;
import com.google.gdata.data.BaseEntry; import com.google.gdata.data.BaseEntry;
import com.google.gdata.data.BaseFeed; import com.google.gdata.data.BaseFeed;
import com.google.gdata.data.ExtensionProfile;
import com.google.gdata.data.Feed;
/** /**
* This is an implementation of the * This is an implementation of the
@ -47,8 +51,6 @@ import com.google.gdata.data.Feed;
public class StorageImplementation implements Storage { public class StorageImplementation implements Storage {
private final StorageCoreController controller; private final StorageCoreController controller;
private ExtensionProfile profile;
private static final Log LOG = LogFactory private static final Log LOG = LogFactory
.getLog(StorageImplementation.class); .getLog(StorageImplementation.class);
@ -56,40 +58,36 @@ public class StorageImplementation implements Storage {
* Creates a new StorageImplementation * Creates a new StorageImplementation
* *
* @throws StorageException - * @throws StorageException -
* if the * if the storage controller can not be obtained
* {@link org.apache.lucene.gdata.storage.StorageController} can *
* not be created *
* @throws IOException -
* if the
* {@link org.apache.lucene.gdata.storage.StorageController} can
* not be created
* @see StorageCoreController#getStorageCoreController()
* *
*/ */
public StorageImplementation() throws IOException, StorageException { public StorageImplementation() throws StorageException {
this.controller = StorageCoreController.getStorageCoreController(); this.controller = (StorageCoreController) GDataServerRegistry
.getRegistry().lookup(StorageController.class,
ComponentType.STORAGECONTROLLER);
if (this.controller == null)
throw new StorageException("Can't get registered StorageController");
} }
/** /**
* @see org.apache.lucene.gdata.storage.Storage#storeEntry(com.google.gdata.data.BaseEntry, * @see org.apache.lucene.gdata.storage.Storage#storeEntry(org.apache.lucene.gdata.data.ServerBaseEntry)
* java.lang.String)
*/ */
public BaseEntry storeEntry(BaseEntry entry, String feedId) public BaseEntry storeEntry(final ServerBaseEntry entry)
throws StorageException { throws StorageException {
if (this.profile == null)
throw new StorageException( if (entry == null)
"Can process ExtensionProfile not set -- is null"); throw new StorageException("entry is null");
if (feedId == null)
throw new StorageException("No feed ID specified -- is null");
StorageModifier modifier = this.controller.getStorageModifier(); StorageModifier modifier = this.controller.getStorageModifier();
String id = this.controller.releaseID(); String id = this.controller.releaseID();
entry.setId(feedId + id); entry.setId(entry.getFeedId() + id);
if (LOG.isInfoEnabled()) if (LOG.isInfoEnabled())
LOG.info("Store entry " + id + " -- feed: " + feedId); LOG.info("Store entry " + id + " -- feed: " + entry.getFeedId());
try { try {
StorageEntryWrapper wrapper = new StorageEntryWrapper(entry, StorageEntryWrapper wrapper = new StorageEntryWrapper(entry,
feedId, StorageOperation.INSERT, this.profile); StorageOperation.INSERT);
modifier.insertEntry(wrapper); modifier.insertEntry(wrapper);
} catch (IOException e) { } catch (IOException e) {
StorageException ex = new StorageException("Can't create Entry -- " StorageException ex = new StorageException("Can't create Entry -- "
@ -99,53 +97,63 @@ public class StorageImplementation implements Storage {
} }
return entry; return entry.getEntry();
} }
/** /**
* @see org.apache.lucene.gdata.storage.Storage#deleteEntry(java.lang.String, * @see org.apache.lucene.gdata.storage.Storage#deleteEntry(org.apache.lucene.gdata.data.ServerBaseEntry)
* java.lang.String)
*/ */
public void deleteEntry(String entryId, String feedId) public void deleteEntry(final ServerBaseEntry entry)
throws StorageException { throws StorageException {
if (this.profile == null)
throw new StorageException(
"Can process ExtensionProfile not set -- is null");
if (feedId == null)
throw new StorageException("No feed ID specified -- is null");
if (entryId == null)
throw new StorageException("No entry ID specified -- is null");
if (LOG.isInfoEnabled())
LOG.info("delete entry " + entryId + " -- feed: " + feedId);
StorageModifier modifier = this.controller.getStorageModifier();
modifier.deleteEntry(entryId, feedId);
}
/**
* @see org.apache.lucene.gdata.storage.Storage#updateEntry(com.google.gdata.data.BaseEntry,
* java.lang.String)
*/
public BaseEntry updateEntry(BaseEntry entry, String feedId)
throws StorageException {
if (this.profile == null)
throw new StorageException(
"Can process ExtensionProfile not set -- is null");
if (feedId == null)
throw new StorageException("No feed ID specified -- is null");
if (entry == null) if (entry == null)
throw new StorageException("enrty is null"); throw new StorageException("Entry is null");
if (entry.getId() == null)
throw new StorageException("No entry ID specified -- is null");
if (LOG.isInfoEnabled())
LOG.info("update entry " + entry.getId() + " -- feed: " + feedId);
StorageModifier modifier = this.controller.getStorageModifier();
if (LOG.isInfoEnabled())
LOG.info("delete entry " + entry.getId() + " -- feed: "
+ entry.getFeedId());
StorageModifier modifier = this.controller.getStorageModifier();
ReferenceCounter<StorageQuery> query = this.controller.getStorageQuery();
try{
if(query.get().isEntryStored(entry.getId(),entry.getFeedId())){
modifier.deleteEntry(new StorageEntryWrapper(entry,StorageOperation.DELETE));
}
else
throw new ResourceNotFoundException("Entry for entry id: "+entry.getId()+" is not stored");
}catch (IOException e) {
throw new StorageException("Can not access storage");
}finally{
query.decrementRef();
}
}
/**
* @see org.apache.lucene.gdata.storage.Storage#updateEntry(org.apache.lucene.gdata.data.ServerBaseEntry)
*/
public BaseEntry updateEntry(ServerBaseEntry entry) throws StorageException {
if (entry == null)
throw new StorageException("entry is null");
if(entry.getId() == null)
throw new StorageException("entry id is null");
if(entry.getFeedId() == null)
throw new StorageException("feed id is null");
if (LOG.isInfoEnabled())
LOG.info("update entry " + entry.getId() + " -- feed: "
+ entry.getFeedId());
StorageModifier modifier = this.controller.getStorageModifier();
ReferenceCounter<StorageQuery> query = this.controller.getStorageQuery();
try { try {
StorageEntryWrapper wrapper = new StorageEntryWrapper(entry, StorageEntryWrapper wrapper = new StorageEntryWrapper(entry,
feedId, StorageOperation.UPDATE, this.profile); StorageOperation.UPDATE);
modifier.updateEntry(wrapper); if(query.get().isEntryStored(entry.getId(),entry.getFeedId()))
modifier.updateEntry(wrapper);
else
throw new ResourceNotFoundException("Entry for entry id: "+entry.getId()+" is not stored");
} catch (IOException e) { } catch (IOException e) {
LOG.error("Can't update entry for feedID: " + feedId LOG.error("Can't update entry for feedID: " + entry.getFeedId()
+ "; entryId: " + entry.getId() + " -- " + e.getMessage(), + "; entryId: " + entry.getId() + " -- " + e.getMessage(),
e); e);
StorageException ex = new StorageException("Can't create Entry -- " StorageException ex = new StorageException("Can't create Entry -- "
@ -155,36 +163,32 @@ public class StorageImplementation implements Storage {
} }
return entry; return entry.getEntry();
} }
/** /**
* @see org.apache.lucene.gdata.storage.Storage#getFeed(java.lang.String, * @see org.apache.lucene.gdata.storage.Storage#getFeed(org.apache.lucene.gdata.data.ServerBaseFeed)
* int, int)
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public BaseFeed getFeed(String feedId, int startIndex, int resultCount) public BaseFeed getFeed(final ServerBaseFeed feed) throws StorageException {
throws StorageException {
if (this.profile == null) if (feed == null)
throw new StorageException( throw new StorageException("feed is null");
"Can process ExtensionProfile not set -- is null");
if (feedId == null)
throw new StorageException("No feed ID specified -- is null");
if (LOG.isInfoEnabled()) if (LOG.isInfoEnabled())
LOG.info("get feed: " + feedId + " startindex: " + startIndex LOG.info("get feed: " + feed.getId() + " startindex: "
+ " resultCount: " + resultCount); + feed.getStartIndex() + " resultCount: "
+ feed.getItemsPerPage());
ReferenceCounter<StorageQuery> query = null; ReferenceCounter<StorageQuery> query = null;
try { try {
query = this.controller.getStorageQuery(); query = this.controller.getStorageQuery();
List<BaseEntry> resultList = query.get().getLatestFeedQuery(feedId, BaseFeed retVal = query.get().getLatestFeedQuery(feed.getId(),
resultCount, startIndex, this.profile); feed.getItemsPerPage(), feed.getStartIndex(),
BaseFeed feed = new Feed(); feed.getServiceConfig());
feed.getEntries().addAll(resultList); return retVal;
return feed;
} catch (Exception e) { } catch (Exception e) {
LOG.error("Can't get latest feed for feedID: " + feedId + " -- " LOG.error("Can't get latest feed for feedID: " + feed.getId()
+ e.getMessage(), e); + " -- " + e.getMessage(), e);
StorageException ex = new StorageException("Can't create Entry -- " StorageException ex = new StorageException("Can't create Entry -- "
+ e.getMessage(), e); + e.getMessage(), e);
ex.setStackTrace(e.getStackTrace()); ex.setStackTrace(e.getStackTrace());
@ -198,27 +202,28 @@ public class StorageImplementation implements Storage {
} }
/** /**
* @see org.apache.lucene.gdata.storage.Storage#getEntry(java.lang.String, * @see org.apache.lucene.gdata.storage.Storage#getEntry(org.apache.lucene.gdata.data.ServerBaseEntry)
* java.lang.String)
*/ */
public BaseEntry getEntry(String entryId, String feedId) public BaseEntry getEntry(final ServerBaseEntry entry)
throws StorageException { throws StorageException {
if (this.profile == null)
throw new StorageException( if (entry == null)
"Can process ExtensionProfile not set -- is null"); throw new StorageException("No entry specified -- is null");
if (feedId == null)
throw new StorageException("No feed ID specified -- is null");
if (entryId == null)
throw new StorageException("No entry ID specified -- is null");
if (LOG.isInfoEnabled()) if (LOG.isInfoEnabled())
LOG.info("get entry " + entryId + " -- feed: " + feedId); LOG.info("get entry " + entry.getId() + " -- feed: "
+ entry.getFeedId());
ReferenceCounter<StorageQuery> query = null; ReferenceCounter<StorageQuery> query = null;
try { try {
query = this.controller.getStorageQuery(); query = this.controller.getStorageQuery();
return query.get().singleEntryQuery(entryId, feedId, this.profile); BaseEntry retVal = query.get().singleEntryQuery(entry.getId(),
entry.getFeedId(), entry.getServiceConfig());
if(retVal == null)
throw new ResourceNotFoundException("can not get entry for entry ID "+entry.getId());
return retVal;
} catch (Exception e) { } catch (Exception e) {
LOG.error("Can't get entry for feedID: " + feedId + "; entryId: " LOG.error("Can't get entry for feedID: " + entry.getFeedId()
+ entryId + " -- " + e.getMessage(), e); + "; entryId: " + entry.getId() + " -- " + e.getMessage(),
e);
StorageException ex = new StorageException("Can't create Entry -- " StorageException ex = new StorageException("Can't create Entry -- "
+ e.getMessage(), e); + e.getMessage(), e);
ex.setStackTrace(e.getStackTrace()); ex.setStackTrace(e.getStackTrace());
@ -231,17 +236,6 @@ public class StorageImplementation implements Storage {
} }
/**
* @see org.apache.lucene.gdata.storage.Storage#getEntries(java.util.List,
* java.lang.String)
*/
public List<BaseEntry> getEntries(List<String> entryIdList, String feedId)
throws StorageException {
throw new StorageException("not implemented yet");
// TODO implement this
}
/** /**
* @see org.apache.lucene.gdata.storage.Storage#close() * @see org.apache.lucene.gdata.storage.Storage#close()
*/ */
@ -250,10 +244,283 @@ public class StorageImplementation implements Storage {
} }
/** /**
* @see org.apache.lucene.gdata.storage.Storage#setExtensionProfile(com.google.gdata.data.ExtensionProfile) * @see org.apache.lucene.gdata.storage.Storage#storeAccount(org.apache.lucene.gdata.data.GDataAccount)
*/ */
public void setExtensionProfile(ExtensionProfile profile) { public void storeAccount(GDataAccount Account) throws StorageException {
this.profile = profile; if (Account == null)
throw new StorageException("Can not save null Account");
ReferenceCounter<StorageQuery> query = null;
try {
query = this.controller.getStorageQuery();
if (query.get().getUser(Account.getName()) != null)
throw new StorageException("Account already exists");
StorageModifier modifier = this.controller.getStorageModifier();
StorageAccountWrapper wrapper = new StorageAccountWrapper(Account);
modifier.createAccount(wrapper);
} catch (Exception e) {
LOG.error("Can't save Account -- " + e.getMessage(), e);
StorageException ex = new StorageException("Can't save Account -- "
+ e.getMessage(), e);
ex.setStackTrace(e.getStackTrace());
throw ex;
} finally {
if (query != null)
query.decrementRef();
}
} }
/**
* @see org.apache.lucene.gdata.storage.Storage#updateAccount(org.apache.lucene.gdata.data.GDataAccount)
*/
public void updateAccount(GDataAccount Account) throws StorageException {
if (Account == null)
throw new StorageException("Can not update null Account");
ReferenceCounter<StorageQuery> query = null;
try {
query = this.controller.getStorageQuery();
if (query.get().getUser(Account.getName()) == null)
throw new StorageException("Account does not exist");
StorageModifier modifier = this.controller.getStorageModifier();
StorageAccountWrapper wrapper = new StorageAccountWrapper(Account);
modifier.updateAccount(wrapper);
} catch (Exception e) {
LOG.error("Can't update Account -- " + e.getMessage(), e);
StorageException ex = new StorageException(
"Can't update Account -- " + e.getMessage(), e);
ex.setStackTrace(e.getStackTrace());
throw ex;
} finally {
if (query != null)
query.decrementRef();
}
}
/**
* @see org.apache.lucene.gdata.storage.Storage#deleteAccount(java.lang.String)
*/
public void deleteAccount(String Accountname) throws StorageException {
if (Accountname == null)
throw new StorageException("can not delete null Account");
ReferenceCounter<StorageQuery> query = null;
try {
query = this.controller.getStorageQuery();
if (query.get().getUser(Accountname) == null)
throw new StorageException("Account does not exist");
StorageModifier modifier = this.controller.getStorageModifier();
modifier.deleteAccount(Accountname);
} catch (Exception e) {
LOG.error("Can't update Account -- " + e.getMessage(), e);
StorageException ex = new StorageException(
"Can't update Account -- " + e.getMessage(), e);
ex.setStackTrace(e.getStackTrace());
throw ex;
} finally {
if (query != null)
query.decrementRef();
}
}
/**
* @see org.apache.lucene.gdata.storage.Storage#storeFeed(org.apache.lucene.gdata.data.ServerBaseFeed,
* java.lang.String)
*/
public void storeFeed(ServerBaseFeed feed, String accountName)
throws StorageException {
if (feed == null)
throw new StorageException("can not insert null feed");
if (accountName == null)
throw new StorageException("accountName must not be null");
ReferenceCounter<StorageQuery> query = null;
try {
query = this.controller.getStorageQuery();
if (query.get().isFeedStored(feed.getId()))
throw new StorageException("feed with feedID " + feed.getId()
+ " is already stored");
StorageModifier modifier = this.controller.getStorageModifier();
StorageFeedWrapper wrapper = new StorageFeedWrapper(feed,
accountName);
modifier.createFeed(wrapper);
} catch (Exception e) {
LOG.error("Can't create feed -- " + e.getMessage(), e);
StorageException ex = new StorageException("Can't create feed -- "
+ e.getMessage(), e);
ex.setStackTrace(e.getStackTrace());
throw ex;
} finally {
if (query != null)
query.decrementRef();
}
}
/**
* @see org.apache.lucene.gdata.storage.Storage#deleteFeed(java.lang.String)
*/
public void deleteFeed(String feedId) throws StorageException {
if (feedId == null)
throw new StorageException("can not delete feed id is null ");
ReferenceCounter<StorageQuery> query = null;
try {
query = this.controller.getStorageQuery();
if (!query.get().isFeedStored(feedId))
throw new StorageException("Account does not exist");
StorageModifier modifier = this.controller.getStorageModifier();
modifier.deleteFeed(feedId);
} catch (Exception e) {
LOG.error("Can't delete feed -- " + e.getMessage(), e);
StorageException ex = new StorageException("Can't create feed -- "
+ e.getMessage(), e);
ex.setStackTrace(e.getStackTrace());
throw ex;
} finally {
if (query == null)
query.decrementRef();
}
}
/**
* @see org.apache.lucene.gdata.storage.Storage#updateFeed(org.apache.lucene.gdata.data.ServerBaseFeed,
* java.lang.String)
*/
public void updateFeed(ServerBaseFeed feed, String accountName)
throws StorageException {
if (feed == null)
throw new StorageException("can not update null feed");
if (accountName == null)
throw new StorageException("accountName must not be null");
ReferenceCounter<StorageQuery> query = null;
try {
query = this.controller.getStorageQuery();
if (!query.get().isFeedStored(feed.getId()))
throw new StorageException("Account does not exist");
StorageModifier modifier = this.controller.getStorageModifier();
StorageFeedWrapper wrapper = new StorageFeedWrapper(feed,
accountName);
modifier.updateFeed(wrapper);
} catch (Exception e) {
LOG.error("Can't create feed -- " + e.getMessage(), e);
StorageException ex = new StorageException("Can't create feed -- "
+ e.getMessage(), e);
ex.setStackTrace(e.getStackTrace());
throw ex;
} finally {
if (query == null)
query.decrementRef();
}
}
/**
* @see org.apache.lucene.gdata.storage.Storage#getServiceForFeed(java.lang.String)
*/
public String getServiceForFeed(String feedId) throws StorageException {
if (feedId == null)
throw new StorageException("no feed for the feedID == null");
ReferenceCounter<StorageQuery> query = null;
try {
query = this.controller.getStorageQuery();
String type = query.get().getService(feedId);
if (type == null)
throw new StorageException("no feed for the feedID == "
+ feedId + " found");
return type;
} catch (Exception e) {
throw new StorageException("Can not access storage", e);
} finally {
if (query != null)
query.decrementRef();
}
}
/**
* @see org.apache.lucene.gdata.storage.Storage#getAccount(java.lang.String)
*/
public GDataAccount getAccount(String accountName) throws StorageException {
if (accountName == null)
throw new StorageException("account name must not be null");
ReferenceCounter<StorageQuery> query = null;
try {
query = this.controller.getStorageQuery();
return query.get().getUser(accountName);
} catch (Exception e) {
throw new StorageException("Can not access storage", e);
} finally {
if (query != null)
query.decrementRef();
}
}
/**
* @see org.apache.lucene.gdata.storage.Storage#getAccountNameForFeedId(java.lang.String)
*/
public String getAccountNameForFeedId(String feedId)
throws StorageException {
if (feedId == null)
throw new StorageException("feedid must not be null");
ReferenceCounter<StorageQuery> query = null;
try {
query = this.controller.getStorageQuery();
String accountName = query.get().getAccountNameForFeedId(feedId);
if (accountName == null)
throw new StorageException("no feed for feedId " + feedId
+ " found");
return accountName;
} catch (IOException e) {
throw new StorageException("Can not access storage - "
+ e.getMessage(), e);
} finally {
if (query != null)
query.decrementRef();
}
}
/**
* @see org.apache.lucene.gdata.storage.Storage#getEntryLastModified(java.lang.String, java.lang.String)
*/
public Long getEntryLastModified(String entryId,String feedId) throws StorageException {
ReferenceCounter<StorageQuery> query = null;
try {
query = this.controller.getStorageQuery();
return new Long(query.get().getEntryLastModified(entryId,feedId));
} catch (IOException e) {
throw new StorageException("Can not access storage - "
+ e.getMessage(), e);
} finally {
if (query != null)
query.decrementRef();
}
}
/**
* @see org.apache.lucene.gdata.storage.Storage#getFeedLastModified(java.lang.String)
*/
public Long getFeedLastModified(String feedId) throws StorageException {
ReferenceCounter<StorageQuery> query = null;
try {
query = this.controller.getStorageQuery();
return new Long(query.get().getFeedLastModified(feedId));
} catch (IOException e) {
throw new StorageException("Can not access storage - "
+ e.getMessage(), e);
} finally {
if (query != null)
query.decrementRef();
}
}
} }

View File

@ -1,6 +1,7 @@
package org.apache.lucene.gdata.storage.lucenestorage; package org.apache.lucene.gdata.storage.lucenestorage;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.LinkedList; import java.util.LinkedList;
@ -13,11 +14,32 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.apache.lucene.document.Document; import org.apache.lucene.document.Document;
import org.apache.lucene.gdata.storage.StorageException; import org.apache.lucene.gdata.storage.StorageException;
import org.apache.lucene.gdata.storage.lucenestorage.StorageEntryWrapper.StorageOperation;
import org.apache.lucene.index.IndexModifier; import org.apache.lucene.index.IndexModifier;
import org.apache.lucene.index.Term; import org.apache.lucene.index.Term;
/** /**
* TODO document this * The StorageModifier is the a Singleton component of the LuceneStorage. There
* is one single instance of this class modifying the index used to store all
* the gdata Entities as Entries, Feeds and Users. This class contains an
* instance of {@link org.apache.lucene.index.IndexModifier} used to manage all
* delete and add actions to the storage.
* <p>
* To prevent the storage component from opening and closing the
* {@link org.apache.lucene.index.IndexModifier} for every modifying operation
* the incoming entry actions (DELETE, UPDATE, INSERT) will be buffered in a
* registered instance of
* {@link org.apache.lucene.gdata.storage.lucenestorage.StorageBuffer}. When a
* certain amout (specified as the persistfactor in the configuration file) of
* modifications have been executed the StorageModifier will persist the
* buffered entries.
* </p>
* <p>
* Feed and User operations won't be buffered. These actions occure not very
* often compared to entry actions. Every call of an user / feed modifying
* operation forces all changes to be written to the storage index.
* </p>
*
* @author Simon Willnauer * @author Simon Willnauer
* *
*/ */
@ -25,10 +47,14 @@ public class StorageModifier {
protected static final Log LOG = LogFactory.getLog(StorageModifier.class); protected static final Log LOG = LogFactory.getLog(StorageModifier.class);
private final List<Term> deletedDocumentQueue; private final List<Term> deletedDocumentQueue;
private final List<Term> deletedForUpdateDocumentQueue; private final List<Term> deletedForUpdateDocumentQueue;
private final Map<String,Document> documentMap; private final Map<String, Document> documentMap;
private final List<Document> forceWriteDocuments;
private final List<Term> forceWriteTerms;
private volatile int persistFactor; private volatile int persistFactor;
@ -45,61 +71,70 @@ public class StorageModifier {
private Lock readLock = this.lock.readLock(); private Lock readLock = this.lock.readLock();
private Lock writeLock = this.lock.writeLock(); private Lock writeLock = this.lock.writeLock();
private final static int DEFAULT_OPTIMIZE_INTERVAL = 10; private final static int DEFAULT_OPTIMIZE_INTERVAL = 10;
private final int optimizeInterval; private final int optimizeInterval;
private int optimizeCounter = 0;
private volatile int optimizeCounter = 0;
private final StorageCoreController controller;
/** /**
* TODO document this * Creates a new StorageModifier
* @param modifier *
* @param buffer * @param controller -
* @param persitsFactor * the registered StorageController
* @param optimizeInterval * @param modifier -
* the IndexModifier
* @param buffer -
* the StorageBuffer
* @param persitsFactor -
* the factor when the changes will be persisted to the storage
* index
* @param optimizeInterval -
* after how many storage operations the index will be optimized
*/ */
public StorageModifier(final IndexModifier modifier, protected StorageModifier(final StorageCoreController controller,
final StorageBuffer buffer, int persitsFactor,int optimizeInterval) { final IndexModifier modifier, final StorageBuffer buffer,
int persitsFactor, int optimizeInterval) {
this.deletedDocumentQueue = new LinkedList<Term>(); this.deletedDocumentQueue = new LinkedList<Term>();
this.deletedForUpdateDocumentQueue = new LinkedList<Term>(); this.deletedForUpdateDocumentQueue = new LinkedList<Term>();
this.documentMap = new HashMap<String,Document>(persitsFactor); this.documentMap = new HashMap<String, Document>(persitsFactor);
this.forceWriteDocuments = new ArrayList<Document>(5);
this.forceWriteTerms = new ArrayList<Term>(5);
this.buffer = buffer; this.buffer = buffer;
this.controller = controller;
this.persistFactor = persitsFactor > 0 ? persitsFactor this.persistFactor = persitsFactor > 0 ? persitsFactor
: DEFAULT_PERSIST_FACTOR; : DEFAULT_PERSIST_FACTOR;
this.modifier = modifier; this.modifier = modifier;
this.optimizeInterval = optimizeInterval < DEFAULT_OPTIMIZE_INTERVAL?DEFAULT_OPTIMIZE_INTERVAL:optimizeInterval; this.optimizeInterval = optimizeInterval < DEFAULT_OPTIMIZE_INTERVAL ? DEFAULT_OPTIMIZE_INTERVAL
: optimizeInterval;
} }
/** /**
* TODO document this * Updates the given entry. First the alredy persisted entry will be
* @param wrapper * removed, after marking as deleted the new Entry will be written.
* @throws StorageException *
* @param wrapper -
* the wrapper containing the entry
* @throws StorageException -
* if the entry can not be stored
*/ */
public void updateEntry(StorageEntryWrapper wrapper) public void updateEntry(StorageEntryWrapper wrapper)
throws StorageException { throws StorageException {
try { if(wrapper.getOperation() != StorageOperation.UPDATE)
this.readLock.lock(); throw new StorageException("Illegal method call -- updateEntry does not accept other storageOperations than update");
Term tempTerm = new Term(StorageEntryWrapper.FIELD_ENTRY_ID, wrapper.getEntryId());
this.buffer.addEntry(wrapper);
this.deletedForUpdateDocumentQueue.add(tempTerm);
this.documentMap.put(wrapper.getEntryId(),wrapper.getLuceneDocument());
storageModified();
} finally {
this.readLock.unlock();
}
}
/**
* TODO document this
* @param wrapper
* @throws StorageException
*/
public void insertEntry(StorageEntryWrapper wrapper) throws StorageException {
this.readLock.lock(); this.readLock.lock();
try { try {
Term tempTerm = new Term(StorageEntryWrapper.FIELD_ENTRY_ID,
wrapper.getEntryId());
this.documentMap.put(wrapper.getEntryId(), wrapper
.getLuceneDocument());
this.deletedForUpdateDocumentQueue.add(tempTerm);
this.buffer.addEntry(wrapper); this.buffer.addEntry(wrapper);
this.documentMap.put(wrapper.getEntryId(),wrapper.getLuceneDocument());
storageModified(); storageModified();
} finally { } finally {
this.readLock.unlock(); this.readLock.unlock();
@ -107,18 +142,172 @@ public class StorageModifier {
} }
/** /**
*TODO document this * Inserts a new Entry to the Lucene index storage
* @param entryId *
* @param feedId * @param wrapper -
* @throws StorageException * the wrapper containing the entry
* @throws StorageException -
* if the entry can not be stored
*/
public void insertEntry(StorageEntryWrapper wrapper)
throws StorageException {
if(wrapper.getOperation() != StorageOperation.INSERT)
throw new StorageException("Illegal method call -- insertEntry does not accept other storage operations than insert");
this.readLock.lock();
try {
this.documentMap.put(wrapper.getEntryId(), wrapper
.getLuceneDocument());
this.buffer.addEntry(wrapper);
storageModified();
} finally {
this.readLock.unlock();
}
}
/**
* Deletes the entry for the given entry id.
* @param wrapper - the wrapper containing the information to delete
*
* @throws StorageException -
* if the entry can not be deleted
* *
*/ */
public void deleteEntry(final String entryId, final String feedId) throws StorageException { public void deleteEntry(final StorageEntryWrapper wrapper)
throws StorageException {
if(wrapper.getOperation() != StorageOperation.DELETE)
throw new StorageException("Illegal method call -- insertEntry does not accept other storage operations than delete");
this.readLock.lock();
try { try {
this.readLock.lock(); Term tempTerm = new Term(StorageEntryWrapper.FIELD_ENTRY_ID,
Term tempTerm = new Term(StorageEntryWrapper.FIELD_ENTRY_ID, entryId); wrapper.getEntryId());
this.buffer.addDeleted(entryId, feedId);
this.deletedDocumentQueue.add(tempTerm); this.deletedDocumentQueue.add(tempTerm);
this.buffer.addDeleted(wrapper.getEntryId(), wrapper.getFeedId());
storageModified();
} finally {
this.readLock.unlock();
}
}
/**
* Adds a new Feed to the storage. Feed action will be not buffered. Call to
* this method forces the index to be written.
*
* @param wrapper -
* the wrapper containing the feed;
* @throws StorageException -
* if the feed can not be written
*/
public void createFeed(StorageFeedWrapper wrapper) throws StorageException {
this.readLock.lock();
try {
this.forceWriteDocuments.add(wrapper.getLuceneDocument());
storageModified();
} finally {
this.readLock.unlock();
}
}
/**
* Adds a new accountr to the storage. User action will be not buffered. Call to
* this method forces the index to be written.
*
* @param account
* -the wrapper containig the user to be persisted
* @throws StorageException -
* if the user can not be persisted.
*/
public void createAccount(StorageAccountWrapper account) throws StorageException {
this.readLock.lock();
try {
this.forceWriteDocuments.add(account.getLuceneDocument());
storageModified();
} finally {
this.readLock.unlock();
}
}
/**
* Deletes the user with the given username. User action will be not
* buffered. Call to this method forces the index to be written.
*
* @param accountName -
* the user to be deleted
* @throws StorageException -
* If the user could not be deleted
*/
public void deleteAccount(String accountName) throws StorageException {
this.readLock.lock();
try {
//TODO delete all feeds and entries of this account
this.forceWriteTerms.add(new Term(
StorageAccountWrapper.FIELD_ACCOUNTNAME, accountName));
storageModified();
} finally {
this.readLock.unlock();
}
}
/**
* User action will be not buffered. Call to this method forces the index to
* be written.
*
* @param user
* -the wrapper containig the user to be persisted
* @throws StorageException -
* if the user can not be persisted.
*/
public void updateAccount(final StorageAccountWrapper user)
throws StorageException {
this.readLock.lock();
try {
this.forceWriteTerms.add(new Term(
StorageAccountWrapper.FIELD_ACCOUNTNAME, user.getUser()
.getName()));
this.forceWriteDocuments.add(user.getLuceneDocument());
storageModified();
} finally {
this.readLock.unlock();
}
}
/**
* Feed action will be not buffered. Call to this method forces the index to
* be written.
*
* @param wrapper -
* the wrapper containig the feed
* @throws StorageException -
* if the feed can not be persisted
*/
public void updateFeed(final StorageFeedWrapper wrapper)
throws StorageException {
this.readLock.lock();
try {
this.forceWriteTerms.add(new Term(StorageFeedWrapper.FIELD_FEED_ID,
wrapper.getFeed().getId()));
this.forceWriteDocuments.add(wrapper.getLuceneDocument());
storageModified();
} finally {
this.readLock.unlock();
}
}
/**
* Deletes the feed with the given feed id Feed action will be not buffered.
* Call to this method forces the index to be written.
* All entries referencing the given feed id will be deleted as well!
* @param feedId -
* the id of the feed to delete
* @throws StorageException -
* if the feed can not be deleted
*/
public void deleteFeed(final String feedId) throws StorageException {
this.readLock.lock();
try {
this.deletedDocumentQueue.add(new Term(StorageEntryWrapper.FIELD_FEED_REFERENCE,feedId));
this.forceWriteTerms.add(new Term(StorageFeedWrapper.FIELD_FEED_ID,
feedId));
storageModified(); storageModified();
} finally { } finally {
this.readLock.unlock(); this.readLock.unlock();
@ -131,7 +320,9 @@ public class StorageModifier {
try { try {
incrementCounter(); incrementCounter();
if (this.persistFactor > this.modifiedCounter) if (this.persistFactor > this.modifiedCounter
&& this.forceWriteDocuments.size() <= 0
&& this.forceWriteTerms.size() <= 0)
return; return;
if (LOG.isInfoEnabled()) if (LOG.isInfoEnabled())
@ -142,7 +333,10 @@ public class StorageModifier {
this.modifiedCounter = 0; this.modifiedCounter = 0;
} catch (IOException e) { } catch (IOException e) {
LOG.error("Writing persistent index failed - Recovering", e); LOG.error("Writing persistent index failed - Recovering", e);
throw new StorageException("could not write to storage index -- "+e.getMessage(),e);
} finally { } finally {
this.readLock.lock(); this.readLock.lock();
this.writeLock.unlock(); this.writeLock.unlock();
@ -150,7 +344,7 @@ public class StorageModifier {
} }
protected void forceWrite() throws IOException, StorageException { protected void forceWrite() throws IOException {
this.writeLock.lock(); this.writeLock.lock();
try { try {
if (LOG.isInfoEnabled()) if (LOG.isInfoEnabled())
@ -164,22 +358,27 @@ public class StorageModifier {
} }
} }
private void requestNewIndexModifier() throws IOException, StorageException { private void requestNewIndexModifier() throws IOException {
StorageCoreController controller = StorageCoreController
.getStorageCoreController(); this.controller.registerNewStorageQuery();
controller.registerNewStorageQuery(); this.buffer = this.controller.releaseNewStorageBuffer();
this.buffer = controller.releaseNewStorageBuffer(); this.modifier = this.controller.createIndexModifier();
this.modifier = controller.createIndexModifier();
} }
private void writePersistentIndex(final boolean optimize) throws IOException { private void writePersistentIndex(final boolean optimize)
throws IOException {
try { try {
/* /*
* first delete all updated documents * first delete all updated documents
*/ */
for(Term entryIdTerm : this.deletedForUpdateDocumentQueue) { for (Term entryIdTerm : this.deletedForUpdateDocumentQueue) {
this.modifier.deleteDocuments(entryIdTerm); this.modifier.deleteDocuments(entryIdTerm);
} }
for (Term term : this.forceWriteTerms) {
this.modifier.deleteDocuments(term);
}
/* /*
* add all documents * add all documents
*/ */
@ -187,34 +386,44 @@ public class StorageModifier {
for (Document doc : documents) { for (Document doc : documents) {
this.modifier.addDocument(doc); this.modifier.addDocument(doc);
} }
/*
* write all users or feeds
*/
for (Document docs : this.forceWriteDocuments) {
this.modifier.addDocument(docs);
}
/* /*
* delete all documents marked as deleted. As the DocumentIDs are * delete all documents marked as deleted. As the DocumentIDs are
* unique the document marked as deleted must not persist after the * unique the document marked as deleted must not persist after the
* index has been written. * index has been written. In the case of an update of a document
* In the case of an update of a document and a previous delete the concurrency component will not allow an update. * and a previous delete the concurrency component will not allow an
* new inserted entries can not be deleted accidently- * update. new inserted entries can not be deleted accidently-
*/ */
for (Term entryIdTerm : this.deletedDocumentQueue) { for (Term entryIdTerm : this.deletedDocumentQueue) {
this.modifier.deleteDocuments(entryIdTerm); this.modifier.deleteDocuments(entryIdTerm);
} }
this.modifier.flush(); this.modifier.flush();
if(optimize){ if (optimize) {
if(LOG.isInfoEnabled()) if (LOG.isInfoEnabled())
LOG.info("Optimizing index -- optimize interval "+this.optimizeInterval); LOG.info("Optimizing index -- optimize interval "
+ this.optimizeInterval);
this.modifier.optimize(); this.modifier.optimize();
} }
} finally { } finally {
if(optimize) if (optimize)
this.optimizeCounter = 0; this.optimizeCounter = 0;
this.modifier.close(); this.modifier.close();
this.deletedForUpdateDocumentQueue.clear(); this.deletedForUpdateDocumentQueue.clear();
this.deletedDocumentQueue.clear(); this.deletedDocumentQueue.clear();
this.documentMap.clear(); this.documentMap.clear();
this.forceWriteDocuments.clear();
this.forceWriteTerms.clear();
} }
} }
protected void close()throws IOException{ protected void close() throws IOException {
this.writeLock.lock(); this.writeLock.lock();
try { try {
if (LOG.isInfoEnabled()) if (LOG.isInfoEnabled())
@ -228,7 +437,7 @@ public class StorageModifier {
} }
} }
private void incrementCounter(){ private void incrementCounter() {
this.optimizeCounter++; this.optimizeCounter++;
this.modifiedCounter++; this.modifiedCounter++;
} }

View File

@ -22,9 +22,13 @@ import java.util.ArrayList;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.lucene.document.Document; import org.apache.lucene.document.Document;
import org.apache.lucene.gdata.server.FeedNotFoundException; import org.apache.lucene.gdata.data.GDataAccount;
import org.apache.lucene.gdata.server.GDataEntityBuilder; import org.apache.lucene.gdata.server.GDataEntityBuilder;
import org.apache.lucene.gdata.server.registry.ProvidedService;
import org.apache.lucene.gdata.storage.StorageException;
import org.apache.lucene.index.Term; import org.apache.lucene.index.Term;
import org.apache.lucene.search.BooleanClause; import org.apache.lucene.search.BooleanClause;
import org.apache.lucene.search.BooleanQuery; import org.apache.lucene.search.BooleanQuery;
@ -37,20 +41,22 @@ import org.apache.lucene.search.TermQuery;
import org.apache.lucene.search.BooleanClause.Occur; import org.apache.lucene.search.BooleanClause.Occur;
import com.google.gdata.data.BaseEntry; import com.google.gdata.data.BaseEntry;
import com.google.gdata.data.ExtensionProfile; import com.google.gdata.data.BaseFeed;
import com.google.gdata.data.DateTime;
import com.google.gdata.util.ParseException; import com.google.gdata.util.ParseException;
/** /**
* StorageQuery wrapps a Lucene {@link org.apache.lucene.search.IndexSearcher} * StorageQuery wrapps a Lucene {@link org.apache.lucene.search.IndexSearcher}
* and a {@link org.apache.lucene.gdata.storage.lucenestorage.StorageBuffer} to * and a {@link org.apache.lucene.gdata.storage.lucenestorage.StorageBuffer} to
* perform all request on the lucene storage. * perform all request on the lucene storage. The wrapped components are thread -
* The wrapped components are thread - safe. * safe.
* <p> * <p>
* An instance of this class will serve all client requests. To obtain the * An instance of this class will serve all client requests. To obtain the
* current instance of the {@link StorageQuery} the method * current instance of the {@link StorageQuery} the method
* {@link org.apache.lucene.gdata.storage.lucenestorage.StorageCoreController#getStorageQuery()} * {@link org.apache.lucene.gdata.storage.lucenestorage.StorageCoreController#getStorageQuery()}
* has to be invoked. This method will release the current StorageQuery. * has to be invoked. This method will release the current StorageQuery.
* </p> * </p>
*
* @see org.apache.lucene.search.IndexSearcher * @see org.apache.lucene.search.IndexSearcher
* @see org.apache.lucene.gdata.storage.lucenestorage.StorageCoreController * @see org.apache.lucene.gdata.storage.lucenestorage.StorageCoreController
* @see org.apache.lucene.gdata.storage.lucenestorage.StorageBuffer * @see org.apache.lucene.gdata.storage.lucenestorage.StorageBuffer
@ -59,6 +65,7 @@ import com.google.gdata.util.ParseException;
* *
*/ */
public class StorageQuery { public class StorageQuery {
private static final Log LOG = LogFactory.getLog(StorageQuery.class);
private final StorageBuffer buffer; private final StorageBuffer buffer;
private final Searcher searcher; private final Searcher searcher;
@ -66,8 +73,8 @@ public class StorageQuery {
/* /*
* Sort the result by timestamp desc * Sort the result by timestamp desc
*/ */
private final Sort timeStampSort = new Sort(new SortField(StorageEntryWrapper.FIELD_TIMESTAMP, private final Sort timeStampSort = new Sort(new SortField(
SortField.STRING, true)); StorageEntryWrapper.FIELD_TIMESTAMP, SortField.STRING, true));
/** /**
* Creates a new StorageQuery * Creates a new StorageQuery
@ -107,7 +114,8 @@ public class StorageQuery {
*/ */
private Hits storageFeedQuery(final String feedId, final Sort sort) private Hits storageFeedQuery(final String feedId, final Sort sort)
throws IOException { throws IOException {
TermQuery query = new TermQuery(new Term(StorageEntryWrapper.FIELD_FEED_ID, feedId)); TermQuery query = new TermQuery(new Term(
StorageEntryWrapper.FIELD_FEED_REFERENCE, feedId));
return this.searcher.search(query, new ModifiedEntryFilter(this.buffer return this.searcher.search(query, new ModifiedEntryFilter(this.buffer
.getExculdList()), sort); .getExculdList()), sort);
@ -154,44 +162,55 @@ public class StorageQuery {
* how many entries are requested * how many entries are requested
* @param startIndex - * @param startIndex -
* the offset of the entriy to start from. * the offset of the entriy to start from.
* @param profil - * @param config -
* the extension profile used to create the entriy instances * the FeedInstanceConfiguration contaning extension profile used
* to create the entriy instances
* @return - an ordered list of {@link BaseEntry} objects, or an empty list * @return - an ordered list of {@link BaseEntry} objects, or an empty list
* if no entries could be found * if no entries could be found
* @throws IOException - * @throws IOException -
* if the index could not be queries or the entries could not be * if the index could not be queries or the entries could not be
* build * build
* @throws FeedNotFoundException -
* if the requested feed is not registered
* @throws ParseException - * @throws ParseException -
* if an entry could not be parsed while building it from the * if an entry could not be parsed while building it from the
* Lucene Document. * Lucene Document.
*/ */
// TODO check input parameter // TODO check input parameter
public List<BaseEntry> getLatestFeedQuery(final String feedId, @SuppressWarnings("unchecked")
public BaseFeed getLatestFeedQuery(final String feedId,
final int resultCount, final int startIndex, final int resultCount, final int startIndex,
final ExtensionProfile profil) throws IOException, final ProvidedService config) throws IOException,
FeedNotFoundException, ParseException { ParseException {
DateTime updated = null;
Hits feedHits = storageFeedQuery(feedId);
if(feedHits.length() == 0)
return null;
BaseFeed retVal = buildFeedFromLuceneDocument(feedHits.doc(0),config);
List<BaseEntry> returnList = new ArrayList<BaseEntry>(resultCount); List<BaseEntry> returnList = new ArrayList<BaseEntry>(resultCount);
List<StorageEntryWrapper> bufferedWrapperList = this.buffer List<StorageEntryWrapper> bufferedWrapperList = this.buffer
.getSortedEntries(feedId); .getSortedEntries(feedId);
int alreadyAdded = 0; int alreadyAdded = 0;
int offset = startIndex - 1; int offset = startIndex - 1;
if (bufferedWrapperList != null if (bufferedWrapperList != null
&& bufferedWrapperList.size() >= startIndex) { && bufferedWrapperList.size() >= startIndex) {
updated = bufferedWrapperList.get(0).getEntry().getUpdated();
for (; alreadyAdded < resultCount; alreadyAdded++) { for (; alreadyAdded < resultCount; alreadyAdded++) {
if ((bufferedWrapperList.size() - offset) > 0) { if ((bufferedWrapperList.size() - offset) > 0) {
StorageEntryWrapper wrappedEntry = bufferedWrapperList StorageEntryWrapper wrappedEntry = bufferedWrapperList
.get(offset++); .get(offset++);
returnList.add(wrappedEntry.getEntry()); returnList
.add(wrappedEntry.getEntry());
} else } else
break; break;
} }
// reset offset // reset offset
offset = startIndex - 1; offset = startIndex - 1;
if (alreadyAdded == resultCount) if (alreadyAdded == resultCount){
return returnList; retVal.getEntries().addAll(returnList);
retVal.setUpdated(updated);
return retVal;
}
} else { } else {
/* /*
* if the buffersize is less than the startindex the buffersize must * if the buffersize is less than the startindex the buffersize must
@ -207,12 +226,22 @@ public class StorageQuery {
for (; (offset < hits.length()) && (alreadyAdded < resultCount); offset++, alreadyAdded++) { for (; (offset < hits.length()) && (alreadyAdded < resultCount); offset++, alreadyAdded++) {
Document doc = hits.doc(offset); Document doc = hits.doc(offset);
BaseEntry entry = buildEntryFromLuceneDocument(doc, profil); BaseEntry entry = buildEntryFromLuceneDocument(doc, config);
returnList.add(entry); returnList.add(entry);
} }
if(updated == null){
try{
long updatedTimeStamp = Long.parseLong(hits.doc(0).get(StorageEntryWrapper.FIELD_TIMESTAMP));
updated = new DateTime(updatedTimeStamp);
}catch (Exception e) {
LOG.warn("could not create DateTime -- "+e.getMessage(),e);
updated = buildEntryFromLuceneDocument(hits.doc(0),config).getUpdated();
}
}
} }
return returnList; retVal.setUpdated(updated);
retVal.getEntries().addAll(returnList);
return retVal;
} }
/** /**
@ -228,22 +257,21 @@ public class StorageQuery {
* the entry to fetch * the entry to fetch
* @param feedId - * @param feedId -
* the feedid eg. feed context * the feedid eg. feed context
* @param profil - * @param config -
* the extension profile used to create the entriy instances * the FeedInstanceConfiguration contaning extension profile used
* to create the entriy instances
* @return - the requested {@link BaseEntry} or <code>null</code> if the * @return - the requested {@link BaseEntry} or <code>null</code> if the
* entry can not be found * entry can not be found
* @throws IOException - * @throws IOException -
* if the index could not be queries or the entries could not be * if the index could not be queries or the entries could not be
* build * build
* @throws FeedNotFoundException -
* if the requested feed is not registered
* @throws ParseException - * @throws ParseException -
* if an entry could not be parsed while building it from the * if an entry could not be parsed while building it from the
* Lucene Document. * Lucene Document.
*/ */
public BaseEntry singleEntryQuery(final String entryId, public BaseEntry singleEntryQuery(final String entryId,
final String feedId, final ExtensionProfile profil) final String feedId, final ProvidedService config)
throws IOException, FeedNotFoundException, ParseException { throws IOException, ParseException {
StorageEntryWrapper wrapper = this.buffer.getEntry(entryId, feedId); StorageEntryWrapper wrapper = this.buffer.getEntry(entryId, feedId);
if (wrapper == null) { if (wrapper == null) {
@ -252,8 +280,13 @@ public class StorageQuery {
return null; return null;
Document doc = hits.doc(0); Document doc = hits.doc(0);
return buildEntryFromLuceneDocument(doc, profil); return buildEntryFromLuceneDocument(doc, config);
} }
/*
* ServerBaseEntry enables the dynamic element of the entry like the
* links to be dynamic. BufferedEntries will be reused until they are
* written.
*/
return wrapper.getEntry(); return wrapper.getEntry();
} }
@ -274,21 +307,21 @@ public class StorageQuery {
* the entriy ids to fetch. * the entriy ids to fetch.
* @param feedId - * @param feedId -
* the feed id eg. feed context. * the feed id eg. feed context.
* @param profil - * @param config -
* the extension profile used to create the entry instances. * the FeedInstanceConfiguration contaning extension profile used
* to create the entriy instances
*
* @return - the list of entries corresponding to the given entry id list. * @return - the list of entries corresponding to the given entry id list.
* @throws IOException - * @throws IOException -
* if the index could not be queries or the entries could not be * if the index could not be queries or the entries could not be
* build * build
* @throws FeedNotFoundException -
* if the requested feed is not registered
* @throws ParseException - * @throws ParseException -
* if an entry could not be parsed while building it from the * if an entry could not be parsed while building it from the
* Lucene Document. * Lucene Document.
*/ */
public List<BaseEntry> entryQuery(List<String> entryIds, public List<BaseEntry> entryQuery(List<String> entryIds,
final String feedId, final ExtensionProfile profil) final String feedId, final ProvidedService config)
throws IOException, FeedNotFoundException, ParseException { throws IOException, ParseException {
List<BaseEntry> resultList = new ArrayList<BaseEntry>(entryIds.size()); List<BaseEntry> resultList = new ArrayList<BaseEntry>(entryIds.size());
List<String> searchList = new ArrayList<String>(entryIds.size()); List<String> searchList = new ArrayList<String>(entryIds.size());
for (String entry : entryIds) { for (String entry : entryIds) {
@ -308,7 +341,7 @@ public class StorageQuery {
while (hitIterator.hasNext()) { while (hitIterator.hasNext()) {
Hit hit = (Hit) hitIterator.next(); Hit hit = (Hit) hitIterator.next();
Document doc = hit.getDocument(); Document doc = hit.getDocument();
BaseEntry entry = buildEntryFromLuceneDocument(doc, profil); BaseEntry entry = buildEntryFromLuceneDocument(doc, config);
resultList.add(entry); resultList.add(entry);
} }
@ -318,15 +351,44 @@ public class StorageQuery {
} }
private BaseEntry buildEntryFromLuceneDocument(final Document doc, private BaseEntry buildEntryFromLuceneDocument(final Document doc,
final ExtensionProfile profil) throws FeedNotFoundException, final ProvidedService config) throws ParseException,
ParseException, IOException { IOException {
StringReader reader = new StringReader(doc.getField(StorageEntryWrapper.FIELD_CONTENT) StringReader reader = new StringReader(doc.getField(
.stringValue()); StorageEntryWrapper.FIELD_CONTENT).stringValue());
return GDataEntityBuilder.buildEntry(doc.getField(StorageEntryWrapper.FIELD_FEED_ID) return GDataEntityBuilder.buildEntry( reader, config);
.stringValue(), reader, profil);
} }
private BaseFeed buildFeedFromLuceneDocument(final Document doc,
final ProvidedService config) throws ParseException,
IOException {
StringReader reader = new StringReader(doc.getField(
StorageFeedWrapper.FIELD_CONTENT).stringValue());
return GDataEntityBuilder
.buildFeed(reader, config);
}
/**
* Queries the storage for an user instance
*
* @param username -
* the username (primary key)
* @return - the user instance if found or <code>null</code> if not exists
* @throws IOException -
* if the storage can not be accessed.
*/
public GDataAccount getUser(final String username) throws IOException {
if (username == null)
return null;
TermQuery query = new TermQuery(new Term(
StorageAccountWrapper.FIELD_ACCOUNTNAME, username));
Hits h = this.searcher.search(query);
if (h.length() == 0)
return null;
return StorageAccountWrapper.buildEntity(h.doc(0));
}
/** /**
* Closes all resources used in the {@link StorageQuery}. The instance can * Closes all resources used in the {@link StorageQuery}. The instance can
* not be reused after invoking this method. * not be reused after invoking this method.
@ -339,4 +401,92 @@ public class StorageQuery {
this.buffer.close(); this.buffer.close();
} }
/**
* Checks whether a feed for the given feedID is stored
* @param feedId - the feed ID
* @return <code>true</code> if and only if a feed is stored for the provided feed ID, <code>false</code> if no feed for the given id is stored
* @throws IOException
*/
public boolean isFeedStored(String feedId)throws IOException{
Hits h = storageFeedQuery(feedId);
return (h.length() > 0);
}
/**
* Looks up the feedtype for the given feed ID
* @param feedID - the feed ID
* @return - the feed type
* @throws IOException - if the storage can not be accessed
*/
public String getService(String feedID) throws IOException {
Hits hits = storageFeedQuery(feedID);
if (hits.length() <= 0)
return null;
Document doc = hits.doc(0);
String feedType = doc.get(StorageFeedWrapper.FIELD_SERVICE_ID);
return feedType;
}
private Hits storageFeedQuery(String feedId) throws IOException {
TermQuery query = new TermQuery(new Term(
StorageFeedWrapper.FIELD_FEED_ID, feedId));
return this.searcher.search(query);
}
/**
* Looks up the account reference for the given feed id
* @param feedId - id of the feed
* @return - the name of the account associated with the feed for the given feed id, or <code>null</code> if the feed is not stored
* @throws IOException - if the storage can not be accessed
*/
public String getAccountNameForFeedId(String feedId) throws IOException {
Hits h = storageFeedQuery(feedId);
if(h.length() == 0)
return null;
Document doc = h.doc(0);
return doc.get(StorageFeedWrapper.FIELD_ACCOUNTREFERENCE);
}
protected long getEntryLastModified(final String entryId,final String feedId) throws IOException, StorageException{
StorageEntryWrapper wrapper = this.buffer.getEntry(entryId,feedId);
if(wrapper != null)
return wrapper.getTimestamp();
Hits h = storageQuery(entryId);
if(h.length() > 0)
try{
return Long.parseLong(h.doc(0).get(StorageEntryWrapper.FIELD_TIMESTAMP));
}catch (Exception e) {
LOG.warn("Can not parse timestamp from entry -- "+h.doc(0).get(StorageEntryWrapper.FIELD_TIMESTAMP));
}
else
throw new StorageException("Entry not found");
return 0;
}
protected long getFeedLastModified(final String feedId)throws IOException{
Long bufferedTime = this.buffer.getFeedLastModified(feedId);
if(bufferedTime != null)
return bufferedTime;
Hits entryHits = storageFeedQuery(feedId,this.timeStampSort);
if(entryHits.length() > 0){
try{
return Long.parseLong(entryHits.doc(0).getField(StorageEntryWrapper.FIELD_TIMESTAMP).stringValue());
}catch (Exception e) {
LOG.warn("Can not parse timestamp from entry -- "+entryHits.doc(0).get(StorageEntryWrapper.FIELD_TIMESTAMP));
}
}
return 0;
}
protected boolean isEntryStored(String entryId,String feedId) throws IOException{
if(this.buffer.getEntry(entryId,feedId)!=null)
return true;
Hits h = storageQuery(entryId);
if(h.length() > 0)
return true;
return false;
}
} }

View File

@ -38,6 +38,7 @@ public class StorageConfigurator {
private final boolean recover; private final boolean recover;
private final boolean ramDirectory;
private static StorageConfigurator INSTANCE = null; private static StorageConfigurator INSTANCE = null;
private final int indexOptimizeInterval; private final int indexOptimizeInterval;
@ -64,7 +65,8 @@ public class StorageConfigurator {
.getProperty("gdata.server.storage.lucene.directory"); .getProperty("gdata.server.storage.lucene.directory");
this.indexOptimizeInterval = Integer.parseInt(properties this.indexOptimizeInterval = Integer.parseInt(properties
.getProperty("gdata.server.storage.lucene.optimizeInterval")); .getProperty("gdata.server.storage.lucene.optimizeInterval"));
this.ramDirectory = Boolean.parseBoolean(properties
.getProperty("gdata.server.storage.lucene.directory.ramDirectory"));
} }
/** /**
@ -141,4 +143,11 @@ public class StorageConfigurator {
return this.indexOptimizeInterval; return this.indexOptimizeInterval;
} }
/**
* @return Returns the ramDirectory.
*/
public boolean isRamDirectory() {
return this.ramDirectory;
}
} }

View File

@ -21,13 +21,13 @@ import junit.framework.TestCase;
import org.apache.lucene.gdata.server.GDataRequest.GDataRequestType; import org.apache.lucene.gdata.server.GDataRequest.GDataRequestType;
import org.apache.lucene.gdata.server.GDataRequest.OutputFormat; import org.apache.lucene.gdata.server.GDataRequest.OutputFormat;
import org.apache.lucene.gdata.server.registry.FeedInstanceConfigurator;
import org.apache.lucene.gdata.server.registry.GDataServerRegistry; import org.apache.lucene.gdata.server.registry.GDataServerRegistry;
import org.apache.lucene.gdata.server.registry.ProvidedService;
import org.apache.lucene.gdata.server.registry.RegistryException;
import org.apache.lucene.gdata.utils.ProvidedServiceStub;
import org.apache.lucene.gdata.utils.StorageStub;
import org.easymock.MockControl; import org.easymock.MockControl;
import com.google.gdata.data.ExtensionProfile;
import com.google.gdata.data.Feed;
/** /**
* *
* @author Simon Willnauer * @author Simon Willnauer
@ -39,14 +39,21 @@ public class TestGDataRequest extends TestCase {
private MockControl control; private MockControl control;
private GDataRequest feedRequest; private GDataRequest feedRequest;
static{
try {
GDataServerRegistry.getRegistry().registerComponent(StorageStub.class);
} catch (RegistryException e) {
e.printStackTrace();
}
}
@Override @Override
protected void setUp() throws Exception { protected void setUp() throws Exception {
FeedInstanceConfigurator configurator = new FeedInstanceConfigurator(); ProvidedService configurator = new ProvidedServiceStub();
configurator.setFeedType(Feed.class); GDataServerRegistry.getRegistry().registerService(configurator);
configurator.setFeedId("feed");
configurator.setExtensionProfileClass(ExtensionProfile.class);
GDataServerRegistry.getRegistry().registerFeed(configurator);
this.control = MockControl.createControl(HttpServletRequest.class); this.control = MockControl.createControl(HttpServletRequest.class);
this.request = (HttpServletRequest) this.control.getMock(); this.request = (HttpServletRequest) this.control.getMock();
this.feedRequest = new GDataRequest(this.request,GDataRequestType.GET); this.feedRequest = new GDataRequest(this.request,GDataRequestType.GET);
@ -216,7 +223,7 @@ public class TestGDataRequest extends TestCase {
public void testGetSelfId() throws GDataRequestException{ public void testGetSelfId() throws GDataRequestException{
String host = "www.apache.org"; String host = "www.apache.org";
String feedAndEntryID = "/feed/entryid"; String feedAndEntryID = "/feed/entryid";
String queryString = "?max-results=25"; String queryString = "max-results=25";
this.control.expectAndDefaultReturn(this.request.getHeader("Host"),host); this.control.expectAndDefaultReturn(this.request.getHeader("Host"),host);
this.control.expectAndDefaultReturn(this.request.getRequestURI(),"/host/feed/entryId/15"); this.control.expectAndDefaultReturn(this.request.getRequestURI(),"/host/feed/entryId/15");
this.control.expectAndDefaultReturn(this.request.getPathInfo(),"/feed/entryId/15"); this.control.expectAndDefaultReturn(this.request.getPathInfo(),"/feed/entryId/15");
@ -227,13 +234,13 @@ public class TestGDataRequest extends TestCase {
queryString); queryString);
this.control.replay(); this.control.replay();
this.feedRequest.initializeRequest(); this.feedRequest.initializeRequest();
String selfID = "http://"+host+"/host/feed/entryId/15"+queryString; String selfID = "http://"+host+"/host/feed/entryId/15?"+queryString;
assertEquals("Self ID",selfID,this.feedRequest.getSelfId()); assertEquals("Self ID",selfID,this.feedRequest.getSelfId());
this.control.reset(); this.control.reset();
queryString = "?alt=rss&max-results=25"; queryString = "alt=rss&max-results=25";
this.control.expectAndDefaultReturn(this.request.getHeader("Host"),host); this.control.expectAndDefaultReturn(this.request.getHeader("Host"),host);
this.control.expectAndDefaultReturn(this.request.getRequestURI(),"/host/feed/entryId/15"); this.control.expectAndDefaultReturn(this.request.getRequestURI(),"/host/feed/entryId/15");
this.control.expectAndDefaultReturn(this.request.getPathInfo(),"/feed/entryId/15"); this.control.expectAndDefaultReturn(this.request.getPathInfo(),"/feed/entryId/15");
@ -244,7 +251,7 @@ public class TestGDataRequest extends TestCase {
queryString); queryString);
this.control.replay(); this.control.replay();
this.feedRequest.initializeRequest(); this.feedRequest.initializeRequest();
selfID = "http://"+host+"/host/feed/entryId/15"+queryString; selfID = "http://"+host+"/host/feed/entryId/15?"+queryString;
assertEquals("Self ID",selfID,this.feedRequest.getSelfId()); assertEquals("Self ID",selfID,this.feedRequest.getSelfId());
this.control.reset(); this.control.reset();
@ -296,7 +303,7 @@ public class TestGDataRequest extends TestCase {
queryString); queryString);
this.control.replay(); this.control.replay();
assertEquals("?"+maxResults,this.feedRequest.getQueryString()); assertEquals(maxResults,this.feedRequest.getQueryString());
this.control.reset(); this.control.reset();
} }
@ -342,7 +349,11 @@ public class TestGDataRequest extends TestCase {
} }
public void testIsEntryRequest(){ public void testgetAuthToken(){
this.control.expectAndDefaultReturn(this.request.getHeader("Authentication"),"GoogleLogin auth=bla");
this.control.replay();
assertEquals("bla",this.feedRequest.getAuthToken());
this.control.verify();
} }
@ -352,7 +363,8 @@ public class TestGDataRequest extends TestCase {
// String queryString = "?max-results=25"; // String queryString = "?max-results=25";
// String startIndex = "&start-index=26"; // String startIndex = "&start-index=26";
// this.control.expectAndDefaultReturn(this.request.getHeader("Host"),host); // this.control.expectAndDefaultReturn(this.request.getHeader("Host"),host);
// this.control.expectAndDefaultReturn(this.request.getPathInfo(),"/feed/entryId/15"); // this.control.expectAndDefaultReturn(this.request.getRequestURI(),"/feed/");
// this.control.expectAndDefaultReturn(this.request.getPathInfo(),"/feed/");
// this.control.expectAndReturn(this.request.getParameter("max-results"),"25",2); // this.control.expectAndReturn(this.request.getParameter("max-results"),"25",2);
// this.control.expectAndReturn(this.request.getParameter("start-index"),null); // this.control.expectAndReturn(this.request.getParameter("start-index"),null);
// this.control.expectAndDefaultReturn(this.request.getParameter("alt"), // this.control.expectAndDefaultReturn(this.request.getParameter("alt"),
@ -361,22 +373,27 @@ public class TestGDataRequest extends TestCase {
// queryString); // queryString);
// this.control.replay(); // this.control.replay();
// this.feedRequest.initializeRequest(); // this.feedRequest.initializeRequest();
// String nextID = "http://"+host+"/feed"+queryString+startIndex; // String nextID = "http://"+host+"/feed/"+queryString+startIndex;
// //
// assertEquals("Next ID",nextID,this.feedRequest.getNextId()); // assertEquals("Next ID",nextID,this.feedRequest.getNextId());
// this.control.reset(); // this.control.reset();
//
//
// queryString = "?alt=rss&max-results=25"; // queryString = "?alt=rss&max-results=25";
// //
// this.control.expectAndDefaultReturn(this.request.getHeader("Host"),host); // this.control.expectAndDefaultReturn(this.request.getHeader("Host"),host);
// this.control.expectAndDefaultReturn(this.request.getPathInfo(),"/feed/entryId/15"); // this.control.expectAndDefaultReturn(this.request.getRequestURI(),"/feed/");
// this.control.expectAndDefaultReturn(this.request.getPathInfo(),"/feed/");
// this.control.expectAndReturn(this.request.getParameter("max-results"),"25",2); // this.control.expectAndReturn(this.request.getParameter("max-results"),"25",2);
// this.control.expectAndReturn(this.request.getParameter("start-index"),"26",2); // this.control.expectAndReturn(this.request.getParameter("start-index"),"26",2);
// this.control.expectAndDefaultReturn(this.request.getParameter("alt"), // this.control.expectAndDefaultReturn(this.request.getParameter("alt"),
// null); // null);
// this.control.expectAndDefaultReturn(this.request.getQueryString(), // this.control.expectAndDefaultReturn(this.request.getQueryString(),
// queryString+startIndex); // queryString+startIndex);
// Enumeration e =
// this.control.expectAndDefaultReturn(this.request.getParameterNames(),)
//
//
// this.control.replay(); // this.control.replay();
// this.feedRequest.initializeRequest(); // this.feedRequest.initializeRequest();
// startIndex = "&start-index=51"; // startIndex = "&start-index=51";
@ -395,7 +412,7 @@ public class TestGDataRequest extends TestCase {
// null); // null);
// this.control.replay(); // this.control.replay();
// this.feedRequest.initializeRequest(); // this.feedRequest.initializeRequest();
// selfID = "http://"+host+"/feed"+"?max-results=25"; // String selfID = "http://"+host+"/feed"+"?max-results=25";
// //
// assertEquals("Self ID",selfID,this.feedRequest.getSelfId()); // assertEquals("Self ID",selfID,this.feedRequest.getSelfId());
// this.control.reset(); // this.control.reset();

View File

@ -71,11 +71,11 @@ public class TestGDataResponse extends TestCase {
PrintWriter writer = new PrintWriter(stringWriter); PrintWriter writer = new PrintWriter(stringWriter);
this.control.expectAndReturn(this.httpResponse.getWriter(),writer); this.control.expectAndReturn(this.httpResponse.getWriter(),writer);
this.httpResponse.setContentType(GDataResponse.XMLMIME_ATOM);
this.response.setOutputFormat(OutputFormat.ATOM); this.response.setOutputFormat(OutputFormat.ATOM);
this.control.replay(); this.control.replay();
this.response.sendResponse(createFeed(),new ExtensionProfile this.response.sendResponse(createFeed(),new ExtensionProfile());
());
assertEquals("Simple XML representation",stringWriter.toString(),generatedFeedAtom); assertEquals("Simple XML representation",stringWriter.toString(),generatedFeedAtom);
this.control.reset(); this.control.reset();
@ -84,6 +84,7 @@ public class TestGDataResponse extends TestCase {
this.control.expectAndReturn(this.httpResponse.getWriter(),writer); this.control.expectAndReturn(this.httpResponse.getWriter(),writer);
this.response.setOutputFormat(OutputFormat.RSS); this.response.setOutputFormat(OutputFormat.RSS);
this.httpResponse.setContentType(GDataResponse.XMLMIME_RSS);
this.control.replay(); this.control.replay();
this.response.sendResponse(createFeed(),new ExtensionProfile this.response.sendResponse(createFeed(),new ExtensionProfile

View File

@ -15,6 +15,13 @@
*/ */
package org.apache.lucene.gdata.server.registry; package org.apache.lucene.gdata.server.registry;
import org.apache.lucene.gdata.server.ServiceFactory;
import org.apache.lucene.gdata.servlet.handler.DefaultRequestHandlerFactory;
import org.apache.lucene.gdata.servlet.handler.RequestHandlerFactory;
import org.apache.lucene.gdata.storage.StorageController;
import org.apache.lucene.gdata.storage.lucenestorage.StorageCoreController;
import com.google.gdata.data.Entry;
import com.google.gdata.data.Feed; import com.google.gdata.data.Feed;
import junit.framework.TestCase; import junit.framework.TestCase;
@ -24,75 +31,102 @@ import junit.framework.TestCase;
* *
*/ */
public class TestFeedRegistry extends TestCase { public class TestFeedRegistry extends TestCase {
private GDataServerRegistry reg; private GDataServerRegistry reg;
private FeedInstanceConfigurator configurator; private ProvidedServiceConfig configurator;
@Override @Override
protected void setUp(){ protected void setUp(){
this.reg = GDataServerRegistry.getRegistry(); this.reg = GDataServerRegistry.getRegistry();
this.configurator = new FeedInstanceConfigurator(); this.configurator = new ProvidedServiceConfig();
} this.configurator.setEntryType(Entry.class);
/**
* @see junit.framework.TestCase#tearDown()
*/
@Override
protected void tearDown() throws Exception {
this.reg.flushRegistry();
}
/**
* Test method for 'org.apache.lucene.gdata.server.registry.FeedRegistry.getRegistry()'
*/
public void testGetRegistry() {
GDataServerRegistry reg1 = GDataServerRegistry.getRegistry();
assertEquals("test singleton",this.reg,reg1);
}
/**
* Test method for 'org.apache.lucene.gdata.server.registry.FeedRegistry.registerFeed(FeedInstanceConfigurator)'
*/
public void testRegisterFeed() {
String feedURL = "myFeed";
registerFeed(feedURL);
assertEquals("Registered Configurator",this.configurator,this.reg.getFeedConfigurator(feedURL));
assertNull("not registered Configurator",this.reg.getFeedConfigurator("somethingElse"));
try{
this.reg.getFeedConfigurator(null);
fail("Exception expected");
}catch (IllegalArgumentException e) {
//
}
}
/**
* Test method for 'org.apache.lucene.gdata.server.registry.FeedRegistry.getFeedConfigurator(String)'
*/
public void testFlushRegistry() {
String feedURL = "testFeed";
registerFeed(feedURL);
assertEquals("Registered Configurator",this.configurator,this.reg.getFeedConfigurator(feedURL));
this.reg.flushRegistry();
assertNull("Registry flushed",this.reg.getFeedConfigurator(feedURL));
}
/**
*
*/
public void testIsFeedRegistered(){
String myFeed = "myFeed";
registerFeed(myFeed);
assertTrue("Feed is registerd",this.reg.isFeedRegistered(myFeed));
assertFalse("null Feed is not registerd",this.reg.isFeedRegistered(null));
assertFalse("Feed is not registerd",this.reg.isFeedRegistered("someOtherFeed"));
}
private void registerFeed(String feedURL){
this.configurator.setFeedType(Feed.class); this.configurator.setFeedType(Feed.class);
this.configurator.setFeedId(feedURL); }
this.reg.registerFeed(this.configurator); /**
* @see junit.framework.TestCase#tearDown()
*/
@Override
protected void tearDown() throws Exception {
this.reg.flushRegistry();
}
/**
* Test method for 'org.apache.lucene.gdata.server.registry.FeedRegistry.getRegistry()'
*/
public void testGetRegistry() {
GDataServerRegistry reg1 = GDataServerRegistry.getRegistry();
assertEquals("test singleton",this.reg,reg1);
}
/**
* Test method for 'org.apache.lucene.gdata.server.registry.FeedRegistry.registerFeed(FeedInstanceConfigurator)'
*/
public void testRegisterService() {
String service = "service";
registerService(service);
assertEquals("Registered Configurator",this.configurator,this.reg.getProvidedService(service));
assertNull("not registered Configurator",this.reg.getProvidedService("something"));
try{
this.reg.getProvidedService(null);
fail("Exception expected");
}catch (IllegalArgumentException e) {
//
}
}
/**
* Test method for 'org.apache.lucene.gdata.server.registry.FeedRegistry.getFeedConfigurator(String)'
*/
public void testFlushRegistry() {
String service = "service";
registerService(service);
assertEquals("Registered Configurator",this.configurator,this.reg.getProvidedService(service));
this.reg.flushRegistry();
assertNull("Registry flushed",this.reg.getProvidedService(service));
}
/**
*
*/
public void testIsFeedRegistered(){
String service = "service";
registerService(service);
assertTrue("Feed is registerd",this.reg.isServiceRegistered(service));
assertFalse("null Feed is not registerd",this.reg.isServiceRegistered(null));
assertFalse("Feed is not registerd",this.reg.isServiceRegistered("something"));
}
private void registerService(String servicename){
this.configurator.setName(servicename);
this.reg.registerService(this.configurator);
}
public void testRegisterComponent() throws RegistryException{
try {
this.reg.registerComponent(StorageController.class);
fail("RegistryException expected");
} catch (RegistryException e) {
//
}
this.reg.registerComponent(StorageCoreController.class);
this.reg.registerComponent(DefaultRequestHandlerFactory.class);
RequestHandlerFactory factory = this.reg.lookup(RequestHandlerFactory.class,ComponentType.REQUESTHANDLERFACTORY);
try{
this.reg.registerComponent(DefaultRequestHandlerFactory.class);
fail("RegistryException expected");
} catch (RegistryException e) {
//
}
this.reg.registerComponent(ServiceFactory.class);
ServiceFactory servicefactory = this.reg.lookup(ServiceFactory.class,ComponentType.SERVICEFACTORY);
assertNotNull(servicefactory);
assertNotNull(factory);
} }
} }

View File

@ -22,7 +22,8 @@ import java.io.Reader;
import junit.framework.TestCase; import junit.framework.TestCase;
import org.apache.lucene.gdata.server.FeedNotFoundException; import org.apache.lucene.gdata.data.ServerBaseEntry;
import org.apache.lucene.gdata.data.ServerBaseFeed;
import org.apache.lucene.gdata.server.GDataEntityBuilder; import org.apache.lucene.gdata.server.GDataEntityBuilder;
import com.google.gdata.data.BaseEntry; import com.google.gdata.data.BaseEntry;
@ -30,6 +31,7 @@ import com.google.gdata.data.BaseFeed;
import com.google.gdata.data.Entry; import com.google.gdata.data.Entry;
import com.google.gdata.data.ExtensionProfile; import com.google.gdata.data.ExtensionProfile;
import com.google.gdata.data.Feed; import com.google.gdata.data.Feed;
import com.google.gdata.data.Source;
import com.google.gdata.util.ParseException; import com.google.gdata.util.ParseException;
/** /**
@ -44,8 +46,9 @@ public class TestGDataEntityBuilder extends TestCase {
private static GDataServerRegistry reg = GDataServerRegistry.getRegistry(); private static GDataServerRegistry reg = GDataServerRegistry.getRegistry();
private Reader reader; private Reader reader;
private static String feedID = "myFeed"; private static String feedID = "myFeed";
private ExtensionProfile profile; private ProvidedServiceConfig config;
private static Class feedType = Feed.class; private static Class feedType = Feed.class;
private static Class entryType = Entry.class;
/** /**
@ -53,11 +56,12 @@ public class TestGDataEntityBuilder extends TestCase {
*/ */
@Override @Override
protected void setUp() throws Exception { protected void setUp() throws Exception {
FeedInstanceConfigurator config = new FeedInstanceConfigurator(); this.config = new ProvidedServiceConfig();
config.setFeedId(feedID);
config.setFeedType(feedType); this.config.setFeedType(feedType);
this.profile = new ExtensionProfile(); this.config.setEntryType(entryType);
reg.registerFeed(config); this.config.setExtensionProfile(new ExtensionProfile());
reg.registerService(this.config);
} }
/** /**
@ -72,24 +76,27 @@ public class TestGDataEntityBuilder extends TestCase {
/** /**
* Test method for 'org.apache.lucene.gdata.data.GDataEntityBuilder.buildFeed(String, Reader)' * Test method for 'org.apache.lucene.gdata.data.GDataEntityBuilder.buildFeed(String, Reader)'
*/ */
public void testBuildFeedStringReader() throws FeedNotFoundException, ParseException, IOException { public void testBuildFeedStringReader() throws ParseException, IOException {
this.reader = new FileReader(incomingFeed); this.reader = new FileReader(incomingFeed);
BaseFeed feed = GDataEntityBuilder.buildFeed(feedID,this.reader,this.profile); BaseFeed feed = GDataEntityBuilder.buildFeed(this.reader,this.config);
assertNotNull(feed); assertNotNull(feed);
assertEquals("feed title",feed.getTitle().getPlainText(), feedTitleFromXML); assertEquals("feed title",feed.getTitle().getPlainText(), feedTitleFromXML);
assertTrue( feed instanceof Feed);
} }
/* /**
* Test method for 'org.apache.lucene.gdata.data.GDataEntityBuilder.buildEntry(String, Reader)' * Test method for 'org.apache.lucene.gdata.data.GDataEntityBuilder.buildEntry(String, Reader)'
*/ */
public void testBuildEntryStringReader() throws FeedNotFoundException, ParseException, IOException { public void testBuildEntryStringReader() throws ParseException, IOException {
this.reader = new FileReader(incomingEntry); this.reader = new FileReader(incomingEntry);
BaseEntry entry = GDataEntityBuilder.buildEntry(feedID,this.reader,this.profile); BaseEntry entry = GDataEntityBuilder.buildEntry(this.reader,this.config);
assertNotNull(entry); assertNotNull(entry);
assertEquals("entry summary",entry.getSummary().getPlainText(),entrySummaryFromXML); assertEquals("entry summary",entry.getSummary().getPlainText(),entrySummaryFromXML);
assertTrue(entry instanceof Entry);
} }

View File

@ -1,81 +1,72 @@
package org.apache.lucene.gdata.storage.lucenestorage; package org.apache.lucene.gdata.storage.lucenestorage;
import java.io.IOException; import java.io.IOException;
import java.util.Date;
import junit.framework.TestCase; import junit.framework.TestCase;
import org.apache.lucene.analysis.standard.StandardAnalyzer; import org.apache.lucene.gdata.data.GDataAccount;
import org.apache.lucene.gdata.server.FeedNotFoundException; import org.apache.lucene.gdata.data.ServerBaseEntry;
import org.apache.lucene.gdata.server.registry.FeedInstanceConfigurator; import org.apache.lucene.gdata.data.ServerBaseFeed;
import org.apache.lucene.gdata.server.registry.ComponentType;
import org.apache.lucene.gdata.server.registry.GDataServerRegistry; import org.apache.lucene.gdata.server.registry.GDataServerRegistry;
import org.apache.lucene.gdata.server.registry.RegistryBuilder; import org.apache.lucene.gdata.server.registry.ProvidedService;
import org.apache.lucene.gdata.storage.StorageController;
import org.apache.lucene.gdata.storage.StorageException; import org.apache.lucene.gdata.storage.StorageException;
import org.apache.lucene.gdata.storage.lucenestorage.StorageCoreController;
import org.apache.lucene.gdata.storage.lucenestorage.StorageEntryWrapper;
import org.apache.lucene.gdata.storage.lucenestorage.StorageModifier;
import org.apache.lucene.gdata.storage.lucenestorage.StorageQuery;
import org.apache.lucene.gdata.storage.lucenestorage.StorageEntryWrapper.StorageOperation; import org.apache.lucene.gdata.storage.lucenestorage.StorageEntryWrapper.StorageOperation;
import org.apache.lucene.gdata.storage.lucenestorage.util.ReferenceCounter; import org.apache.lucene.gdata.storage.lucenestorage.util.ReferenceCounter;
import org.apache.lucene.index.IndexWriter; import org.apache.lucene.gdata.utils.ProvidedServiceStub;
import org.apache.lucene.index.Term; import org.apache.lucene.index.Term;
import org.apache.lucene.search.BooleanClause;
import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.Hits; import org.apache.lucene.search.Hits;
import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query; import org.apache.lucene.search.Query;
import org.apache.lucene.search.TermQuery; import org.apache.lucene.search.TermQuery;
import org.apache.lucene.store.Directory; import org.apache.lucene.store.Directory;
import org.apache.lucene.store.RAMDirectory;
import com.google.gdata.data.BaseEntry; import com.google.gdata.data.BaseEntry;
import com.google.gdata.data.DateTime; import com.google.gdata.data.DateTime;
import com.google.gdata.data.Entry; import com.google.gdata.data.Entry;
import com.google.gdata.data.ExtensionProfile;
import com.google.gdata.data.Feed;
import com.google.gdata.data.PlainTextConstruct; import com.google.gdata.data.PlainTextConstruct;
import com.google.gdata.data.TextConstruct;
import com.google.gdata.data.TextContent; import com.google.gdata.data.TextContent;
import com.google.gdata.util.ParseException; import com.google.gdata.util.ParseException;
public class TestStorageModifier extends TestCase { public class TestStorageModifier extends TestCase {
private StorageModifier modifier; private StorageModifier modifier;
private int count = 1; private int count = 1;
private ExtensionProfile profile; private ProvidedService configurator;
private Directory dir;
private Directory dir;
private StorageCoreController controller;
private static String feedId = "myFeed"; private static String feedId = "myFeed";
private static String username = "simon";
private static String password = "test";
private static String service = "myService";
protected void setUp() throws Exception { protected void setUp() throws Exception {
FeedInstanceConfigurator configurator = new FeedInstanceConfigurator(); GDataServerRegistry.getRegistry().registerComponent(
configurator.setFeedType(Feed.class); StorageCoreController.class);
configurator.setFeedId(feedId); this.configurator = new ProvidedServiceStub();
configurator.setExtensionProfileClass(ExtensionProfile.class); GDataServerRegistry.getRegistry().registerService(this.configurator);
GDataServerRegistry.getRegistry().registerFeed(configurator); this.controller = (StorageCoreController) GDataServerRegistry
dir = new RAMDirectory(); .getRegistry().lookup(StorageController.class,
this.profile = new ExtensionProfile(); ComponentType.STORAGECONTROLLER);
IndexWriter writer; this.modifier = this.controller.getStorageModifier();
this.dir = this.controller.getDirectory();
writer = new IndexWriter(dir,new StandardAnalyzer(),true);
writer.close();
modifier = StorageCoreController.getStorageCoreController(dir).getStorageModifier();
} }
protected void tearDown() throws Exception { protected void tearDown() throws Exception {
this.count = 1; this.count = 1;
// destroy all resources // destroy all resources
GDataServerRegistry.getRegistry().destroy();//TODO remove dependency here GDataServerRegistry.getRegistry().destroy();// TODO remove dependency
// here
}
/*
* Test method for
* 'org.apache.lucene.storage.lucenestorage.StorageModifier.StorageModifier(Directory,
* int)'
*/
public void testStorageModifier() {
} }
@ -83,75 +74,91 @@ public class TestStorageModifier extends TestCase {
* Test method for * Test method for
* 'org.apache.lucene.storage.lucenestorage.StorageModifier.updateEntry(StroageEntryWrapper)' * 'org.apache.lucene.storage.lucenestorage.StorageModifier.updateEntry(StroageEntryWrapper)'
*/ */
public void testUpdateEntry() throws IOException, InterruptedException, FeedNotFoundException, ParseException, StorageException { public void testUpdateEntry() throws IOException, InterruptedException,
ParseException, StorageException {
testInsertEntry(); testInsertEntry();
for(int i = 1; i < this.count; i++){ for (int i = 1; i < this.count; i++) {
Entry e = new Entry(); Entry e = new Entry();
e.setId(""+i); e.setId("" + i);
String insertString = "Hello world"+i; String insertString = "Hello world" + i;
e.setTitle(new PlainTextConstruct(insertString)); e.setTitle(new PlainTextConstruct(insertString));
StorageEntryWrapper wrapper = new StorageEntryWrapper(e,feedId,StorageOperation.UPDATE,this.profile); ServerBaseEntry en = getServerEntry(e);
StorageEntryWrapper wrapper = new StorageEntryWrapper(en,
StorageOperation.UPDATE);
this.modifier.updateEntry(wrapper); this.modifier.updateEntry(wrapper);
ReferenceCounter<StorageQuery> innerQuery = StorageCoreController.getStorageCoreController().getStorageQuery(); ReferenceCounter<StorageQuery> innerQuery = this.controller
BaseEntry fetchedEntry = innerQuery.get().singleEntryQuery(""+i,feedId,this.profile); .getStorageQuery();
assertEquals("updated Title:",insertString,fetchedEntry.getTitle().getPlainText()); BaseEntry fetchedEntry = innerQuery.get().singleEntryQuery("" + i,
feedId, this.configurator);
assertEquals("updated Title:", insertString, fetchedEntry
.getTitle().getPlainText());
} }
// double updates // double updates
for(int i = 1; i < this.count; i++){ for (int i = 1; i < this.count; i++) {
Entry e = new Entry(); Entry e = new Entry();
e.setId(""+i); e.setId("" + i);
String insertString = "Hello world"+i; String insertString = "Hello world" + i;
e.setTitle(new PlainTextConstruct(insertString)); e.setTitle(new PlainTextConstruct(insertString));
StorageEntryWrapper wrapper = new StorageEntryWrapper(e,feedId,StorageOperation.UPDATE,this.profile); ServerBaseEntry en = getServerEntry(e);
StorageEntryWrapper wrapper = new StorageEntryWrapper(en,
StorageOperation.UPDATE);
this.modifier.updateEntry(wrapper); this.modifier.updateEntry(wrapper);
e = new Entry(); e = new Entry();
e.setId(""+i); e.setId("" + i);
insertString = "Foo Bar"+i; insertString = "Foo Bar" + i;
e.setTitle(new PlainTextConstruct(insertString)); e.setTitle(new PlainTextConstruct(insertString));
wrapper = new StorageEntryWrapper(e,feedId,StorageOperation.UPDATE,this.profile); en = getServerEntry(e);
wrapper = new StorageEntryWrapper(en,
StorageOperation.UPDATE);
this.modifier.updateEntry(wrapper); this.modifier.updateEntry(wrapper);
ReferenceCounter<StorageQuery> innerQuery = StorageCoreController.getStorageCoreController().getStorageQuery(); ReferenceCounter<StorageQuery> innerQuery = this.controller
.getStorageQuery();
BaseEntry fetchedEntry = innerQuery.get().singleEntryQuery(""+i,feedId,this.profile); BaseEntry fetchedEntry = innerQuery.get().singleEntryQuery("" + i,
assertEquals("updated Title:",insertString,fetchedEntry.getTitle().getPlainText()); feedId, this.configurator);
assertEquals("updated Title:", insertString, fetchedEntry
.getTitle().getPlainText());
} }
} }
/* /*
* Test method for * Test method for
* 'org.apache.lucene.storage.lucenestorage.StorageModifier.insertEntry(StroageEntryWrapper)' * 'org.apache.lucene.storage.lucenestorage.StorageModifier.insertEntry(StroageEntryWrapper)'
*/ */
public void testInsertEntry() throws IOException, InterruptedException, FeedNotFoundException, ParseException, StorageException { public void testInsertEntry() throws IOException, InterruptedException,
ParseException, StorageException {
Thread a = getRunnerThread(this.count); Thread a = getRunnerThread(this.count);
a.start(); a.start();
Thread b = getRunnerThread((this.count+=10)); Thread b = getRunnerThread((this.count += 10));
b.start(); b.start();
a.join(); a.join();
for (int i = 1; i < this.count ; i++) { for (int i = 1; i < this.count; i++) {
ReferenceCounter<StorageQuery> innerQuery = StorageCoreController.getStorageCoreController().getStorageQuery(); ReferenceCounter<StorageQuery> innerQuery = this.controller
BaseEntry e = innerQuery.get().singleEntryQuery(""+i,feedId,this.profile); .getStorageQuery();
assertEquals("get entry for id"+i,""+i,e.getId()); BaseEntry e = innerQuery.get().singleEntryQuery("" + i, feedId,
this.configurator);
assertEquals("get entry for id" + i, "" + i, e.getId());
} }
b.join(); b.join();
ReferenceCounter<StorageQuery> query = StorageCoreController.getStorageCoreController().getStorageQuery(); ReferenceCounter<StorageQuery> query = this.controller
.getStorageQuery();
this.count+=10; this.count += 10;
for (int i = 1; i < this.count ; i++) { for (int i = 1; i < this.count; i++) {
BaseEntry e = query.get().singleEntryQuery(""+i,feedId,this.profile); BaseEntry e = query.get().singleEntryQuery("" + i, feedId,
assertEquals("get entry for id"+i,""+i,e.getId()); this.configurator);
assertEquals("get entry for id" + i, "" + i, e.getId());
} }
BaseEntry e = query.get().singleEntryQuery(""+this.count,feedId,this.profile); BaseEntry e = query.get().singleEntryQuery("" + this.count, feedId,
assertNull("not entry for ID",e); this.configurator);
assertNull("not entry for ID", e);
query.decrementRef(); query.decrementRef();
} }
@ -160,70 +167,196 @@ public class TestStorageModifier extends TestCase {
* Test method for * Test method for
* 'org.apache.lucene.storage.lucenestorage.StorageModifier.deleteEntry(String)' * 'org.apache.lucene.storage.lucenestorage.StorageModifier.deleteEntry(String)'
*/ */
public void testDeleteEntry() throws IOException, InterruptedException, FeedNotFoundException, ParseException, StorageException { public void testDeleteEntry() throws IOException, InterruptedException,
ParseException, StorageException {
testInsertEntry(); testInsertEntry();
for (int i = 1; i < this.count ; i++) { for (int i = 1; i < this.count; i++) {
if(i%2 == 0 || i< 10){ if (i % 2 == 0 || i < 10) {
this.modifier.deleteEntry(""+i,feedId); ServerBaseEntry entry = new ServerBaseEntry();
entry.setId("" + i);
entry.setFeedId(feedId);
this.modifier.deleteEntry(new StorageEntryWrapper(entry,StorageOperation.DELETE));
} }
ReferenceCounter<StorageQuery> query = StorageCoreController.getStorageCoreController().getStorageQuery(); ReferenceCounter<StorageQuery> query = this.controller
if(i%2 == 0 || i< 10){ .getStorageQuery();
assertNull(query.get().singleEntryQuery(""+i,feedId,this.profile)); if (i % 2 == 0 || i < 10) {
} assertNull(query.get().singleEntryQuery("" + i, feedId,
else this.configurator));
assertEquals(""+i,query.get().singleEntryQuery(""+i,feedId,this.profile).getId()); } else
assertEquals("" + i, query.get().singleEntryQuery("" + i,
feedId, this.configurator).getId());
query.decrementRef(); query.decrementRef();
} }
StorageCoreController.getStorageCoreController().forceWrite(); this.controller.forceWrite();
IndexSearcher searcher = new IndexSearcher(this.dir); IndexSearcher searcher = new IndexSearcher(this.dir);
for (int i = 1; i < this.count ; i++) { for (int i = 1; i < this.count; i++) {
Query luceneQuery = new TermQuery(new Term(StorageEntryWrapper.FIELD_ENTRY_ID,""+i)); Query luceneQuery = new TermQuery(new Term(
StorageEntryWrapper.FIELD_ENTRY_ID, "" + i));
Hits hits = searcher.search(luceneQuery); Hits hits = searcher.search(luceneQuery);
if(i%2 == 0 || i< 10){ if (i % 2 == 0 || i < 10) {
assertEquals(0,hits.length()); assertEquals(0, hits.length());
} } else
else assertEquals(1, hits.length());
assertEquals(1,hits.length());
} }
searcher.close(); searcher.close();
} }
public void testSaveUser() throws StorageException, IOException {
private Thread getRunnerThread(int idIndex){ GDataAccount user = new GDataAccount();
user.setName(username);
user.setPassword(password);
StorageAccountWrapper wrapper = new StorageAccountWrapper(user);
this.modifier.createAccount(wrapper);
IndexSearcher searcher = new IndexSearcher(this.dir);
Query q = new TermQuery(new Term(StorageAccountWrapper.FIELD_ACCOUNTNAME,
username));
Hits h = searcher.search(q);
assertEquals("length == 1", 1, h.length());
GDataAccount storedUser = StorageAccountWrapper.buildEntity(h.doc(0));
assertTrue(storedUser.equals(user));
searcher.close();
}
public void testDeleteUser() throws StorageException, IOException {
testSaveUser();
this.modifier.deleteAccount(username);
IndexSearcher searcher = new IndexSearcher(this.dir);
Query q = new TermQuery(new Term(StorageAccountWrapper.FIELD_ACCOUNTNAME,
username));
Hits h = searcher.search(q);
assertEquals("length == 0", 0, h.length());
searcher.close();
}
public void testUpdateUser() throws StorageException, IOException {
testSaveUser();
GDataAccount user = new GDataAccount();
user.setName(username);
user.setPassword("newPass");
StorageAccountWrapper wrapper = new StorageAccountWrapper(user);
this.modifier.updateAccount(wrapper);
IndexSearcher searcher = new IndexSearcher(this.dir);
Query q = new TermQuery(new Term(StorageAccountWrapper.FIELD_ACCOUNTNAME,
username));
Hits h = searcher.search(q);
assertEquals("length == 1", 1, h.length());
GDataAccount storedUser = StorageAccountWrapper.buildEntity(h.doc(0));
assertTrue(storedUser.equals(user));
assertFalse(storedUser.getPassword().equals(password));
searcher.close();
}
public void testSaveFeed() throws IOException, StorageException {
String title = "myTitle";
ServerBaseFeed feed = new ServerBaseFeed();
feed.setId(feedId);
feed.setTitle(new PlainTextConstruct(title));
feed.setServiceType(service);
feed.setServiceConfig(this.configurator);
StorageFeedWrapper wrapper = new StorageFeedWrapper(feed,username);
this.modifier.createFeed(wrapper);
IndexSearcher searcher = new IndexSearcher(this.dir);
Query q = new TermQuery(new Term(StorageFeedWrapper.FIELD_FEED_ID,
feedId));
Hits h = searcher.search(q);
assertEquals("length == 1", 1, h.length());
searcher.close();
}
public void testDeleteFeed() throws IOException, StorageException {
testSaveFeed();
Entry e = new Entry();
e.setTitle(new PlainTextConstruct("hello world"));
ServerBaseEntry entry = new ServerBaseEntry(e);
entry.setFeedId(feedId);
entry.setId("testme");
entry.setServiceConfig(this.configurator);
StorageEntryWrapper entryWrapper = new StorageEntryWrapper(entry,StorageOperation.INSERT);
this.modifier.insertEntry(entryWrapper);
this.modifier.forceWrite();
this.modifier.deleteFeed(feedId);
IndexSearcher searcher = new IndexSearcher(this.dir);
Query q = new TermQuery(new Term(StorageFeedWrapper.FIELD_FEED_ID,
feedId));
Query q1 = new TermQuery(new Term(StorageEntryWrapper.FIELD_FEED_REFERENCE,
feedId));
BooleanQuery boolQuery = new BooleanQuery();
boolQuery.add(q,BooleanClause.Occur.SHOULD);
boolQuery.add(q1,BooleanClause.Occur.SHOULD);
Hits h = searcher.search(boolQuery);
assertEquals("length == 0", 0, h.length());
searcher.close();
}
/**
* @throws IOException
* @throws StorageException
*/
public void testUpdateFeed() throws IOException, StorageException {
testSaveFeed();
ServerBaseFeed feed = new ServerBaseFeed();
String title = "myTitle";
String newusername = "doug";
feed.setTitle(new PlainTextConstruct(title));
feed.setId(feedId);
feed.setServiceType(service);
feed.setServiceConfig(this.configurator);
StorageFeedWrapper wrapper = new StorageFeedWrapper(feed,newusername);
this.modifier.updateFeed(wrapper);
IndexSearcher searcher = new IndexSearcher(this.dir);
Query q = new TermQuery(new Term(StorageFeedWrapper.FIELD_FEED_ID,
feedId));
Hits h = searcher.search(q);
assertEquals("length == 1", 1, h.length());
assertTrue(h.doc(0).get(StorageFeedWrapper.FIELD_ACCOUNTREFERENCE).equals(newusername));
searcher.close();
}
private Thread getRunnerThread(int idIndex) {
Thread t = new Thread(new Runner(idIndex)); Thread t = new Thread(new Runner(idIndex));
return t; return t;
} }
private class Runner implements Runnable{ private class Runner implements Runnable {
private int idIndex; private int idIndex;
public Runner(int idIndex){
public Runner(int idIndex) {
this.idIndex = idIndex; this.idIndex = idIndex;
} }
public void run() {
for (int i = idIndex; i < idIndex+10; i++) {
BaseEntry e = buildEntry(""+i); public void run() {
for (int i = idIndex; i < idIndex + 10; i++) {
BaseEntry e = buildEntry("" + i);
try { try {
StorageEntryWrapper wrapper = new StorageEntryWrapper(e,feedId,StorageOperation.INSERT,new ExtensionProfile()); ServerBaseEntry en = new ServerBaseEntry(e);
modifier.insertEntry(wrapper); en.setFeedId(feedId);
en.setServiceConfig(configurator);
StorageEntryWrapper wrapper = new StorageEntryWrapper(en,
StorageOperation.INSERT);
modifier.insertEntry(wrapper);
} catch (Exception e1) { } catch (Exception e1) {
e1.printStackTrace(); e1.printStackTrace();
} }
} }
}// end run
}//end run private BaseEntry buildEntry(String id) {
private BaseEntry buildEntry(String id){
Entry e = new Entry(); Entry e = new Entry();
e.setId(id); e.setId(id);
e.setTitle(new PlainTextConstruct("Monty Python")); e.setTitle(new PlainTextConstruct("Monty Python"));
@ -231,12 +364,12 @@ public class TestStorageModifier extends TestCase {
e.setPublished(DateTime.now()); e.setPublished(DateTime.now());
e.setUpdated(DateTime.now()); e.setUpdated(DateTime.now());
String content = "1st soldier with a keen interest in birds: Who goes there?" + String content = "1st soldier with a keen interest in birds: Who goes there?"
"King Arthur: It is I, Arthur, son of Uther Pendragon, from the castle of Camelot. King of the Britons, defeater of the Saxons, Sovereign of all England!" + + "King Arthur: It is I, Arthur, son of Uther Pendragon, from the castle of Camelot. King of the Britons, defeater of the Saxons, Sovereign of all England!"
"1st soldier with a keen interest in birds: Pull the other one!" + + "1st soldier with a keen interest in birds: Pull the other one!"
"King Arthur: I am, and this is my trusty servant Patsy. We have ridden the length and breadth of the land in search of knights who will join me in my court at Camelot. I must speak with your lord and master." + + "King Arthur: I am, and this is my trusty servant Patsy. We have ridden the length and breadth of the land in search of knights who will join me in my court at Camelot. I must speak with your lord and master."
"1st soldier with a keen interest in birds: What? Ridden on a horse?" + + "1st soldier with a keen interest in birds: What? Ridden on a horse?"
"King Arthur: Yes!"; + "King Arthur: Yes!";
e.setContent(new TextContent(new PlainTextConstruct(content))); e.setContent(new TextContent(new PlainTextConstruct(content)));
e.setSummary(new PlainTextConstruct("The Holy Grail")); e.setSummary(new PlainTextConstruct("The Holy Grail"));
return e; return e;
@ -244,4 +377,11 @@ public class TestStorageModifier extends TestCase {
} }
private ServerBaseEntry getServerEntry(BaseEntry e){
ServerBaseEntry en = new ServerBaseEntry(e);
en.setFeedId(feedId);
en.setServiceConfig(this.configurator);
return en;
}
} }

View File

@ -1,74 +1,85 @@
package org.apache.lucene.gdata.storage.lucenestorage; package org.apache.lucene.gdata.storage.lucenestorage;
import java.io.IOException; import java.io.IOException;
import java.net.URL;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import junit.framework.TestCase; import junit.framework.TestCase;
import org.apache.lucene.analysis.standard.StandardAnalyzer; import org.apache.lucene.gdata.data.GDataAccount;
import org.apache.lucene.gdata.server.FeedNotFoundException; import org.apache.lucene.gdata.data.ServerBaseEntry;
import org.apache.lucene.gdata.server.registry.FeedInstanceConfigurator; import org.apache.lucene.gdata.data.ServerBaseFeed;
import org.apache.lucene.gdata.server.registry.ComponentType;
import org.apache.lucene.gdata.server.registry.GDataServerRegistry; import org.apache.lucene.gdata.server.registry.GDataServerRegistry;
import org.apache.lucene.gdata.server.registry.RegistryBuilder; import org.apache.lucene.gdata.server.registry.ProvidedService;
import org.apache.lucene.gdata.storage.StorageController;
import org.apache.lucene.gdata.storage.StorageException; import org.apache.lucene.gdata.storage.StorageException;
import org.apache.lucene.gdata.storage.lucenestorage.StorageCoreController;
import org.apache.lucene.gdata.storage.lucenestorage.StorageEntryWrapper;
import org.apache.lucene.gdata.storage.lucenestorage.StorageModifier;
import org.apache.lucene.gdata.storage.lucenestorage.StorageQuery;
import org.apache.lucene.gdata.storage.lucenestorage.StorageEntryWrapper.StorageOperation; import org.apache.lucene.gdata.storage.lucenestorage.StorageEntryWrapper.StorageOperation;
import org.apache.lucene.gdata.storage.lucenestorage.util.ReferenceCounter; import org.apache.lucene.gdata.storage.lucenestorage.util.ReferenceCounter;
import org.apache.lucene.index.IndexWriter; import org.apache.lucene.gdata.utils.ProvidedServiceStub;
import org.apache.lucene.store.Directory; import org.apache.lucene.store.Directory;
import org.apache.lucene.store.RAMDirectory;
import com.google.gdata.data.BaseEntry; import com.google.gdata.data.BaseEntry;
import com.google.gdata.data.BaseFeed;
import com.google.gdata.data.DateTime; import com.google.gdata.data.DateTime;
import com.google.gdata.data.Entry; import com.google.gdata.data.Entry;
import com.google.gdata.data.ExtensionProfile;
import com.google.gdata.data.Feed;
import com.google.gdata.util.ParseException; import com.google.gdata.util.ParseException;
public class TestStorageQuery extends TestCase { public class TestStorageQuery extends TestCase {
private StorageModifier modifier; private StorageModifier modifier;
private int count = 30; private int count = 30;
private ReferenceCounter<StorageQuery> query; private ReferenceCounter<StorageQuery> query;
private ExtensionProfile profile; private ProvidedService configurator;
private StorageCoreController controller;
private Directory dir; private Directory dir;
private static String feedId = "myFeed"; private static String feedId = "myFeed";
private static String accountName = "simon";
private static String service = ProvidedServiceStub.SERVICE_NAME;
protected void setUp() throws Exception { protected void setUp() throws Exception {
FeedInstanceConfigurator configurator = new FeedInstanceConfigurator(); GDataServerRegistry.getRegistry().registerComponent(StorageCoreController.class);
configurator.setFeedType(Feed.class); this.configurator = new ProvidedServiceStub();
configurator.setFeedId(feedId);
configurator.setExtensionProfileClass(ExtensionProfile.class);
GDataServerRegistry.getRegistry().registerFeed(configurator); GDataServerRegistry.getRegistry().registerService(this.configurator);
this.profile = new ExtensionProfile(); this.controller = (StorageCoreController)GDataServerRegistry.getRegistry().lookup(StorageController.class,ComponentType.STORAGECONTROLLER);
this.dir = new RAMDirectory(); this.modifier = this.controller.getStorageModifier();
IndexWriter writer; this.dir = this.controller.getDirectory();
writer = new IndexWriter(this.dir,new StandardAnalyzer(),true); ServerBaseFeed feed = new ServerBaseFeed();
writer.close(); feed.setId(feedId);
this.modifier = StorageCoreController.getStorageCoreController(this.dir).getStorageModifier(); feed.setServiceType(service);
feed.setServiceConfig(this.configurator);
StorageFeedWrapper wrapper = new StorageFeedWrapper(feed,accountName);
this.modifier.createFeed(wrapper);
insertEntries(this.count); insertEntries(this.count);
this.query = StorageCoreController.getStorageCoreController().getStorageQuery(); this.query = this.controller.getStorageQuery();
} }
public void insertEntries(int count) throws IOException,InterruptedException, StorageException{ /**
* @param entrycount
* @throws IOException
* @throws InterruptedException
* @throws StorageException
*/
public void insertEntries(int entrycount) throws IOException,InterruptedException, StorageException{
List<StorageEntryWrapper> tempList = new ArrayList<StorageEntryWrapper>(); List<StorageEntryWrapper> tempList = new ArrayList<StorageEntryWrapper>();
for (int i = 0; i <= count ; i++) { for (int i = 0; i <= entrycount ; i++) {
Entry entry = new Entry(); ServerBaseEntry entry = new ServerBaseEntry(new Entry());
entry.setId(""+i); entry.setId(""+i);
entry.setServiceConfig(this.configurator);
entry.setUpdated(new DateTime(System.currentTimeMillis(),0)); entry.setUpdated(new DateTime(System.currentTimeMillis(),0));
StorageEntryWrapper wrapper = new StorageEntryWrapper(entry,feedId,StorageOperation.INSERT,this.profile); entry.setFeedId(feedId);
StorageEntryWrapper wrapper = new StorageEntryWrapper(entry,StorageOperation.INSERT);
tempList.add(i,wrapper); tempList.add(i,wrapper);
// force different timestamps --> DateTime 2006-06-05T13:37:55.724Z // force different timestamps --> DateTime 2006-06-05T13:37:55.724Z
Thread.sleep(50); Thread.sleep(10);
} }
for (StorageEntryWrapper entry : tempList) { for (StorageEntryWrapper entry : tempList) {
@ -82,24 +93,33 @@ public class TestStorageQuery extends TestCase {
protected void tearDown() throws Exception { protected void tearDown() throws Exception {
this.query.decrementRef(); this.query.decrementRef();
GDataServerRegistry.getRegistry().destroy();//TODO remove dependency here GDataServerRegistry.getRegistry().destroy();
}
/*
*
*/
public void testAccountNameQuery() throws IOException, StorageException{
ReferenceCounter<StorageQuery> query = this.controller.getStorageQuery();
assertEquals(accountName,query.get().getAccountNameForFeedId(feedId));
assertNull(query.get().getAccountNameForFeedId("someId"));
} }
/* /*
* Test method for 'org.apache.lucene.storage.lucenestorage.StorageQuery.feedQuery(String, int, int)' * Test method for 'org.apache.lucene.storage.lucenestorage.StorageQuery.feedQuery(String, int, int)'
*/ */
public void testFeedQuery() throws IOException, FeedNotFoundException, ParseException, StorageException { public void testFeedQuery() throws IOException, ParseException, StorageException {
FeedQueryHelper(this.query); feedQueryHelper(this.query);
StorageCoreController.getStorageCoreController().forceWrite(); this.controller.forceWrite();
ReferenceCounter<StorageQuery> queryAssureWritten = StorageCoreController.getStorageCoreController().getStorageQuery(); ReferenceCounter<StorageQuery> queryAssureWritten = this.controller.getStorageQuery();
assertNotSame(queryAssureWritten,this.query); assertNotSame(queryAssureWritten,this.query);
FeedQueryHelper(queryAssureWritten); feedQueryHelper(queryAssureWritten);
queryAssureWritten.decrementRef(); queryAssureWritten.decrementRef();
} }
private void FeedQueryHelper(ReferenceCounter<StorageQuery> currentQuery) throws IOException, FeedNotFoundException, ParseException{ private void feedQueryHelper(ReferenceCounter<StorageQuery> currentQuery) throws IOException, ParseException{
List<BaseEntry> entryList = currentQuery.get().getLatestFeedQuery(feedId,25,1,this.profile); BaseFeed feed = currentQuery.get().getLatestFeedQuery(feedId,25,1,this.configurator);
List<BaseEntry> entryList = feed.getEntries();
assertTrue("listSize: "+entryList.size(),entryList.size() == 25); assertTrue("listSize: "+entryList.size(),entryList.size() == 25);
BaseEntry tempEntry = null; BaseEntry tempEntry = null;
@ -116,7 +136,8 @@ public class TestStorageQuery extends TestCase {
// test sub retrieve sublist // test sub retrieve sublist
int offset = 15; int offset = 15;
int resultCount = 5; int resultCount = 5;
List<BaseEntry> entrySubList = currentQuery.get().getLatestFeedQuery(feedId,resultCount,offset,this.profile); feed = currentQuery.get().getLatestFeedQuery(feedId,resultCount,offset,this.configurator);
List<BaseEntry> entrySubList = feed.getEntries();
assertTrue("listSize: "+entrySubList.size(),entrySubList.size() == resultCount); assertTrue("listSize: "+entrySubList.size(),entrySubList.size() == resultCount);
offset--; offset--;
@ -134,9 +155,9 @@ public class TestStorageQuery extends TestCase {
/* /*
* Test method for 'org.apache.lucene.storage.lucenestorage.StorageQuery.singleEntryQuery(String, String)' * Test method for 'org.apache.lucene.storage.lucenestorage.StorageQuery.singleEntryQuery(String, String)'
*/ */
public void testSingleEntryQuery() throws FeedNotFoundException, ParseException, IOException { public void testSingleEntryQuery() throws ParseException, IOException {
for (int i = 1; i <= this.count; i++) { for (int i = 1; i <= this.count; i++) {
BaseEntry entry = this.query.get().singleEntryQuery(""+i,feedId,this.profile); BaseEntry entry = this.query.get().singleEntryQuery(""+i,feedId,this.configurator);
assertEquals(""+i,entry.getId()); assertEquals(""+i,entry.getId());
} }
@ -145,24 +166,92 @@ public class TestStorageQuery extends TestCase {
/* /*
* Test method for 'org.apache.lucene.storage.lucenestorage.StorageQuery.entryQuery(List<String>, String)' * Test method for 'org.apache.lucene.storage.lucenestorage.StorageQuery.entryQuery(List<String>, String)'
*/ */
public void testEntryQuery() throws FeedNotFoundException, ParseException, IOException, StorageException { public void testEntryQuery() throws ParseException, IOException, StorageException {
entryQueryHelper(this.query); entryQueryHelper(this.query);
StorageCoreController.getStorageCoreController().forceWrite(); this.controller.forceWrite();
ReferenceCounter<StorageQuery> queryAssureWritten = StorageCoreController.getStorageCoreController().getStorageQuery(); ReferenceCounter<StorageQuery> queryAssureWritten = this.controller.getStorageQuery();
assertNotSame(queryAssureWritten,query); assertNotSame(queryAssureWritten,query);
entryQueryHelper(queryAssureWritten); entryQueryHelper(queryAssureWritten);
queryAssureWritten.decrementRef(); queryAssureWritten.decrementRef();
} }
public void testGetUser() throws StorageException, IOException{
this.modifier.forceWrite();
GDataAccount user = new GDataAccount();
user.setName("simon");
user.setPassword("pass");
user.setAuthorname("simon willnauer");
user.setAuthorMail("simon@apache.org");
user.setAuthorLink(new URL("http://www.apache.org"));
private void entryQueryHelper(ReferenceCounter<StorageQuery> currentQuery) throws IOException, FeedNotFoundException, ParseException{
this.modifier.createAccount(new StorageAccountWrapper(user));
GDataAccount queriedUser = this.query.get().getUser("simon");
assertNull(queriedUser);
ReferenceCounter<StorageQuery> tempQuery = this.controller.getStorageQuery();
queriedUser = tempQuery.get().getUser("simon");
assertTrue(queriedUser.equals(user));
assertTrue(queriedUser.getAuthorMail().equals(user.getAuthorMail()));
assertTrue(queriedUser.getAuthorLink().equals(user.getAuthorLink()));
assertTrue(queriedUser.getAuthorname().equals(user.getAuthorname()));
assertTrue(queriedUser.getPassword().equals(user.getPassword()));
}
public void testIsEntryStored() throws IOException{
assertTrue(this.query.get().isEntryStored(""+(this.count-1),feedId));
assertFalse(this.query.get().isEntryStored("someOther",feedId));
this.modifier.forceWrite();
assertTrue(this.query.get().isEntryStored(""+(this.count-1),feedId));
this.query = this.controller.getStorageQuery();
assertTrue(this.query.get().isEntryStored(""+(this.count-1),feedId));
assertFalse(this.query.get().isEntryStored("someOther",feedId));
}
public void testGetEntryLastModied() throws IOException, StorageException{
ServerBaseEntry entry = new ServerBaseEntry(new Entry());
entry.setId("test");
entry.setServiceConfig(this.configurator);
entry.setUpdated(new DateTime(System.currentTimeMillis(),0));
entry.setFeedId(feedId);
StorageEntryWrapper wrapper = new StorageEntryWrapper(entry,StorageOperation.INSERT);
this.modifier.insertEntry(wrapper);
assertEquals(entry.getUpdated().getValue(),this.query.get().getEntryLastModified("test",feedId));
this.modifier.forceWrite();
assertEquals(entry.getUpdated().getValue(),this.query.get().getEntryLastModified("test",feedId));
this.query = this.controller.getStorageQuery();
assertEquals(entry.getUpdated().getValue(),this.query.get().getEntryLastModified("test",feedId));
try{
this.query.get().getEntryLastModified("some",feedId);
fail("exception expected");
}catch (Exception e) {
e.printStackTrace();
}
}
public void testGetFeedLastModified() throws StorageException, IOException{
ServerBaseEntry entry = new ServerBaseEntry(new Entry());
entry.setId("test");
entry.setServiceConfig(this.configurator);
entry.setUpdated(new DateTime(System.currentTimeMillis(),0));
entry.setFeedId(feedId);
StorageEntryWrapper wrapper = new StorageEntryWrapper(entry,StorageOperation.INSERT);
this.modifier.insertEntry(wrapper);
assertEquals(entry.getUpdated().getValue(),this.query.get().getFeedLastModified(feedId));
this.modifier.forceWrite();
assertEquals(entry.getUpdated().getValue(),this.query.get().getFeedLastModified(feedId));
this.query = this.controller.getStorageQuery();
assertEquals(entry.getUpdated().getValue(),this.query.get().getFeedLastModified(feedId));
}
private void entryQueryHelper(ReferenceCounter<StorageQuery> currentQuery) throws IOException, ParseException{
List<String> entryIdList = new ArrayList<String>(); List<String> entryIdList = new ArrayList<String>();
for (int i = 1; i <= this.count; i++) { for (int i = 1; i <= this.count; i++) {
entryIdList.add(""+i); entryIdList.add(""+i);
} }
List<BaseEntry> entryList = currentQuery.get().entryQuery(entryIdList,feedId,this.profile); List<BaseEntry> entryList = currentQuery.get().entryQuery(entryIdList,feedId,this.configurator);
assertEquals(entryIdList.size(),entryList.size()); assertEquals(entryIdList.size(),entryList.size());
List<String> entryIdCompare = new ArrayList<String>(); List<String> entryIdCompare = new ArrayList<String>();
for (BaseEntry entry : entryList) { for (BaseEntry entry : entryList) {
@ -172,4 +261,6 @@ public class TestStorageQuery extends TestCase {
} }
} }

View File

@ -1,24 +1,57 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/j2ee" <web-app xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee web-app_2_4.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee web-app_2_4.xsd"
version="2.4"> version="2.4">
<display-name>Lucene GData - Server</display-name> <display-name>Lucene GData - Server</display-name>
<description> <description>
Server-side implementation of the GData protocol based on Apache Server-side implementation of the GData protocol based on Apache
- Lucene - Lucene
</description> </description>
<listener> <listener>
<listener-class> org.apache.lucene.gdata.server.registry.RegistryContextListener</listener-class> <listener-class>
</listener> org.apache.lucene.gdata.server.registry.RegistryContextListener
<servlet> </listener-class>
<servlet-name>ControllerServlet</servlet-name> </listener>
<servlet-class> <servlet>
org.apache.lucene.gdata.servlet.RequestControllerServlet <servlet-name>ControllerServlet</servlet-name>
</servlet-class> <servlet-class>
</servlet> org.apache.lucene.gdata.servlet.RequestControllerServlet
<servlet-mapping> </servlet-class>
<servlet-name>ControllerServlet</servlet-name> </servlet>
<url-pattern>/*</url-pattern> <servlet-mapping>
</servlet-mapping> <servlet-name>ControllerServlet</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>AuthenticationServlet</servlet-name>
<servlet-class>
org.apache.lucene.gdata.servlet.AuthenticationServlet
</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>AuthenticationServlet</servlet-name>
<url-pattern>/accounts/ClientLogin</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>FeedAdminServlet</servlet-name>
<servlet-class>
org.apache.lucene.gdata.servlet.FeedAdministrationServlet
</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>FeedAdminServlet</servlet-name>
<url-pattern>/admin/feed</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>AccountAdminServlet</servlet-name>
<servlet-class>
org.apache.lucene.gdata.servlet.AccountAdministrationServlet
</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>AccountAdminServlet</servlet-name>
<url-pattern>/admin/account</url-pattern>
</servlet-mapping>
</web-app> </web-app>