Index: trunk/build.xml =================================================================== --- trunk/build.xml (revision 412202) +++ trunk/build.xml (working copy) @@ -379,6 +379,7 @@ + @@ -386,7 +387,7 @@ - + Index: trunk/contrib/gdata-server/webroot/WEB-INF/web.xml =================================================================== --- trunk/contrib/gdata-server/webroot/WEB-INF/web.xml (revision 0) +++ trunk/contrib/gdata-server/webroot/WEB-INF/web.xml (revision 0) @@ -0,0 +1,24 @@ + + + Lucene GData - Server + + Server-side implementation of the GData protocol based on Apache + - Lucene + + + org.apache.lucene.gdata.server.registry.RegistryContextListener + + + ControllerServlet + + org.apache.lucene.gdata.servlet.RequestControllerServlet + + + + ControllerServlet + /* + + \ No newline at end of file Index: trunk/contrib/gdata-server/webroot/WEB-INF/classes/lucenestorage.properties.xml =================================================================== --- trunk/contrib/gdata-server/webroot/WEB-INF/classes/lucenestorage.properties.xml (revision 0) +++ trunk/contrib/gdata-server/webroot/WEB-INF/classes/lucenestorage.properties.xml (revision 0) @@ -0,0 +1,11 @@ + + + +Lucene Storage Properties +20 +20 +20 +/tmp/storage/ +true +false + \ No newline at end of file Index: trunk/contrib/gdata-server/lib/gdata-client-1.0.jar =================================================================== Cannot display: file marked as a binary type. svn:mime-type = application/octet-stream Property changes on: trunk/contrib/gdata-server/lib/gdata-client-1.0.jar ___________________________________________________________________ Name: svn:mime-type + application/octet-stream Index: trunk/contrib/gdata-server/lib/easymock.jar =================================================================== Cannot display: file marked as a binary type. svn:mime-type = application/octet-stream Property changes on: trunk/contrib/gdata-server/lib/easymock.jar ___________________________________________________________________ Name: svn:mime-type + application/octet-stream Index: trunk/contrib/gdata-server/lib/servlet-api.jar =================================================================== Cannot display: file marked as a binary type. svn:mime-type = application/octet-stream Property changes on: trunk/contrib/gdata-server/lib/servlet-api.jar ___________________________________________________________________ Name: svn:mime-type + application/octet-stream Index: trunk/contrib/gdata-server/lib/commons-logging-1.1.jar =================================================================== Cannot display: file marked as a binary type. svn:mime-type = application/octet-stream Property changes on: trunk/contrib/gdata-server/lib/commons-logging-1.1.jar ___________________________________________________________________ Name: svn:mime-type + application/octet-stream Index: trunk/contrib/gdata-server/lib/log4j-1.2.13.jar =================================================================== Cannot display: file marked as a binary type. svn:mime-type = application/octet-stream Property changes on: trunk/contrib/gdata-server/lib/log4j-1.2.13.jar ___________________________________________________________________ Name: svn:mime-type + application/octet-stream Index: trunk/contrib/gdata-server/src/test/org/apache/lucene/gdata/servlet/TestAbstractGdataServlet.java =================================================================== --- trunk/contrib/gdata-server/src/test/org/apache/lucene/gdata/servlet/TestAbstractGdataServlet.java (revision 0) +++ trunk/contrib/gdata-server/src/test/org/apache/lucene/gdata/servlet/TestAbstractGdataServlet.java (revision 0) @@ -0,0 +1,282 @@ +package org.apache.lucene.gdata.servlet; + +import java.io.IOException; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.easymock.MockControl; + +import junit.framework.TestCase; + +/** + * @author Simon Willnauer + * + */ +public class TestAbstractGdataServlet extends TestCase { + private static final String METHOD_DELETE = "DELETE"; + + private static final String METHOD_GET = "GET"; + + private static final String METHOD_POST = "POST"; + + private static final String METHOD_PUT = "PUT"; + + private static final String METHOD_HEADER_NAME = "x-http-method-override"; + + private HttpServletRequest mockRequest = null; + + private HttpServletResponse mockResponse = null; + + private AbstractGdataServlet servletInstance = null; + + private MockControl requestMockControl; + + private MockControl responseMockControl; + + protected void setUp() throws Exception { + this.requestMockControl = MockControl + .createControl(HttpServletRequest.class); + this.responseMockControl = MockControl + .createControl(HttpServletResponse.class); + this.mockRequest = (HttpServletRequest) this.requestMockControl + .getMock(); + this.mockResponse = (HttpServletResponse) this.responseMockControl + .getMock(); + this.servletInstance = new StubGDataServlet(); + } + + /** + * Test method for + * 'org.apache.lucene.gdata.servlet.AbstractGdataServlet.service(HttpServletRequest, + * HttpServletResponse)' + */ + public void testServiceHttpServletRequestHttpServletResponseDelete() { + this.requestMockControl.expectAndDefaultReturn(this.mockRequest + .getMethod(), METHOD_DELETE); + this.requestMockControl.expectAndDefaultReturn(this.mockRequest + .getHeader(METHOD_HEADER_NAME), METHOD_DELETE); + this.requestMockControl.replay(); + + try { + this.servletInstance.service(this.mockRequest, this.mockResponse); + } catch (ServletException e) { + fail("ServeltExpception not expected"); + } catch (IOException e) { + fail("IOExpception not expected"); + } + + this.requestMockControl.verify(); + this.requestMockControl.reset(); + + this.requestMockControl.expectAndDefaultReturn(this.mockRequest + .getMethod(), METHOD_POST); + this.requestMockControl.expectAndDefaultReturn(this.mockRequest + .getHeader(METHOD_HEADER_NAME), METHOD_DELETE); + this.requestMockControl.replay(); + + try { + this.servletInstance.service(this.mockRequest, this.mockResponse); + } catch (ServletException e) { + fail("ServeltExpception not expected"); + } catch (IOException e) { + fail("IOExpception not expected"); + } + + this.requestMockControl.verify(); + } + + /** + * + */ + public void testServiceNullOverrideHeader() { + this.requestMockControl.expectAndDefaultReturn(this.mockRequest + .getMethod(), METHOD_POST); + this.requestMockControl.expectAndDefaultReturn(this.mockRequest + .getHeader(METHOD_HEADER_NAME), null); + this.requestMockControl.replay(); + + try { + this.servletInstance.service(this.mockRequest, this.mockResponse); + } catch (ServletException e) { + fail("ServeltExpception not expected"); + } catch (IOException e) { + fail("IOExpception not expected"); + } + + this.requestMockControl.verify(); + this.requestMockControl.reset(); + } + + /** + * Test method for + * 'org.apache.lucene.gdata.servlet.AbstractGdataServlet.service(HttpServletRequest, + * HttpServletResponse)' + */ + public void testServiceHttpServletRequestHttpServletResponsePOST() { + this.requestMockControl.expectAndDefaultReturn(this.mockRequest + .getMethod(), METHOD_POST); + this.requestMockControl.expectAndDefaultReturn(this.mockRequest + .getHeader(METHOD_HEADER_NAME), METHOD_POST); + this.requestMockControl.replay(); + + try { + this.servletInstance.service(this.mockRequest, this.mockResponse); + } catch (ServletException e) { + fail("ServeltExpception not expected"); + } catch (IOException e) { + fail("IOExpception not expected"); + } + + this.requestMockControl.verify(); + this.requestMockControl.reset(); + + this.requestMockControl.expectAndDefaultReturn(this.mockRequest + .getMethod(), METHOD_PUT); + this.requestMockControl.expectAndDefaultReturn(this.mockRequest + .getHeader(METHOD_HEADER_NAME), METHOD_POST); + this.requestMockControl.replay(); + + try { + this.servletInstance.service(this.mockRequest, this.mockResponse); + } catch (ServletException e) { + fail("ServeltExpception not expected"); + } catch (IOException e) { + fail("IOExpception not expected"); + } + + this.requestMockControl.verify(); + } + + /** + * Test method for + * 'org.apache.lucene.gdata.servlet.AbstractGdataServlet.service(HttpServletRequest, + * HttpServletResponse)' + */ + public void testServiceHttpServletRequestHttpServletResponsePUT() { + this.requestMockControl.expectAndDefaultReturn(this.mockRequest + .getMethod(), METHOD_PUT); + this.requestMockControl.expectAndDefaultReturn(this.mockRequest + .getHeader(METHOD_HEADER_NAME), METHOD_PUT); + this.requestMockControl.replay(); + + try { + this.servletInstance.service(this.mockRequest, this.mockResponse); + } catch (ServletException e) { + fail("ServeltExpception not expected"); + } catch (IOException e) { + fail("IOExpception not expected"); + } + + this.requestMockControl.verify(); + this.requestMockControl.reset(); + + this.requestMockControl.expectAndDefaultReturn(this.mockRequest + .getMethod(), METHOD_POST); + this.requestMockControl.expectAndDefaultReturn(this.mockRequest + .getHeader(METHOD_HEADER_NAME), METHOD_PUT); + this.requestMockControl.replay(); + + try { + this.servletInstance.service(this.mockRequest, this.mockResponse); + } catch (ServletException e) { + fail("ServeltExpception not expected"); + } catch (IOException e) { + fail("IOExpception not expected"); + } + + this.requestMockControl.verify(); + } + + /** + * Test method for + * 'org.apache.lucene.gdata.servlet.AbstractGdataServlet.service(HttpServletRequest, + * HttpServletResponse)' + */ + public void testServiceHttpServletRequestHttpServletResponseGET() { + this.requestMockControl.expectAndDefaultReturn(this.mockRequest + .getMethod(), METHOD_GET); + this.requestMockControl.expectAndDefaultReturn(this.mockRequest + .getHeader(METHOD_HEADER_NAME), METHOD_GET); + this.requestMockControl.replay(); + + try { + this.servletInstance.service(this.mockRequest, this.mockResponse); + } catch (ServletException e) { + fail("ServeltExpception not expected"); + } catch (IOException e) { + fail("IOExpception not expected"); + } + + this.requestMockControl.verify(); + this.requestMockControl.reset(); + + this.requestMockControl.expectAndDefaultReturn(this.mockRequest + .getMethod(), METHOD_POST); + this.requestMockControl.expectAndDefaultReturn(this.mockRequest + .getHeader(METHOD_HEADER_NAME), METHOD_GET); + this.requestMockControl.replay(); + + try { + this.servletInstance.service(this.mockRequest, this.mockResponse); + } catch (ServletException e) { + fail("ServeltExpception not expected"); + } catch (IOException e) { + fail("IOExpception not expected"); + } + + this.requestMockControl.verify(); + + } + /** + * Stub Implementation for AbstractGdataServlet + * @author Simon Willnauer + * + */ + static class StubGDataServlet extends AbstractGdataServlet { + + private static final long serialVersionUID = -6271464588547620925L; + + protected void doDelete(HttpServletRequest arg0, + HttpServletResponse arg1) { + if (arg0.getHeader(METHOD_HEADER_NAME) == null) + assertEquals("Http-Method --DELETE--", METHOD_DELETE, arg0 + .getMethod()); + else + assertEquals("Http-Method override --DELETE--", METHOD_DELETE, + arg0.getHeader(METHOD_HEADER_NAME)); + + } + + protected void doGet(HttpServletRequest arg0, HttpServletResponse arg1) { + if (arg0.getHeader(METHOD_HEADER_NAME) == null) + assertEquals("Http-Method --GET--", arg0.getMethod(), + METHOD_GET); + else + assertEquals("Http-Method override --GET--", arg0 + .getHeader(METHOD_HEADER_NAME), METHOD_GET); + } + + protected void doPost(HttpServletRequest arg0, HttpServletResponse arg1) { + if (arg0.getHeader(METHOD_HEADER_NAME) == null) + assertEquals("Http-Method --POST--", arg0.getMethod(), + METHOD_POST); + else + assertEquals("Http-Method override --POST--", METHOD_POST, arg0 + .getHeader(METHOD_HEADER_NAME)); + + } + + protected void doPut(HttpServletRequest arg0, HttpServletResponse arg1) { + if (arg0.getHeader(METHOD_HEADER_NAME) == null) + assertEquals("Http-Method --PUT--", arg0.getMethod(), + METHOD_PUT); + else + assertEquals("Http-Method override --PUT--", arg0 + .getHeader(METHOD_HEADER_NAME), METHOD_PUT); + } + + } + +} Index: trunk/contrib/gdata-server/src/test/org/apache/lucene/gdata/storage/lucenestorage/TestStorageModifier.java =================================================================== --- trunk/contrib/gdata-server/src/test/org/apache/lucene/gdata/storage/lucenestorage/TestStorageModifier.java (revision 0) +++ trunk/contrib/gdata-server/src/test/org/apache/lucene/gdata/storage/lucenestorage/TestStorageModifier.java (revision 0) @@ -0,0 +1,247 @@ +package org.apache.lucene.gdata.storage.lucenestorage; + +import java.io.IOException; +import java.util.Date; + +import junit.framework.TestCase; + +import org.apache.lucene.analysis.standard.StandardAnalyzer; +import org.apache.lucene.gdata.server.FeedNotFoundException; +import org.apache.lucene.gdata.server.registry.FeedInstanceConfigurator; +import org.apache.lucene.gdata.server.registry.GDataServerRegistry; +import org.apache.lucene.gdata.server.registry.RegistryBuilder; +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.util.ReferenceCounter; +import org.apache.lucene.index.IndexWriter; +import org.apache.lucene.index.Term; +import org.apache.lucene.search.Hits; +import org.apache.lucene.search.IndexSearcher; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.TermQuery; +import org.apache.lucene.store.Directory; +import org.apache.lucene.store.RAMDirectory; + +import com.google.gdata.data.BaseEntry; +import com.google.gdata.data.DateTime; +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.TextConstruct; +import com.google.gdata.data.TextContent; +import com.google.gdata.util.ParseException; + +public class TestStorageModifier extends TestCase { + private StorageModifier modifier; + private int count = 1; + + private ExtensionProfile profile; + private Directory dir; + + + private static String feedId = "myFeed"; + + protected void setUp() throws Exception { + FeedInstanceConfigurator configurator = new FeedInstanceConfigurator(); + configurator.setFeedType(Feed.class); + configurator.setFeedId(feedId); + configurator.setExtensionProfileClass(ExtensionProfile.class); + GDataServerRegistry.getRegistry().registerFeed(configurator); + dir = new RAMDirectory(); + this.profile = new ExtensionProfile(); + IndexWriter writer; + + writer = new IndexWriter(dir,new StandardAnalyzer(),true); + writer.close(); + modifier = StorageCoreController.getStorageCoreController(dir).getStorageModifier(); + + + } + + protected void tearDown() throws Exception { + this.count = 1; + // destroy all resources + GDataServerRegistry.getRegistry().destroy();//TODO remove dependency here + + } + + /* + * Test method for + * 'org.apache.lucene.storage.lucenestorage.StorageModifier.StorageModifier(Directory, + * int)' + */ + public void testStorageModifier() { + + } + + /* + * Test method for + * 'org.apache.lucene.storage.lucenestorage.StorageModifier.updateEntry(StroageEntryWrapper)' + */ + public void testUpdateEntry() throws IOException, InterruptedException, FeedNotFoundException, ParseException, StorageException { + testInsertEntry(); + for(int i = 1; i < this.count; i++){ + Entry e = new Entry(); + e.setId(""+i); + String insertString = "Hello world"+i; + e.setTitle(new PlainTextConstruct(insertString)); + StorageEntryWrapper wrapper = new StorageEntryWrapper(e,feedId,StorageOperation.UPDATE,this.profile); + this.modifier.updateEntry(wrapper); + ReferenceCounter innerQuery = StorageCoreController.getStorageCoreController().getStorageQuery(); + BaseEntry fetchedEntry = innerQuery.get().singleEntryQuery(""+i,feedId,this.profile); + assertEquals("updated Title:",insertString,fetchedEntry.getTitle().getPlainText()); + } + // double updates + for(int i = 1; i < this.count; i++){ + Entry e = new Entry(); + e.setId(""+i); + String insertString = "Hello world"+i; + e.setTitle(new PlainTextConstruct(insertString)); + StorageEntryWrapper wrapper = new StorageEntryWrapper(e,feedId,StorageOperation.UPDATE,this.profile); + this.modifier.updateEntry(wrapper); + + e = new Entry(); + e.setId(""+i); + insertString = "Foo Bar"+i; + e.setTitle(new PlainTextConstruct(insertString)); + wrapper = new StorageEntryWrapper(e,feedId,StorageOperation.UPDATE,this.profile); + this.modifier.updateEntry(wrapper); + + ReferenceCounter innerQuery = StorageCoreController.getStorageCoreController().getStorageQuery(); + + BaseEntry fetchedEntry = innerQuery.get().singleEntryQuery(""+i,feedId,this.profile); + assertEquals("updated Title:",insertString,fetchedEntry.getTitle().getPlainText()); + } + + + + } + + /* + * Test method for + * 'org.apache.lucene.storage.lucenestorage.StorageModifier.insertEntry(StroageEntryWrapper)' + */ + public void testInsertEntry() throws IOException, InterruptedException, FeedNotFoundException, ParseException, StorageException { + + Thread a = getRunnerThread(this.count); + a.start(); + + Thread b = getRunnerThread((this.count+=10)); + b.start(); + a.join(); + for (int i = 1; i < this.count ; i++) { + ReferenceCounter innerQuery = StorageCoreController.getStorageCoreController().getStorageQuery(); + BaseEntry e = innerQuery.get().singleEntryQuery(""+i,feedId,this.profile); + assertEquals("get entry for id"+i,""+i,e.getId()); + + + } + b.join(); + ReferenceCounter query = StorageCoreController.getStorageCoreController().getStorageQuery(); + + this.count+=10; + for (int i = 1; i < this.count ; i++) { + BaseEntry e = query.get().singleEntryQuery(""+i,feedId,this.profile); + assertEquals("get entry for id"+i,""+i,e.getId()); + } + + BaseEntry e = query.get().singleEntryQuery(""+this.count,feedId,this.profile); + assertNull("not entry for ID",e); + query.decrementRef(); + + } + + /* + * Test method for + * 'org.apache.lucene.storage.lucenestorage.StorageModifier.deleteEntry(String)' + */ + public void testDeleteEntry() throws IOException, InterruptedException, FeedNotFoundException, ParseException, StorageException { + testInsertEntry(); + for (int i = 1; i < this.count ; i++) { + if(i%2 == 0 || i< 10){ + this.modifier.deleteEntry(""+i,feedId); + } + ReferenceCounter query = StorageCoreController.getStorageCoreController().getStorageQuery(); + if(i%2 == 0 || i< 10){ + assertNull(query.get().singleEntryQuery(""+i,feedId,this.profile)); + } + else + assertEquals(""+i,query.get().singleEntryQuery(""+i,feedId,this.profile).getId()); + query.decrementRef(); + } + + StorageCoreController.getStorageCoreController().forceWrite(); + IndexSearcher searcher = new IndexSearcher(this.dir); + + for (int i = 1; i < this.count ; i++) { + Query luceneQuery = new TermQuery(new Term(StorageEntryWrapper.FIELD_ENTRY_ID,""+i)); + Hits hits = searcher.search(luceneQuery); + if(i%2 == 0 || i< 10){ + + assertEquals(0,hits.length()); + } + else + assertEquals(1,hits.length()); + } + searcher.close(); + + } + + + private Thread getRunnerThread(int idIndex){ + Thread t = new Thread(new Runner(idIndex)); + return t; + } + + private class Runner implements Runnable{ + private int idIndex; + public Runner(int idIndex){ + this.idIndex = idIndex; + } + public void run() { + for (int i = idIndex; i < idIndex+10; i++) { + + BaseEntry e = buildEntry(""+i); + try { + StorageEntryWrapper wrapper = new StorageEntryWrapper(e,feedId,StorageOperation.INSERT,new ExtensionProfile()); + modifier.insertEntry(wrapper); + } catch (Exception e1) { + + e1.printStackTrace(); + } + + + + + } + + + }//end run + + private BaseEntry buildEntry(String id){ + Entry e = new Entry(); + e.setId(id); + e.setTitle(new PlainTextConstruct("Monty Python")); + + e.setPublished(DateTime.now()); + + e.setUpdated(DateTime.now()); + 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!" + + "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." + + "1st soldier with a keen interest in birds: What? Ridden on a horse?" + + "King Arthur: Yes!"; + e.setContent(new TextContent(new PlainTextConstruct(content))); + e.setSummary(new PlainTextConstruct("The Holy Grail")); + return e; + } + + } + +} Index: trunk/contrib/gdata-server/src/test/org/apache/lucene/gdata/storage/lucenestorage/TestModifiedEntryFilter.java =================================================================== --- trunk/contrib/gdata-server/src/test/org/apache/lucene/gdata/storage/lucenestorage/TestModifiedEntryFilter.java (revision 0) +++ trunk/contrib/gdata-server/src/test/org/apache/lucene/gdata/storage/lucenestorage/TestModifiedEntryFilter.java (revision 0) @@ -0,0 +1,66 @@ +package org.apache.lucene.gdata.storage.lucenestorage; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import junit.framework.TestCase; + +import org.apache.lucene.analysis.standard.StandardAnalyzer; +import org.apache.lucene.document.Document; +import org.apache.lucene.document.Field; +import org.apache.lucene.gdata.storage.lucenestorage.ModifiedEntryFilter; +import org.apache.lucene.gdata.storage.lucenestorage.StorageEntryWrapper; +import org.apache.lucene.index.IndexReader; +import org.apache.lucene.index.IndexWriter; +import org.apache.lucene.index.Term; +import org.apache.lucene.search.Hits; +import org.apache.lucene.search.IndexSearcher; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.Searcher; +import org.apache.lucene.search.TermQuery; +import org.apache.lucene.store.RAMDirectory; + +public class TestModifiedEntryFilter extends TestCase { + IndexWriter writer; + IndexReader reader; + List excludeList; + String feedID = "feed"; + String fieldFeedId = "feedID"; + protected void setUp() throws Exception { + RAMDirectory dir = new RAMDirectory(); + this.writer = new IndexWriter(dir,new StandardAnalyzer(),true); + Document doc = new Document(); + doc.add(new Field(StorageEntryWrapper.FIELD_ENTRY_ID,"1",Field.Store.YES,Field.Index.UN_TOKENIZED)); + doc.add(new Field(fieldFeedId,feedID,Field.Store.YES,Field.Index.UN_TOKENIZED)); + Document doc1 = new Document(); + doc1.add(new Field(StorageEntryWrapper.FIELD_ENTRY_ID,"2",Field.Store.YES,Field.Index.UN_TOKENIZED)); + doc1.add(new Field(fieldFeedId,feedID,Field.Store.YES,Field.Index.UN_TOKENIZED)); + this.writer.addDocument(doc); + this.writer.addDocument(doc1); + this.writer.close(); + this.reader = IndexReader.open(dir); + this.excludeList = new ArrayList(); + this.excludeList.add("1"); + + + } + + protected void tearDown() throws Exception { + super.tearDown(); + } + public void testFilter() throws IOException{ + Searcher s = new IndexSearcher(this.reader); + Query q = new TermQuery(new Term(fieldFeedId,feedID)); + Hits hits = s.search(q); + assertEquals(2,hits.length()); + + hits = s.search(q,new ModifiedEntryFilter(this.excludeList)); + assertEquals(1,hits.length()); + this.excludeList.add("2"); + + hits = s.search(q,new ModifiedEntryFilter(this.excludeList)); + assertEquals(0,hits.length()); + + } +} Index: trunk/contrib/gdata-server/src/test/org/apache/lucene/gdata/storage/lucenestorage/TestStorageQuery.java =================================================================== --- trunk/contrib/gdata-server/src/test/org/apache/lucene/gdata/storage/lucenestorage/TestStorageQuery.java (revision 0) +++ trunk/contrib/gdata-server/src/test/org/apache/lucene/gdata/storage/lucenestorage/TestStorageQuery.java (revision 0) @@ -0,0 +1,175 @@ +package org.apache.lucene.gdata.storage.lucenestorage; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import junit.framework.TestCase; + +import org.apache.lucene.analysis.standard.StandardAnalyzer; +import org.apache.lucene.gdata.server.FeedNotFoundException; +import org.apache.lucene.gdata.server.registry.FeedInstanceConfigurator; +import org.apache.lucene.gdata.server.registry.GDataServerRegistry; +import org.apache.lucene.gdata.server.registry.RegistryBuilder; +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.util.ReferenceCounter; +import org.apache.lucene.index.IndexWriter; +import org.apache.lucene.store.Directory; +import org.apache.lucene.store.RAMDirectory; + +import com.google.gdata.data.BaseEntry; +import com.google.gdata.data.DateTime; +import com.google.gdata.data.Entry; +import com.google.gdata.data.ExtensionProfile; +import com.google.gdata.data.Feed; +import com.google.gdata.util.ParseException; + +public class TestStorageQuery extends TestCase { + private StorageModifier modifier; + private int count = 30; + private ReferenceCounter query; + private ExtensionProfile profile; + + private Directory dir; + private static String feedId = "myFeed"; + protected void setUp() throws Exception { + FeedInstanceConfigurator configurator = new FeedInstanceConfigurator(); + configurator.setFeedType(Feed.class); + configurator.setFeedId(feedId); + configurator.setExtensionProfileClass(ExtensionProfile.class); + GDataServerRegistry.getRegistry().registerFeed(configurator); + this.profile = new ExtensionProfile(); + this.dir = new RAMDirectory(); + IndexWriter writer; + writer = new IndexWriter(this.dir,new StandardAnalyzer(),true); + writer.close(); + this.modifier = StorageCoreController.getStorageCoreController(this.dir).getStorageModifier(); + insertEntries(this.count); + this.query = StorageCoreController.getStorageCoreController().getStorageQuery(); + + + + } + + + public void insertEntries(int count) throws IOException,InterruptedException, StorageException{ + List tempList = new ArrayList(); + for (int i = 0; i <= count ; i++) { + Entry entry = new Entry(); + entry.setId(""+i); + + entry.setUpdated(new DateTime(System.currentTimeMillis(),0)); + StorageEntryWrapper wrapper = new StorageEntryWrapper(entry,feedId,StorageOperation.INSERT,this.profile); + tempList.add(i,wrapper); + + // force different timestamps --> DateTime 2006-06-05T13:37:55.724Z + Thread.sleep(50); + + } + for (StorageEntryWrapper entry : tempList) { + this.modifier.insertEntry(entry); + } + + + + + } + + protected void tearDown() throws Exception { + this.query.decrementRef(); + GDataServerRegistry.getRegistry().destroy();//TODO remove dependency here + } + + /* + * Test method for 'org.apache.lucene.storage.lucenestorage.StorageQuery.feedQuery(String, int, int)' + */ + public void testFeedQuery() throws IOException, FeedNotFoundException, ParseException, StorageException { + FeedQueryHelper(this.query); + StorageCoreController.getStorageCoreController().forceWrite(); + ReferenceCounter queryAssureWritten = StorageCoreController.getStorageCoreController().getStorageQuery(); + + assertNotSame(queryAssureWritten,this.query); + FeedQueryHelper(queryAssureWritten); + queryAssureWritten.decrementRef(); + } + private void FeedQueryHelper(ReferenceCounter currentQuery) throws IOException, FeedNotFoundException, ParseException{ + List entryList = currentQuery.get().getLatestFeedQuery(feedId,25,1,this.profile); + + assertTrue("listSize: "+entryList.size(),entryList.size() == 25); + + BaseEntry tempEntry = null; + for (BaseEntry entry : entryList) { + + assertNotNull("entry",entry); + if(tempEntry != null){ + assertTrue(tempEntry.getUpdated().compareTo(entry.getUpdated())>=0) ; + tempEntry = entry; + }else + tempEntry = entry; + + } + // test sub retrieve sublist + int offset = 15; + int resultCount = 5; + List entrySubList = currentQuery.get().getLatestFeedQuery(feedId,resultCount,offset,this.profile); + + assertTrue("listSize: "+entrySubList.size(),entrySubList.size() == resultCount); + offset--; + for (BaseEntry entry : entrySubList) { + + assertEquals(entry.getId(),entryList.get(offset).getId()); + offset++; + + } + + + + } + + /* + * Test method for 'org.apache.lucene.storage.lucenestorage.StorageQuery.singleEntryQuery(String, String)' + */ + public void testSingleEntryQuery() throws FeedNotFoundException, ParseException, IOException { + for (int i = 1; i <= this.count; i++) { + BaseEntry entry = this.query.get().singleEntryQuery(""+i,feedId,this.profile); + assertEquals(""+i,entry.getId()); + } + + } + + /* + * Test method for 'org.apache.lucene.storage.lucenestorage.StorageQuery.entryQuery(List, String)' + */ + public void testEntryQuery() throws FeedNotFoundException, ParseException, IOException, StorageException { + entryQueryHelper(this.query); + StorageCoreController.getStorageCoreController().forceWrite(); + ReferenceCounter queryAssureWritten = StorageCoreController.getStorageCoreController().getStorageQuery(); + + assertNotSame(queryAssureWritten,query); + entryQueryHelper(queryAssureWritten); + queryAssureWritten.decrementRef(); + } + + + private void entryQueryHelper(ReferenceCounter currentQuery) throws IOException, FeedNotFoundException, ParseException{ + + List entryIdList = new ArrayList(); + for (int i = 1; i <= this.count; i++) { + entryIdList.add(""+i); + } + List entryList = currentQuery.get().entryQuery(entryIdList,feedId,this.profile); + assertEquals(entryIdList.size(),entryList.size()); + List entryIdCompare = new ArrayList(); + for (BaseEntry entry : entryList) { + entryIdCompare.add(entry.getId()); + } + assertTrue(entryIdList.containsAll(entryIdCompare)); + + } + +} Index: trunk/contrib/gdata-server/src/test/org/apache/lucene/gdata/storage/TestIDGenerator.java =================================================================== --- trunk/contrib/gdata-server/src/test/org/apache/lucene/gdata/storage/TestIDGenerator.java (revision 0) +++ trunk/contrib/gdata-server/src/test/org/apache/lucene/gdata/storage/TestIDGenerator.java (revision 0) @@ -0,0 +1,52 @@ +package org.apache.lucene.gdata.storage; + +import java.util.ArrayList; +import java.util.List; + +import junit.framework.TestCase; + +import org.apache.lucene.gdata.storage.IDGenerator; + +/** + * @author Simon Willnauer + * + */ +public class TestIDGenerator extends TestCase { + private IDGenerator idgen; + + private int initialCap = 100; + + @Override + protected void setUp() throws Exception { + this.idgen = new IDGenerator(this.initialCap); + + + } + + @Override + protected void tearDown() throws Exception { + this.idgen.stopIDGenerator(); + } + + /** + * Test method for 'org.apache.lucene.gdata.storage.IDGenerator.getUID()' + * @throws InterruptedException + */ + public void testGetUID() throws InterruptedException { + + List idlist = new ArrayList(); + //TODO think about a better way to test this + for (int i = 0; i < 1000; i++) { + String id = this.idgen.getUID(); + assertNotNull(id); + assertFalse(idlist.contains(id)); + idlist.add(id); + + + + } + + } + + +} Property changes on: trunk/contrib/gdata-server/src/test/org/apache/lucene/gdata/storage/TestIDGenerator.java ___________________________________________________________________ Name: svn:executable + * Index: trunk/contrib/gdata-server/src/test/org/apache/lucene/gdata/server/TestGDataResponse.java =================================================================== --- trunk/contrib/gdata-server/src/test/org/apache/lucene/gdata/server/TestGDataResponse.java (revision 0) +++ trunk/contrib/gdata-server/src/test/org/apache/lucene/gdata/server/TestGDataResponse.java (revision 0) @@ -0,0 +1,161 @@ +package org.apache.lucene.gdata.server; + +import java.io.IOException; +import java.io.PrintWriter; +import java.io.StringWriter; + +import javax.servlet.http.HttpServletResponse; + +import junit.framework.TestCase; + +import org.apache.lucene.gdata.server.GDataRequest.OutputFormat; +import org.easymock.MockControl; + +import com.google.gdata.data.Entry; +import com.google.gdata.data.ExtensionProfile; +import com.google.gdata.data.Feed; +import com.google.gdata.data.PlainTextConstruct; +/** + * + * @author Simon Willnauer + * + */ +public class TestGDataResponse extends TestCase { + private GDataResponse response; + private HttpServletResponse httpResponse; + private MockControl control; + private static String generatedFeedAtom = "Test"; + private static String generatedEntryAtom = "Test"; + private static String generatedFeedRSS = "Test"; + private static String generatedEntryRSS = "Test"; + protected void setUp() throws Exception { + this.control = MockControl.createControl(HttpServletResponse.class); + this.httpResponse = (HttpServletResponse)this.control.getMock(); + this.response = new GDataResponse(this.httpResponse); + + } + + protected void tearDown() throws Exception { + super.tearDown(); + } + + + public void testConstructor(){ + try{ + new GDataResponse(null); + fail("IllegalArgumentExceptin expected"); + }catch (IllegalArgumentException e) { + // TODO: handle exception + } + } + /* + * Test method for 'org.apache.lucene.gdata.server.GDataResponse.sendResponse(BaseFeed, ExtensionProfile)' + */ + public void testSendResponseBaseFeedExtensionProfile() throws IOException { + try{ + Feed f = null; + this.response.sendResponse(f,new ExtensionProfile()); + fail("Exception expected"); + }catch (IllegalArgumentException e) { + // + } + + try{ + Feed f = createFeed(); + this.response.sendResponse(f,null); + fail("Exception expected"); + }catch (IllegalArgumentException e) { + // + } + StringWriter stringWriter = new StringWriter(); + PrintWriter writer = new PrintWriter(stringWriter); + + this.control.expectAndReturn(this.httpResponse.getWriter(),writer); + this.response.setOutputFormat(OutputFormat.ATOM); + this.control.replay(); + + this.response.sendResponse(createFeed(),new ExtensionProfile + ()); + assertEquals("Simple XML representation",stringWriter.toString(),generatedFeedAtom); + this.control.reset(); + + stringWriter = new StringWriter(); + writer = new PrintWriter(stringWriter); + + this.control.expectAndReturn(this.httpResponse.getWriter(),writer); + this.response.setOutputFormat(OutputFormat.RSS); + this.control.replay(); + + this.response.sendResponse(createFeed(),new ExtensionProfile + ()); + assertEquals("Simple XML representation",stringWriter.toString(),generatedFeedRSS); + + + + + } + + /* + * Test method for 'org.apache.lucene.gdata.server.GDataResponse.sendResponse(BaseEntry, ExtensionProfile)' + */ + public void testSendResponseBaseEntryExtensionProfile() throws IOException { + try{ + Entry e = null; + this.response.sendResponse(e,new ExtensionProfile()); + fail("Exception expected"); + }catch (IllegalArgumentException e) { + // + } + try{ + Entry e = createEntry(); + this.response.sendResponse(e,null); + fail("Exception expected"); + }catch (IllegalArgumentException e) { + // + } +// // test Atom output + StringWriter stringWriter = new StringWriter(); + PrintWriter writer = new PrintWriter(stringWriter); + + this.control.expectAndReturn(this.httpResponse.getWriter(),writer); + this.response.setOutputFormat(OutputFormat.ATOM); + this.control.replay(); + + this.response.sendResponse(createEntry(),new ExtensionProfile + ()); + assertEquals("Simple XML representation ATOM",stringWriter.toString(),generatedEntryAtom); + + // test rss output + this.control.reset(); + stringWriter = new StringWriter(); + writer = new PrintWriter(stringWriter); + + this.control.expectAndReturn(this.httpResponse.getWriter(),writer); + this.response.setOutputFormat(OutputFormat.RSS); + this.control.replay(); + + this.response.sendResponse(createEntry(),new ExtensionProfile + ()); + + assertEquals("Simple XML representation RSS",stringWriter.toString(),generatedEntryRSS); + + + + } + + /* create a simple feed */ + private Feed createFeed(){ + Feed feed = new Feed(); + + feed.getEntries().add(createEntry()); + + return feed; + } + /* create a simple entry */ + private Entry createEntry(){ + Entry e = new Entry(); + e.setTitle(new PlainTextConstruct("Test")); + return e; + } + +} Index: trunk/contrib/gdata-server/src/test/org/apache/lucene/gdata/server/registry/TestGDataEntityBuilder.java =================================================================== --- trunk/contrib/gdata-server/src/test/org/apache/lucene/gdata/server/registry/TestGDataEntityBuilder.java (revision 0) +++ trunk/contrib/gdata-server/src/test/org/apache/lucene/gdata/server/registry/TestGDataEntityBuilder.java (revision 0) @@ -0,0 +1,98 @@ +/** + * 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; + +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.io.Reader; + +import junit.framework.TestCase; + +import org.apache.lucene.gdata.server.FeedNotFoundException; +import org.apache.lucene.gdata.server.GDataEntityBuilder; + +import com.google.gdata.data.BaseEntry; +import com.google.gdata.data.BaseFeed; +import com.google.gdata.data.Entry; +import com.google.gdata.data.ExtensionProfile; +import com.google.gdata.data.Feed; +import com.google.gdata.util.ParseException; + +/** + * @author Simon Willnauer + * + */ +public class TestGDataEntityBuilder extends TestCase { + private static File incomingFeed = new File("src/test/org/apache/lucene/gdata/server/registry/TestEntityBuilderIncomingFeed.xml"); + private static File incomingEntry = new File("src/test/org/apache/lucene/gdata/server/registry/TestEntityBuilderIncomingEntry.xml"); + private static String feedTitleFromXML = "Simon Willnauer"; + private static String entrySummaryFromXML = "When: 2006-12-23 to 2006-12-31 America/Los_Angeles"; + private static GDataServerRegistry reg = GDataServerRegistry.getRegistry(); + private Reader reader; + private static String feedID = "myFeed"; + private ExtensionProfile profile; + private static Class feedType = Feed.class; + + + /** + * @see junit.framework.TestCase#setUp() + */ + @Override + protected void setUp() throws Exception { + FeedInstanceConfigurator config = new FeedInstanceConfigurator(); + config.setFeedId(feedID); + config.setFeedType(feedType); + this.profile = new ExtensionProfile(); + reg.registerFeed(config); + } + + /** + * @see junit.framework.TestCase#tearDown() + */ + @Override + protected void tearDown() throws Exception { + reg.flushRegistry(); + this.reader = null; + } + + /** + * Test method for 'org.apache.lucene.gdata.data.GDataEntityBuilder.buildFeed(String, Reader)' + */ + public void testBuildFeedStringReader() throws FeedNotFoundException, ParseException, IOException { + this.reader = new FileReader(incomingFeed); + BaseFeed feed = GDataEntityBuilder.buildFeed(feedID,this.reader,this.profile); + assertNotNull(feed); + assertEquals("feed title",feed.getTitle().getPlainText(), feedTitleFromXML); + assertTrue( feed instanceof Feed); + + } + + /* + * Test method for 'org.apache.lucene.gdata.data.GDataEntityBuilder.buildEntry(String, Reader)' + */ + public void testBuildEntryStringReader() throws FeedNotFoundException, ParseException, IOException { + this.reader = new FileReader(incomingEntry); + BaseEntry entry = GDataEntityBuilder.buildEntry(feedID,this.reader,this.profile); + assertNotNull(entry); + assertEquals("entry summary",entry.getSummary().getPlainText(),entrySummaryFromXML); + assertTrue(entry instanceof Entry); + + } + + + +} Index: trunk/contrib/gdata-server/src/test/org/apache/lucene/gdata/server/registry/TestEntityBuilderIncomingEntry.xml =================================================================== --- trunk/contrib/gdata-server/src/test/org/apache/lucene/gdata/server/registry/TestEntityBuilderIncomingEntry.xml (revision 0) +++ trunk/contrib/gdata-server/src/test/org/apache/lucene/gdata/server/registry/TestEntityBuilderIncomingEntry.xml (revision 0) @@ -0,0 +1,21 @@ + + + + http://www.google.com/calendar/feeds/simon.willnauer%40googlemail.com/public/basic/af4b5ca305c80f96f42c9af66c5b04a8473c949c + + 2006-12-23T00:00:00.000Z + 2006-05-23T16:42:48.000Z + + + + When: 2006-12-23 to 2006-12-31 America/Los_Angeles<br> + + + + + + \ No newline at end of file Index: trunk/contrib/gdata-server/src/test/org/apache/lucene/gdata/server/registry/TestEntityBuilderIncomingFeed.xml =================================================================== --- trunk/contrib/gdata-server/src/test/org/apache/lucene/gdata/server/registry/TestEntityBuilderIncomingFeed.xml (revision 0) +++ trunk/contrib/gdata-server/src/test/org/apache/lucene/gdata/server/registry/TestEntityBuilderIncomingFeed.xml (revision 0) @@ -0,0 +1,90 @@ + + + + http://www.google.com/calendar/feeds/simon.willnauer%40googlemail.com/public/basic + + 2006-05-27T11:47:55.000Z + Simon Willnauer + Simon Willnauer + + + + + + + + Simon Willnauer + simon.willnauer@googlemail.com + + + Google Calendar + + 25 + + + http://www.google.com/calendar/feeds/simon.willnauer%40googlemail.com/public/basic/af4b5ca305c80f96f42c9af66c5b04a8473c949c + + 2006-12-23T00:00:00.000Z + 2006-05-23T16:42:48.000Z + + + + When: 2006-12-23 to 2006-12-31 America/Los_Angeles<br> + + + + + + + + + http://www.google.com/calendar/feeds/simon.willnauer%40googlemail.com/public/basic/d5402951792ce690f6a45e51143deb78f4fffac4 + + 2006-05-26T00:00:00.000Z + 2006-05-20T22:17:44.000Z + + + + When: 2006-05-26 to 2006-05-27 America/Los_Angeles<br> + + + + + + + + + http://www.google.com/calendar/feeds/simon.willnauer%40googlemail.com/public/basic/21630e853795ea81d5e792d0ab082ad1c44256e4 + + 2006-06-19T14:00:00.000Z + 2006-05-17T16:10:57.000Z + + + + When: 2006-06-19 07:00:00 to 08:00:00 + America/Los_Angeles<br> + + + + + + + \ No newline at end of file Index: trunk/contrib/gdata-server/src/test/org/apache/lucene/gdata/server/registry/TestFeedRegistry.java =================================================================== --- trunk/contrib/gdata-server/src/test/org/apache/lucene/gdata/server/registry/TestFeedRegistry.java (revision 0) +++ trunk/contrib/gdata-server/src/test/org/apache/lucene/gdata/server/registry/TestFeedRegistry.java (revision 0) @@ -0,0 +1,98 @@ +/** + * 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; + +import com.google.gdata.data.Feed; + +import junit.framework.TestCase; + +/** + * @author Simon Willnauer + * + */ +public class TestFeedRegistry extends TestCase { + private GDataServerRegistry reg; + private FeedInstanceConfigurator configurator; + @Override + protected void setUp(){ + this.reg = GDataServerRegistry.getRegistry(); + this.configurator = new FeedInstanceConfigurator(); + } + /** + * @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.setFeedId(feedURL); + this.reg.registerFeed(this.configurator); + } + +} Index: trunk/contrib/gdata-server/src/test/org/apache/lucene/gdata/server/TestGDataRequest.java =================================================================== --- trunk/contrib/gdata-server/src/test/org/apache/lucene/gdata/server/TestGDataRequest.java (revision 0) +++ trunk/contrib/gdata-server/src/test/org/apache/lucene/gdata/server/TestGDataRequest.java (revision 0) @@ -0,0 +1,403 @@ +/** + * 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; + +import javax.servlet.http.HttpServletRequest; + +import junit.framework.TestCase; + +import org.apache.lucene.gdata.server.GDataRequest.GDataRequestType; +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.easymock.MockControl; + +import com.google.gdata.data.ExtensionProfile; +import com.google.gdata.data.Feed; + +/** + * + * @author Simon Willnauer + * + */ +public class TestGDataRequest extends TestCase { + private HttpServletRequest request; + + private MockControl control; + + private GDataRequest feedRequest; + + @Override + protected void setUp() throws Exception { + FeedInstanceConfigurator configurator = new FeedInstanceConfigurator(); + configurator.setFeedType(Feed.class); + configurator.setFeedId("feed"); + configurator.setExtensionProfileClass(ExtensionProfile.class); + GDataServerRegistry.getRegistry().registerFeed(configurator); + this.control = MockControl.createControl(HttpServletRequest.class); + this.request = (HttpServletRequest) this.control.getMock(); + this.feedRequest = new GDataRequest(this.request,GDataRequestType.GET); + + } + + protected void tearDown() throws Exception { + super.tearDown(); + this.control.reset(); + } + + public void testConstructor() { + try { + new GDataRequest(null,GDataRequestType.GET); + fail("IllegalArgumentException expected"); + } catch (IllegalArgumentException e) { + // + } + try { + new GDataRequest(null,null); + fail("IllegalArgumentException expected"); + } catch (IllegalArgumentException e) { + // + } + try { + new GDataRequest(this.request,null); + fail("IllegalArgumentException expected"); + } catch (IllegalArgumentException e) { + // + } + } + + public void testGetFeedId() throws GDataRequestException { + + this.control.expectAndDefaultReturn(this.request.getPathInfo(), + "/feed/1/1"); + this.control.expectAndDefaultReturn(this.request.getParameter("alt"), + null); + this.control.replay(); + this.feedRequest.initializeRequest(); + assertEquals("feedID", this.feedRequest.getFeedId(), "feed"); + this.control.reset(); + + } + + public void testEmptyPathInfo() { + this.control.expectAndDefaultReturn(this.request.getPathInfo(), "/"); + this.control.expectAndDefaultReturn(this.request.getParameter("alt"), + null); + this.control.replay(); + try { + this.feedRequest.initializeRequest(); + + fail("FeedRequestException expected"); + } catch (GDataRequestException e) { + // expected + } catch (Exception e) { + fail("FeedRequestException expected"); + } + + } + + public void testGetFeedIdWithoutEntry() throws GDataRequestException { + this.control + .expectAndDefaultReturn(this.request.getPathInfo(), "/feed"); + this.control.expectAndDefaultReturn(this.request.getParameter("alt"), + null); + this.control.replay(); + this.feedRequest.initializeRequest(); + assertEquals("feedID", this.feedRequest.getFeedId(), "feed"); + } + + public void testGetEntyId() throws GDataRequestException { + + this.control.expectAndDefaultReturn(this.request.getPathInfo(), + "/feed/1/15"); + this.control.expectAndDefaultReturn(this.request.getParameter("alt"), + null); + this.control.replay(); + this.feedRequest.initializeRequest(); + assertEquals("entryid", this.feedRequest.getEntryId(), "1"); + assertEquals("feedId", this.feedRequest.getFeedId(), "feed"); + assertEquals("entryid", this.feedRequest.getEntryVersion(), "15"); + this.control.reset(); + + } + + public void testSetResponseFormatAtom() throws GDataRequestException { + this.control.expectAndDefaultReturn(this.request.getParameter("alt"), + "atom"); + this.control + .expectAndDefaultReturn(this.request.getPathInfo(), "/feed"); + this.control.replay(); + this.feedRequest.initializeRequest(); + assertEquals("ResponseFromat Atom", this.feedRequest + .getRequestedResponseFormat(), OutputFormat.ATOM); + this.control.reset(); + } + + public void testSetResponseFormatRSS() throws GDataRequestException { + this.control.expectAndDefaultReturn(this.request.getParameter("alt"), + "rss"); + this.control + .expectAndDefaultReturn(this.request.getPathInfo(), "/feed"); + this.control.replay(); + this.feedRequest.initializeRequest(); + assertEquals("ResponseFromat RSS", this.feedRequest + .getRequestedResponseFormat(), OutputFormat.RSS); + this.control.reset(); + } + + public void testSetResponseFormatKeepAtom() throws GDataRequestException { + this.control.expectAndDefaultReturn(this.request.getParameter("alt"), + "fooBar"); + this.control + .expectAndDefaultReturn(this.request.getPathInfo(), "/feed"); + this.control.replay(); + this.feedRequest.initializeRequest(); + assertEquals("ResponseFromat Atom", this.feedRequest + .getRequestedResponseFormat(), OutputFormat.ATOM); + this.control.reset(); + } + + public void testSetResponseFormatNull() throws GDataRequestException { + this.control.expectAndDefaultReturn(this.request.getParameter("alt"), + null); + + this.control + .expectAndDefaultReturn(this.request.getPathInfo(), "/feed"); + this.control.replay(); + this.feedRequest.initializeRequest(); + assertEquals("ResponseFromat Atom", this.feedRequest + .getRequestedResponseFormat(), OutputFormat.ATOM); + this.control.reset(); + } + + public void testGetItemsPerPage() throws GDataRequestException { + this.control.expectAndReturn(this.request.getParameter("max-results"), + null); + this.control.replay(); + assertEquals("default value 25", 25, this.feedRequest.getItemsPerPage()); + this.control.verify(); + this.control.reset(); + + this.control.expectAndReturn(this.request.getParameter("max-results"), + "24", 2); + this.control.replay(); + assertEquals("24 results", 24, this.feedRequest.getItemsPerPage()); + this.control.verify(); + this.control.reset(); + + this.control.expectAndReturn(this.request.getParameter("max-results"), + "-1", 2); + this.control.replay(); + assertEquals("25 results", 25, this.feedRequest.getItemsPerPage()); + this.control.verify(); + this.control.reset(); + + this.control.expectAndReturn(this.request.getParameter("max-results"), + "helloworld", 2); + this.control.replay(); + assertEquals("25 results", 25, this.feedRequest.getItemsPerPage()); + this.control.verify(); + this.control.reset(); + } + + public void testGetSelfId() throws GDataRequestException{ + String host = "www.apache.org"; + String feedAndEntryID = "/feed/entryid"; + String queryString = "?max-results=25"; + this.control.expectAndDefaultReturn(this.request.getHeader("Host"),host); + this.control.expectAndDefaultReturn(this.request.getRequestURI(),"/host/feed/entryId/15"); + this.control.expectAndDefaultReturn(this.request.getPathInfo(),"/feed/entryId/15"); + this.control.expectAndReturn(this.request.getParameter("max-results"),"25",2); + this.control.expectAndDefaultReturn(this.request.getParameter("alt"), + null); + this.control.expectAndDefaultReturn(this.request.getQueryString(), + queryString); + this.control.replay(); + this.feedRequest.initializeRequest(); + String selfID = "http://"+host+"/host/feed/entryId/15"+queryString; + + assertEquals("Self ID",selfID,this.feedRequest.getSelfId()); + this.control.reset(); + + + queryString = "?alt=rss&max-results=25"; + this.control.expectAndDefaultReturn(this.request.getHeader("Host"),host); + this.control.expectAndDefaultReturn(this.request.getRequestURI(),"/host/feed/entryId/15"); + this.control.expectAndDefaultReturn(this.request.getPathInfo(),"/feed/entryId/15"); + this.control.expectAndReturn(this.request.getParameter("max-results"),"25",2); + this.control.expectAndDefaultReturn(this.request.getParameter("alt"), + null); + this.control.expectAndDefaultReturn(this.request.getQueryString(), + queryString); + this.control.replay(); + this.feedRequest.initializeRequest(); + selfID = "http://"+host+"/host/feed/entryId/15"+queryString; + + assertEquals("Self ID",selfID,this.feedRequest.getSelfId()); + this.control.reset(); + + queryString = ""; + this.control.expectAndDefaultReturn(this.request.getHeader("Host"),host); + this.control.expectAndDefaultReturn(this.request.getRequestURI(),"/host/feed/entryId/15"); + this.control.expectAndDefaultReturn(this.request.getPathInfo(),"/feed/entryId/15"); + this.control.expectAndDefaultReturn(this.request.getParameter("max-results"),null); + this.control.expectAndDefaultReturn(this.request.getParameter("alt"), + null); + this.control.expectAndDefaultReturn(this.request.getQueryString(), + null); + this.control.replay(); + this.feedRequest.initializeRequest(); + selfID = "http://"+host+"/host/feed/entryId/15"+"?max-results=25"; + + assertEquals("Self ID",selfID,this.feedRequest.getSelfId()); + this.control.reset(); + } + + public void testGetQueryString(){ + String maxResults = "max-results=25"; + String queryString = "?"+maxResults; + this.control.expectAndReturn(this.request.getParameter("max-results"),"25",2); + + this.control.expectAndDefaultReturn(this.request.getQueryString(), + queryString); + this.control.replay(); + + assertEquals(queryString,this.feedRequest.getQueryString()); + this.control.reset(); + // test no result defined + queryString = "?alt=rss"; + this.control.expectAndDefaultReturn(this.request.getParameter("max-results"),null); + + this.control.expectAndDefaultReturn(this.request.getQueryString(), + queryString); + this.control.replay(); + + assertEquals(queryString+"&"+maxResults,this.feedRequest.getQueryString()); + this.control.reset(); + +// test no result defined && query == null + queryString = null; + this.control.expectAndDefaultReturn(this.request.getParameter("max-results"),null); + + this.control.expectAndDefaultReturn(this.request.getQueryString(), + queryString); + this.control.replay(); + + assertEquals("?"+maxResults,this.feedRequest.getQueryString()); + this.control.reset(); + + } + + public void testIsFeedRequest() throws GDataRequestException{ + String host = "www.apache.org"; + String feedAndEntryID = "/feed"; + + this.control.expectAndDefaultReturn(this.request.getHeader("Host"),host); + this.control.expectAndDefaultReturn(this.request.getRequestURI(),"/host/feed"); + this.control.expectAndDefaultReturn(this.request.getPathInfo(),"/feed"); + + this.control.expectAndDefaultReturn(this.request.getParameter("alt"), + null); + this.control.expectAndDefaultReturn(this.request.getQueryString(), + null); + this.control.replay(); + this.feedRequest.initializeRequest(); + + + assertTrue(this.feedRequest.isFeedRequested()); + assertFalse(this.feedRequest.isEntryRequested()); + this.control.reset(); + + host = "www.apache.org"; + feedAndEntryID = "/feed/1"; + + this.control.expectAndDefaultReturn(this.request.getHeader("Host"),host); + this.control.expectAndDefaultReturn(this.request.getRequestURI(),"/host/feed/1"); + this.control.expectAndDefaultReturn(this.request.getPathInfo(),feedAndEntryID); + + this.control.expectAndDefaultReturn(this.request.getParameter("alt"), + null); + this.control.expectAndDefaultReturn(this.request.getQueryString(), + null); + this.control.replay(); + this.feedRequest.initializeRequest(); + + + assertFalse(this.feedRequest.isFeedRequested()); + assertTrue(this.feedRequest.isEntryRequested()); + this.control.reset(); + + + } + public void testIsEntryRequest(){ + + } + + public void testGetNextId() throws GDataRequestException{ +// String host = "www.apache.org"; +// String feedAndEntryID = "/feed/entryid"; +// String queryString = "?max-results=25"; +// String startIndex = "&start-index=26"; +// this.control.expectAndDefaultReturn(this.request.getHeader("Host"),host); +// this.control.expectAndDefaultReturn(this.request.getPathInfo(),"/feed/entryId/15"); +// this.control.expectAndReturn(this.request.getParameter("max-results"),"25",2); +// this.control.expectAndReturn(this.request.getParameter("start-index"),null); +// this.control.expectAndDefaultReturn(this.request.getParameter("alt"), +// null); +// this.control.expectAndDefaultReturn(this.request.getQueryString(), +// queryString); +// this.control.replay(); +// this.feedRequest.initializeRequest(); +// String nextID = "http://"+host+"/feed"+queryString+startIndex; +// +// assertEquals("Next ID",nextID,this.feedRequest.getNextId()); +// this.control.reset(); +// +// +// queryString = "?alt=rss&max-results=25"; +// +// this.control.expectAndDefaultReturn(this.request.getHeader("Host"),host); +// this.control.expectAndDefaultReturn(this.request.getPathInfo(),"/feed/entryId/15"); +// this.control.expectAndReturn(this.request.getParameter("max-results"),"25",2); +// this.control.expectAndReturn(this.request.getParameter("start-index"),"26",2); +// this.control.expectAndDefaultReturn(this.request.getParameter("alt"), +// null); +// this.control.expectAndDefaultReturn(this.request.getQueryString(), +// queryString+startIndex); +// this.control.replay(); +// this.feedRequest.initializeRequest(); +// startIndex = "&start-index=51"; +// nextID = "http://"+host+"/feed"+queryString+startIndex; +// +// assertEquals("Next ID 51",nextID,this.feedRequest.getNextId()); +// this.control.reset(); +// +// queryString = ""; +// this.control.expectAndDefaultReturn(this.request.getHeader("Host"),host); +// this.control.expectAndDefaultReturn(this.request.getPathInfo(),"/feed/entryId/15"); +// this.control.expectAndDefaultReturn(this.request.getParameter("max-results"),null); +// this.control.expectAndDefaultReturn(this.request.getParameter("alt"), +// null); +// this.control.expectAndDefaultReturn(this.request.getQueryString(), +// null); +// this.control.replay(); +// this.feedRequest.initializeRequest(); +// selfID = "http://"+host+"/feed"+"?max-results=25"; +// +// assertEquals("Self ID",selfID,this.feedRequest.getSelfId()); +// this.control.reset(); + } +} Index: trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/servlet/RequestControllerServlet.java =================================================================== --- trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/servlet/RequestControllerServlet.java (revision 0) +++ trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/servlet/RequestControllerServlet.java (revision 0) @@ -0,0 +1,122 @@ +/** + * 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.servlet; + +import java.io.IOException; + +import javax.servlet.ServletConfig; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.commons.logging.Log; +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.RequestHandlerFactory; + +/** + * Provides a clean basic interface for GDATA Client API and requests to the + * GDATA Server. This Servlet dispatches the incoming requests to defined GDATA + * request handlers. Each of the handler processes the incoming request and + * responds according to the requested action. + * + * @author Simon Willnauer + * + */ +public class RequestControllerServlet extends AbstractGdataServlet { + private static RequestHandlerFactory HANDLER_FACTORY = null; + private static final Log LOGGER = LogFactory.getLog(RequestControllerServlet.class); + + /** + * Version ID since this class implements + * + * @see java.io.Serializable + */ + private static final long serialVersionUID = 7540810742476175576L; + + /** + * @see javax.servlet.http.HttpServlet#doDelete(javax.servlet.http.HttpServletRequest, + * javax.servlet.http.HttpServletResponse) + */ + @Override + protected void doDelete(HttpServletRequest arg0, HttpServletResponse arg1) + throws ServletException, IOException { + GDataRequestHandler hanlder = HANDLER_FACTORY.getDeleteHandler(); + if(LOGGER.isInfoEnabled()) + LOGGER.info("Process DELETE request"); + + hanlder.processRequest(arg0, arg1); + } + + /** + * @see javax.servlet.http.HttpServlet#doGet(javax.servlet.http.HttpServletRequest, + * javax.servlet.http.HttpServletResponse) + */ + @Override + protected void doGet(HttpServletRequest arg0, HttpServletResponse arg1) + throws ServletException, IOException { + GDataRequestHandler hanlder = HANDLER_FACTORY.getQueryHandler(); + if(LOGGER.isInfoEnabled()) + LOGGER.info("Process GET request"); + + hanlder.processRequest(arg0, arg1); + } + + /** + * @see javax.servlet.http.HttpServlet#doPost(javax.servlet.http.HttpServletRequest, + * javax.servlet.http.HttpServletResponse) + */ + @Override + protected void doPost(HttpServletRequest arg0, HttpServletResponse arg1) + throws ServletException, IOException { + GDataRequestHandler hanlder = HANDLER_FACTORY.getInsertHandler(); + if(LOGGER.isInfoEnabled()) + LOGGER.info("Process POST request"); + hanlder.processRequest(arg0, arg1); + } + + /** + * @see javax.servlet.http.HttpServlet#doPut(javax.servlet.http.HttpServletRequest, + * javax.servlet.http.HttpServletResponse) + */ + @Override + protected void doPut(HttpServletRequest arg0, HttpServletResponse arg1) + throws ServletException, IOException { + GDataRequestHandler hanlder = HANDLER_FACTORY.getUpdateHandler(); + if(LOGGER.isInfoEnabled()) + LOGGER.info("Process PUT request"); + 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); + + } + + +} Index: trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/servlet/AbstractGdataServlet.java =================================================================== --- trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/servlet/AbstractGdataServlet.java (revision 0) +++ trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/servlet/AbstractGdataServlet.java (revision 0) @@ -0,0 +1,97 @@ +/** + * 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.servlet; + +import java.io.IOException; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + * + * Provides an abstract class to be subclassed to create an GDATA servlet + * suitable for a GDATA serverside implementation. + * + * @see javax.servlet.http.HttpServlet + * + * @author Simon Willnauer + * + */ +public abstract class AbstractGdataServlet extends HttpServlet { + private static final String METHOD_HEADER_NAME = "x-http-method-override"; + + private static final String METHOD_DELETE = "DELETE"; + + private static final String METHOD_GET = "GET"; + + private static final String METHOD_POST = "POST"; + + private static final String METHOD_PUT = "PUT"; + + /** + * This overwrites the protected service method to dispatch + * the request to the correponding do method. There is + * ususaly no need for overwriting this method. The GData protool and the + * Google GData API uses the x-http-method-override header to + * get through firewalls. The http method will be overritten by the + * x-http-method-override and dispatched to the + * doXxx methods defined in this class. This method + * is an GDATA-specific version of the {@link javax.servlet.Servlet#service} + * method. + * + * @see HttpServlet#service(javax.servlet.http.HttpServletRequest, + * javax.servlet.http.HttpServletResponse) + */ + @Override + protected void service(HttpServletRequest arg0, HttpServletResponse arg1) + throws ServletException, IOException { + if (arg0.getHeader(METHOD_HEADER_NAME) == null) { + super.service(arg0, arg1); + return; + } + overrideMethod(arg0, arg1); + + } + + private void overrideMethod(HttpServletRequest arg0, + HttpServletResponse arg1) throws ServletException, IOException { + final String method = arg0.getMethod(); + final String overrideHeaderMethod = arg0.getHeader(METHOD_HEADER_NAME); + if (overrideHeaderMethod.equals(method)) { + super.service(arg0, arg1); + return; + } + // These methodes are use by GDATA Client APIs + if (overrideHeaderMethod.equals(METHOD_DELETE)) { + doDelete(arg0, arg1); + } else if (overrideHeaderMethod.equals(METHOD_GET)) { + doGet(arg0, arg1); + } else if (overrideHeaderMethod.equals(METHOD_POST)) { + doPost(arg0, arg1); + } else if (overrideHeaderMethod.equals(METHOD_PUT)) { + doPut(arg0, arg1); + } else { + // if another method has been overwritten follow the HttpServlet + // implementation + super.service(arg0, arg1); + } + + } + +} Index: trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/servlet/handler/DefaultGetHandler.java =================================================================== --- trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/servlet/handler/DefaultGetHandler.java (revision 0) +++ trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/servlet/handler/DefaultGetHandler.java (revision 0) @@ -0,0 +1,104 @@ +/** + * 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.servlet.handler; + +import java.io.IOException; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +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.GDataRequest.GDataRequestType; + +import com.google.gdata.data.BaseEntry; +import com.google.gdata.data.BaseFeed; + +/** + * Default Handler implementation. This handler processes the incoming + * {@link org.apache.lucene.gdata.server.GDataRequest} and retrieves the + * requested feed from the underlaying storage. + *

+ * This hander also processes search queries and retrives the search hits from + * the underlaying search component. The user query will be accessed via the + * {@link org.apache.lucene.gdata.server.GDataRequest} instance passed to the + * {@link Service} class. + *

+ * + * + * @author Simon Willnauer + * + */ +public class DefaultGetHandler extends AbstractGdataRequestHandler { + private static final Log LOG = LogFactory.getLog(DefaultGetHandler.class); + + /** + * @see org.apache.lucene.gdata.servlet.handler.AbstractGdataRequestHandler#processRequest(javax.servlet.http.HttpServletRequest, + * javax.servlet.http.HttpServletResponse) + */ + @Override + public void processRequest(HttpServletRequest request, + HttpServletResponse response) throws IOException, ServletException { + try { + initializeRequestHandler(request, response, GDataRequestType.GET); + } catch (GDataRequestException e) { + sendError(); + return; + } + Service service = getService(); + try { + if (LOG.isInfoEnabled()) + LOG.info("Requested output formate: " + + this.feedRequest.getRequestedResponseFormat()); + this.feedResponse.setOutputFormat(this.feedRequest + .getRequestedResponseFormat()); + if(this.feedRequest.isFeedRequested()){ + BaseFeed feed = service + .getFeed(this.feedRequest, this.feedResponse); + + this.feedResponse.sendResponse(feed, this.feedRequest.getExtensionProfile()); + }else{ + BaseEntry entry = service.getSingleEntry(this.feedRequest,this.feedResponse); + if(entry == null){ + this.feedResponse.setError(HttpServletResponse.SC_NOT_FOUND); + sendError(); + } + this.feedResponse.sendResponse(entry, this.feedRequest.getExtensionProfile()); + } + + + } catch (ServiceException e) { // TODO handle exceptions to send exact + // response + LOG.error("Could not process GetFeed request - " + e.getMessage(), + e); + this.feedResponse.setError(HttpServletResponse.SC_BAD_REQUEST); // TODO + // change + // this + sendError(); + } + + + + } + + + +} Index: trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/servlet/handler/DefaultRequestHandlerFactory.java =================================================================== --- trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/servlet/handler/DefaultRequestHandlerFactory.java (revision 0) +++ trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/servlet/handler/DefaultRequestHandlerFactory.java (revision 0) @@ -0,0 +1,69 @@ +/** + * 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.servlet.handler; + +/** + * Default implementation for RequestHandlerFactory Builds the + * {@link org.apache.lucene.gdata.servlet.handler.GDataRequestHandler} + * instances. + * + * @author Simon Willnauer + * + */ +public class DefaultRequestHandlerFactory extends RequestHandlerFactory { + + DefaultRequestHandlerFactory() { + // + } + + /** + * @see org.apache.lucene.gdata.servlet.handler.RequestHandlerFactory#getUpdateHandler() + */ + @Override + 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(); + } + +} Index: trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/servlet/handler/DefaultDeleteHandler.java =================================================================== --- trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/servlet/handler/DefaultDeleteHandler.java (revision 0) +++ trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/servlet/handler/DefaultDeleteHandler.java (revision 0) @@ -0,0 +1,84 @@ +/** + * 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.servlet.handler; + +import java.io.IOException; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.lucene.gdata.server.FeedNotFoundException; +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.GDataRequest.GDataRequestType; + +/** + * Default Handler implementation. This handler processes the incoming + * {@link org.apache.lucene.gdata.server.GDataRequest} and deletes the requested + * feed entry from the storage and the search component. + *

+ * The handler sends following response to the client: + *

+ *
    + *
  1. if the entry could be deleted - HTTP status code 200 OK
  2. + *
  3. if an error occures - HTTP status code 500 INTERNAL SERVER ERROR
  4. + *
  5. if the resource could not found - HTTP status code 404 NOT FOUND
  6. + *
+ * + * @author Simon Willnauer + * + */ +public class DefaultDeleteHandler extends AbstractGdataRequestHandler { + private static final Log LOG = LogFactory + .getLog(DefaultDeleteHandler.class); + + /** + * @throws ServletException + * @see org.apache.lucene.gdata.servlet.handler.AbstractGdataRequestHandler#processRequest(javax.servlet.http.HttpServletRequest, + * javax.servlet.http.HttpServletResponse) + */ + @Override + public void processRequest(HttpServletRequest request, + HttpServletResponse response) throws IOException, ServletException { + try { + initializeRequestHandler(request, response,GDataRequestType.DELETE); + } catch (GDataRequestException e) { + sendError(); + return; + } + + Service service = getService(); + try { + 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(); + } + + } + +} Index: trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/servlet/handler/DefaultUpdateHandler.java =================================================================== --- trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/servlet/handler/DefaultUpdateHandler.java (revision 0) +++ trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/servlet/handler/DefaultUpdateHandler.java (revision 0) @@ -0,0 +1,94 @@ +/** + * 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.servlet.handler; + +import java.io.IOException; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.lucene.gdata.server.FeedNotFoundException; +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.GDataRequest.GDataRequestType; + +import com.google.gdata.data.BaseEntry; + +/** + * Default Handler implementation. This handler processes the incoming + * {@link org.apache.lucene.gdata.server.GDataRequest} and updates the requested + * feed entry in the storage and the search component. + *

+ * The handler sends following response to the client: + *

+ *
    + *
  1. if the entry was successfully updated - HTTP status code 200 OK
  2. + *
  3. if an error occures - HTTP status code 500 INTERNAL SERVER ERROR
  4. + *
  5. if the resource could not found - HTTP status code 404 NOT FOUND
  6. + *
+ * + * @author Simon Willnauer + * + */ +public class DefaultUpdateHandler extends AbstractGdataRequestHandler { + private static final Log LOG = LogFactory + .getLog(DefaultUpdateHandler.class); + + /** + * @throws ServletException + * @see org.apache.lucene.gdata.servlet.handler.AbstractGdataRequestHandler#processRequest(javax.servlet.http.HttpServletRequest, + * javax.servlet.http.HttpServletResponse) + */ + @Override + public void processRequest(HttpServletRequest request, + HttpServletResponse response) throws IOException, ServletException { + try { + initializeRequestHandler(request, response,GDataRequestType.UPDATE); + } catch (GDataRequestException e) { + sendError(); + return; + } + + Service service = getService(); + try { + BaseEntry entry = service.updateEntry(this.feedRequest, + this.feedResponse); + setFeedResponseFormat(); + setFeedResponseStatus(HttpServletResponse.SC_OK); + 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) { + + LOG.error("Could not process UpdateFeed request - " + + e.getMessage(), e); + setError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + + sendError(); + } + + } + +} Index: trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/servlet/handler/GDataRequestHandlerException.java =================================================================== --- trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/servlet/handler/GDataRequestHandlerException.java (revision 0) +++ trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/servlet/handler/GDataRequestHandlerException.java (revision 0) @@ -0,0 +1,64 @@ +/** + * 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.servlet.handler; + +/** + * @author Simon Willnauer + * + */ +public class GDataRequestHandlerException extends RuntimeException { + + /** + * + */ + private static final long serialVersionUID = -418225239671624153L; + + + /** + * + */ + public GDataRequestHandlerException() { + super(); + + } + + /** + * @param arg0 + */ + public GDataRequestHandlerException(String arg0) { + super(arg0); + + } + + /** + * @param arg0 + * @param arg1 + */ + public GDataRequestHandlerException(String arg0, Throwable arg1) { + super(arg0, arg1); + + } + + /** + * @param arg0 + */ + public GDataRequestHandlerException(Throwable arg0) { + super(arg0); + + } + +} Index: trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/servlet/handler/DefaultInsertHandler.java =================================================================== --- trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/servlet/handler/DefaultInsertHandler.java (revision 0) +++ trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/servlet/handler/DefaultInsertHandler.java (revision 0) @@ -0,0 +1,83 @@ +/** + * 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.servlet.handler; + +import java.io.IOException; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +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.GDataRequest.GDataRequestType; + +import com.google.gdata.data.BaseEntry; + +/** + * Default Handler implementation. This handler processes the incoming + * {@link org.apache.lucene.gdata.server.GDataRequest} and inserts the requested + * feed entry into the storage and the search component. + *

+ * The handler sends following response to the client: + *

+ *
    + *
  1. if the entry was added - HTTP status code 200 OK
  2. + *
  3. if an error occures - HTTP status code 500 INTERNAL SERVER ERROR
  4. + *
  5. if the resource could not found - HTTP status code 404 NOT FOUND
  6. + *
+ *

The added entry will be send back to the client if the insert request was successful.

+ * + * @author Simon Willnauer + * + */ +public class DefaultInsertHandler extends AbstractGdataRequestHandler { + private static final Log LOG = LogFactory.getLog(DefaultInsertHandler.class); + /** + * @throws ServletException + * @see org.apache.lucene.gdata.servlet.handler.GDataRequestHandler#processRequest(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) + */ + @Override + public void processRequest(HttpServletRequest request, + HttpServletResponse response) throws IOException, ServletException { + try { + initializeRequestHandler(request,response,GDataRequestType.INSERT); + } catch (GDataRequestException e) { + sendError(); + return; + } + + Service service = getService(); + try{ + BaseEntry entry = service.createEntry(this.feedRequest,this.feedResponse); + setFeedResponseFormat(); + setFeedResponseStatus(HttpServletResponse.SC_CREATED); + this.feedResponse.sendResponse(entry, this.feedRequest.getExtensionProfile()); + + }catch (ServiceException e) { + LOG.error("Could not process GetFeed request - "+e.getMessage(),e); + setError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + this.feedResponse.sendError(); + } + + + } + +} Index: trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/servlet/handler/GDataRequestHandler.java =================================================================== --- trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/servlet/handler/GDataRequestHandler.java (revision 0) +++ trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/servlet/handler/GDataRequestHandler.java (revision 0) @@ -0,0 +1,55 @@ +/** + * 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.servlet.handler; + +import java.io.IOException; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + * + * Based on the Command pattern [GoF], the Command and Controller Strategy + * suggests providing a generic interface to the handler components to which the + * controller may delegate responsibility, minimizing the coupling among these + * components. + * + * Adding to or changing the work that needs to be completed by these handlers + * does not require any changes to the interface between the controller and the + * handlers, but rather to the type and/or content of the commands. This provides + * a flexible and easily extensible mechanism for developers to add request + * handling behaviors. + * + * The controller invokes the processRequest method from the corresponding servlet doXXX + * method to delegate the request to the handler. + * + * + * @author Simon Willnauer + * + */ +public interface GDataRequestHandler { + /** + * Processes the GDATA Client request + * + * @param request - the client request to be processed + * @param response - the response to the client request + * @throws ServletException - if a servlet exception is thrown by the request or response + * @throws IOException - if an input/output error occurs due to accessing an IO steam + */ + public abstract void processRequest(HttpServletRequest request, + HttpServletResponse response) throws ServletException, IOException; +} Index: trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/servlet/handler/AbstractGdataRequestHandler.java =================================================================== --- trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/servlet/handler/AbstractGdataRequestHandler.java (revision 0) +++ trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/servlet/handler/AbstractGdataRequestHandler.java (revision 0) @@ -0,0 +1,96 @@ +/** + * 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.servlet.handler; + +import java.io.IOException; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.lucene.gdata.server.GDataRequest; +import org.apache.lucene.gdata.server.GDataRequestException; +import org.apache.lucene.gdata.server.GDataResponse; +import org.apache.lucene.gdata.server.Service; +import org.apache.lucene.gdata.server.ServiceFactory; +import org.apache.lucene.gdata.server.GDataRequest.GDataRequestType; + +/** + * @author Simon Willnauer + * + */ +public abstract class AbstractGdataRequestHandler implements + GDataRequestHandler { + private final static Log LOG = LogFactory + .getLog(AbstractGdataRequestHandler.class); + + + protected GDataRequest feedRequest; + protected GDataResponse feedResponse; + + /** + * @see org.apache.lucene.gdata.servlet.handler.GDataRequestHandler#processRequest(javax.servlet.http.HttpServletRequest, + * javax.servlet.http.HttpServletResponse) + */ + public abstract void processRequest(HttpServletRequest request, + HttpServletResponse response) throws ServletException, IOException; + + protected void initializeRequestHandler(final HttpServletRequest request, final HttpServletResponse response, final GDataRequestType type) + throws GDataRequestException { + this.feedRequest = new GDataRequest(request, type); + this.feedResponse = new GDataResponse(response); + try { + this.feedRequest.initializeRequest(); + } catch (GDataRequestException e) { + this.feedResponse.setError(HttpServletResponse.SC_NOT_FOUND); + LOG.warn("Couldn't initialize FeedRequest - " + e.getMessage(), e); + throw e; + } + } + + + + protected void sendError() throws IOException { + this.feedResponse.sendError(); + + } + + protected void setFeedResponseFormat() { + this.feedResponse.setOutputFormat(this.feedRequest.getRequestedResponseFormat()); + } + + protected void setFeedResponseStatus(int status) { + this.feedResponse.setResponseCode(status); + } + + protected void setError(int error) { + this.feedResponse.setError(error); + } + + protected Service getService() throws ServletException { + ServiceFactory serviceFactory = ServiceFactory.getInstance(); + Service service = serviceFactory.getService(); + if(service == null) + throw new ServletException("Service not available"); + return service; + } + + + +} Index: trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/servlet/handler/package.html =================================================================== --- trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/servlet/handler/package.html (revision 0) +++ trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/servlet/handler/package.html (revision 0) @@ -0,0 +1,10 @@ + + + + + + + +GData Request Handler. + + Index: trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/servlet/handler/RequestHandlerFactory.java =================================================================== --- trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/servlet/handler/RequestHandlerFactory.java (revision 0) +++ trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/servlet/handler/RequestHandlerFactory.java (revision 0) @@ -0,0 +1,122 @@ +/** + * 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.servlet.handler; + +/** + * @author Simon Willnauer + * + */ +public abstract class RequestHandlerFactory { + + private static RequestHandlerFactory INSTANCE = null; + + /** + * This method creates a singleton instance of the given type. The fist call + * will create an instance of the given class which will be returned in + * every subsequent call. Any subsequent call to this method will ignore the + * 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( + Class factoryImplementation) { + if (INSTANCE == null) { + + INSTANCE = createInstance(factoryImplementation); + } + return INSTANCE; + } + + /** + * Singleton - Pattern using private constructor + * + */ + RequestHandlerFactory() { + 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. + * @return - an RequestHandlerInstance + */ + public abstract GDataRequestHandler getUpdateHandler(); + + /** + * Creates a DeleteHandler which processes a GDATA DELETE request. + * @return - an RequestHandlerInstance + */ + public abstract GDataRequestHandler getDeleteHandler(); + + /** + * Creates a QueryHandler which processes a GDATA Query / Get request. + * @return - an RequestHandlerInstance + */ + public abstract GDataRequestHandler getQueryHandler(); + + /** + * Creates a InsertHandler which processes a GDATA Insert request. + * @return - an RequestHandlerInstance + */ + public abstract GDataRequestHandler getInsertHandler(); + + + + 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); + + } + + } + +} Index: trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/servlet/package.html =================================================================== --- trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/servlet/package.html (revision 0) +++ trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/servlet/package.html (revision 0) @@ -0,0 +1,10 @@ + + + + + + + +Servlets acting as basic interfaces for gdata requests. + + Index: trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/storage/lucenestorage/StorageCoreController.java =================================================================== --- trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/storage/lucenestorage/StorageCoreController.java (revision 0) +++ trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/storage/lucenestorage/StorageCoreController.java (revision 0) @@ -0,0 +1,286 @@ +package org.apache.lucene.gdata.storage.lucenestorage; + +import java.io.File; +import java.io.IOException; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.lucene.analysis.standard.StandardAnalyzer; +import org.apache.lucene.gdata.server.registry.GDataServerRegistry; +import org.apache.lucene.gdata.storage.IDGenerator; +import org.apache.lucene.gdata.storage.StorageController; +import org.apache.lucene.gdata.storage.StorageException; +import org.apache.lucene.gdata.storage.lucenestorage.configuration.StorageConfigurator; +import org.apache.lucene.gdata.storage.lucenestorage.util.ReferenceCounter; +import org.apache.lucene.index.IndexModifier; +import org.apache.lucene.search.IndexSearcher; +import org.apache.lucene.store.Directory; +import org.apache.lucene.store.FSDirectory; + +/** + * TODO document this + * @author Simon Willnauer + * + */ +public class StorageCoreController implements StorageController{ + protected static final Log LOG = LogFactory.getLog(StorageCoreController.class); + private IndexSearcher searcher; + private static StorageCoreController coreController; + private final Directory storageDir; + private final StorageModifier modifier; + private ReferenceCounter storageQuery; + private StorageBuffer currentBuffer; + private Object storageControllerLock = new Object(); + private static final int DEFAULT_STORAGE_BUFFER_SIZE = 10; + private static final int DEFAULT_STORAGE_PERSIST_FACTOR = 10; + private static final String STORAGELOG = ".lucenestorage"; + private int storageBufferSize; + private int storagePersistFactor; + private StorageConfigurator configurator; + private IDGenerator idGenerator; + private int indexOptimizeInterval; + + private StorageCoreController()throws IOException, StorageException{ + this(null); + } + + + + + private StorageCoreController(final Directory dir) throws IOException, StorageException { + synchronized (StorageCoreController.class) { + try{ + this.idGenerator = new IDGenerator(10); + }catch (Exception e) { + throw new StorageException("Can't create ID Generator",e); + } + + boolean createNewStorage = false; + + if(dir == null){ + this.configurator = StorageConfigurator.getStorageConfigurator(); + String storageDirPath = this.configurator.getStorageDirectory(); + 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)){ + this.storageDir = FSDirectory.getDirectory(storeDir,true); + createNewStorage = true; + } + else + throw new StorageException("could not create storage log file in "+storageDirPath); + + }else + this.storageDir = FSDirectory.getDirectory(storeDir,false); + }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.modifier = createStorageModifier(createNewStorage); + this.searcher = new IndexSearcher(this.storageDir); + + + GDataServerRegistry.getRegistry().registerStorage(this);// TODO reverse dependency here + + + + } + + } + private StorageModifier createStorageModifier(boolean create) throws IOException{ + IndexModifier indexModifier = new IndexModifier(this.storageDir,new StandardAnalyzer(),create); + return new StorageModifier(indexModifier,this.currentBuffer,this.storagePersistFactor,this.indexOptimizeInterval); + } + /**TODO document this + * @return + */ + public StorageModifier getStorageModifier(){ + return this.modifier; + } + + /**TODO document this + * @return + * @throws IOException + * @throws StorageException + */ + public static StorageCoreController getStorageCoreController() throws IOException, StorageException{ + 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 getStorageQuery() throws IOException { + synchronized (this.storageControllerLock) { + + if(this.storageQuery == null){ + this.storageQuery = getNewStorageQueryHolder(new StorageQuery(this.currentBuffer,this.searcher)); + if(LOG.isInfoEnabled()) + LOG.info("Relese new StorageQuery"); + } + this.storageQuery.increamentReference(); + return this.storageQuery; + } + } + + private ReferenceCounter getNewStorageQueryHolder(final StorageQuery query){ + ReferenceCounter holder = new ReferenceCounter(query){ + public void close(){ + try{ + if(LOG.isInfoEnabled()) + LOG.info("close StorageQuery -- zero references remaining"); + this.resource.close(); + }catch (IOException e) { + LOG.warn("Error during close call on StorageQuery"+e.getMessage(),e); + } + } + }; + holder.increamentReference(); + return holder; + } + + + + protected void registerNewStorageQuery() throws IOException{ + if(LOG.isInfoEnabled()) + LOG.info("new StorageQuery requested -- create new storage buffer"); + synchronized (this.storageControllerLock) { + if(this.storageQuery != null) + this.storageQuery.decrementRef(); + this.searcher = new IndexSearcher(this.storageDir); + this.storageQuery = null; + this.currentBuffer = new StorageBuffer(this.storageBufferSize); + + } + + } + + + protected StorageBuffer releaseNewStorageBuffer() { + synchronized (this.storageControllerLock) { + return this.currentBuffer; + } + } + + /**TODO document this + * @return + * @throws IOException + */ + public IndexModifier createIndexModifier() throws IOException { + if(LOG.isInfoEnabled()) + LOG.info("new IndexModifier created - release to StorageModifier"); + synchronized (this.storageControllerLock) { + return new IndexModifier(this.storageDir,new StandardAnalyzer(),false); + } + } + + private void close() throws IOException{ + synchronized (this.storageControllerLock) { + if(LOG.isInfoEnabled()) + LOG.info("StorageController has been closed -- server is shutting down -- release all resources"); + if(this.storageQuery != null) + this.storageQuery.decrementRef(); + coreController = null; + this.modifier.close(); + //TODO make sure all resources will be released + } + } + /**TODO document this + * @return + */ + public int getStorageBufferSize() { + return this.storageBufferSize; + } + /** + * @param storageBufferSize + */ + public void setStorageBufferSize(int storageBufferSize) { + this.storageBufferSize = storageBufferSize; + } + /**TODO document this + * @return + */ + public int getStoragePersistFactor() { + return this.storagePersistFactor; + } + /** + * @param storagePersistFactor + */ + public void setStoragePersistFactor(int storagePersistFactor) { + this.storagePersistFactor = storagePersistFactor; + } + /** + * @throws IOException + * @throws StorageException + */ + public void forceWrite()throws IOException, StorageException{ + this.modifier.forceWrite(); + } + + + private boolean createLuceneStorageLog(File storageDirectory) throws IOException{ + if(storageDirectory.isDirectory() && !storageDirectory.exists()){ + storageDirectory.createNewFile(); + } + File file = new File(storageDirectory.getAbsolutePath()+System.getProperty("file.separator")+STORAGELOG); + return file.createNewFile(); + + + } + + + /**TODO document this + * @return + * @throws StorageException + */ + public synchronized String releaseID() throws StorageException{ + try{ + return this.idGenerator.getUID(); + }catch (InterruptedException e) { + throw new StorageException("Can't release new ID",e); + } + + } + + + + /** + * @see org.apache.lucene.gdata.storage.StorageController#destroy() + */ + public void destroy() { + try{ + close(); + }catch (Exception e) { + LOG.error("Closing StorageCoreController failed -- "+e.getMessage(),e); + } + } +} Index: trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/storage/lucenestorage/StorageModifier.java =================================================================== --- trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/storage/lucenestorage/StorageModifier.java (revision 0) +++ trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/storage/lucenestorage/StorageModifier.java (revision 0) @@ -0,0 +1,236 @@ +package org.apache.lucene.gdata.storage.lucenestorage; + +import java.io.IOException; +import java.util.Collection; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.lucene.document.Document; +import org.apache.lucene.gdata.storage.StorageException; +import org.apache.lucene.index.IndexModifier; +import org.apache.lucene.index.Term; + +/** + * TODO document this + * @author Simon Willnauer + * + */ +public class StorageModifier { + protected static final Log LOG = LogFactory.getLog(StorageModifier.class); + + private final List deletedDocumentQueue; + private final List deletedForUpdateDocumentQueue; + + private final Map documentMap; + + + private volatile int persistFactor; + + private volatile int modifiedCounter = 0; + + private static int DEFAULT_PERSIST_FACTOR = 10; + + private StorageBuffer buffer; + + private IndexModifier modifier; + + private ReentrantReadWriteLock lock = new ReentrantReadWriteLock(false); + + private Lock readLock = this.lock.readLock(); + + private Lock writeLock = this.lock.writeLock(); + private final static int DEFAULT_OPTIMIZE_INTERVAL = 10; + private final int optimizeInterval; + private int optimizeCounter = 0; + + /** + * TODO document this + * @param modifier + * @param buffer + * @param persitsFactor + * @param optimizeInterval + */ + public StorageModifier(final IndexModifier modifier, + final StorageBuffer buffer, int persitsFactor,int optimizeInterval) { + this.deletedDocumentQueue = new LinkedList(); + this.deletedForUpdateDocumentQueue = new LinkedList(); + this.documentMap = new HashMap(persitsFactor); + this.buffer = buffer; + + this.persistFactor = persitsFactor > 0 ? persitsFactor + : DEFAULT_PERSIST_FACTOR; + this.modifier = modifier; + this.optimizeInterval = optimizeInterval < DEFAULT_OPTIMIZE_INTERVAL?DEFAULT_OPTIMIZE_INTERVAL:optimizeInterval; + + } + + /** + * TODO document this + * @param wrapper + * @throws StorageException + */ + public void updateEntry(StorageEntryWrapper wrapper) + throws StorageException { + try { + this.readLock.lock(); + 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(); + try { + + this.buffer.addEntry(wrapper); + this.documentMap.put(wrapper.getEntryId(),wrapper.getLuceneDocument()); + storageModified(); + } finally { + this.readLock.unlock(); + } + } + + /** + *TODO document this + * @param entryId + * @param feedId + * @throws StorageException + * + */ + public void deleteEntry(final String entryId, final String feedId) throws StorageException { + try { + this.readLock.lock(); + Term tempTerm = new Term(StorageEntryWrapper.FIELD_ENTRY_ID, entryId); + this.buffer.addDeleted(entryId, feedId); + this.deletedDocumentQueue.add(tempTerm); + storageModified(); + } finally { + this.readLock.unlock(); + } + } + + private void storageModified() throws StorageException { + this.readLock.unlock(); + this.writeLock.lock(); + + try { + incrementCounter(); + if (this.persistFactor > this.modifiedCounter) + return; + + if (LOG.isInfoEnabled()) + LOG.info("Storage modified for " + this.modifiedCounter + + " times. Write Persistent index"); + writePersistentIndex((this.optimizeCounter >= this.optimizeInterval)); + requestNewIndexModifier(); + + this.modifiedCounter = 0; + } catch (IOException e) { + LOG.error("Writing persistent index failed - Recovering", e); + } finally { + this.readLock.lock(); + this.writeLock.unlock(); + } + + } + + protected void forceWrite() throws IOException, StorageException { + this.writeLock.lock(); + try { + if (LOG.isInfoEnabled()) + LOG.info("ForceWrite called -- current modifiedCounter: " + + this.modifiedCounter + " - persisting changes"); + writePersistentIndex(true); + requestNewIndexModifier(); + this.modifiedCounter = 0; + } finally { + this.writeLock.unlock(); + } + } + + private void requestNewIndexModifier() throws IOException, StorageException { + StorageCoreController controller = StorageCoreController + .getStorageCoreController(); + controller.registerNewStorageQuery(); + this.buffer = controller.releaseNewStorageBuffer(); + this.modifier = controller.createIndexModifier(); + } + + private void writePersistentIndex(final boolean optimize) throws IOException { + try { + /* + * first delete all updated documents + */ + for(Term entryIdTerm : this.deletedForUpdateDocumentQueue) { + this.modifier.deleteDocuments(entryIdTerm); + } + /* + * add all documents + */ + Collection documents = this.documentMap.values(); + for (Document doc : documents) { + this.modifier.addDocument(doc); + } + /* + * delete all documents marked as deleted. As the DocumentIDs are + * unique the document marked as deleted must not persist after the + * index has been written. + * In the case of an update of a document and a previous delete the concurrency component will not allow an update. + * new inserted entries can not be deleted accidently- + */ + for (Term entryIdTerm : this.deletedDocumentQueue) { + this.modifier.deleteDocuments(entryIdTerm); + } + this.modifier.flush(); + if(optimize){ + if(LOG.isInfoEnabled()) + LOG.info("Optimizing index -- optimize interval "+this.optimizeInterval); + this.modifier.optimize(); + } + + } finally { + if(optimize) + this.optimizeCounter = 0; + this.modifier.close(); + this.deletedForUpdateDocumentQueue.clear(); + this.deletedDocumentQueue.clear(); + this.documentMap.clear(); + } + } + + protected void close()throws IOException{ + this.writeLock.lock(); + try { + if (LOG.isInfoEnabled()) + LOG.info("ForceWrite called -- current modifiedCounter: " + + this.modifiedCounter + " - persisting changes"); + + writePersistentIndex(true); + this.modifiedCounter = 0; + } finally { + this.writeLock.unlock(); + } + } + + private void incrementCounter(){ + this.optimizeCounter++; + this.modifiedCounter++; + } + +} Index: trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/storage/lucenestorage/StorageEntryWrapper.java =================================================================== --- trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/storage/lucenestorage/StorageEntryWrapper.java (revision 0) +++ trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/storage/lucenestorage/StorageEntryWrapper.java (revision 0) @@ -0,0 +1,188 @@ +/** + * 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.lucenestorage; + +import java.io.IOException; +import java.io.StringWriter; + +import org.apache.lucene.document.Document; +import org.apache.lucene.document.Field; + +import com.google.gdata.data.BaseEntry; +import com.google.gdata.data.ExtensionProfile; +import com.google.gdata.util.common.xml.XmlWriter; + +/** + * This immutable class wrapps Entries for an internal Storage representation of + * an entry. This class also acts as a Documentfactory for lucene documents to + * be stored inside the index. + * + * @author Simon Willnauer + * + */ +public class StorageEntryWrapper implements Comparable { + private static final String INTERNAL_ENCODING = "UTF-8"; + + /** + * lucene field name Entry id + */ + public final static String FIELD_ENTRY_ID = "entryId"; + + /** + * lucene field name feed id + */ + public final static String FIELD_FEED_ID = "feedId"; + + /** + * lucene field name entry content + */ + public final static String FIELD_CONTENT = "content"; + + /** + * lucene field name creating timestamp + */ + public final static String FIELD_TIMESTAMP = "timestamp"; + + private final String entryId; + + private final String feedId; + + private final String content; + + private final transient BaseEntry entry; + + private final Long timestamp; + + private transient Document document; + + private StorageOperation operation; + + private final ExtensionProfile profile; + + /** + * Creates a new StorageEntryWrapper. + * + * @param entry - + * the entry to wrap + * @param feedId - + * the feed id + * @param operation - + * the StorageOperation + * @param profile - + * the ExtensionProfil for the given entry + * @throws IOException - + * if the entry content can not be generated + */ + protected StorageEntryWrapper(final BaseEntry entry, final String feedId, + StorageOperation operation, final ExtensionProfile profile) + throws IOException { + this.entry = entry; + this.operation = operation; + this.entryId = entry.getId(); + this.feedId = feedId; + this.profile = profile; + this.content = buildContent(); + this.timestamp = new Long(System.currentTimeMillis()); + + } + + private String buildContent() throws IOException { + StringWriter writer = new StringWriter(); + XmlWriter xmlWriter = new XmlWriter(writer, INTERNAL_ENCODING); + this.entry.generateAtom(xmlWriter, this.profile); + return writer.toString(); + + } + + /** + * @return - the lucene document representing the entry + */ + public Document getLuceneDocument() { + if (this.document != null) + return this.document; + this.document = new Document(); + this.document.add(new Field("entryId", this.entryId, Field.Store.YES, + Field.Index.UN_TOKENIZED)); + this.document.add(new Field("feedId", this.feedId, Field.Store.YES, + Field.Index.UN_TOKENIZED)); + this.document.add(new Field("content", this.content, + Field.Store.COMPRESS, Field.Index.UN_TOKENIZED)); + this.document.add(new Field("timestamp", this.timestamp.toString(), + Field.Store.YES, Field.Index.UN_TOKENIZED)); + + return this.document; + + } + + /** + * @return - the wrapped entry + */ + public BaseEntry getEntry() { + return this.entry; + } + + /** + * @return - the entry id + */ + public String getEntryId() { + return this.entryId; + } + + /** + * @return - the feed id + */ + public String getFeedId() { + return this.feedId; + } + + /** + * Storage operations + * + * @author Simon Willnauer + * + */ + public static enum StorageOperation { + /** + * delete + */ + DELETE, + /** + * update + */ + UPDATE, + /** + * insert + */ + INSERT + } + + /** + * @return the specified storage operation + */ + public StorageOperation getOperation() { + return this.operation; + } + + /** + * @see java.lang.Comparable#compareTo(T) + */ + public int compareTo(StorageEntryWrapper arg0) { + return arg0.timestamp == this.timestamp ? 0 + : (arg0.timestamp > this.timestamp ? 1 : -1); + } + +} Index: trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/storage/lucenestorage/configuration/StorageConfigurator.java =================================================================== --- trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/storage/lucenestorage/configuration/StorageConfigurator.java (revision 0) +++ trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/storage/lucenestorage/configuration/StorageConfigurator.java (revision 0) @@ -0,0 +1,144 @@ +/** + * 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.lucenestorage.configuration; + +import java.io.InputStream; +import java.util.Properties; + +/** + * This clas loads the Storage configuration file and sets all properties. If + * the properties can not be loaded an {@link java.lang.Error} will be thrown. + * The configuration file lucenestorage.properties.xml should be available in the classpath. + * + * @author Simon Willnauer + * + */ +public class StorageConfigurator { + private final int storageBufferSize; + + private final int storagepersistFactor; + + private final String storageDirectory; + + private final boolean keepRecoveredFiles; + + private final boolean recover; + + private static StorageConfigurator INSTANCE = null; + + private final int indexOptimizeInterval; + + private StorageConfigurator() { + InputStream stream = StorageConfigurator.class + .getResourceAsStream("/lucenestorage.properties.xml"); + Properties properties = new Properties(); + try { + properties.loadFromXML(stream); + + } catch (Exception e) { + throw new StorageConfigurationError("Could not load properties", e); + } + this.storageBufferSize = Integer.parseInt(properties + .getProperty("gdata.server.storage.lucene.buffersize")); + this.storagepersistFactor = Integer.parseInt(properties + .getProperty("gdata.server.storage.lucene.persistFactor")); + this.recover = Boolean.parseBoolean(properties + .getProperty("gdata.server.storage.lucene.recover")); + this.keepRecoveredFiles = Boolean.parseBoolean(properties + .getProperty("gdata.server.storage.lucene.recover.keepFiles")); + this.storageDirectory = properties + .getProperty("gdata.server.storage.lucene.directory"); + this.indexOptimizeInterval = Integer.parseInt(properties + .getProperty("gdata.server.storage.lucene.optimizeInterval")); + + } + + /** + * @return - the storage configurator + */ + public static synchronized StorageConfigurator getStorageConfigurator() { + if (INSTANCE == null) + INSTANCE = new StorageConfigurator(); + return INSTANCE; + } + + /** + * Keep recovering files. -- will use a lot of disk space + * + * @return true if the storage is supposed to keep the + * recovering files. + */ + public boolean isKeepRecoveredFiles() { + return this.keepRecoveredFiles; + } + + /** + * @return true if the storage is supposed to use recovering. + * @see org.apache.lucene.gdata.storage.lucenestorage.StorageModifier + */ + public boolean isRecover() { + return this.recover; + } + + /** + * @return - the configured storage buffer size + * @see org.apache.lucene.gdata.storage.lucenestorage.StorageBuffer + */ + public int getStorageBufferSize() { + return this.storageBufferSize; + } + + /** + * @return - the configured storage directory + * @see org.apache.lucene.gdata.storage.lucenestorage.StorageModifier + */ + public String getStorageDirectory() { + return this.storageDirectory; + } + + /** + * @return - the persist factor + * @see org.apache.lucene.gdata.storage.lucenestorage.StorageModifier + */ + public int getStoragepersistFactor() { + return this.storagepersistFactor; + } + + protected class StorageConfigurationError extends Error { + + /** + * + */ + private static final long serialVersionUID = 5261674332036111464L; + + protected StorageConfigurationError(String arg0, Throwable arg1) { + super(arg0, arg1); + + } + + } + + /** + * @return - the optimize interval + * @see org.apache.lucene.gdata.storage.lucenestorage.StorageModifier + */ + public int getIndexOptimizeInterval() { + + return this.indexOptimizeInterval; + } + +} Index: trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/storage/lucenestorage/configuration/package.html =================================================================== --- trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/storage/lucenestorage/configuration/package.html (revision 0) +++ trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/storage/lucenestorage/configuration/package.html (revision 0) @@ -0,0 +1,10 @@ + + + + + + + +Lucene Storage utils + + Property changes on: trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/storage/lucenestorage/configuration/package.html ___________________________________________________________________ Name: svn:executable + * Index: trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/storage/lucenestorage/ModifiedEntryFilter.java =================================================================== --- trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/storage/lucenestorage/ModifiedEntryFilter.java (revision 0) +++ trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/storage/lucenestorage/ModifiedEntryFilter.java (revision 0) @@ -0,0 +1,80 @@ +/** + * 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.lucenestorage; + +import java.io.IOException; +import java.util.BitSet; +import java.util.List; + +import org.apache.lucene.index.IndexReader; +import org.apache.lucene.index.Term; +import org.apache.lucene.index.TermDocs; +import org.apache.lucene.search.Filter; + +/** + * The {@link ModifiedEntryFilter} filters the given entryIds from the lucene + * {@link org.apache.lucene.search.Hits} set. This filter is used to prevent the + * storage from retrieving already deleted or updated entries still remainig in + * the {@link org.apache.lucene.gdata.storage.lucenestorage.StorageBuffer}. + * + * @see org.apache.lucene.search.Filter + * + * @author Simon Willnauer + * + */ +public class ModifiedEntryFilter extends Filter { + /** + * impl Serializable + */ + private static final long serialVersionUID = -1551686287704213591L; + + private final List entyIds; + + /** + * Creates a new {@link ModifiedEntryFilter} + * @param entryIds the entry id's to filter + * + */ + public ModifiedEntryFilter(List entryIds) { + super(); + this.entyIds = entryIds; + } + + /** + * @see org.apache.lucene.search.Filter#bits(org.apache.lucene.index.IndexReader) + */ + @Override + public BitSet bits(IndexReader reader) throws IOException { + BitSet bitSet = new BitSet(reader.maxDoc()); + bitSet.flip(0, reader.maxDoc()); // set all docs + int[] docs = new int[1]; + int[] freq = new int[1]; + for (String id : this.entyIds) { + if (id != null) { + TermDocs termDocs = reader.termDocs(new Term( + StorageEntryWrapper.FIELD_ENTRY_ID, id)); + int count = termDocs.read(docs, freq); + if (count == 1) + bitSet.flip(docs[0]); + + } + } + + return bitSet; + } + +} Index: trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/storage/lucenestorage/StorageImplementation.java =================================================================== --- trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/storage/lucenestorage/StorageImplementation.java (revision 0) +++ trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/storage/lucenestorage/StorageImplementation.java (revision 0) @@ -0,0 +1,259 @@ +/** + * 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.lucenestorage; + +import java.io.IOException; +import java.util.List; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.lucene.gdata.storage.Storage; +import org.apache.lucene.gdata.storage.StorageException; +import org.apache.lucene.gdata.storage.lucenestorage.StorageEntryWrapper.StorageOperation; +import org.apache.lucene.gdata.storage.lucenestorage.util.ReferenceCounter; + +import com.google.gdata.data.BaseEntry; +import com.google.gdata.data.BaseFeed; +import com.google.gdata.data.ExtensionProfile; +import com.google.gdata.data.Feed; + +/** + * This is an implementation of the + * {@link org.apache.lucene.gdata.storage.Storage} interface. The + * StorageImplementation provides access to the + * {@link org.apache.lucene.gdata.storage.lucenestorage.StorageQuery} and the + * {@link org.apache.lucene.gdata.storage.lucenestorage.StorageModifier}. This + * class will be instanciated per client request. + * + * + * + * @author Simon Willnauer + * + */ +public class StorageImplementation implements Storage { + private final StorageCoreController controller; + + private ExtensionProfile profile; + + private static final Log LOG = LogFactory + .getLog(StorageImplementation.class); + + /** + * Creates a new StorageImplementation + * + * @throws StorageException - + * if the + * {@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 { + this.controller = StorageCoreController.getStorageCoreController(); + } + + /** + * @see org.apache.lucene.gdata.storage.Storage#storeEntry(com.google.gdata.data.BaseEntry, + * java.lang.String) + */ + public BaseEntry storeEntry(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"); + StorageModifier modifier = this.controller.getStorageModifier(); + String id = this.controller.releaseID(); + entry.setId(feedId + id); + if (LOG.isInfoEnabled()) + LOG.info("Store entry " + id + " -- feed: " + feedId); + + try { + StorageEntryWrapper wrapper = new StorageEntryWrapper(entry, + feedId, StorageOperation.INSERT, this.profile); + modifier.insertEntry(wrapper); + } catch (IOException e) { + StorageException ex = new StorageException("Can't create Entry -- " + + e.getMessage(), e); + ex.setStackTrace(e.getStackTrace()); + throw ex; + + } + + return entry; + } + + /** + * @see org.apache.lucene.gdata.storage.Storage#deleteEntry(java.lang.String, + * java.lang.String) + */ + public void deleteEntry(String entryId, 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 (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) + throw new StorageException("enrty 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(); + + try { + StorageEntryWrapper wrapper = new StorageEntryWrapper(entry, + feedId, StorageOperation.UPDATE, this.profile); + modifier.updateEntry(wrapper); + } catch (IOException e) { + LOG.error("Can't update entry for feedID: " + feedId + + "; entryId: " + entry.getId() + " -- " + e.getMessage(), + e); + StorageException ex = new StorageException("Can't create Entry -- " + + e.getMessage(), e); + ex.setStackTrace(e.getStackTrace()); + throw ex; + + } + + return entry; + + } + + /** + * @see org.apache.lucene.gdata.storage.Storage#getFeed(java.lang.String, + * int, int) + */ + @SuppressWarnings("unchecked") + public BaseFeed getFeed(String feedId, int startIndex, int resultCount) + 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 (LOG.isInfoEnabled()) + LOG.info("get feed: " + feedId + " startindex: " + startIndex + + " resultCount: " + resultCount); + ReferenceCounter query = null; + try { + query = this.controller.getStorageQuery(); + List resultList = query.get().getLatestFeedQuery(feedId, + resultCount, startIndex, this.profile); + BaseFeed feed = new Feed(); + feed.getEntries().addAll(resultList); + return feed; + } catch (Exception e) { + LOG.error("Can't get latest feed for feedID: " + feedId + " -- " + + e.getMessage(), e); + StorageException ex = new StorageException("Can't create Entry -- " + + e.getMessage(), e); + ex.setStackTrace(e.getStackTrace()); + throw ex; + + } finally { + if (query != null) + query.decrementRef(); + } + + } + + /** + * @see org.apache.lucene.gdata.storage.Storage#getEntry(java.lang.String, + * java.lang.String) + */ + public BaseEntry getEntry(String entryId, 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 (entryId == null) + throw new StorageException("No entry ID specified -- is null"); + if (LOG.isInfoEnabled()) + LOG.info("get entry " + entryId + " -- feed: " + feedId); + ReferenceCounter query = null; + try { + query = this.controller.getStorageQuery(); + return query.get().singleEntryQuery(entryId, feedId, this.profile); + } catch (Exception e) { + LOG.error("Can't get entry for feedID: " + feedId + "; entryId: " + + entryId + " -- " + e.getMessage(), e); + StorageException ex = new StorageException("Can't create Entry -- " + + e.getMessage(), e); + ex.setStackTrace(e.getStackTrace()); + throw ex; + + } finally { + if (query != null) + query.decrementRef(); + } + + } + + /** + * @see org.apache.lucene.gdata.storage.Storage#getEntries(java.util.List, + * java.lang.String) + */ + public List getEntries(List entryIdList, String feedId) + throws StorageException { + throw new StorageException("not implemented yet"); + // TODO implement this + + } + + /** + * @see org.apache.lucene.gdata.storage.Storage#close() + */ + public void close() { + // + } + + /** + * @see org.apache.lucene.gdata.storage.Storage#setExtensionProfile(com.google.gdata.data.ExtensionProfile) + */ + public void setExtensionProfile(ExtensionProfile profile) { + this.profile = profile; + } + +} Index: trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/storage/lucenestorage/StorageQuery.java =================================================================== --- trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/storage/lucenestorage/StorageQuery.java (revision 0) +++ trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/storage/lucenestorage/StorageQuery.java (revision 0) @@ -0,0 +1,342 @@ +/** + * 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.lucenestorage; + +import java.io.IOException; +import java.io.StringReader; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.apache.lucene.document.Document; +import org.apache.lucene.gdata.server.FeedNotFoundException; +import org.apache.lucene.gdata.server.GDataEntityBuilder; +import org.apache.lucene.index.Term; +import org.apache.lucene.search.BooleanClause; +import org.apache.lucene.search.BooleanQuery; +import org.apache.lucene.search.Hit; +import org.apache.lucene.search.Hits; +import org.apache.lucene.search.Searcher; +import org.apache.lucene.search.Sort; +import org.apache.lucene.search.SortField; +import org.apache.lucene.search.TermQuery; +import org.apache.lucene.search.BooleanClause.Occur; + +import com.google.gdata.data.BaseEntry; +import com.google.gdata.data.ExtensionProfile; +import com.google.gdata.util.ParseException; + +/** + * StorageQuery wrapps a Lucene {@link org.apache.lucene.search.IndexSearcher} + * and a {@link org.apache.lucene.gdata.storage.lucenestorage.StorageBuffer} to + * perform all request on the lucene storage. + * The wrapped components are thread - safe. + *

+ * An instance of this class will serve all client requests. To obtain the + * current instance of the {@link StorageQuery} the method + * {@link org.apache.lucene.gdata.storage.lucenestorage.StorageCoreController#getStorageQuery()} + * has to be invoked. This method will release the current StorageQuery. + *

+ * @see org.apache.lucene.search.IndexSearcher + * @see org.apache.lucene.gdata.storage.lucenestorage.StorageCoreController + * @see org.apache.lucene.gdata.storage.lucenestorage.StorageBuffer + * + * @author Simon Willnauer + * + */ +public class StorageQuery { + private final StorageBuffer buffer; + + private final Searcher searcher; + + /* + * Sort the result by timestamp desc + */ + private final Sort timeStampSort = new Sort(new SortField(StorageEntryWrapper.FIELD_TIMESTAMP, + SortField.STRING, true)); + + /** + * Creates a new StorageQuery + * + * @param buffer - + * the buffer instance to get the buffered inserts, updates from. + * @param searcher - + * the searcher instance to use to query the storage index. + * + * + */ + protected StorageQuery(final StorageBuffer buffer, final Searcher searcher) { + + this.buffer = buffer; + this.searcher = searcher; + + } + + private Hits storageQuery(List entryId) throws IOException { + BooleanQuery query = new BooleanQuery(); + /* + * query the index using a BooleanQuery + */ + for (String id : entryId) { + TermQuery termQuery = new TermQuery(new Term( + StorageEntryWrapper.FIELD_ENTRY_ID, id)); + // use an OR query + query.add(new BooleanClause(termQuery, Occur.SHOULD)); + } + + return this.searcher.search(query, new ModifiedEntryFilter(this.buffer + .getExculdList())); + } + + /* + * query the storage index for a entire feed. + */ + private Hits storageFeedQuery(final String feedId, final Sort sort) + throws IOException { + TermQuery query = new TermQuery(new Term(StorageEntryWrapper.FIELD_FEED_ID, feedId)); + return this.searcher.search(query, new ModifiedEntryFilter(this.buffer + .getExculdList()), sort); + + } + + /* + * get a single entry + */ + private Hits storageQuery(String entryId) throws IOException { + TermQuery termQuery = new TermQuery(new Term( + StorageEntryWrapper.FIELD_ENTRY_ID, entryId)); + /* + * Filter entries inside the buffer, buffered entries might contain + * deleted entries. These entries must be found!! + */ + return this.searcher.search(termQuery, new ModifiedEntryFilter( + this.buffer.getExculdList())); + + } + + /** + * This method fetches the latest feed entries from the storage. Feed + * ususaly requested via a search query or as a simple query to the REST + * interface. + *

+ * The REST interface requestes all the entries from a Storage. The Storage + * retrieves the entries corresponding to the parameters specified. This + * method first requests the latest entries or updated entries from the + * {@link StorageBuffer}. If the buffer already contains enought entries + * for the the specified result count the entires will be returned. If not, + * the underlaying lucene index will be searcher for all documents of the + * specified feed sorted by storing timestamp desc. + *

+ *

+ * The entries will be searched in a feed context specified by the given + * feed ID + *

+ * + * + * @param feedId - + * the requested feed, this id will be used to retrieve the + * entries. + * @param resultCount - + * how many entries are requested + * @param startIndex - + * the offset of the entriy to start from. + * @param profil - + * the extension profile used to create the entriy instances + * @return - an ordered list of {@link BaseEntry} objects, or an empty list + * if no entries could be found + * @throws IOException - + * if the index could not be queries or the entries could not be + * build + * @throws FeedNotFoundException - + * if the requested feed is not registered + * @throws ParseException - + * if an entry could not be parsed while building it from the + * Lucene Document. + */ + // TODO check input parameter + public List getLatestFeedQuery(final String feedId, + final int resultCount, final int startIndex, + final ExtensionProfile profil) throws IOException, + FeedNotFoundException, ParseException { + List returnList = new ArrayList(resultCount); + List bufferedWrapperList = this.buffer + .getSortedEntries(feedId); + int alreadyAdded = 0; + int offset = startIndex - 1; + if (bufferedWrapperList != null + && bufferedWrapperList.size() >= startIndex) { + + for (; alreadyAdded < resultCount; alreadyAdded++) { + if ((bufferedWrapperList.size() - offset) > 0) { + StorageEntryWrapper wrappedEntry = bufferedWrapperList + .get(offset++); + returnList.add(wrappedEntry.getEntry()); + } else + break; + } + // reset offset + offset = startIndex - 1; + if (alreadyAdded == resultCount) + return returnList; + } else { + /* + * if the buffersize is less than the startindex the buffersize must + * be considered. Sublists would not be a repeatable read part of + * the whole list + */ + if (bufferedWrapperList != null) + offset = startIndex - 1 - bufferedWrapperList.size(); + } + + Hits hits = storageFeedQuery(feedId, this.timeStampSort); + if (hits.length() > 0) { + + for (; (offset < hits.length()) && (alreadyAdded < resultCount); offset++, alreadyAdded++) { + Document doc = hits.doc(offset); + BaseEntry entry = buildEntryFromLuceneDocument(doc, profil); + returnList.add(entry); + } + + } + return returnList; + } + + /** + * This method retrieves a single entry from the storage. If the + * {@link StorageBuffer} does not contain the requested entry the + * underlaying storage index will be searched. + *

+ * The Entry will be searched in a feed context specified by the given feed + * ID + *

+ * + * @param entryId - + * the entry to fetch + * @param feedId - + * the feedid eg. feed context + * @param profil - + * the extension profile used to create the entriy instances + * @return - the requested {@link BaseEntry} or null if the + * entry can not be found + * @throws IOException - + * if the index could not be queries or the entries could not be + * build + * @throws FeedNotFoundException - + * if the requested feed is not registered + * @throws ParseException - + * if an entry could not be parsed while building it from the + * Lucene Document. + */ + public BaseEntry singleEntryQuery(final String entryId, + final String feedId, final ExtensionProfile profil) + throws IOException, FeedNotFoundException, ParseException { + StorageEntryWrapper wrapper = this.buffer.getEntry(entryId, feedId); + + if (wrapper == null) { + Hits hits = storageQuery(entryId); + if (hits.length() <= 0) + return null; + Document doc = hits.doc(0); + + return buildEntryFromLuceneDocument(doc, profil); + } + return wrapper.getEntry(); + + } + + /** + * Fetches the requested entries from the storage. The given list contains + * entry ids to be looked up in the storage. First the {@link StorageBuffer} + * will be queried for the entry ids. If not all of the entries remain in + * the buffer the underlaying lucene index will be searched. The entries are + * not guaranteed to be in the same order as they are in the given id list. + * Entry ID's not found in the index or the buffer will be omitted. + *

+ * The entries will be searched in a feed context specified by the given + * feed ID + *

+ * + * @param entryIds - + * the entriy ids to fetch. + * @param feedId - + * the feed id eg. feed context. + * @param profil - + * the extension profile used to create the entry instances. + * @return - the list of entries corresponding to the given entry id list. + * @throws IOException - + * if the index could not be queries or the entries could not be + * build + * @throws FeedNotFoundException - + * if the requested feed is not registered + * @throws ParseException - + * if an entry could not be parsed while building it from the + * Lucene Document. + */ + public List entryQuery(List entryIds, + final String feedId, final ExtensionProfile profil) + throws IOException, FeedNotFoundException, ParseException { + List resultList = new ArrayList(entryIds.size()); + List searchList = new ArrayList(entryIds.size()); + for (String entry : entryIds) { + + StorageEntryWrapper bufferedEntry = this.buffer.getEntry(entry, + feedId); + if (bufferedEntry != null) { + resultList.add(bufferedEntry.getEntry()); + } else + searchList.add(entry); + } + if (searchList.isEmpty()) + return resultList; + + Hits hits = storageQuery(searchList); + Iterator hitIterator = hits.iterator(); + while (hitIterator.hasNext()) { + Hit hit = (Hit) hitIterator.next(); + Document doc = hit.getDocument(); + BaseEntry entry = buildEntryFromLuceneDocument(doc, profil); + resultList.add(entry); + + } + + return resultList; + + } + + private BaseEntry buildEntryFromLuceneDocument(final Document doc, + final ExtensionProfile profil) throws FeedNotFoundException, + ParseException, IOException { + StringReader reader = new StringReader(doc.getField(StorageEntryWrapper.FIELD_CONTENT) + .stringValue()); + return GDataEntityBuilder.buildEntry(doc.getField(StorageEntryWrapper.FIELD_FEED_ID) + .stringValue(), reader, profil); + + } + + /** + * Closes all resources used in the {@link StorageQuery}. The instance can + * not be reused after invoking this method. + * + * @throws IOException - + * if the resouces can not be closed + */ + public void close() throws IOException { + this.searcher.close(); + this.buffer.close(); + } + +} Index: trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/storage/lucenestorage/util/ReferenceCounter.java =================================================================== --- trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/storage/lucenestorage/util/ReferenceCounter.java (revision 0) +++ trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/storage/lucenestorage/util/ReferenceCounter.java (revision 0) @@ -0,0 +1,77 @@ +/** + * 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.lucenestorage.util; + +import java.util.concurrent.atomic.AtomicInteger; + +/** + * A reference counting utility. This is use to keep track of released objects + * of Type. + * + * @author Simon Willnauer + * @param - + * the type of the object + * + */ +public abstract class ReferenceCounter { + protected final Type resource; + + private AtomicInteger refcounter = new AtomicInteger(); + + /** + * @param resource - + * the resouce to track + * + */ + public ReferenceCounter(Type resource) { + this.resource = resource; + } + + /** + * + * Decrements the reference. If no references remain the + * {@link ReferenceCounter#close()} method will be inoked; + */ + public final void decrementRef() { + if (this.refcounter.decrementAndGet() == 0) + close(); + } + + /** + * A custom implementation. Performs an action if no reference remaining + * + */ + protected abstract void close(); + + /** + * Increments the reference + * + * @return the refernece object + */ + public final ReferenceCounter increamentReference() { + this.refcounter.incrementAndGet(); + return this; + } + + /** + * @return - the resource to keep track of + */ + public final Type get() { + return this.resource; + } + +} Index: trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/storage/lucenestorage/util/package.html =================================================================== --- trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/storage/lucenestorage/util/package.html (revision 0) +++ trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/storage/lucenestorage/util/package.html (revision 0) @@ -0,0 +1,10 @@ + + + + + + + +Storage Configuration + + Property changes on: trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/storage/lucenestorage/util/package.html ___________________________________________________________________ Name: svn:executable + * Index: trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/storage/lucenestorage/StorageBuffer.java =================================================================== --- trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/storage/lucenestorage/StorageBuffer.java (revision 0) +++ trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/storage/lucenestorage/StorageBuffer.java (revision 0) @@ -0,0 +1,249 @@ +/** + * 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.lucenestorage; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.lucene.gdata.storage.lucenestorage.StorageEntryWrapper.StorageOperation; + +/** + * 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 + * modifying the index all the time an altering request comes in is not + * efficent. The entries will be added to the buffer to be available for + * incoming storage queries. If the loadfactor for the + * {@link org.apache.lucene.gdata.storage.lucenestorage.StorageModifier} is + * reached the modifier will perform a batch update on the index. Each entry + * will be associated with a feed id inside a associative datastructure to + * return a requested entry efficiently. + *

+ * This implementation uses {@link java.util.concurrent.locks.ReadWriteLock}. + * The read lock may be held simultaneously by multiple reader threads, so long + * as there are no writers. The write lock is exclusive.

+ * + * @see java.util.concurrent.locks.ReentrantReadWriteLock + * @see org.apache.lucene.gdata.storage.lucenestorage.StorageModifier + * @see org.apache.lucene.gdata.storage.lucenestorage.StorageCoreController + * + * @author Simon Willnauer + * + */ +public class StorageBuffer { + private static final Log LOG = LogFactory.getLog(StorageBuffer.class); + + private final Map> bufferMap; + + private final List excludeList; + + private final ReadWriteLock lock = new ReentrantReadWriteLock(true); + + private final Lock readLock = this.lock.readLock(); + + private final Lock writeLock = this.lock.writeLock(); + + private final static int DEFAULT_BUFFER_COUNT = 10; + + /** + * Constructs a new StorageBuffer. + *

+ * The expectedBufferCount sould be higher than the maximum of entries added + * to the buffer, resizing the buffer is very efficient. For detailed + * infomation {@link HashMap} as this is used inside the buffer + *

+ * + * @param expectedBufferCount - + * the expected size of the buffer + * + */ + protected StorageBuffer(final int expectedBufferCount) { + this.bufferMap = new HashMap>( + expectedBufferCount < DEFAULT_BUFFER_COUNT ? DEFAULT_BUFFER_COUNT + : expectedBufferCount); + this.excludeList = new ArrayList( + expectedBufferCount < DEFAULT_BUFFER_COUNT ? DEFAULT_BUFFER_COUNT + : expectedBufferCount); + } + + /** + * Adds a {@link StorageEntryWrapper} to the buffer. If a wrapper + * representing the same entry are already in the buffer the wrapper will be + * replaced. + * + * @param wrapper - + * the wrapper to buffer + */ + public void addEntry(final StorageEntryWrapper wrapper) { + this.writeLock.lock(); + try { + if (LOG.isInfoEnabled()) + LOG.info(" Buffering wrapper - " + wrapper.getOperation() + + " ID: " + wrapper.getEntryId() + " FeedID: " + + wrapper.getFeedId()); + if (wrapper.getOperation().equals(StorageOperation.DELETE)) + return; + + String feedId = wrapper.getFeedId(); + if (this.bufferMap.containsKey(feedId)) + this.bufferMap.get(feedId).put(wrapper.getEntryId(), wrapper); + else { + Map newFeedMap = new HashMap( + 20); + newFeedMap.put(wrapper.getEntryId(), wrapper); + this.bufferMap.put(feedId, newFeedMap); + } + } finally { + /* + * add all to exclude from searches doc will be available via the + * buffer + */ + this.excludeList.add(wrapper.getEntryId()); + this.writeLock.unlock(); + } + } + + /** + * Returns all entries for the given feed id sorted by the update timestamp + * desc. + * + * @param feedId - + * the feed id + * @return a {@link List} of all {@link StorageEntryWrapper} object buffered + * in this buffer or an empty list if not entry has been buffered + * for the given feed + */ + public List getSortedEntries(String feedId) { + this.readLock.lock(); + try { + if (!this.bufferMap.containsKey(feedId)) + return null; + Map tempMap = this.bufferMap + .get(feedId); + if (tempMap == null) + return null; + Collection col = tempMap.values(); + List returnList = new ArrayList( + col); + Collections.sort(returnList); + return returnList; + + } finally { + this.readLock.unlock(); + } + + } + + /** + * Adds a deleted entry to the buffer. + * + * @param entryId - + * the deleted entry id + * @param feedId - + * the feed of the entry + */ + public void addDeleted(final String entryId, final String feedId) { + this.writeLock.lock(); + try { + this.excludeList.add(entryId); + Map tempMap = this.bufferMap + .get(feedId); + if (tempMap == null) + return; + tempMap.remove(entryId); + } finally { + this.writeLock.unlock(); + + } + + } + + /** + * Returns an entry for the given entry id in the feed context spezified by + * the feed id; + * + * @param entryId - + * the id of the entry to return + * @param feedId - + * the feed containing the entry + * @return - the entry or null if the corresponding entry is + * not in the buffer. + */ + public StorageEntryWrapper getEntry(final String entryId, + final String feedId) { + this.readLock.lock(); + try { + + if (this.bufferMap.containsKey(feedId)) + return this.bufferMap.get(feedId).get(entryId); + return null; + + } finally { + this.readLock.unlock(); + } + } + + /** + * The buffer contains updated and delete entries. These entries are already + * available in the lucene index but should not be found during search. + * + *

+ * this list contains all entries should not be found by the index searcher + *

+ * + * @see ModifiedEntryFilter + * @return - a {@link List} of entries to be omitted from a lucene index + * search + */ + public List getExculdList() { + this.readLock.lock(); + try { + return this.excludeList; + } finally { + this.readLock.unlock(); + } + } + + // not synchronized + private void clearBuffer() { + this.bufferMap.clear(); + this.excludeList.clear(); + + } + + /** + * clears the buffer - + */ + public void close() { + this.writeLock.lock(); + try { + clearBuffer(); + } finally { + this.writeLock.unlock(); + } + + } + +} Index: trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/storage/lucenestorage/package.html =================================================================== --- trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/storage/lucenestorage/package.html (revision 0) +++ trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/storage/lucenestorage/package.html (revision 0) @@ -0,0 +1,10 @@ + + + + + + + +Lucene storage implementation + + Property changes on: trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/storage/lucenestorage/package.html ___________________________________________________________________ Name: svn:executable + * Index: trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/storage/IDGenerator.java =================================================================== --- trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/storage/IDGenerator.java (revision 0) +++ trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/storage/IDGenerator.java (revision 0) @@ -0,0 +1,171 @@ +/** + * 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.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.BlockingQueue; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * This is the main entry ID generator to generate unique ids for each entry. + * The Generator uses {@link java.security.SecureRandom} Numbers and the + * {@link java.lang.System#currentTimeMillis()} to create a semi-unique sting; + * The string will be digested by a {@link java.security.MessageDigest} which + * returns a byte array. The generator encodes the byte array as a hex string. + *

+ * The generated Id's will cached in a + * {@link java.util.concurrent.BlockingQueue} and reproduced if an id has been + * removed. + *

+ * + * @author Simon Willnauer + * + */ +public class IDGenerator { + private final SecureRandom secureRandom; + + private final MessageDigest mdigest; + + private final BlockingQueue blockingQueue; + + private Thread runner; + + private static final int DEFAULT_CAPACITY = 10; + + protected static final Log LOGGER = LogFactory.getLog(IDGenerator.class); + + /** + * 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 + * ignored. + * + * @param capacity - + * capacity of the prebuild id queue + * @throws NoSuchAlgorithmException - + * if the algorithm does not exist + */ + public IDGenerator(int capacity) throws NoSuchAlgorithmException { + + this.secureRandom = SecureRandom.getInstance("SHA1PRNG"); + this.mdigest = MessageDigest.getInstance("SHA-1"); + this.blockingQueue = new ArrayBlockingQueue( + (capacity < DEFAULT_CAPACITY ? DEFAULT_CAPACITY : capacity), + false); + startIDProducer(); + + } + + /** + * This method takes a gnerated id from the IDProducer queue and retruns it. + * If no ID is available this method will wait until an ID is produced. This + * implementation is thread-safe. + * + * @return a UID + * @throws InterruptedException - + * if interrupted while waiting + */ + public String getUID() throws InterruptedException { + return this.blockingQueue.take(); + } + + private void startIDProducer() { + if (this.runner == null) { + UIDProducer producer = new UIDProducer(this.blockingQueue, + this.secureRandom, this.mdigest); + this.runner = new Thread(producer); + this.runner.start(); + } + } + + /** + * @return the current size of the queue + */ + public int getQueueSize() { + return this.blockingQueue.size(); + } + + /** + * Stops the id-producer + */ + public void stopIDGenerator() { + this.runner.interrupt(); + } + + private class UIDProducer implements Runnable { + SecureRandom random; + + BlockingQueue queue; + + MessageDigest digest; + + UIDProducer(BlockingQueue queue, SecureRandom random, + MessageDigest digest) { + this.queue = queue; + this.random = random; + this.digest = digest; + + } + + /** + * @see java.lang.Runnable#run() + */ + public void run() { + + while (true) { + try { + this.queue.put(produce()); + } catch (InterruptedException e) { + LOGGER + .warn("UIDProducer has been interrupted -- runner is going down"); + return; + } + } + + } + + private String produce() { + String randomNumber = new Integer(this.random.nextInt()).toString(); + byte[] byteResult = this.digest.digest(randomNumber.getBytes()); + return hexEncode(byteResult); + } + + } + + /** + * Encodes a given byte array into a hex string. + * + * @param input - + * the byte array to encode + * @return hex string representation of the given byte array + */ + static String hexEncode(byte[] input) { + StringBuffer result = new StringBuffer(); + char[] digits = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + 'a', 'b', 'c', 'd', 'e', 'f' }; + for (int idx = 0; idx < input.length; ++idx) { + byte b = input[idx]; + result.append(digits[(b & 0xf0) >> 4]); + result.append(digits[b & 0x0f]); + } + return result.toString(); + } +} Index: trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/storage/StorageException.java =================================================================== --- trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/storage/StorageException.java (revision 0) +++ trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/storage/StorageException.java (revision 0) @@ -0,0 +1,76 @@ +/** + * 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; + +/** + * The StorageException will be throw if any error or exception inside the + * storage implementation occures. This exception hides all other exceptions + * from inside the storage. + * + * @author Simon Willnauer + * + */ +public class StorageException extends Exception { + + /** + * + */ + private static final long serialVersionUID = -4997572416934126511L; + + /** + * Constructs a new StorageException + */ + public StorageException() { + super(); + + } + + /** + * Constructs a new StorageException + * + * @param message - + * the exception message + */ + public StorageException(String message) { + super(message); + + } + + /** + * Constructs a new StorageException + * + * @param message - + * the exception message + * @param cause - + * the root cause of this exception + */ + public StorageException(String message, Throwable cause) { + super(message, cause); + + } + + /** + * Constructs a new StorageException + * + * @param cause - + * the root cause of this exception + */ + public StorageException(Throwable cause) { + super(cause); + + } + +} Index: trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/storage/Storage.java =================================================================== --- trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/storage/Storage.java (revision 0) +++ trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/storage/Storage.java (revision 0) @@ -0,0 +1,100 @@ +/** + * 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.util.List; + +import com.google.gdata.data.BaseEntry; +import com.google.gdata.data.BaseFeed; +import com.google.gdata.data.ExtensionProfile; + +/** + * This is the main storage interface. The Storage represents the internal + * server storage. It acts as a Database to persist the feed data. + * This inferface is not public yet!! + * + * @author Simon Willnauer + * + */ +public interface Storage { + + /** + * This stores an incoming entry for a later retrival. + * The Entry will be associated with the feedid. + * @param entry - the entry + * @param feedId - the feedID + * @return - the stored Entry + * @throws StorageException + */ + public abstract BaseEntry storeEntry(BaseEntry entry, String feedId) + throws StorageException; + + /** + * @param entryId + * @param feedId + * @throws StorageException + */ + public abstract void deleteEntry(String entryId, String feedId) + throws StorageException; + + /** + * @param entry + * @param feedId + * @return + * @throws StorageException + */ + public abstract BaseEntry updateEntry(BaseEntry entry, String feedId) + throws StorageException; + + /** + * @param feedId + * @param startIndex + * @param resultCount + * @return + * @throws StorageException + */ + public abstract BaseFeed getFeed(String feedId, int startIndex, + int resultCount) throws StorageException; + + /** + * @param entryId + * @param feedId + * @return + * @throws StorageException + */ + public abstract BaseEntry getEntry(String entryId, String feedId) + throws StorageException; + + /** + * @param entryIdList + * @param feedId + * @return + * @throws StorageException + */ + public abstract List getEntries(List entryIdList, + String feedId) throws StorageException; + + /** + * @param profile + */ + public abstract void setExtensionProfile(final ExtensionProfile profile); + + /** + * close this storage instance + */ + public abstract void close(); + +} Index: trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/storage/StorageController.java =================================================================== --- trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/storage/StorageController.java (revision 0) +++ trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/storage/StorageController.java (revision 0) @@ -0,0 +1,27 @@ +/** + * 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; + +/** + * @author Simon Willnauer + * + */ +public interface StorageController { +/** + * Destroys the controller + */ +public abstract void destroy(); +} Index: trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/storage/StorageFactory.java =================================================================== --- trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/storage/StorageFactory.java (revision 0) +++ trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/storage/StorageFactory.java (revision 0) @@ -0,0 +1,44 @@ +/** + * 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; + + } + } +} Index: trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/storage/package.html =================================================================== --- trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/storage/package.html (revision 0) +++ trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/storage/package.html (revision 0) @@ -0,0 +1,10 @@ + + + + + + + +Feed / Enty storage + + Index: trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/server/FeedNotFoundException.java =================================================================== --- trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/server/FeedNotFoundException.java (revision 0) +++ trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/server/FeedNotFoundException.java (revision 0) @@ -0,0 +1,52 @@ +package org.apache.lucene.gdata.server; + + +/** + * Will be thrown if a requested feed could not be found or is not + * registerd. + * + * @author Simon Willnauer + * + */ +public class FeedNotFoundException extends ServiceException { + + private static final long serialVersionUID = 1L; + + /** + * Constructs a FeedNotFoundException + */ + public FeedNotFoundException() { + super(); + + } + + /** + * @param arg0 - + * message + * @param arg1 - + * cause + */ + public FeedNotFoundException(String arg0, Throwable arg1) { + super(arg0, arg1); + + } + + /** + * @param arg0 - + * message + */ + public FeedNotFoundException(String arg0) { + super(arg0); + + } + + /** + * @param arg0 - + * cause + */ + public FeedNotFoundException(Throwable arg0) { + super(arg0); + + } + +} \ No newline at end of file Index: trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/server/GDataResponse.java =================================================================== --- trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/server/GDataResponse.java (revision 0) +++ trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/server/GDataResponse.java (revision 0) @@ -0,0 +1,242 @@ +/** + * 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; + +import java.io.IOException; +import java.io.Writer; + +import javax.servlet.http.HttpServletResponse; + +import org.apache.lucene.gdata.server.GDataRequest.OutputFormat; + +import com.google.gdata.data.BaseEntry; +import com.google.gdata.data.BaseFeed; +import com.google.gdata.data.ExtensionProfile; +import com.google.gdata.util.common.xml.XmlWriter; +import com.google.gdata.util.common.xml.XmlWriter.Namespace; + +/** + * The FeedRequest Class wraps the curren HttpServletResponse. Any action on the + * HttpServletRequest will be executed via this class. This represents an + * abstraction on the plain {@link HttpServletResponse}. Any action which has + * to be performed on the underlaying {@link HttpServletResponse} will be + * executed within this class. + *

+ * The GData basicly writes two different kinds ouf reponse to the output + * stream. + *

    + *
  1. update, delete or insert requests will respond with a statuscode and if + * successful the feed entry modified or created
  2. + *
  3. get requests will respond with a statuscode and if successful the + * requested feed
  4. + *
+ * + * For this purpose the {@link GDataResponse} class provides the overloaded + * method + * {@link org.apache.lucene.gdata.server.GDataResponse#sendResponse(BaseEntry, ExtensionProfile)} + * which sends the entry e.g feed to the output stream. + *

+ * + * + * + * + * @author Simon Willnauer + * + */ +public class GDataResponse { + private int error; + + private boolean isError = false; + + private String encoding; + + private OutputFormat outputFormat; + + private final HttpServletResponse response; + + private static final String DEFAUL_NAMESPACE_URI = "http://www.w3.org/2005/Atom"; + + private static final Namespace DEFAULT_NAMESPACE = new Namespace("", + DEFAUL_NAMESPACE_URI); + + /** + * Creates a new GDataResponse + * + * @param response - + * The underlaying {@link HttpServletResponse} + */ + public GDataResponse(HttpServletResponse response) { + if (response == null) + throw new IllegalArgumentException("response must not be null"); + this.response = response; + this.response.setContentType("text/xml"); + } + + /** + * Sets an error code to this FeedResponse. + * + * @param errorCode - + * {@link HttpServletResponse} error code + */ + public void setError(int errorCode) { + this.isError = true; + this.error = errorCode; + } + /** + * Sets the status of the underlaying response + * @see HttpServletResponse + * @param responseCode - the status of the response + */ + public void setResponseCode(int responseCode){ + this.response.setStatus(responseCode); + } + /** + * This method sends the specified error to the user if set + * + * @throws IOException - + * if an I/O Exception occures + */ + public void sendError() throws IOException { + if (this.isError) + this.response.sendError(this.error); + } + + /** + * @return - the {@link HttpServletResponse} writer + * @throws IOException - + * If an I/O exception occures + */ + public Writer getWriter() throws IOException { + return this.response.getWriter(); + } + + /** + * Sends a response for a get e.g. query request. This method must not + * invoked in a case of an error performing the requeste action. + * + * @param feed - + * the feed to respond to the client + * @param profile - + * the extension profil for the feed to write + * @throws IOException - + * if an I/O exception accures, often caused by an already + * closed Writer or OutputStream + * + */ + public void sendResponse(BaseFeed feed, ExtensionProfile profile) + throws IOException { + if (feed == null) + throw new IllegalArgumentException("feed must not be null"); + if(profile == null) + throw new IllegalArgumentException("extension profil must not be null"); + XmlWriter writer = createWriter(); + + if (this.outputFormat.equals(OutputFormat.ATOM)) + feed.generateAtom(writer, profile); + else + feed.generateRss(writer, profile); + + } + + /** + * + * 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. + * If the specified response format is ATOM the default namespace will be set to ATOM. + * @param entry - + * the modified / created entry to send + * @param profile - + * the entries extension profile + * @throws IOException - + * if an I/O exception accures, often caused by an already + * closed Writer or OutputStream + */ + public void sendResponse(BaseEntry entry, ExtensionProfile profile) + throws IOException { + if (entry == null) + throw new IllegalArgumentException("entry must not be null"); + if(profile == null) + throw new IllegalArgumentException("extension profil must not be null"); + XmlWriter writer = createWriter(); + if (this.outputFormat.equals(OutputFormat.ATOM)) + entry.generateAtom(writer, profile); + else + entry.generateRss(writer, profile); + } + + private XmlWriter createWriter() throws IOException { + XmlWriter writer = new XmlWriter(getWriter(), this.encoding); + // set the default namespace to Atom if Atom is the response format + if(this.outputFormat.equals(OutputFormat.ATOM)) + writer.setDefaultNamespace(DEFAULT_NAMESPACE); + return writer; + } + + /** + * This encoding will be used to encode the xml representation of feed or + * entry written to the {@link HttpServletResponse} output stream. + * + * @return - the entry / feed encoding + */ + public String getEncoding() { + return this.encoding; + } + + /** + * This encoding will be used to encode the xml representation of feed or + * entry written to the {@link HttpServletResponse} output stream. UTF-8 + * ISO-8859-1 + * + * @param encoding - + * string represents the encoding + */ + public void setEncoding(String encoding) { + this.encoding = encoding; + } + + /** + * @return - the response + * {@link org.apache.lucene.gdata.server.GDataRequest.OutputFormat} + */ + public OutputFormat getOutputFormat() { + return this.outputFormat; + } + + /** + * @param outputFormat - + * the response + * {@link org.apache.lucene.gdata.server.GDataRequest.OutputFormat} + */ + public void setOutputFormat(OutputFormat outputFormat) { + this.outputFormat = outputFormat; + } + /** + * @see Object#toString() + */ + @Override + public String toString(){ + StringBuilder builder = new StringBuilder(" GDataResponse: "); + builder.append("Error: ").append(this.error); + builder.append(" outputFormat: ").append(getOutputFormat()); + builder.append(" encoding: ").append(this.encoding); + + return builder.toString(); + + + } + +} Index: trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/server/ServiceException.java =================================================================== --- trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/server/ServiceException.java (revision 0) +++ trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/server/ServiceException.java (revision 0) @@ -0,0 +1,63 @@ +/** + * 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; + +/** + * @author Simon Willnauer + * + */ +public class ServiceException extends Exception { + + /** + * + */ + private static final long serialVersionUID = -7099825107871876584L; + + /** + * + */ + public ServiceException() { + super(); + + } + + /** + * @param arg0 + */ + public ServiceException(String arg0) { + super(arg0); + + } + + /** + * @param arg0 + * @param arg1 + */ + public ServiceException(String arg0, Throwable arg1) { + super(arg0, arg1); + + } + + /** + * @param arg0 + */ + public ServiceException(Throwable arg0) { + super(arg0); + + } + +} Index: trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/server/Service.java =================================================================== --- trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/server/Service.java (revision 0) +++ trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/server/Service.java (revision 0) @@ -0,0 +1,139 @@ +/** + * 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; + +import com.google.gdata.data.BaseEntry; +import com.google.gdata.data.BaseFeed; + + +/** + * The Service class represents an interface to access the GData service + * componentes of the GData-Server. It encapsulates all interactions with the + * GData client. + *

+ * This class provides the base level common functionality required to access + * the GData components. It is also designed to act as a base class that can be + * extended for specific types of underlaying server components as different + * indexing or storage components. + *

+ *

+ * It could also encapsulate caching mechanismn build on top of the storage to + * reduce load on the storage component + *

+ * + * @author Simon Willnauer + * + * + */ +public abstract class Service { + + /** + * Service method to create an entry in an already created and existing + * feed. This method will create the entry and passes the entry to the + * indexing component to make the new entry accessable via get-queries. + * The response and the corresponding http status code will be added to the + * given FeedResponse. + * + * @param request - + * the current FeedRequest + * @param response - + * the current FeedResponse + * @return - the entry which has been created + * @throws ServiceException - + * if the corresponding feed does not exist or the storage can + * not be accessed + */ + public abstract BaseEntry createEntry(final GDataRequest request, + final GDataResponse response) throws ServiceException; + + /** + * Service Method to delete an entry specified in the given FeedRequest. + * This method will remove the entry permanently. There will be no + * possiblity to restore the entry. The response and the corresponding http + * status code will be added to the given FeedResponse. + * + * @param request - + * the current FeedRequest + * @param response - + * the current FeedResponse + * @return - the entry wich has been deleted + * @throws ServiceException - + * if the entry does not exist or the storage can not be + * accessed + */ + public abstract BaseEntry deleteEntry(GDataRequest request, final GDataResponse response) + throws ServiceException; + + /** + * Service method to update an existing entry in a existing feed context. + * The entry version will be checked and a ServiceException + * will be thrown if the version to update is outdated. The new entry will + * be passed to the indexing component to make the version accessable via + * get-queries. + * + * @param request - + * the current FeedRequest + * @param response - + * the current FeedResponse + * @return - the entry wich has been updated + * @throws ServiceException - + * if the corresponding feed does not exist, the storage can not + * be accessed or the version to update is out of date. + */ + public abstract BaseEntry updateEntry(final GDataRequest request, + final GDataResponse response) throws ServiceException; + + /** + * Service method to retrieve a requested Feed. The feed will also be added to + * the given FeedResponse instance and can also be accessed + * via the FeedResponse object. + * + * @param request - + * the current FeedRequest + * @param response - + * the current FeedResponse + * @return - the requested feed + * + * @throws ServiceException - + * If the storage can not be accessed or the requested feed does + * not exist. + */ + public abstract BaseFeed getFeed(final GDataRequest request, final GDataResponse response) + throws ServiceException; + + /** + * Service method to retrieve a requested entry. The entry will also be added to + * the given FeedResponse instance and can also be accessed + * via the FeedResponse object. + * + * @param request - + * the current FeedRequest + * @param response - + * the current FeedResponse + * @return - the requested entry + * + * @throws ServiceException - + * If the storage can not be accessed or the requested entry does + * not exist. + */ + public abstract BaseEntry getSingleEntry(final GDataRequest request, final GDataResponse response) + throws ServiceException; + + + + +} Index: trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/server/GDataService.java =================================================================== --- trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/server/GDataService.java (revision 0) +++ trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/server/GDataService.java (revision 0) @@ -0,0 +1,275 @@ +/** + * 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; + +import java.io.IOException; +import java.util.List; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.lucene.gdata.server.registry.GDataServerRegistry; +import org.apache.lucene.gdata.storage.Storage; +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.BaseFeed; +import com.google.gdata.data.DateTime; +import com.google.gdata.data.Generator; +import com.google.gdata.data.Link; +import com.google.gdata.util.ParseException; + +/** + * @author Simon Willnauer + * + */ +public class GDataService extends Service { + private static final Log LOGGER = LogFactory.getLog(GDataService.class); + + private Storage storage; + + private GDataServerRegistry registry = GDataServerRegistry.getRegistry(); + + private static final Generator generator; + + private static final String generatorName = "Lucene GData-Server"; + + private static final String generatorURI = "http://lucene.apache.org"; + static { + generator = new Generator(); + generator.setName(generatorName); + generator.setUri(generatorURI); + generator.setVersion("0.1"); + } + + protected GDataService() throws ServiceException { + try { + this.storage = StorageFactory.getStorage(); + + } catch (StorageException e) { + LOGGER + .fatal( + "Can't get Storage Instance -- can't serve any requests", + e); + ServiceException ex = new ServiceException( + "Can't get Storage instance" + e.getMessage(), e); + ex.setStackTrace(e.getStackTrace()); + throw ex; + } + } + + /** + * @see org.apache.lucene.gdata.server.Service#createEntry(org.apache.lucene.gdata.server.GDataRequest, + * org.apache.lucene.gdata.server.GDataResponse) + */ + @Override + public BaseEntry createEntry(GDataRequest request, GDataResponse response) + throws ServiceException { + + checkFeedIsRegisterd(request); + if (LOGGER.isInfoEnabled()) + LOGGER.info("create Entry for feedId: " + request.getFeedId()); + BaseEntry entry = buildEntry(request); + setUpdateTime(entry); + try { + + this.storage.storeEntry(entry, request.getFeedId()); + } catch (Exception e) { + ServiceException ex = new ServiceException("Could not store entry", + e); + ex.setStackTrace(e.getStackTrace()); + throw ex; + } + return entry; + } + + /** + * @see org.apache.lucene.gdata.server.Service#deleteEntry(org.apache.lucene.gdata.server.GDataRequest, + * org.apache.lucene.gdata.server.GDataResponse) + */ + @Override + public BaseEntry deleteEntry(GDataRequest request, GDataResponse response) + throws ServiceException { + checkFeedIsRegisterd(request); + String entryid = request.getEntryId(); + String feedid = request.getFeedId(); + try { + this.storage.deleteEntry(entryid, feedid); + } catch (Exception e) { + ServiceException ex = new ServiceException( + "Could not delete entry", e); + ex.setStackTrace(e.getStackTrace()); + throw ex; + } + return null; + } + + /** + * @see org.apache.lucene.gdata.server.Service#updateEntry(org.apache.lucene.gdata.server.GDataRequest, + * org.apache.lucene.gdata.server.GDataResponse) + */ + @Override + public BaseEntry updateEntry(GDataRequest request, GDataResponse response) + throws ServiceException { + checkFeedIsRegisterd(request); + + BaseEntry entry = buildEntry(request); + String feedid = request.getFeedId(); + if (LOGGER.isInfoEnabled()) + LOGGER.info("update Entry" + entry.getId() + " for feedId: " + + feedid); + setUpdateTime(entry); + try { + this.storage.updateEntry(entry, feedid); + } catch (StorageException e) { + ServiceException ex = new ServiceException( + "Could not update entry", e); + ex.setStackTrace(e.getStackTrace()); + throw ex; + } + return entry; + } + + /** + * @see org.apache.lucene.gdata.server.Service#getFeed(org.apache.lucene.gdata.server.GDataRequest, + * org.apache.lucene.gdata.server.GDataResponse) + */ + @SuppressWarnings("unchecked") + @Override + public BaseFeed getFeed(GDataRequest request, GDataResponse response) + throws ServiceException { + checkFeedIsRegisterd(request); + + try { + // TODO remove when storing feeds is implemented just for + // development + BaseFeed feed = this.storage.getFeed(request.getFeedId(), request + .getStartIndex(), request.getItemsPerPage()); + buildDynamicFeedElements(request, feed); + List list = feed.getEntries(); + addContextPath(list, request.getContextPath()); + return feed; + } catch (StorageException e) { + ServiceException ex = new ServiceException("Could not get feed", e); + ex.setStackTrace(e.getStackTrace()); + throw ex; + } + + } + + /* + * 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) { + Link retVal = new Link(); + retVal.setHref(href); + retVal.setRel(rel); + retVal.setType(type); + return retVal; + } + + /* + * every entry has an ID which has to have a prefix. The prefix is the + * context path of the requested feed. This will be used to request the + * entry directly + */ + private void addContextPath(List 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 { + return GDataEntityBuilder.buildEntry(request); + + } catch (ParseException e) { + ServiceException ex = new ServiceException( + "Could not parse entry from incoming request", e); + ex.setStackTrace(e.getStackTrace()); + throw ex; + } catch (IOException e) { + ServiceException ex = new ServiceException( + "Could not read or open input stream", e); + ex.setStackTrace(e.getStackTrace()); + throw ex; + } + } + + /* + * checks whether the reqeuested feed is registered + */ + private void checkFeedIsRegisterd(final GDataRequest request) + throws FeedNotFoundException { + 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; + } + + /** + * @see org.apache.lucene.gdata.server.Service#getSingleEntry(org.apache.lucene.gdata.server.GDataRequest, + * org.apache.lucene.gdata.server.GDataResponse) + */ + @Override + public BaseEntry getSingleEntry(GDataRequest request, GDataResponse response) + throws ServiceException { + checkFeedIsRegisterd(request); + + try { + BaseEntry entry = this.storage.getEntry(request.getEntryId(), + request.getFeedId()); + if(entry == null) + return null; + addcontextPath(entry, request.getContextPath()); + return entry; + } catch (StorageException e) { + ServiceException ex = new ServiceException("Could not get feed", e); + ex.setStackTrace(e.getStackTrace()); + throw ex; + } + } + +} Index: trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/server/GDataEntityBuilder.java =================================================================== --- trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/server/GDataEntityBuilder.java (revision 0) +++ trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/server/GDataEntityBuilder.java (revision 0) @@ -0,0 +1,171 @@ +/** + * 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; + +import java.io.IOException; +import java.io.Reader; + +import org.apache.lucene.gdata.server.registry.DataBuilderException; +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.BaseFeed; +import com.google.gdata.data.ExtensionProfile; +import com.google.gdata.util.ParseException; + +/** + * {@link com.google.gdata.data.BaseFeed}, + * {@link com.google.gdata.data.BaseEntry} instances have to be build from a + * {@link java.io.Reader} instance as they come in from a client request or out + * of a storage. + *

+ * To provide a generic builder class the {@link GDataEntityBuilder} requests + * the type of the feed / entry and the corresponding + * {@link com.google.gdata.data.ExtensionProfile} form the global + * {@link org.apache.lucene.gdata.server.registry.GDataServerRegistry} and builds the + * instances from the provided reader. + *

+ * + * @author Simon Willnauer + * + */ +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 + * the {@link GDataRequest} + * + * @param request - + * the request to build the instance from + * @return - a BaseFeed instance + * @throws FeedNotFoundException - + * if the feed is not registered + * @throws IOException - + * if an I/O Exception occures on the provided reader + * @throws ParseException - + * if the feed could not be parsed + */ + public static BaseFeed buildFeed(final GDataRequest request) + throws FeedNotFoundException, IOException, ParseException { + if (request == null) + throw new IllegalArgumentException("request must not be null"); + return buildFeed(request.getFeedId(), request.getReader(),request.getExtensionProfile()); + } + + /** + * Builds a {@link BaseFeed} from the provided {@link Reader} + * + * @param feedId - + * the feed ID to request the feed type from the registry + * @param reader - + * the reader to build the feed from + * @param profile - extension profile to parse the resource + * @return - a BaseFeed instance + * @throws FeedNotFoundException - + * if the feed is not registered + * @throws IOException - + * if an I/O Exception occures on the provided reader + * @throws ParseException - + * if the feed could not be parsed + */ + public static BaseFeed buildFeed(final String feedId, final Reader reader,final ExtensionProfile profile) + throws FeedNotFoundException, ParseException, IOException { + + BaseFeed retVal = null; + try { + retVal = (BaseFeed) createEntityInstance(feedId); + } 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; + } + + /** + * Builds a {@link BaseEntry} instance from the {@link Reader} provided by + * the {@link GDataRequest} + * + * @param request - + * the request to build the instance from + * @return - a BaseEntry instance + * @throws FeedNotFoundException - + * if the feed, requested by the client is not registered + * @throws IOException - + * if an I/O Exception occures on the provided reader + * @throws ParseException - + * if the entry could not be parsed + */ + public static BaseEntry buildEntry(final GDataRequest request) + throws FeedNotFoundException, IOException, ParseException { + if (request == null) + throw new IllegalArgumentException("request must not be null"); + return buildEntry(request.getFeedId(), request.getReader(),request.getExtensionProfile()); + } + + /** + * Builds a {@link BaseFeed} instance from the {@link Reader} provided by + * the {@link GDataRequest} + * @param feedId - + * the feed ID to request the feed type from the registry + * @param reader - + * the reader to build the feed from + * @param profile - extension profile to parse the resource + * @return - a BaseFeed instance + * @throws FeedNotFoundException - + * if the feed is not registered + * @throws IOException - + * if an I/O Exception occures on the provided reader + * @throws ParseException - + * if the entry could not be parsed + */ + public static BaseEntry buildEntry(final String feedId, final Reader reader,final ExtensionProfile profile) + throws FeedNotFoundException, ParseException, IOException { + + BaseEntry retVal = null; + try { + retVal = ((BaseFeed) createEntityInstance(feedId)).createEntry(); + } catch (FeedNotFoundException e) { + throw e; + } catch (Exception e) { + DataBuilderException ex = new DataBuilderException( + "Could not build Entry for Entry class ", e); + ex.setStackTrace(e.getStackTrace()); + throw ex; + } + retVal.parseAtom(new ExtensionProfile(), reader); + return retVal; + } + + 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(); + } + +} Index: trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/server/GDataRequestException.java =================================================================== --- trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/server/GDataRequestException.java (revision 0) +++ trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/server/GDataRequestException.java (revision 0) @@ -0,0 +1,67 @@ +/** + * 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; + +/** + * This exception wraps all exceptions occure inside the {@link org.apache.lucene.gdata.server.GDataRequest} + * @author Simon Willnauer + * + */ +public class GDataRequestException extends Exception { + + /** + * Serial version ID. -> Implements Serializable + */ + private static final long serialVersionUID = -4440777051466950723L; + + /** + * Constructs a new GDataException + */ + public GDataRequestException() { + super(); + + } + + /** + * Constructs a new GDataException with a given message string + * @param arg0 - the excpetion message + */ + public GDataRequestException(String arg0) { + super(arg0); + + } + + /** + * Constructs a new GDataException with a given message string and cause + * @param arg0 - the exception message + * @param arg1 - the exception who caused this exception + */ + public GDataRequestException(String arg0, Throwable arg1) { + super(arg0, arg1); + + } + + /** + * Constructs a new GDataException with a given cause + * @param arg0 - exception cause + */ + public GDataRequestException(Throwable arg0) { + super(arg0); + + } + +} Index: trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/server/registry/DataBuilderException.java =================================================================== --- trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/server/registry/DataBuilderException.java (revision 0) +++ trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/server/registry/DataBuilderException.java (revision 0) @@ -0,0 +1,62 @@ +/** + * 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 + } + +} Index: trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/server/registry/RegistryBuilder.java =================================================================== --- trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/server/registry/RegistryBuilder.java (revision 0) +++ trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/server/registry/RegistryBuilder.java (revision 0) @@ -0,0 +1,41 @@ +/** + * 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; + +import com.google.gdata.data.ExtensionProfile; +import com.google.gdata.data.Feed; + +/** + * @author Simon Willnauer + * + */ +public class RegistryBuilder { + + /** + * + */ + public static void buildRegistry(){ + // TODO Implement this!! -- just for develping purposes + GDataServerRegistry reg = GDataServerRegistry.getRegistry(); + FeedInstanceConfigurator configurator = new FeedInstanceConfigurator(); + configurator.setFeedType(Feed.class); + configurator.setFeedId("weblog"); + configurator.setExtensionProfileClass(ExtensionProfile.class); + reg.registerFeed(configurator); + + } + +} Index: trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/server/registry/GDataServerRegistry.java =================================================================== --- trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/server/registry/GDataServerRegistry.java (revision 0) +++ trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/server/registry/GDataServerRegistry.java (revision 0) @@ -0,0 +1,154 @@ +/** + * 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; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.commons.logging.Log; +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 + * feed configurations will be registered here. Feed configurations contain + * several informationsa about GData feed like: + *
    + *
  1. the feed id - where the feed can be accessed via http methodes
  2. + *
  3. the feed type - feed types are implementations of the abstract + * {@link com.google.gdata.data.BaseFeed}
  4. + *
+ * The registry will be set up at start up of the server application and can be + * accessed from other components to get configurations according to incoming + * requests. + * + * @author Simon Willnauer + * + */ +public class GDataServerRegistry { + private static GDataServerRegistry INSTANCE; + + private StorageController storageInstance; + + private static final Log LOGGER = LogFactory + .getLog(GDataServerRegistry.class); + + private final Map feedTypMap = new HashMap(); + + private GDataServerRegistry() { + // private - singleton + } + + /** + * @return a Sinleton registry instance + */ + public static synchronized GDataServerRegistry getRegistry() { + if (INSTANCE == null) + INSTANCE = new GDataServerRegistry(); + return INSTANCE; + } + + /** + * Registers a {@link FeedInstanceConfigurator} + * + * @param configurator - + * the configurator to register in the registry + */ + public void registerFeed(FeedInstanceConfigurator configurator) { + if (configurator == null) { + LOGGER.warn("Feedconfigurator is null -- skip registration"); + return; + } + this.feedTypMap.put(configurator.getFeedId(), configurator); + } + + /** + * Looks up the {@link FeedInstanceConfigurator} by the given feed id. + * + * @param feedId + * @return - the {@link FeedInstanceConfigurator} or null if + * the no configuration for this feed has been registered + */ + public FeedInstanceConfigurator getFeedConfigurator(String feedId) { + if (feedId == null) + throw new IllegalArgumentException( + "Feed URL is null - must not be null to get registered feedtype"); + return this.feedTypMap.get(feedId); + } + + protected void flushRegistry() { + this.feedTypMap.clear(); + } + + /** + * @param feedId - + * the id of the feed as the feed is registered + * @return - true if and only if the feed is registered, + * otherwise false. + */ + public boolean isFeedRegistered(String feedId) { + return this.feedTypMap.containsKey(feedId); + + } + + /** + * @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 + */ + public void destroy() { + flushRegistry(); + this.storageInstance.destroy(); + this.storageInstance = null; + + } + + /** + * Creates the {@link ExtensionProfile} for a registered feed + * @param feedId - the feed id + * @return - the extension profil for this feed of null if + * the feed is not registered or the extension profile could not be + * instanciated + */ + public ExtensionProfile getExtensionProfile(final String feedId) { + FeedInstanceConfigurator configurator = this.feedTypMap.get(feedId); + if (configurator == null) + return null; + Class clazz = configurator.getExtensionProfilClass(); + try { + return (ExtensionProfile) clazz.newInstance(); + } catch (Exception e) { + LOGGER + .error("Can not create instance of ExtensionProfil for class: " + + clazz + " -- feedId: " + feedId); + + } + return null; + } + +} Property changes on: trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/server/registry/GDataServerRegistry.java ___________________________________________________________________ Name: svn:executable + * Index: trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/server/registry/RegistryContextListener.java =================================================================== --- trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/server/registry/RegistryContextListener.java (revision 0) +++ trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/server/registry/RegistryContextListener.java (revision 0) @@ -0,0 +1,65 @@ +/** + * 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; + +import javax.servlet.ServletContextEvent; +import javax.servlet.ServletContextListener; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * This Listener creates the + * {@link org.apache.lucene.gdata.server.registry.GDataServerRegistry} when the + * context is loaded. The registry will be loaded before the + * {@link org.apache.lucene.gdata.servlet.RequestControllerServlet} is loaded. + * The Registry will be loaded and set up befor the REST interface is available. + *

+ * This ContextListener has to be configured in the web.xml + * deployment descriptor.

+ * + * + * @author Simon Willnauer + * + */ +public class RegistryContextListener implements ServletContextListener { + private GDataServerRegistry serverRegistry; + + private static final Log LOG = LogFactory + .getLog(RegistryContextListener.class); + + + + /** + * @see javax.servlet.ServletContextListener#contextInitialized(javax.servlet.ServletContextEvent) + */ + public void contextInitialized(ServletContextEvent arg0) { + LOG.info("RegistryContextListener has been loaded"); + RegistryBuilder.buildRegistry(); + this.serverRegistry = GDataServerRegistry.getRegistry(); + } + + /** + * @see javax.servlet.ServletContextListener#contextDestroyed(javax.servlet.ServletContextEvent) + */ + public void contextDestroyed(ServletContextEvent arg0) { + LOG.info("Destroying context"); + this.serverRegistry.destroy(); + + } + +} Index: trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/server/registry/FeedInstanceConfigurator.java =================================================================== --- trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/server/registry/FeedInstanceConfigurator.java (revision 0) +++ trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/server/registry/FeedInstanceConfigurator.java (revision 0) @@ -0,0 +1,66 @@ +/** + * 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; + } + + +} Index: trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/server/registry/package.html =================================================================== --- trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/server/registry/package.html (revision 0) +++ trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/server/registry/package.html (revision 0) @@ -0,0 +1,10 @@ + + + + + + + +Internal registry - registering feeds and configurations + + \ No newline at end of file Index: trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/server/ServiceFactory.java =================================================================== --- trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/server/ServiceFactory.java (revision 0) +++ trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/server/ServiceFactory.java (revision 0) @@ -0,0 +1,57 @@ +/** + * 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; + + +/** + * The {@link ServiceFactory} creates {@link Service} implementations to access + * the GData - Server components. + * + * @author Simon Willnauer + * + */ +public class ServiceFactory { + + private static ServiceFactory INSTANCE = null; + + /** + * @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 + } + + /** + * Creates a {@link Service} implementation. + * + * @return a Service Implementation + */ + public Service getService() { + try{ + return new GDataService(); + }catch (Exception e) { + // + } + return null; + } +} Index: trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/server/GDataRequest.java =================================================================== --- trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/server/GDataRequest.java (revision 0) +++ trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/server/GDataRequest.java (revision 0) @@ -0,0 +1,442 @@ +/** + * 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; + +import java.io.IOException; +import java.io.Reader; +import java.util.Enumeration; +import java.util.Map; +import java.util.StringTokenizer; + +import javax.servlet.http.HttpServletRequest; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.lucene.gdata.server.registry.GDataServerRegistry; + +import com.google.gdata.data.ExtensionProfile; + +/** + * The GDataRequest Class wraps the incoming HttpServletRequest. Needed + * information coming with the HttpServletRequest can be accessed directly. It + * represents an abstraction on the plain HttpServletRequest. Every GData + * specific data coming from the client will be availiable and can be accessed + * via the GDataRequest. + *

+ * GDataRequest instances will be passed to any action requested by the client. + * This class also holds the logic to retrieve important information like + * response format, the reqeusted feed instance and query parameters. + * + *

+ * + * @author Simon Willnauer + * + */ +/* this class might be extracted as an interface in later development */ +public class GDataRequest { + + private static final Log LOG = LogFactory.getLog(GDataRequest.class); + + private static final String RESPONSE_FORMAT_PARAMETER = "alt"; + + private static final String RESPONSE_FORMAT_PARAMETER_RSS = "rss"; + + private static final int DEFAULT_ITEMS_PER_PAGE = 25; + + private static final int DEFAULT_START_INDEX = 1; + + private static final String START_INDEX_NEXT_PAGE_PARAMETER = "start-index"; + + private static final String ITEMS_PER_PAGE_PARAMETER = "max-results"; + + private String contextPath; + + @SuppressWarnings("unused") + private static final String RESPONSE_FORMAT_PARAMETER_ATOM = "atom"; + + // Atom is the default resopnse format + private OutputFormat responseFormat = OutputFormat.ATOM; + + private final HttpServletRequest request; + + private String feedId = null; + + private String entryId = null; + + private ExtensionProfile extensionProfile= null; + + private String entryVersion = null; + + private GDataRequestType type; + + /** + * Creates a new FeedRequest + * + * @param requst - + * the incoming HttpServletReqeust + * @param type - + * the request type + * + */ + public GDataRequest(final HttpServletRequest requst, + final GDataRequestType type) { + if (requst == null) + throw new IllegalArgumentException("request must not be null "); + if (type == null) + throw new IllegalArgumentException("request type must not be null "); + this.request = requst; + this.type = type; + + } + + /** + * Initialize the GDataRequest. This will initialize all needed values / + * attributes in this request. + * + * @throws GDataRequestException + */ + public void initializeRequest() throws GDataRequestException { + generateIdentificationProperties(); + setOutputFormat(); + /* + * ExtensionProfile is used for building the Entry / Feed Instances from an inputstream or reader + */ + this.extensionProfile = GDataServerRegistry.getRegistry().getExtensionProfile(this.feedId); + if(this.extensionProfile == null) + throw new GDataRequestException("feed is not registered or extension profile could not be created"); + } + + /** + * @return - the id of the requested feed + */ + public String getFeedId() { + + return this.feedId; + } + + /** + * @return - the entry id of the requested Entry if specified, otherwise + * null + */ + public String getEntryId() { + + return this.entryId; + } + + /** + * @return the version Id of the requested Entry if specified, otherwise + * null + */ + public String getEntryVersion() { + return this.entryVersion; + } + + /** + * A Reader instance to read form the client input stream + * + * @return - the HttpServletRequest {@link Reader} + * @throws IOException - + * if an I/O Exception occures + */ + public Reader getReader() throws IOException { + return this.request.getReader(); + } + + /** + * Returns the {@link HttpServletRequest} parameter map containig all GET + * request parameters. + * + * @return the parameter map + */ + @SuppressWarnings("unchecked") + public Map getQueryParameter() { + return this.request.getParameterMap(); + } + + /** + * The {@link HttpServletRequest} request parameter names + * + * @return parameter names enumeration + */ + @SuppressWarnings("unchecked") + public Enumeration getQueryParameterNames() { + return this.request.getParameterNames(); + } + + /** + * Either Atom or RSS + * + * @return - The output format requested by the client + */ + public OutputFormat getRequestedResponseFormat() { + + return this.responseFormat; + } + + private void generateIdentificationProperties() + throws GDataRequestException { + /* generate all needed data to identify the requested feed/entry */ + String pathInfo = this.request.getPathInfo(); + /* + * TODO this has to be changed to support the category queries. Category + * queries could also be rewrited in the Servlet. + */ + if (pathInfo.length() <= 1) + throw new GDataRequestException( + "No feed or entry specified for this request"); + StringTokenizer tokenizer = new StringTokenizer(pathInfo, "/"); + this.feedId = tokenizer.nextToken(); + this.entryId = tokenizer.hasMoreTokens() ? tokenizer.nextToken() : ""; + this.entryVersion = tokenizer.hasMoreTokens() ? tokenizer.nextToken() + : ""; + + } + + private void setOutputFormat() { + String formatParameter = this.request + .getParameter(RESPONSE_FORMAT_PARAMETER); + if (formatParameter == null) + return; + if (formatParameter.equalsIgnoreCase(RESPONSE_FORMAT_PARAMETER_RSS)) + this.responseFormat = OutputFormat.RSS; + + } + + /** + * @return - the number of returned items per page + */ + public int getItemsPerPage() { + + if (this.request.getParameter(ITEMS_PER_PAGE_PARAMETER) == null) + return DEFAULT_ITEMS_PER_PAGE; + int retval = -1; + try { + retval = new Integer(this.request + .getParameter(ITEMS_PER_PAGE_PARAMETER)).intValue(); + } catch (Exception e) { + LOG.warn("Intems per page could not be parsed - " + e.getMessage(), + e); + } + return retval < 0 ? DEFAULT_ITEMS_PER_PAGE : retval; + } + + /** + * Start index represents the number of the first entry of the query - + * result. The order depends on the query. Is the query a search query the + * this value will be assinged to the score in a common feed query the value + * will be assigned to the update time of the entries. + * + * @return - the requested start index + */ + public int getStartIndex() { + if (this.request.getParameter(START_INDEX_NEXT_PAGE_PARAMETER) == null) + return DEFAULT_START_INDEX; + int retval = -1; + try { + retval = new Integer(this.request + .getParameter(START_INDEX_NEXT_PAGE_PARAMETER)).intValue(); + } catch (Exception e) { + LOG.warn("Start-index could not be parsed - " + e.getMessage(), e); + } + return retval < 0 ? DEFAULT_START_INDEX : retval; + } + + /** + * The selfid is href pointing to the requested resource + * + * @return - the self id + */ + public String getSelfId() { + StringBuilder builder = new StringBuilder(); + builder.append(buildRequestIDString(false)); + + builder.append(getQueryString()); + + return builder.toString(); + } + + /** + * The href id of the next page of the requested resource. + * + * @return the id of the next page + */ + public String getNextId() { + // StringBuilder builder = new StringBuilder(); + // builder.append(buildRequestIDString()); + // + // builder.append(getQueryString()); + // + // if(this.request.getParameter(START_INDEX_NEXT_PAGE_PARAMETER)== + // null){ + // builder.append("&").append(START_INDEX_NEXT_PAGE_PARAMETER).append("="); + // builder.append(DEFAULT_ITEMS_PER_PAGE+1); + // } + // else{ + // + // int next = 0; + // try{ + // next = + // Integer.parseInt(this.request.getParameter(START_INDEX_NEXT_PAGE_PARAMETER)); + // }catch (Exception e) { + // // + // } + // + // if(next < 0) + // builder.append(DEFAULT_ITEMS_PER_PAGE+1); + // else + // builder.append(next+DEFAULT_ITEMS_PER_PAGE); + // int pos = builder.indexOf(START_INDEX_NEXT_PAGE_PARAMETER); + // boolean end = builder.lastIndexOf("&",pos) < pos; + // builder.replace(pos+START_INDEX_NEXT_PAGE_PARAMETER.length()+1,pos+START_INDEX_NEXT_PAGE_PARAMETER.length()+3,""+next); + // + // + // System.out.println(end); + // } + // + // + // + // return builder.toString(); + return buildRequestIDString(false); + + } + + private String buildRequestIDString(boolean endingSlash) { + StringBuilder builder = new StringBuilder("http://"); + builder.append(this.request.getHeader("Host")); + builder.append(this.request.getRequestURI()); + if (endingSlash && !this.request.getRequestURI().endsWith("/")) + builder.append("/"); + + return builder.toString(); + } + + /** + * This will return the current query string including all parameters. + * Additionaly the max-resul parameter will be added if not + * specified. + *

+ * max-resul indicates the number of results returned to the + * client. The default value is 25. + *

+ * + * @return - the query string incluing all parameters + */ + public String getQueryString() { + String retVal = this.request.getQueryString(); + + if (this.request.getParameter(ITEMS_PER_PAGE_PARAMETER) != null) + return retVal; + String tempString = (retVal == null ? "?" + ITEMS_PER_PAGE_PARAMETER + + "=" + DEFAULT_ITEMS_PER_PAGE : "&" + ITEMS_PER_PAGE_PARAMETER + + "=" + DEFAULT_ITEMS_PER_PAGE); + + return retVal == null ? tempString : retVal + tempString; + + } + + /** + * This enum represents the OutputFormat of the GDATA Server + * + * @author Simon Willnauer + * + */ + public static enum OutputFormat { + /** + * Output format ATOM. ATOM is the default response format. + */ + ATOM, + /** + * Output format RSS + */ + RSS + } + + /** + * Returns the requested path including the domain name and the requested + * resource http://www.apache.org/path/resource/ + * + * @return the context path + */ + public String getContextPath() { + if (this.contextPath == null) + this.contextPath = buildRequestIDString(true); + return this.contextPath; + } + + /** + * Indicates the request type + * + * @author Simon Willnauer + * + */ + public enum GDataRequestType { + /** + * Type FeedRequest + */ + GET, + /** + * Type UpdateRequest + */ + UPDATE, + /** + * Type DeleteRequest + */ + DELETE, + /** + * Type InsertRequest + */ + INSERT + } + + /** + * {@link GDataRequestType} + * + * @return the current request type + */ + public GDataRequestType getType() { + return this.type; + } + + /** + * If the reuquest is a {@link GDataRequestType#GET} request and there is + * no entry id specified, the requested resource is a feed. + * + * @return - true if an only if the requested resource is a feed + */ + public boolean isFeedRequested() { + + 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 + * an entry id specified, the requested resource is an entry. + * + * @return - true if an only if the requested resource is an entry + */ + public boolean isEntryRequested() { + return !this.isFeedRequested(); + } + + /** + * @return - the extensionProfile for the requested resource + */ + public ExtensionProfile getExtensionProfile() { + return this.extensionProfile; + } + +} Index: trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/server/package.html =================================================================== --- trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/server/package.html (revision 0) +++ trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/server/package.html (revision 0) @@ -0,0 +1,10 @@ + + + + + + + +GData-Server classes encapsulation all protocol-level interactions and underlaying GData components. + + \ No newline at end of file Index: trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/data/package.html =================================================================== --- trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/data/package.html (revision 0) +++ trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/data/package.html (revision 0) @@ -0,0 +1,10 @@ + + + + + + + +Contains classes for the internal representation of GData feeds and entries. + + \ No newline at end of file Index: trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/package.html =================================================================== --- trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/package.html (revision 0) +++ trunk/contrib/gdata-server/src/java/org/apache/lucene/gdata/package.html (revision 0) @@ -0,0 +1 @@ +Top-level package. \ No newline at end of file Index: trunk/contrib/gdata-server/src/java/lucenestorage.properties.xml =================================================================== --- trunk/contrib/gdata-server/src/java/lucenestorage.properties.xml (revision 0) +++ trunk/contrib/gdata-server/src/java/lucenestorage.properties.xml (revision 0) @@ -0,0 +1,11 @@ + + + +Lucene Storage Properties +20 +20 +20 +/tmp/storage/ +true +false + Index: trunk/contrib/gdata-server/build.xml =================================================================== --- trunk/contrib/gdata-server/build.xml (revision 0) +++ trunk/contrib/gdata-server/build.xml (revision 0) @@ -0,0 +1,50 @@ + + + + + + Serverside Google Data API implementation + + + + + + + + + + + + + + + + + + + + + + + + + + Prepare dist directory + + + + + Distributing GData War + + + + + + + + + + + + \ No newline at end of file