├── docker ├── .dockerignore ├── README.md ├── Dockerfile └── start.sh ├── atomhopper └── src │ ├── main │ ├── resources │ │ └── META-INF │ │ │ ├── placeholder-for-h2-db │ │ │ └── atom-server.cfg.xml │ └── webapp │ │ └── META-INF │ │ ├── PlaceholderForAtomHopperH2Files │ │ ├── PlaceholderForAtomHopperLogFiles │ │ ├── context.xml │ │ └── template-logback.xml │ └── deb │ └── control │ └── control ├── documentation ├── src │ └── resources │ │ ├── figures │ │ ├── .gitignore │ │ ├── ah.odg │ │ ├── ah-intro.png │ │ └── .~lock.ah.odg# │ │ ├── samples │ │ ├── .gitignore │ │ ├── ah-feedhead.xml │ │ ├── ah-atom-entry-simple.xml │ │ ├── ah-namespace-feed-products.xml │ │ ├── ah-log4j-properties.txt │ │ ├── ah-namespace-feed-news.xml │ │ ├── ah-multifeed-atom-server.cfg.xml │ │ ├── ah-web.xml │ │ ├── ah-application-context-db-h2.xml │ │ └── ah-application-context-db-mysql.xml │ │ └── img │ │ ├── atomhopper-logo.png │ │ └── atomhopper-sponsored-by-rackspace.png ├── .gitignore ├── .~lock.README.md# ├── README.md └── devops.json ├── test-util ├── src │ ├── test │ │ └── java │ │ │ └── org │ │ │ └── atomhopper │ │ │ └── util │ │ │ └── .gitignore │ └── main │ │ └── java │ │ └── org │ │ └── atomhopper │ │ └── util │ │ └── TestHelper.java └── pom.xml ├── adapters ├── jdbc │ └── src │ │ ├── main │ │ ├── resources │ │ │ └── ddl │ │ │ │ └── jdbc │ │ │ │ ├── add-unique-constraint.sql │ │ │ │ ├── serial-modification.sql │ │ │ │ ├── atomhopper-entries-default-timestamp.sql │ │ │ │ ├── add-eventtype-tenantid.sql │ │ │ │ ├── atomhopper-database-schema-ddl-postgres.sql │ │ │ │ └── atomhopper-fresh-schema-ddl-postgres.sql │ │ └── java │ │ │ └── org │ │ │ └── atomhopper │ │ │ └── jdbc │ │ │ ├── query │ │ │ └── SearchType.java │ │ │ ├── adapter │ │ │ └── JdbcFeedInformation.java │ │ │ └── model │ │ │ └── PersistedEntry.java │ │ └── test │ │ └── java │ │ └── org │ │ └── atomhopper │ │ └── jdbc │ │ └── adapter │ │ └── JdbcFeedInformationTest.java ├── migration │ ├── src │ │ └── main │ │ │ └── java │ │ │ └── org │ │ │ └── atomhopper │ │ │ └── migration │ │ │ ├── domain │ │ │ ├── MigrationReadFrom.java │ │ │ └── MigrationWriteTo.java │ │ │ └── adapter │ │ │ └── MigrationFeedInformation.java │ └── pom.xml ├── hibernate │ └── src │ │ ├── main │ │ ├── java │ │ │ └── org │ │ │ │ └── atomhopper │ │ │ │ ├── hibernate │ │ │ │ ├── actions │ │ │ │ │ ├── SimpleSessionAction.java │ │ │ │ │ ├── ComplexSessionAction.java │ │ │ │ │ └── PersistAction.java │ │ │ │ ├── query │ │ │ │ │ └── CategoryCriteriaGenerator.java │ │ │ │ ├── adapter │ │ │ │ │ └── HibernateFeedInformation.java │ │ │ │ └── HibernateSessionManager.java │ │ │ │ └── dbal │ │ │ │ ├── AtomDatabaseException.java │ │ │ │ └── FeedRepository.java │ │ └── resources │ │ │ ├── dbscript │ │ │ └── DBMaintenanceSQL.txt │ │ │ └── ddl │ │ │ └── postgres │ │ │ └── atomhopper-database-schema-ddl-postgres.sql │ │ └── test │ │ └── java │ │ └── org │ │ └── atomhopper │ │ └── hibernate │ │ ├── actions │ │ └── PersistActionTest.java │ │ ├── HibernateFeedRepositoryTestMain.java │ │ └── adapter │ │ └── HibernateFeedPublisherTest.java ├── mongodb │ ├── src │ │ ├── main │ │ │ └── java │ │ │ │ └── org │ │ │ │ └── atomhopper │ │ │ │ └── mongodb │ │ │ │ ├── query │ │ │ │ └── CategoryCriteriaGenerator.java │ │ │ │ ├── domain │ │ │ │ └── PersistedCategory.java │ │ │ │ └── adapter │ │ │ │ ├── MongodbFeedInformation.java │ │ │ │ └── MongodbUtilities.java │ │ └── test │ │ │ └── java │ │ │ └── org │ │ │ └── atomhopper │ │ │ └── mongodb │ │ │ └── adapter │ │ │ ├── MongodbFeedInformationTest.java │ │ │ └── MongodbUtilitiesTest.java │ └── pom.xml └── postgres-adapter │ ├── src │ ├── main │ │ ├── java │ │ │ └── org │ │ │ │ └── atomhopper │ │ │ │ └── postgres │ │ │ │ ├── query │ │ │ │ ├── EntryRowMapper.java │ │ │ │ └── EntryResultSetExtractor.java │ │ │ │ ├── adapter │ │ │ │ └── PostgresFeedInformation.java │ │ │ │ └── model │ │ │ │ └── PersistedEntry.java │ │ └── resources │ │ │ └── ddl │ │ │ └── postgres │ │ │ └── atomhopper-database-schema-ddl-postgres.sql │ └── test │ │ └── java │ │ └── org │ │ └── atomhopper │ │ └── postgres │ │ ├── query │ │ └── CategoryStringGeneratorTest.java │ │ └── adapter │ │ └── PostgresFeedInformationTest.java │ └── pom.xml ├── hopper └── src │ ├── main │ ├── java │ │ └── org │ │ │ └── atomhopper │ │ │ ├── adapter │ │ │ ├── TemplateTarget.java │ │ │ ├── AtomHopperAdapter.java │ │ │ ├── request │ │ │ │ ├── adapter │ │ │ │ │ ├── PutEntryRequest.java │ │ │ │ │ ├── DeleteEntryRequest.java │ │ │ │ │ ├── GetEntryRequest.java │ │ │ │ │ ├── PostEntryRequest.java │ │ │ │ │ ├── GetCategoriesRequest.java │ │ │ │ │ ├── impl │ │ │ │ │ │ ├── RequestParsingException.java │ │ │ │ │ │ ├── PutEntryRequestImpl.java │ │ │ │ │ │ ├── DeleteEntryRequestImpl.java │ │ │ │ │ │ ├── GetEntryRequestImpl.java │ │ │ │ │ │ ├── GetCategoriesRequestImpl.java │ │ │ │ │ │ ├── PostEntryRequestImpl.java │ │ │ │ │ │ └── GetFeedRequestImpl.java │ │ │ │ │ └── GetFeedRequest.java │ │ │ │ ├── feed │ │ │ │ │ ├── FeedRequest.java │ │ │ │ │ └── AbstractFeedRequest.java │ │ │ │ ├── entry │ │ │ │ │ ├── EntryRequest.java │ │ │ │ │ └── AbstractEntryRequest.java │ │ │ │ ├── RequestQueryParameter.java │ │ │ │ ├── ClientRequest.java │ │ │ │ └── AbstractClientRequest.java │ │ │ ├── PublicationException.java │ │ │ ├── impl │ │ │ │ ├── AbstractDisabledAdapter.java │ │ │ │ ├── DisabledFeedInformation.java │ │ │ │ ├── DisabledPublisher.java │ │ │ │ └── DisabledFeedSource.java │ │ │ ├── NotImplemented.java │ │ │ ├── FeedInformation.java │ │ │ ├── jpa │ │ │ │ ├── PersistedFeed.java │ │ │ │ └── PersistedCategory.java │ │ │ ├── FeedPublisher.java │ │ │ └── FeedSource.java │ │ │ ├── util │ │ │ ├── uri │ │ │ │ ├── template │ │ │ │ │ ├── TemplateTargetKey.java │ │ │ │ │ ├── TemplateParameters.java │ │ │ │ │ ├── URITemplate.java │ │ │ │ │ ├── URITemplateParameter.java │ │ │ │ │ └── EnumKeyedTemplateParameters.java │ │ │ │ ├── UriToUrlResolver.java │ │ │ │ ├── URISchemeMapper.java │ │ │ │ ├── ClasspathSchemeMapper.java │ │ │ │ └── CustomSchemeResolver.java │ │ │ ├── config │ │ │ │ ├── resource │ │ │ │ │ ├── ConfigurationResource.java │ │ │ │ │ ├── ConfigurationResourceException.java │ │ │ │ │ ├── file │ │ │ │ │ │ └── FileConfigurationResource.java │ │ │ │ │ └── uri │ │ │ │ │ │ └── URIConfigurationResource.java │ │ │ │ ├── ConfigurationParser.java │ │ │ │ ├── ConfigurationParserException.java │ │ │ │ └── AbstractConfigurationParser.java │ │ │ ├── context │ │ │ │ ├── AdapterNotFoundException.java │ │ │ │ ├── AdapterConstructionException.java │ │ │ │ └── AdapterGetter.java │ │ │ └── reflection │ │ │ │ └── ReflectionException.java │ │ │ ├── abdera │ │ │ ├── response │ │ │ │ ├── InternalServerException.java │ │ │ │ ├── ResponseHandler.java │ │ │ │ ├── EmptyBodyResponseHandler.java │ │ │ │ ├── EntryResponseHandler.java │ │ │ │ └── AbstractResponseHandler.java │ │ │ ├── TargetResolverField.java │ │ │ ├── filter │ │ │ │ ├── AdapterResponseInterceptor.java │ │ │ │ ├── FeedPagingProcessor.java │ │ │ │ ├── FeedConfigurationResponseProcessor.java │ │ │ │ ├── FeedEntityTagProcessor.java │ │ │ │ └── SelectiveURIJSONFilter.java │ │ │ ├── TargetAwareAbstractCollectionAdapter.java │ │ │ └── WorkspaceManager.java │ │ │ ├── config │ │ │ ├── ConfigurationException.java │ │ │ └── AtomHopperConfigurationPreprocessor.java │ │ │ ├── response │ │ │ ├── ResponseParameter.java │ │ │ ├── EmptyBody.java │ │ │ ├── AdapterResponse.java │ │ │ └── FeedSourceAdapterResponse.java │ │ │ ├── exceptions │ │ │ ├── ServletInitException.java │ │ │ └── ContextAdapterResolutionException.java │ │ │ ├── servlet │ │ │ ├── ApplicationContextAdapter.java │ │ │ ├── ServletInitParameter.java │ │ │ ├── DefaultEmptyContext.java │ │ │ └── ServletSpringContext.java │ │ │ ├── ExternalConfigLoaderContextListener.java │ │ │ ├── dbal │ │ │ └── PageDirection.java │ │ │ ├── LogBackConfigLoader.java │ │ │ └── AtomHopperVersionServlet.java │ └── resources │ │ ├── META-INF │ │ └── schema │ │ │ ├── config │ │ │ └── bindings.xjb │ │ │ └── examples │ │ │ └── config │ │ │ ├── broken-feed-server-config.xml │ │ │ └── feed-server-config.xml │ │ └── template-logback.xml │ └── test │ ├── resources │ └── org │ │ └── atomhopper │ │ └── config │ │ └── WorkspaceConfigProcessorTest │ │ ├── noArchiveWithArchive.xml │ │ ├── archiveWithNoCurrent.xml │ │ ├── noArchiveWithCurrent.xml │ │ ├── archiveWithCurrent.xml │ │ └── archiveWithArchive.xml │ └── java │ └── org │ └── atomhopper │ ├── LogBackConfigLoaderTest.java │ ├── util │ ├── config │ │ ├── resource │ │ │ ├── file │ │ │ │ └── FileConfigurationResourceTest.java │ │ │ └── uri │ │ │ │ └── URIConfigurationResourceTest.java │ │ └── AbstractConfigurationParserTest.java │ ├── uri │ │ └── ClasspathSchemeMapperTest.java │ ├── TargetRegexBuilderFeedTest.java │ ├── TargetRegexBuilderTestParent.java │ ├── TargetRegexBuilderWorkspaceTest.java │ └── TargetRegexBuilderTest.java │ ├── ExternalConfigLoaderContextListenerTest.java │ ├── adapter │ ├── impl │ │ ├── DisabledFeedInformationTest.java │ │ └── DisabledFeedSourceTest.java │ └── request │ │ └── impl │ │ └── PostEntryRequestImplTest.java │ └── config │ └── SchemaTest.java ├── Jenkinsfile ├── .gitignore ├── tests ├── jetty-killer │ └── src │ │ └── main │ │ ├── java │ │ └── org │ │ │ └── atomhopper │ │ │ └── jettykiller │ │ │ ├── CommandLineArguments.java │ │ │ ├── AtomHopperServerControl.java │ │ │ └── JettyKiller.java │ │ └── resources │ │ └── log4j.properties ├── regression │ └── src │ │ └── test │ │ └── jmeter │ │ └── user.properties └── customjdbc-regression │ └── src │ └── test │ └── jmeter │ ├── postgres-adaptor-override-on-application-context.xml │ └── postgres-adaptor-override-off-application-context.xml ├── server └── src │ └── main │ ├── resources │ ├── META-INF │ │ └── atom-server.cfg.xml │ └── logback.xml │ └── java │ └── org │ └── atomhopper │ └── server │ ├── CommandLineArguments.java │ ├── MonitorThread.java │ └── AtomHopperServer.java └── test-suite └── src ├── main ├── java │ └── org │ │ └── atomhopper │ │ ├── adapter │ │ └── impl │ │ │ └── AtomEntry.java │ │ └── jetty │ │ └── AtomHopperJettyServerBuilder.java └── webapp │ └── WEB-INF │ └── web.xml └── test └── java └── org └── atomhopper ├── JettyIntegrationTestHarness.java ├── GetVersionPathTest.java └── abdera └── filter └── SelectiveURIJSONFilterTest.java /docker/.dockerignore: -------------------------------------------------------------------------------- 1 | README.md -------------------------------------------------------------------------------- /atomhopper/src/main/resources/META-INF/placeholder-for-h2-db: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /atomhopper/src/main/webapp/META-INF/PlaceholderForAtomHopperH2Files: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /atomhopper/src/main/webapp/META-INF/PlaceholderForAtomHopperLogFiles: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /documentation/src/resources/figures/.gitignore: -------------------------------------------------------------------------------- 1 | !.gitignore 2 | 3 | -------------------------------------------------------------------------------- /documentation/src/resources/samples/.gitignore: -------------------------------------------------------------------------------- 1 | !.gitignore 2 | 3 | -------------------------------------------------------------------------------- /test-util/src/test/java/org/atomhopper/util/.gitignore: -------------------------------------------------------------------------------- 1 | !.gitignore 2 | -------------------------------------------------------------------------------- /documentation/.gitignore: -------------------------------------------------------------------------------- 1 | !.gitignore 2 | target 3 | */target 4 | */*/target 5 | -------------------------------------------------------------------------------- /adapters/jdbc/src/main/resources/ddl/jdbc/add-unique-constraint.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE entries ADD CONSTRAINT entryid_unique UNIQUE(entryid); -------------------------------------------------------------------------------- /atomhopper/src/main/webapp/META-INF/context.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /documentation/src/resources/figures/ah.odg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rackerlabs/atom-hopper/HEAD/documentation/src/resources/figures/ah.odg -------------------------------------------------------------------------------- /documentation/.~lock.README.md#: -------------------------------------------------------------------------------- 1 | Rose Coste,rose.coste,M17UAGY,02.04.2012 15:31,file:///Users/rose.coste/Library/Application%20Support/OpenOffice.org/3; -------------------------------------------------------------------------------- /documentation/src/resources/figures/ah-intro.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rackerlabs/atom-hopper/HEAD/documentation/src/resources/figures/ah-intro.png -------------------------------------------------------------------------------- /documentation/src/resources/img/atomhopper-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rackerlabs/atom-hopper/HEAD/documentation/src/resources/img/atomhopper-logo.png -------------------------------------------------------------------------------- /documentation/src/resources/figures/.~lock.ah.odg#: -------------------------------------------------------------------------------- 1 | Rose Coste,rose.coste,M17UAGY,22.03.2012 00:31,file:///Users/rose.coste/Library/Application%20Support/OpenOffice.org/3; -------------------------------------------------------------------------------- /hopper/src/main/java/org/atomhopper/adapter/TemplateTarget.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.adapter; 2 | 3 | public enum TemplateTarget { 4 | WORKSPACE, 5 | FEED, 6 | ENTRY 7 | } 8 | -------------------------------------------------------------------------------- /documentation/src/resources/img/atomhopper-sponsored-by-rackspace.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rackerlabs/atom-hopper/HEAD/documentation/src/resources/img/atomhopper-sponsored-by-rackspace.png -------------------------------------------------------------------------------- /hopper/src/main/java/org/atomhopper/util/uri/template/TemplateTargetKey.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.util.uri.template; 2 | 3 | public enum TemplateTargetKey { 4 | WORKSPACE, 5 | FEED 6 | } 7 | -------------------------------------------------------------------------------- /adapters/migration/src/main/java/org/atomhopper/migration/domain/MigrationReadFrom.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.migration.domain; 2 | 3 | public enum MigrationReadFrom { 4 | NEW, 5 | OLD 6 | } 7 | -------------------------------------------------------------------------------- /adapters/migration/src/main/java/org/atomhopper/migration/domain/MigrationWriteTo.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.migration.domain; 2 | 3 | public enum MigrationWriteTo { 4 | NEW, 5 | OLD, 6 | BOTH 7 | } 8 | -------------------------------------------------------------------------------- /hopper/src/main/java/org/atomhopper/abdera/response/InternalServerException.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.abdera.response; 2 | 3 | /** 4 | * 5 | * 6 | */ 7 | public class InternalServerException extends Exception { 8 | 9 | } 10 | -------------------------------------------------------------------------------- /hopper/src/main/java/org/atomhopper/adapter/AtomHopperAdapter.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.adapter; 2 | 3 | import java.util.Map; 4 | 5 | public interface AtomHopperAdapter { 6 | 7 | void setParameters(Map params); 8 | } 9 | -------------------------------------------------------------------------------- /adapters/jdbc/src/main/resources/ddl/jdbc/serial-modification.sql: -------------------------------------------------------------------------------- 1 | BEGIN; 2 | 3 | ALTER TABLE entries ADD id bigserial; 4 | ALTER TABLE entries DROP CONSTRAINT entries_pkey; 5 | ALTER TABLE entries ADD PRIMARY KEY (datelastupdated, id); 6 | 7 | COMMIT; 8 | -------------------------------------------------------------------------------- /atomhopper/src/deb/control/control: -------------------------------------------------------------------------------- 1 | Package: [[name]] 2 | Version: [[version]] 3 | Section: misc 4 | Priority: optional 5 | Architecture: all 6 | Depends: sun-java6-jre, tomcat6 7 | Maintainer: Atom Hopper Team 8 | Description: Atom Hopper 9 | Distribution: development -------------------------------------------------------------------------------- /hopper/src/main/java/org/atomhopper/adapter/request/adapter/PutEntryRequest.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.adapter.request.adapter; 2 | 3 | import org.atomhopper.adapter.request.entry.EntryRequest; 4 | 5 | public interface PutEntryRequest extends EntryRequest { 6 | } 7 | -------------------------------------------------------------------------------- /adapters/hibernate/src/main/java/org/atomhopper/hibernate/actions/SimpleSessionAction.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.hibernate.actions; 2 | 3 | import org.hibernate.Session; 4 | 5 | public interface SimpleSessionAction { 6 | 7 | void perform(Session liveSession); 8 | } 9 | -------------------------------------------------------------------------------- /hopper/src/main/java/org/atomhopper/adapter/request/adapter/DeleteEntryRequest.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.adapter.request.adapter; 2 | 3 | import org.atomhopper.adapter.request.entry.EntryRequest; 4 | 5 | public interface DeleteEntryRequest extends EntryRequest { 6 | } 7 | -------------------------------------------------------------------------------- /adapters/hibernate/src/main/java/org/atomhopper/hibernate/actions/ComplexSessionAction.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.hibernate.actions; 2 | 3 | import org.hibernate.Session; 4 | 5 | public interface ComplexSessionAction { 6 | 7 | T perform(Session liveSession); 8 | } 9 | -------------------------------------------------------------------------------- /adapters/jdbc/src/main/resources/ddl/jdbc/atomhopper-entries-default-timestamp.sql: -------------------------------------------------------------------------------- 1 | BEGIN; 2 | 3 | ALTER TABLE entries ALTER COLUMN creationdate SET DEFAULT current_timestamp; 4 | ALTER TABLE entries ALTER COLUMN datelastupdated SET DEFAULT current_timestamp; 5 | 6 | COMMIT; 7 | -------------------------------------------------------------------------------- /hopper/src/main/java/org/atomhopper/adapter/request/feed/FeedRequest.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.adapter.request.feed; 2 | 3 | import org.atomhopper.adapter.request.ClientRequest; 4 | 5 | public interface FeedRequest extends ClientRequest { 6 | String getFeedName(); 7 | } 8 | -------------------------------------------------------------------------------- /hopper/src/main/java/org/atomhopper/adapter/request/entry/EntryRequest.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.adapter.request.entry; 2 | 3 | import org.atomhopper.adapter.request.feed.FeedRequest; 4 | 5 | public interface EntryRequest extends FeedRequest { 6 | 7 | String getEntryId(); 8 | } 9 | -------------------------------------------------------------------------------- /adapters/hibernate/src/main/java/org/atomhopper/hibernate/query/CategoryCriteriaGenerator.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.hibernate.query; 2 | 3 | import org.hibernate.Criteria; 4 | 5 | public interface CategoryCriteriaGenerator { 6 | 7 | void enhanceCriteria(Criteria ongoingCriteria); 8 | } 9 | -------------------------------------------------------------------------------- /hopper/src/main/java/org/atomhopper/abdera/TargetResolverField.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.abdera; 2 | 3 | public enum TargetResolverField { 4 | ENTRY, 5 | PARAMETERS, 6 | FEED, 7 | USER, 8 | WORKSPACE, 9 | CONTEXT_PATH, 10 | FEED_TYPE, 11 | MARKER 12 | } 13 | -------------------------------------------------------------------------------- /adapters/hibernate/src/main/java/org/atomhopper/dbal/AtomDatabaseException.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.dbal; 2 | 3 | public class AtomDatabaseException extends RuntimeException { 4 | 5 | public AtomDatabaseException(String message, Throwable cause) { 6 | super(message, cause); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /adapters/jdbc/src/main/resources/ddl/jdbc/add-eventtype-tenantid.sql: -------------------------------------------------------------------------------- 1 | BEGIN; 2 | 3 | ALTER TABLE entries ADD COLUMN eventtype text; 4 | ALTER TABLE entries ADD COLUMN tenantid text; 5 | 6 | CREATE INDEX eventtype_idx on entries( eventtype ); 7 | CREATE INDEX tenantid_idx on entries( tenantid ); 8 | 9 | COMMIT; -------------------------------------------------------------------------------- /adapters/mongodb/src/main/java/org/atomhopper/mongodb/query/CategoryCriteriaGenerator.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.mongodb.query; 2 | 3 | import org.springframework.data.mongodb.core.query.Query; 4 | 5 | public interface CategoryCriteriaGenerator { 6 | 7 | void enhanceCriteria(Query ongoingQuery); 8 | } 9 | -------------------------------------------------------------------------------- /adapters/jdbc/src/main/java/org/atomhopper/jdbc/query/SearchType.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.jdbc.query; 2 | 3 | public enum SearchType { 4 | FEED_FORWARD, 5 | FEED_BACKWARD, 6 | FEED_HEAD, 7 | LAST_PAGE, 8 | NEXT_LINK, 9 | BY_TIMESTAMP_FORWARD, 10 | BY_TIMESTAMP_BACKWARD 11 | } 12 | -------------------------------------------------------------------------------- /hopper/src/main/java/org/atomhopper/util/config/resource/ConfigurationResource.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.util.config.resource; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | 6 | public interface ConfigurationResource { 7 | 8 | InputStream getInputStream() throws IOException; 9 | } 10 | -------------------------------------------------------------------------------- /hopper/src/main/java/org/atomhopper/util/uri/UriToUrlResolver.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.util.uri; 2 | 3 | import java.net.MalformedURLException; 4 | import java.net.URI; 5 | import java.net.URL; 6 | 7 | public interface UriToUrlResolver { 8 | 9 | URL toURL(URI uri) throws MalformedURLException; 10 | 11 | } 12 | -------------------------------------------------------------------------------- /Jenkinsfile: -------------------------------------------------------------------------------- 1 | pipeline { 2 | agent any 3 | 4 | triggers { 5 | githubPush() 6 | } 7 | tools { 8 | maven 'MavenTest' 9 | } 10 | stages { 11 | stage ('Test') { 12 | steps { 13 | sh 'mvn initialize test' 14 | } 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /hopper/src/main/java/org/atomhopper/adapter/request/adapter/GetEntryRequest.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.adapter.request.adapter; 2 | 3 | import org.apache.abdera.model.Entry; 4 | import org.atomhopper.adapter.request.entry.EntryRequest; 5 | 6 | public interface GetEntryRequest extends EntryRequest { 7 | Entry newEntry(); 8 | } 9 | -------------------------------------------------------------------------------- /hopper/src/main/java/org/atomhopper/util/context/AdapterNotFoundException.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.util.context; 2 | 3 | /** 4 | * 5 | * 6 | */ 7 | public class AdapterNotFoundException extends RuntimeException { 8 | 9 | public AdapterNotFoundException(String string) { 10 | super(string); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /hopper/src/main/java/org/atomhopper/adapter/request/adapter/PostEntryRequest.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.adapter.request.adapter; 2 | 3 | import org.apache.abdera.model.Entry; 4 | import org.atomhopper.adapter.request.feed.FeedRequest; 5 | 6 | public interface PostEntryRequest extends FeedRequest { 7 | 8 | Entry getEntry(); 9 | } 10 | -------------------------------------------------------------------------------- /hopper/src/main/java/org/atomhopper/config/ConfigurationException.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.config; 2 | 3 | /** 4 | * 5 | * 6 | */ 7 | public class ConfigurationException extends RuntimeException { 8 | 9 | public ConfigurationException(String message, Throwable cause) { 10 | super(message, cause); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /hopper/src/main/java/org/atomhopper/response/ResponseParameter.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.response; 2 | 3 | /** 4 | * 5 | * 6 | */ 7 | public enum ResponseParameter { 8 | PREVIOUS_MARKER, 9 | NEXT_MARKER; 10 | 11 | @Override 12 | public String toString() { 13 | return super.toString().toLowerCase(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /hopper/src/main/java/org/atomhopper/util/reflection/ReflectionException.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.util.reflection; 2 | 3 | /** 4 | * 5 | * 6 | */ 7 | public class ReflectionException extends RuntimeException { 8 | 9 | public ReflectionException(String message, Throwable cause) { 10 | super(message, cause); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /hopper/src/main/java/org/atomhopper/adapter/request/adapter/GetCategoriesRequest.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.adapter.request.adapter; 2 | 3 | import org.apache.abdera.model.Categories; 4 | import org.atomhopper.adapter.request.ClientRequest; 5 | 6 | public interface GetCategoriesRequest extends ClientRequest { 7 | 8 | Categories newCategories(); 9 | } 10 | -------------------------------------------------------------------------------- /hopper/src/main/java/org/atomhopper/util/context/AdapterConstructionException.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.util.context; 2 | 3 | /** 4 | * 5 | * 6 | */ 7 | public class AdapterConstructionException extends RuntimeException { 8 | 9 | public AdapterConstructionException(String string, Throwable thrwbl) { 10 | super(string, thrwbl); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /hopper/src/main/java/org/atomhopper/util/uri/URISchemeMapper.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.util.uri; 2 | 3 | import java.net.MalformedURLException; 4 | import java.net.URI; 5 | import java.net.URL; 6 | 7 | public interface URISchemeMapper { 8 | 9 | boolean canMap(URI uriToMatch); 10 | 11 | URL toURL(URI uriToMap) throws MalformedURLException; 12 | 13 | } 14 | -------------------------------------------------------------------------------- /hopper/src/main/java/org/atomhopper/util/uri/template/TemplateParameters.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.util.uri.template; 2 | 3 | import java.util.Map; 4 | 5 | public interface TemplateParameters { 6 | 7 | void set(URITemplateParameter parameter, Object value); 8 | 9 | Map toMap(); 10 | 11 | T getTargetTemplateKey(); 12 | } 13 | -------------------------------------------------------------------------------- /hopper/src/main/java/org/atomhopper/util/config/resource/ConfigurationResourceException.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.util.config.resource; 2 | 3 | /** 4 | * 5 | 6 | */ 7 | public class ConfigurationResourceException extends RuntimeException { 8 | 9 | public ConfigurationResourceException(String string, Throwable thrwbl) { 10 | super(string, thrwbl); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /hopper/src/main/java/org/atomhopper/adapter/PublicationException.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.adapter; 2 | 3 | public class PublicationException extends RuntimeException { 4 | 5 | public PublicationException(String message) { 6 | super(message); 7 | } 8 | 9 | public PublicationException(String message, Throwable cause) { 10 | super(message, cause); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /hopper/src/main/java/org/atomhopper/adapter/impl/AbstractDisabledAdapter.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.adapter.impl; 2 | 3 | import org.atomhopper.adapter.AtomHopperAdapter; 4 | 5 | import java.util.Map; 6 | 7 | public abstract class AbstractDisabledAdapter implements AtomHopperAdapter { 8 | 9 | @Override 10 | public void setParameters(Map params) { 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /documentation/src/resources/samples/ah-feedhead.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /hopper/src/main/java/org/atomhopper/abdera/filter/AdapterResponseInterceptor.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.abdera.filter; 2 | 3 | import org.apache.abdera.protocol.server.RequestContext; 4 | import org.atomhopper.response.AdapterResponse; 5 | 6 | /** 7 | * 8 | * 9 | */ 10 | public interface AdapterResponseInterceptor { 11 | 12 | void process(RequestContext rc, AdapterResponse adapterResponse); 13 | } 14 | -------------------------------------------------------------------------------- /adapters/hibernate/src/main/resources/dbscript/DBMaintenanceSQL.txt: -------------------------------------------------------------------------------- 1 | # With the ON DELETE CASCADE constriant on the referenced columns this query is no longer needed. 2 | # 3 | #DELETE FROM categoryentryreferences 4 | #WHERE categoryentryreferences.entryid 5 | #IN (SELECT entries.entryid FROM entries WHERE datelastupdated < (now() - '8 day'::interval)); 6 | 7 | DELETE FROM entries WHERE datelastupdated < (now() - '8 day'::interval); 8 | -------------------------------------------------------------------------------- /documentation/README.md: -------------------------------------------------------------------------------- 1 | # This README relates only to documentation for Atom Hopper # 2 | 3 | Within /documentation/, the source XML for books published at http://atomhopper.org/ is in /src/resources/. 4 | 5 | Within /documentation/src/resources/, each file is the source for one book. Elements included within books, such as figures, code samples, and sections used in multiple books, are in subdirectories such as /figures/, /samples/ and /chapters/. 6 | -------------------------------------------------------------------------------- /hopper/src/main/java/org/atomhopper/adapter/request/adapter/impl/RequestParsingException.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.adapter.request.adapter.impl; 2 | 3 | public class RequestParsingException extends RuntimeException { 4 | 5 | public RequestParsingException(String string, Throwable thrwbl) { 6 | super(string, thrwbl); 7 | } 8 | 9 | public RequestParsingException(String string) { 10 | super(string); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /hopper/src/main/java/org/atomhopper/exceptions/ServletInitException.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.exceptions; 2 | 3 | import javax.servlet.ServletException; 4 | 5 | public class ServletInitException extends ServletException { 6 | 7 | public ServletInitException(String message) { 8 | super(message); 9 | } 10 | 11 | public ServletInitException(String message, Throwable cause) { 12 | super(message, cause); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /hopper/src/main/java/org/atomhopper/util/config/ConfigurationParser.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.util.config; 2 | 3 | import org.atomhopper.util.config.resource.ConfigurationResource; 4 | 5 | public interface ConfigurationParser { 6 | 7 | T read(); 8 | 9 | Class getConfigurationClass(); 10 | 11 | void setConfigurationResource(ConfigurationResource resource); 12 | 13 | ConfigurationResource getConfigurationResource(); 14 | } 15 | -------------------------------------------------------------------------------- /hopper/src/main/java/org/atomhopper/exceptions/ContextAdapterResolutionException.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.exceptions; 2 | 3 | public class ContextAdapterResolutionException extends RuntimeException { 4 | 5 | public ContextAdapterResolutionException(String message, Throwable cause) { 6 | super(message, cause); 7 | } 8 | 9 | public ContextAdapterResolutionException(String message) { 10 | super(message); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /hopper/src/main/java/org/atomhopper/servlet/ApplicationContextAdapter.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.servlet; 2 | 3 | import javax.servlet.ServletContext; 4 | 5 | 6 | /** 7 | * 8 | * @author jhopper 9 | */ 10 | public interface ApplicationContextAdapter { 11 | 12 | void usingServletContext(ServletContext context); 13 | 14 | T fromContext(Class classToCastTo); 15 | 16 | T fromContext(String refName, Class classToCastTo); 17 | } -------------------------------------------------------------------------------- /documentation/src/resources/samples/ah-atom-entry-simple.xml: -------------------------------------------------------------------------------- 1 | 2 | This is a greeting from John Doe 3 | 2011-09-22T21:39:49.904Z 4 | 5 | John Doe 6 | 7 | Hello World 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /adapters/mongodb/src/main/java/org/atomhopper/mongodb/domain/PersistedCategory.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.mongodb.domain; 2 | 3 | 4 | public class PersistedCategory { 5 | 6 | private String term; 7 | 8 | public PersistedCategory(String term) { 9 | this.term = term; 10 | } 11 | 12 | public String getValue() { 13 | return this.term; 14 | } 15 | 16 | public void setValue(String term) { 17 | this.term = term; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /hopper/src/main/java/org/atomhopper/adapter/NotImplemented.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.adapter; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | * This annotation may or may not be used now that interfaces are split along 10 | * domain lines 11 | */ 12 | @Retention(RetentionPolicy.RUNTIME) 13 | @Target(ElementType.METHOD) 14 | public @interface NotImplemented { 15 | } 16 | -------------------------------------------------------------------------------- /adapters/hibernate/src/main/java/org/atomhopper/hibernate/actions/PersistAction.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.hibernate.actions; 2 | 3 | import org.hibernate.Session; 4 | 5 | public class PersistAction implements SimpleSessionAction { 6 | 7 | private final Object persistMe; 8 | 9 | public PersistAction(Object persistMe) { 10 | this.persistMe = persistMe; 11 | } 12 | 13 | @Override 14 | public void perform(Session liveSession) { 15 | liveSession.persist(persistMe); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /hopper/src/main/resources/META-INF/schema/config/bindings.xjb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /adapters/postgres-adapter/src/main/java/org/atomhopper/postgres/query/EntryRowMapper.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.postgres.query; 2 | 3 | import java.sql.ResultSet; 4 | import java.sql.SQLException; 5 | 6 | import org.springframework.jdbc.core.RowMapper; 7 | 8 | public class EntryRowMapper implements RowMapper { 9 | @Override 10 | public Object mapRow(ResultSet rs, int line) throws SQLException { 11 | EntryResultSetExtractor extractor = new EntryResultSetExtractor(); 12 | return extractor.extractData(rs); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /hopper/src/main/java/org/atomhopper/abdera/TargetAwareAbstractCollectionAdapter.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.abdera; 2 | 3 | import org.apache.abdera.protocol.server.impl.AbstractCollectionAdapter; 4 | 5 | public abstract class TargetAwareAbstractCollectionAdapter extends AbstractCollectionAdapter { 6 | 7 | private final String target; 8 | 9 | public TargetAwareAbstractCollectionAdapter(String collectionSpec) { 10 | this.target = collectionSpec; 11 | } 12 | 13 | public String getTarget() { 14 | return target; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /hopper/src/main/java/org/atomhopper/adapter/request/adapter/impl/PutEntryRequestImpl.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.adapter.request.adapter.impl; 2 | 3 | import org.apache.abdera.protocol.server.RequestContext; 4 | import org.atomhopper.adapter.request.adapter.PutEntryRequest; 5 | import org.atomhopper.adapter.request.entry.AbstractEntryRequest; 6 | 7 | public class PutEntryRequestImpl extends AbstractEntryRequest implements PutEntryRequest { 8 | 9 | public PutEntryRequestImpl(RequestContext abderaRequestContext) { 10 | super(abderaRequestContext); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## intellij stuff 2 | .idea/ 3 | 4 | ## java specific 5 | !.gitignore 6 | *.iml 7 | *.iws 8 | *.ipr 9 | target 10 | */target 11 | */*/target 12 |   13 | ## generic files to ignore 14 | *~ 15 | *.lock 16 | *.DS_Store 17 | *.swp 18 | *.out 19 | /test-suite/nb-configuration.xml 20 | /hopper/nb-configuration.xml 21 | /server/nb-configuration.xml 22 | /server/nbactions.xml 23 | /test-suite/nbactions.xml 24 | /hopper/nbactions.xml 25 | /server/nbactions-pack-server-jar.xml 26 | /ah-war/catalog.xml 27 | /server/catalog.xml 28 | /test-suite/catalog.xml 29 | /ah-war/nbactions.xml 30 | -------------------------------------------------------------------------------- /hopper/src/main/java/org/atomhopper/servlet/ServletInitParameter.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.servlet; 2 | 3 | /** 4 | * Enumeration for any initialization parameters that the servlet may expect. 5 | */ 6 | public enum ServletInitParameter { 7 | 8 | CONTEXT_ADAPTER_CLASS("context-adapter-class"), 9 | CONFIGURATION_LOCATION("config-location"); 10 | 11 | private final String value; 12 | 13 | private ServletInitParameter(String value) { 14 | this.value = value; 15 | } 16 | 17 | @Override 18 | public String toString() { 19 | return value; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /hopper/src/main/java/org/atomhopper/adapter/request/adapter/impl/DeleteEntryRequestImpl.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.adapter.request.adapter.impl; 2 | 3 | import org.apache.abdera.protocol.server.RequestContext; 4 | import org.atomhopper.adapter.request.adapter.DeleteEntryRequest; 5 | import org.atomhopper.adapter.request.entry.AbstractEntryRequest; 6 | 7 | /** 8 | * 9 | * 10 | */ 11 | public class DeleteEntryRequestImpl extends AbstractEntryRequest implements DeleteEntryRequest { 12 | 13 | public DeleteEntryRequestImpl(RequestContext abderaRequestContext) { 14 | super(abderaRequestContext); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /tests/jetty-killer/src/main/java/org/atomhopper/jettykiller/CommandLineArguments.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.jettykiller; 2 | 3 | 4 | 5 | import org.kohsuke.args4j.Option; 6 | 7 | public class CommandLineArguments { 8 | 9 | private static final String DEFAULT_PORT_INFO = "(Default is port 8818, range is 1024 to 49150)"; 10 | private static final int STOPPORT = 8818; 11 | 12 | @Option(name = "-s", aliases = {"--shutdown-port", "-p", "--port"}, 13 | usage = "The port used to communicate a shutdown to Atom Hopper " + DEFAULT_PORT_INFO) 14 | public final Integer stopport = STOPPORT; 15 | 16 | } 17 | -------------------------------------------------------------------------------- /hopper/src/main/java/org/atomhopper/adapter/request/adapter/GetFeedRequest.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.adapter.request.adapter; 2 | 3 | import org.apache.abdera.model.Feed; 4 | import org.atomhopper.adapter.request.feed.FeedRequest; 5 | 6 | import java.util.List; 7 | 8 | /** 9 | * 10 | * 11 | */ 12 | public interface GetFeedRequest extends FeedRequest { 13 | 14 | Feed newFeed(); 15 | 16 | List getCategories(); 17 | 18 | String getSearchQuery(); 19 | 20 | String getPageMarker(); 21 | 22 | String getPageSize(); 23 | 24 | String getDirection(); 25 | 26 | String getStartingAt(); 27 | } 28 | -------------------------------------------------------------------------------- /hopper/src/main/java/org/atomhopper/abdera/response/ResponseHandler.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.abdera.response; 2 | 3 | import org.apache.abdera.protocol.server.RequestContext; 4 | import org.apache.abdera.protocol.server.ResponseContext; 5 | import org.atomhopper.abdera.filter.AdapterResponseInterceptor; 6 | import org.atomhopper.response.AdapterResponse; 7 | 8 | public interface ResponseHandler { 9 | 10 | void addResponseInterceptor(AdapterResponseInterceptor adapterResponseInterceptor); 11 | 12 | void clearResponseInterceptors(); 13 | 14 | ResponseContext handleResponse(RequestContext rc, AdapterResponse adapterResponse); 15 | } 16 | -------------------------------------------------------------------------------- /hopper/src/main/java/org/atomhopper/adapter/request/RequestQueryParameter.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.adapter.request; 2 | 3 | public enum RequestQueryParameter { 4 | 5 | CATEGORIES("categories"), 6 | SEARCH("search"), 7 | PAGE_DIRECTION("direction"), 8 | MARKER("marker"), 9 | PAGE_LIMIT("limit"), 10 | STARTING_AT("startingAt"); 11 | 12 | //Class contents 13 | private final String stringValue; 14 | 15 | private RequestQueryParameter(String stringValue) { 16 | this.stringValue = stringValue; 17 | } 18 | 19 | @Override 20 | public String toString() { 21 | return stringValue; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /hopper/src/main/java/org/atomhopper/util/uri/ClasspathSchemeMapper.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.util.uri; 2 | 3 | import org.apache.commons.lang.StringUtils; 4 | 5 | import java.net.URI; 6 | import java.net.URL; 7 | 8 | public class ClasspathSchemeMapper implements URISchemeMapper { 9 | 10 | @Override 11 | public boolean canMap(URI uriToMatch) { 12 | final String uriScheme = uriToMatch.getScheme(); 13 | 14 | return !StringUtils.isBlank(uriScheme) && uriScheme.startsWith("classpath"); 15 | } 16 | 17 | @Override 18 | public URL toURL(URI uri) { 19 | return getClass().getResource(uri.getPath()); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /hopper/src/main/java/org/atomhopper/util/uri/template/URITemplate.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.util.uri.template; 2 | 3 | public enum URITemplate { 4 | 5 | WORKSPACE("{scheme=}://{host=}{-prefix|:|port}{target_base}/{workspace}"), 6 | FEED(WORKSPACE.toString() + "/{feed}{-prefix|/entries/|entry}/{-opt|?|lochint,limit}{-join|&|lochint,limit}"); 7 | 8 | //Class Contents 9 | private final String templateString; 10 | 11 | private URITemplate(String templateString) { 12 | this.templateString = templateString; 13 | } 14 | 15 | @Override 16 | public String toString() { 17 | return templateString; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /tests/regression/src/test/jmeter/user.properties: -------------------------------------------------------------------------------- 1 | #--------------------------------------------------------------------------- 2 | # AtomHopper Tests Configuration 3 | #--------------------------------------------------------------------------- 4 | 5 | host=10.14.209.232 6 | port=8080 7 | protocol=http 8 | 9 | feed=namespace/feed 10 | paging.feed.archive=namespace-p/feed/archive 11 | paging.feed=namespace-p/feed 12 | 13 | buildinfo.version=1.1.8 14 | buildinfo.groupid=org.atomhopper 15 | buildinfo.artifactid=atomhopper 16 | 17 | crossfeed.feed1=namespace1/feedA 18 | crossfeed.feed2=namespace1/feedB 19 | crossfeed.feed3=namespace2/feedA 20 | crossfeed.feed4=namespace2/feedB 21 | 22 | -------------------------------------------------------------------------------- /hopper/src/main/java/org/atomhopper/response/EmptyBody.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.response; 2 | 3 | public final class EmptyBody { 4 | 5 | private static final EmptyBody INSTANCE = new EmptyBody(); 6 | 7 | public static EmptyBody getInstance() { 8 | return INSTANCE; 9 | } 10 | 11 | private EmptyBody() { 12 | } 13 | 14 | @Override 15 | public int hashCode() { 16 | return INSTANCE.hashCode(); 17 | } 18 | 19 | @Override 20 | public boolean equals(Object obj) { 21 | if (obj != null) { 22 | return obj.hashCode() == hashCode(); 23 | } else { 24 | return false; 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /hopper/src/main/java/org/atomhopper/adapter/request/adapter/impl/GetEntryRequestImpl.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.adapter.request.adapter.impl; 2 | 3 | import org.apache.abdera.model.Entry; 4 | import org.apache.abdera.protocol.server.RequestContext; 5 | import org.atomhopper.adapter.request.adapter.GetEntryRequest; 6 | import org.atomhopper.adapter.request.entry.AbstractEntryRequest; 7 | 8 | public class GetEntryRequestImpl extends AbstractEntryRequest implements GetEntryRequest { 9 | 10 | public GetEntryRequestImpl(RequestContext abderaRequestContext) { 11 | super(abderaRequestContext); 12 | } 13 | 14 | @Override 15 | public Entry newEntry() { 16 | return getAbdera().newEntry(); 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /hopper/src/main/java/org/atomhopper/adapter/request/ClientRequest.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.adapter.request; 2 | 3 | import org.apache.abdera.Abdera; 4 | import org.atomhopper.util.uri.template.TemplateParameters; 5 | 6 | import java.util.List; 7 | 8 | /** 9 | * A client request contains only the bare minimum needed to express the request 10 | * contents; in this case by wrapping an Abdera RequestContext. 11 | */ 12 | public interface ClientRequest { 13 | 14 | String getTargetParameter(String parameter); 15 | 16 | String getRequestParameter(String parameter); 17 | 18 | List getRequestParameters(String parameter); 19 | 20 | String urlFor(TemplateParameters param); 21 | 22 | Abdera getAbdera(); 23 | } 24 | -------------------------------------------------------------------------------- /hopper/src/main/java/org/atomhopper/util/uri/template/URITemplateParameter.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.util.uri.template; 2 | 3 | public enum URITemplateParameter { 4 | 5 | HOST_SCHEME("scheme"), 6 | HOST_DOMAIN("host"), 7 | HOST_PORT("port"), 8 | WORKSPACE_RESOURCE("workspace"), 9 | FEED_RESOURCE("feed"), 10 | ENTRY_RESOURCE("entry"), 11 | MARKER("lochint"), 12 | PAGE_LIMIT("limit"); 13 | 14 | //Class contents 15 | private final String stringRepresentation; 16 | 17 | private URITemplateParameter(String stringRepresentation) { 18 | this.stringRepresentation = stringRepresentation; 19 | } 20 | 21 | @Override 22 | public String toString() { 23 | return stringRepresentation; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /hopper/src/main/java/org/atomhopper/adapter/request/adapter/impl/GetCategoriesRequestImpl.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.adapter.request.adapter.impl; 2 | 3 | import org.apache.abdera.model.Categories; 4 | import org.apache.abdera.protocol.server.RequestContext; 5 | import org.atomhopper.adapter.request.AbstractClientRequest; 6 | import org.atomhopper.adapter.request.adapter.GetCategoriesRequest; 7 | 8 | public class GetCategoriesRequestImpl extends AbstractClientRequest implements GetCategoriesRequest { 9 | 10 | public GetCategoriesRequestImpl(RequestContext abderaRequestContext) { 11 | super(abderaRequestContext); 12 | } 13 | 14 | @Override 15 | public Categories newCategories() { 16 | return getAbdera().newCategories(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /hopper/src/main/java/org/atomhopper/adapter/request/feed/AbstractFeedRequest.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.adapter.request.feed; 2 | 3 | import org.apache.abdera.protocol.server.RequestContext; 4 | import org.atomhopper.abdera.TargetResolverField; 5 | import org.atomhopper.adapter.request.AbstractClientRequest; 6 | 7 | public abstract class AbstractFeedRequest extends AbstractClientRequest implements FeedRequest { 8 | 9 | protected AbstractFeedRequest(RequestContext abderaRequestContext) { 10 | super(abderaRequestContext); 11 | } 12 | 13 | @Override 14 | public String getFeedName() { 15 | return getTargetParameter(TargetResolverField.WORKSPACE.name()) + "/" + 16 | getTargetParameter(TargetResolverField.FEED.name()); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /hopper/src/main/java/org/atomhopper/util/config/ConfigurationParserException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2010 Rackspace. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | */ 10 | package org.atomhopper.util.config; 11 | 12 | /** 13 | * 14 | * 15 | */ 16 | public class ConfigurationParserException extends RuntimeException { 17 | 18 | public ConfigurationParserException(String message, Throwable cause) { 19 | super(message, cause); 20 | } 21 | 22 | public ConfigurationParserException(String message) { 23 | super(message); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /hopper/src/main/java/org/atomhopper/util/config/resource/file/FileConfigurationResource.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.util.config.resource.file; 2 | 3 | import org.atomhopper.util.config.resource.ConfigurationResource; 4 | 5 | import java.io.File; 6 | import java.io.FileInputStream; 7 | import java.io.IOException; 8 | import java.io.InputStream; 9 | 10 | public class FileConfigurationResource implements ConfigurationResource { 11 | 12 | private final File configurationFile; 13 | 14 | public FileConfigurationResource(String resourcePath) { 15 | configurationFile = new File(resourcePath); 16 | } 17 | 18 | @Override 19 | public InputStream getInputStream() throws IOException { 20 | return new FileInputStream(configurationFile); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /hopper/src/main/resources/META-INF/schema/examples/config/broken-feed-server-config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /documentation/src/resources/samples/ah-namespace-feed-products.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /hopper/src/main/java/org/atomhopper/adapter/request/entry/AbstractEntryRequest.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.adapter.request.entry; 2 | 3 | import org.apache.abdera.protocol.server.RequestContext; 4 | import org.atomhopper.abdera.TargetResolverField; 5 | import org.atomhopper.adapter.request.feed.AbstractFeedRequest; 6 | 7 | public class AbstractEntryRequest extends AbstractFeedRequest implements EntryRequest { 8 | 9 | private final String entryId; 10 | 11 | protected AbstractEntryRequest(RequestContext abderaRequestContext) { 12 | super(abderaRequestContext); 13 | 14 | entryId = abderaRequestContext.getTarget().getParameter(TargetResolverField.ENTRY.name()); 15 | } 16 | 17 | @Override 18 | public String getEntryId() { 19 | return entryId; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /adapters/jdbc/src/main/java/org/atomhopper/jdbc/adapter/JdbcFeedInformation.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.jdbc.adapter; 2 | 3 | import org.apache.abdera.model.Categories; 4 | import org.atomhopper.adapter.FeedInformation; 5 | import org.atomhopper.adapter.NotImplemented; 6 | import org.atomhopper.adapter.request.adapter.GetCategoriesRequest; 7 | import org.atomhopper.adapter.request.feed.FeedRequest; 8 | 9 | public class JdbcFeedInformation implements FeedInformation { 10 | 11 | @Override 12 | public String getId(FeedRequest feedRequest) { 13 | throw new UnsupportedOperationException("Not supported yet."); 14 | } 15 | 16 | @Override 17 | @NotImplemented 18 | public Categories getCategories(GetCategoriesRequest getCategoriesRequest) { 19 | throw new UnsupportedOperationException("Not supported yet."); 20 | } 21 | } -------------------------------------------------------------------------------- /tests/jetty-killer/src/main/resources/log4j.properties: -------------------------------------------------------------------------------- 1 | # Root logger outputs to console for now 2 | log4j.rootLogger=WARN, consoleOut 3 | 4 | #log4j.logger.org.hibernate.SQL=DEBUG, consoleOut 5 | #log4j.additivity.org.hibernate.SQL=false 6 | 7 | # Console 8 | log4j.appender.consoleOut=org.apache.log4j.ConsoleAppender 9 | log4j.appender.consoleOut.layout=org.apache.log4j.PatternLayout 10 | log4j.appender.consoleOut.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n 11 | 12 | # File 13 | #log4j.appender.defaultFile=org.apache.log4j.RollingFileAppender 14 | #log4j.appender.defaultFile.File=/tmp/senseLog.log 15 | #log4j.appender.defaultFile.MaxFileSize=2MB 16 | #log4j.appender.defaultFile.MaxBackupIndex=2 17 | #log4j.appender.defaultFile.layout = org.apache.log4j.PatternLayout 18 | #log4j.appender.defaultFile.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n -------------------------------------------------------------------------------- /adapters/mongodb/src/main/java/org/atomhopper/mongodb/adapter/MongodbFeedInformation.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.mongodb.adapter; 2 | 3 | import org.apache.abdera.model.Categories; 4 | import org.atomhopper.adapter.FeedInformation; 5 | import org.atomhopper.adapter.NotImplemented; 6 | import org.atomhopper.adapter.request.adapter.GetCategoriesRequest; 7 | import org.atomhopper.adapter.request.feed.FeedRequest; 8 | 9 | public class MongodbFeedInformation implements FeedInformation { 10 | 11 | @Override 12 | public String getId(FeedRequest feedRequest) { 13 | throw new UnsupportedOperationException("Not supported yet."); 14 | } 15 | 16 | @Override @NotImplemented 17 | public Categories getCategories(GetCategoriesRequest getCategoriesRequest) { 18 | throw new UnsupportedOperationException("Not supported yet."); 19 | } 20 | } -------------------------------------------------------------------------------- /documentation/src/resources/samples/ah-log4j-properties.txt: -------------------------------------------------------------------------------- 1 | # Root logger outputs to console for now 2 | log4j.rootLogger=WARN, consoleOut 3 | 4 | #log4j.logger.org.hibernate.SQL=DEBUG, consoleOut 5 | #log4j.additivity.org.hibernate.SQL=false 6 | 7 | # Console 8 | log4j.appender.consoleOut=org.apache.log4j.ConsoleAppender 9 | log4j.appender.consoleOut.layout=org.apache.log4j.PatternLayout 10 | log4j.appender.consoleOut.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n 11 | 12 | # File 13 | #log4j.appender.defaultFile=org.apache.log4j.RollingFileAppender 14 | #log4j.appender.defaultFile.File=/tmp/senseLog.log 15 | #log4j.appender.defaultFile.MaxFileSize=2MB 16 | #log4j.appender.defaultFile.MaxBackupIndex=2 17 | #log4j.appender.defaultFile.layout = org.apache.log4j.PatternLayout 18 | #log4j.appender.defaultFile.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n -------------------------------------------------------------------------------- /adapters/postgres-adapter/src/main/java/org/atomhopper/postgres/adapter/PostgresFeedInformation.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.postgres.adapter; 2 | 3 | import org.apache.abdera.model.Categories; 4 | import org.atomhopper.adapter.FeedInformation; 5 | import org.atomhopper.adapter.NotImplemented; 6 | import org.atomhopper.adapter.request.adapter.GetCategoriesRequest; 7 | import org.atomhopper.adapter.request.feed.FeedRequest; 8 | 9 | public class PostgresFeedInformation implements FeedInformation { 10 | 11 | @Override 12 | public String getId(FeedRequest feedRequest) { 13 | throw new UnsupportedOperationException("Not supported yet."); 14 | } 15 | 16 | @Override 17 | @NotImplemented 18 | public Categories getCategories(GetCategoriesRequest getCategoriesRequest) { 19 | throw new UnsupportedOperationException("Not supported yet."); 20 | } 21 | } -------------------------------------------------------------------------------- /hopper/src/main/java/org/atomhopper/adapter/FeedInformation.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.adapter; 2 | 3 | import org.apache.abdera.model.Categories; 4 | import org.atomhopper.adapter.request.adapter.GetCategoriesRequest; 5 | import org.atomhopper.adapter.request.feed.FeedRequest; 6 | 7 | public interface FeedInformation { 8 | 9 | /** 10 | * Requests the ID of the underlying feed as a string value. 11 | * 12 | * @return 13 | */ 14 | String getId(FeedRequest getFeedRequest); 15 | 16 | /** 17 | * Retrieves a list of categories supported by this feed source. 18 | * 19 | * A workspace provider may poll all of its associated feeds for their 20 | * categories and then aggregate them into a service document. 21 | * 22 | * @return 23 | */ 24 | Categories getCategories(GetCategoriesRequest getCategoriesRequest); 25 | } 26 | -------------------------------------------------------------------------------- /server/src/main/resources/META-INF/atom-server.cfg.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /test-suite/src/main/java/org/atomhopper/adapter/impl/AtomEntry.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.adapter.impl; 2 | 3 | import org.apache.abdera.model.Entry; 4 | 5 | import java.util.Calendar; 6 | 7 | /** 8 | * 9 | 10 | */ 11 | public class AtomEntry implements Comparable { 12 | 13 | private final Entry entry; 14 | private final Calendar updated; 15 | 16 | public AtomEntry(Entry entry) { 17 | this.entry = entry; 18 | 19 | updated = Calendar.getInstance(); 20 | } 21 | 22 | public Entry getEntry() { 23 | return entry; 24 | } 25 | 26 | public Calendar getUpdated() { 27 | return updated; 28 | } 29 | 30 | public void updateTimestamp() { 31 | updated.setTimeInMillis(System.currentTimeMillis()); 32 | } 33 | 34 | @Override 35 | public int compareTo(AtomEntry t) { 36 | return getUpdated().compareTo(t.getUpdated()); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /adapters/postgres-adapter/src/test/java/org/atomhopper/postgres/query/CategoryStringGeneratorTest.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.postgres.query; 2 | 3 | 4 | import org.junit.Test; 5 | 6 | import static junit.framework.Assert.assertEquals; 7 | 8 | public class CategoryStringGeneratorTest { 9 | 10 | final String SINGLE_CAT = "+Cat1"; 11 | final String SINGLE_CAT_RESULT = "{cat1}"; 12 | 13 | final String MULTI_CAT = "+Cat1+Cat2"; 14 | final String MULTI_CAT_RESULT = "{cat1,cat2}"; 15 | 16 | @Test 17 | public void shouldGenerateStringForSingleCategory() { 18 | String result = CategoryStringGenerator.getPostgresCategoryString(SINGLE_CAT); 19 | assertEquals(result, SINGLE_CAT_RESULT); 20 | } 21 | 22 | @Test 23 | public void shouldGenerateStringForMulitpleCategories() { 24 | String result = CategoryStringGenerator.getPostgresCategoryString(MULTI_CAT); 25 | assertEquals(result, MULTI_CAT_RESULT); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /hopper/src/test/resources/org/atomhopper/config/WorkspaceConfigProcessorTest/noArchiveWithArchive.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /hopper/src/main/java/org/atomhopper/adapter/request/adapter/impl/PostEntryRequestImpl.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.adapter.request.adapter.impl; 2 | 3 | import org.apache.abdera.model.Entry; 4 | import org.apache.abdera.protocol.server.RequestContext; 5 | import org.atomhopper.adapter.request.adapter.PostEntryRequest; 6 | import org.atomhopper.adapter.request.feed.AbstractFeedRequest; 7 | 8 | /** 9 | * 10 | * 11 | */ 12 | public class PostEntryRequestImpl extends AbstractFeedRequest implements PostEntryRequest { 13 | 14 | public PostEntryRequestImpl(RequestContext abderaRequestContext) { 15 | super(abderaRequestContext); 16 | } 17 | 18 | @Override 19 | public Entry getEntry() { 20 | try { 21 | return getRequestContext().getDocument().getRoot(); 22 | } catch (Exception ex) { 23 | throw new RequestParsingException("Failed to read in ATOM Entry data. Reason: " + ex.getMessage(), ex); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /documentation/src/resources/samples/ah-namespace-feed-news.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /hopper/src/test/resources/org/atomhopper/config/WorkspaceConfigProcessorTest/archiveWithNoCurrent.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /hopper/src/main/java/org/atomhopper/adapter/impl/DisabledFeedInformation.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.adapter.impl; 2 | 3 | import org.apache.abdera.model.Categories; 4 | import org.atomhopper.adapter.FeedInformation; 5 | import org.atomhopper.adapter.request.adapter.GetCategoriesRequest; 6 | import org.atomhopper.adapter.request.feed.FeedRequest; 7 | 8 | public final class DisabledFeedInformation implements FeedInformation { 9 | 10 | private static final DisabledFeedInformation INSTANCE = new DisabledFeedInformation(); 11 | 12 | public static DisabledFeedInformation getInstance() { 13 | return INSTANCE; 14 | } 15 | 16 | private DisabledFeedInformation() { 17 | } 18 | 19 | @Override 20 | public String getId(FeedRequest feedRequest) { 21 | return "atomhopper:no-id"; 22 | } 23 | 24 | @Override 25 | public Categories getCategories(GetCategoriesRequest getCategoriesRequest) { 26 | return getCategoriesRequest.newCategories(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /hopper/src/main/java/org/atomhopper/abdera/filter/FeedPagingProcessor.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.abdera.filter; 2 | 3 | import java.util.Calendar; 4 | import java.util.TimeZone; 5 | import org.apache.abdera.model.Feed; 6 | import org.apache.abdera.protocol.server.RequestContext; 7 | import org.atomhopper.response.AdapterResponse; 8 | 9 | 10 | public class FeedPagingProcessor implements AdapterResponseInterceptor { 11 | @Override 12 | public void process(RequestContext rc, AdapterResponse adapterResponse) { 13 | final Feed f = adapterResponse.getBody(); 14 | 15 | // If there are no entries in the feed 16 | if (f == null || f.getEntries() == null || f.getEntries().isEmpty()) { 17 | return; 18 | } 19 | // Add an updated element to the feed 20 | final Calendar localNow = Calendar.getInstance(TimeZone.getDefault()); 21 | localNow.setTimeInMillis(System.currentTimeMillis()); 22 | f.setUpdated(localNow.getTime()); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /test-util/src/main/java/org/atomhopper/util/TestHelper.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.util; 2 | 3 | import org.junit.Assert; 4 | 5 | /** 6 | * User: sbrayman 7 | * Date: Sep 29, 2011 8 | *

9 | * Assert not equals extension to junit. 10 | */ 11 | 12 | public final class TestHelper { 13 | 14 | private TestHelper() { 15 | } 16 | 17 | public static void assertNotEquals(Object shouldNotBe, Object actual) { 18 | assertNotEquals(null, shouldNotBe, actual); 19 | } 20 | 21 | public static void assertNotEquals(String message, Object shouldNotBe, Object actual) { 22 | 23 | String formatted = ""; 24 | 25 | if (message != null) { 26 | formatted = message + " "; 27 | } 28 | 29 | try { 30 | Assert.assertEquals(shouldNotBe, actual); 31 | } catch (Throwable valuesNotEqual) { 32 | return; 33 | } 34 | 35 | Assert.fail(formatted + "expected:<" + shouldNotBe + "> equaled:<" + actual + ">"); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /hopper/src/main/java/org/atomhopper/servlet/DefaultEmptyContext.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.servlet; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | import javax.servlet.ServletContext; 7 | 8 | public class DefaultEmptyContext implements ApplicationContextAdapter { 9 | 10 | private static final Logger LOG = LoggerFactory.getLogger(DefaultEmptyContext.class); 11 | 12 | @Override 13 | public T fromContext(Class classToCastTo) { 14 | return null; 15 | } 16 | 17 | @Override 18 | public T fromContext(String refName, Class classToCastTo) { 19 | return null; 20 | } 21 | 22 | @Override 23 | public void usingServletContext(ServletContext context) { 24 | LOG.warn("Missing context adapter init-parameter for servlet: " 25 | + ServletInitParameter.CONTEXT_ADAPTER_CLASS.toString() 26 | + " - Please note that this will enforce a default, empty context adapter for this servlet."); 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /test-suite/src/test/java/org/atomhopper/JettyIntegrationTestHarness.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper; 2 | 3 | import org.atomhopper.jetty.AtomHopperJettyServerBuilder; 4 | import org.eclipse.jetty.server.Server; 5 | import org.junit.AfterClass; 6 | import org.junit.BeforeClass; 7 | 8 | /** 9 | * Note: This test harness expects to have the statically configured port 10 | * available. 11 | * 12 | * TODO: Make the port configurable? 13 | */ 14 | public class JettyIntegrationTestHarness { 15 | 16 | private static Server serverInstance; 17 | 18 | @BeforeClass 19 | public static void startServer() throws Exception { 20 | serverInstance = new AtomHopperJettyServerBuilder(getPort()).newServer(); 21 | serverInstance.start(); 22 | } 23 | 24 | @AfterClass 25 | public static void stopServer() throws Exception { 26 | if (serverInstance != null) { 27 | serverInstance.stop(); 28 | } 29 | } 30 | 31 | public static int getPort() { 32 | return 24156; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /hopper/src/test/resources/org/atomhopper/config/WorkspaceConfigProcessorTest/noArchiveWithCurrent.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /hopper/src/test/resources/org/atomhopper/config/WorkspaceConfigProcessorTest/archiveWithCurrent.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /hopper/src/main/java/org/atomhopper/response/AdapterResponse.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.response; 2 | 3 | import org.apache.abdera.util.EntityTag; 4 | import org.springframework.http.HttpStatus; 5 | 6 | 7 | public interface AdapterResponse { 8 | 9 | /** 10 | * 11 | * @return 12 | */ 13 | T getBody(); 14 | 15 | /** 16 | * 17 | * @param name 18 | * @return 19 | */ 20 | String getParameter(ResponseParameter name); 21 | 22 | /** 23 | * Setting a parameter will take the value's toString() value and use it to 24 | * represent the value. 25 | * 26 | * @param name 27 | * @param value 28 | * @return 29 | */ 30 | AdapterResponse withParameter(ResponseParameter name, Object value); 31 | 32 | /** 33 | * 34 | * @return 35 | */ 36 | String getMessage(); 37 | 38 | /** 39 | * 40 | * @return 41 | */ 42 | HttpStatus getResponseStatus(); 43 | 44 | void setEntityTag(EntityTag entityTag); 45 | EntityTag getEntityTag(); 46 | } 47 | -------------------------------------------------------------------------------- /hopper/src/test/java/org/atomhopper/LogBackConfigLoaderTest.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper; 2 | 3 | import ch.qos.logback.core.joran.spi.JoranException; 4 | import org.junit.Before; 5 | import org.junit.Rule; 6 | import org.junit.Test; 7 | import org.junit.experimental.runners.Enclosed; 8 | import org.junit.rules.ExpectedException; 9 | import org.junit.runner.RunWith; 10 | 11 | import java.io.IOException; 12 | 13 | @RunWith(Enclosed.class) 14 | public class LogBackConfigLoaderTest { 15 | 16 | public static class WhenLoadingConfig { 17 | 18 | String someFakeFileLocation; 19 | 20 | @Before 21 | public void setUp() throws Exception { 22 | someFakeFileLocation = "/noSuchLocation"; 23 | } 24 | 25 | @Rule 26 | public ExpectedException exception = ExpectedException.none(); 27 | 28 | @Test 29 | public void shouldThrowIOException() throws IOException, JoranException { 30 | exception.expect(IOException.class); 31 | new LogBackConfigLoader(someFakeFileLocation); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /hopper/src/test/java/org/atomhopper/util/config/resource/file/FileConfigurationResourceTest.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.util.config.resource.file; 2 | 3 | import org.junit.Before; 4 | import org.junit.Rule; 5 | import org.junit.Test; 6 | import org.junit.experimental.runners.Enclosed; 7 | import org.junit.rules.ExpectedException; 8 | import org.junit.runner.RunWith; 9 | 10 | import java.io.IOException; 11 | 12 | @RunWith(Enclosed.class) 13 | public class FileConfigurationResourceTest { 14 | 15 | public static class WhenUsingConfigFiles { 16 | 17 | String someFakeFileLocation; 18 | 19 | @Before 20 | public void setUp() throws Exception { 21 | someFakeFileLocation = "/noSuchLocation"; 22 | } 23 | 24 | @Rule 25 | public ExpectedException exception = ExpectedException.none(); 26 | 27 | @Test 28 | public void shouldThrowIOException() throws Exception { 29 | exception.expect(IOException.class); 30 | new FileConfigurationResource(someFakeFileLocation).getInputStream(); 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /adapters/postgres-adapter/src/main/java/org/atomhopper/postgres/query/EntryResultSetExtractor.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.postgres.query; 2 | 3 | import java.sql.ResultSet; 4 | import java.sql.SQLException; 5 | import java.util.Arrays; 6 | 7 | import org.atomhopper.postgres.model.PersistedEntry; 8 | 9 | import org.springframework.jdbc.core.ResultSetExtractor; 10 | 11 | public class EntryResultSetExtractor implements ResultSetExtractor { 12 | 13 | @Override 14 | public Object extractData(ResultSet rs) throws SQLException { 15 | 16 | PersistedEntry entry = new PersistedEntry(); 17 | entry.setFeed(rs.getString("feed")); 18 | entry.setCreationDate(rs.getTimestamp("creationdate")); 19 | entry.setDateLastUpdated(rs.getTimestamp("datelastupdated")); 20 | entry.setEntryBody(rs.getString("entrybody")); 21 | entry.setEntryId(rs.getString("entryid")); 22 | 23 | Object[] objArray = (Object[]) rs.getArray("categories").getArray(); 24 | entry.setCategories(Arrays.copyOf(objArray, objArray.length, String[].class)); 25 | return entry; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /hopper/src/test/java/org/atomhopper/util/config/AbstractConfigurationParserTest.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.util.config; 2 | 3 | import org.junit.Ignore; 4 | import org.junit.Test; 5 | import org.junit.experimental.runners.Enclosed; 6 | import org.junit.runner.RunWith; 7 | 8 | @RunWith(Enclosed.class) 9 | public class AbstractConfigurationParserTest { 10 | 11 | private static final String EXPECTED_CONFIGURATION = "some config thing"; 12 | 13 | public static class WhenReadingConfigurations { 14 | 15 | @Test(expected = IllegalStateException.class) 16 | public void shouldRejectReadingFromNullConfigurationResources() { 17 | new TestableConfigurationParser().read(); 18 | } 19 | } 20 | 21 | @Ignore 22 | public static class TestableConfigurationParser extends AbstractConfigurationParser { 23 | 24 | public TestableConfigurationParser() { 25 | super(String.class); 26 | } 27 | 28 | @Override 29 | protected String readConfiguration() { 30 | return EXPECTED_CONFIGURATION; 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /adapters/hibernate/src/main/java/org/atomhopper/hibernate/adapter/HibernateFeedInformation.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.hibernate.adapter; 2 | 3 | import org.apache.abdera.model.Categories; 4 | import org.atomhopper.adapter.FeedInformation; 5 | import org.atomhopper.adapter.NotImplemented; 6 | import org.atomhopper.adapter.request.adapter.GetCategoriesRequest; 7 | import org.atomhopper.adapter.request.feed.FeedRequest; 8 | import org.atomhopper.dbal.FeedRepository; 9 | 10 | public class HibernateFeedInformation implements FeedInformation { 11 | 12 | private final FeedRepository feedRepository; 13 | 14 | public HibernateFeedInformation(FeedRepository feedRepository) { 15 | this.feedRepository = feedRepository; 16 | } 17 | 18 | @Override 19 | public String getId(FeedRequest feedRequest) { 20 | return feedRepository.getFeed(feedRequest.getFeedName()).getFeedId(); 21 | } 22 | 23 | @Override @NotImplemented 24 | public Categories getCategories(GetCategoriesRequest getCategoriesRequest) { 25 | throw new UnsupportedOperationException("Not supported yet."); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /hopper/src/test/resources/org/atomhopper/config/WorkspaceConfigProcessorTest/archiveWithArchive.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /hopper/src/main/java/org/atomhopper/ExternalConfigLoaderContextListener.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper; 2 | 3 | import javax.servlet.ServletContextEvent; 4 | import javax.servlet.ServletContextListener; 5 | 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | 9 | /** 10 | * Simple utility listener to load certain properties before Spring Starts up. 11 | * 12 | * This code is modified from https://bowerstudios.com/node/896 13 | */ 14 | public class ExternalConfigLoaderContextListener implements ServletContextListener { 15 | 16 | private static final Logger LOGGER = LoggerFactory.getLogger(ExternalConfigLoaderContextListener.class); 17 | 18 | @Override 19 | public void contextInitialized(ServletContextEvent sce) { 20 | final String configLocation = "/etc/atomhopper/"; 21 | 22 | try { 23 | new LogBackConfigLoader(configLocation + "logback.xml"); 24 | } catch (Exception e) { 25 | LOGGER.error("Unable to read config file", e); 26 | } 27 | } 28 | 29 | @Override 30 | public void contextDestroyed(ServletContextEvent sce) { 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /hopper/src/test/java/org/atomhopper/ExternalConfigLoaderContextListenerTest.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper; 2 | 3 | import org.junit.Rule; 4 | import org.junit.Test; 5 | import org.junit.experimental.runners.Enclosed; 6 | import org.junit.rules.ExpectedException; 7 | import org.junit.runner.RunWith; 8 | 9 | import javax.servlet.ServletContextEvent; 10 | 11 | import static org.mockito.Mockito.mock; 12 | 13 | @RunWith(Enclosed.class) 14 | public class ExternalConfigLoaderContextListenerTest { 15 | 16 | public static class WhenInitializingContext { 17 | 18 | ExternalConfigLoaderContextListener externalConfigLoaderContextListener; 19 | 20 | @Rule 21 | public ExpectedException exception = ExpectedException.none(); 22 | 23 | @Test 24 | public void shouldThrowErrorOnInitialization() throws Exception { 25 | exception.expect(ClassFormatError.class); 26 | externalConfigLoaderContextListener = new ExternalConfigLoaderContextListener(); 27 | externalConfigLoaderContextListener.contextInitialized(mock(ServletContextEvent.class)); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /hopper/src/main/java/org/atomhopper/dbal/PageDirection.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.dbal; 2 | 3 | /** 4 | * The PageDirection to request the page forward of the marker or backward 5 | * of the marker. The direction is based on the temporal direction of the feed 6 | * where forward is any time period after the creation date of the marker and 7 | * backward is any time period before the creation date of the marker. 8 | * 9 | * Forward pages should provides the next page, marker inclusive - AKA Previous. 10 | * Backward pages should provides the previous page, marker exclusive - AKA Next. 11 | * 12 | * 13 | * HEAD (First Entry in Live Feed) PAST (Older Entries) 14 | * ========================================================================= 15 | * M 16 | * PREVIOUS a NEXT 17 | * BACKWARD r FORWARD 18 | * k 19 | * e 20 | * r 21 | * @author zinic 22 | */ 23 | public enum PageDirection { 24 | 25 | FORWARD, BACKWARD 26 | } -------------------------------------------------------------------------------- /hopper/src/main/java/org/atomhopper/abdera/filter/FeedConfigurationResponseProcessor.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.abdera.filter; 2 | 3 | import org.apache.abdera.model.Feed; 4 | import org.apache.abdera.protocol.server.RequestContext; 5 | import org.atomhopper.config.v1_0.Author; 6 | import org.atomhopper.config.v1_0.FeedConfiguration; 7 | import org.atomhopper.response.AdapterResponse; 8 | 9 | public class FeedConfigurationResponseProcessor implements AdapterResponseInterceptor { 10 | private final FeedConfiguration feedConfiguration; 11 | 12 | public FeedConfigurationResponseProcessor(FeedConfiguration feedConfiguration) { 13 | this.feedConfiguration = feedConfiguration; 14 | } 15 | 16 | @Override 17 | public void process(RequestContext rc, AdapterResponse adapterResponse) { 18 | final Feed feed = adapterResponse.getBody(); 19 | Author author = feedConfiguration.getAuthor(); 20 | 21 | if (author != null) { 22 | String name = author.getName(); 23 | if ((name != null) && !(name.isEmpty()) && (feed.getAuthor() == null)) { 24 | feed.addAuthor(name); 25 | } 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /documentation/src/resources/samples/ah-multifeed-atom-server.cfg.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /hopper/src/main/java/org/atomhopper/util/uri/template/EnumKeyedTemplateParameters.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.util.uri.template; 2 | 3 | import java.util.Collections; 4 | import java.util.HashMap; 5 | import java.util.Map; 6 | 7 | //TODO: Consider making domain scoped child classes with helper methods 8 | // Something like setMarker(String markerId) or setPageLimit(int pageLimit) 9 | public class EnumKeyedTemplateParameters implements TemplateParameters { 10 | 11 | private final T temlpateTargetKey; 12 | private final Map parameterMap; 13 | 14 | public EnumKeyedTemplateParameters(T temlpateTargetKey) { 15 | this.temlpateTargetKey = temlpateTargetKey; 16 | this.parameterMap = new HashMap(); 17 | } 18 | 19 | @Override 20 | public void set(URITemplateParameter parameter, Object value) { 21 | parameterMap.put(parameter.toString(), value); 22 | } 23 | 24 | @Override 25 | public Map toMap() { 26 | return Collections.unmodifiableMap(parameterMap); 27 | } 28 | 29 | @Override 30 | public T getTargetTemplateKey() { 31 | return temlpateTargetKey; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /adapters/hibernate/src/test/java/org/atomhopper/hibernate/actions/PersistActionTest.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.hibernate.actions; 2 | 3 | import org.hibernate.Session; 4 | import org.junit.Before; 5 | import org.junit.Test; 6 | import org.junit.experimental.runners.Enclosed; 7 | import org.junit.runner.RunWith; 8 | 9 | import static org.mockito.Mockito.mock; 10 | import static org.mockito.Mockito.only; 11 | import static org.mockito.Mockito.verify; 12 | 13 | /** 14 | * User: sbrayman 15 | * Date: Sep 23, 2011 16 | */ 17 | 18 | @RunWith(Enclosed.class) 19 | public class PersistActionTest { 20 | 21 | public static class WhenPersistingAnAction { 22 | private Session session; 23 | private Object persistMe; 24 | private PersistAction persistAction; 25 | 26 | @Before 27 | public void setUp() throws Exception { 28 | session = mock(Session.class); 29 | persistMe = mock(Object.class); 30 | persistAction = new PersistAction(persistMe); 31 | } 32 | 33 | @Test 34 | public void shouldPersistSession() throws Exception { 35 | persistAction.perform(session); 36 | verify(session, only()).persist(persistMe); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /adapters/mongodb/src/main/java/org/atomhopper/mongodb/adapter/MongodbUtilities.java: -------------------------------------------------------------------------------- 1 | 2 | package org.atomhopper.mongodb.adapter; 3 | 4 | public final class MongodbUtilities { 5 | 6 | private MongodbUtilities() { 7 | throw new AssertionError(); 8 | } 9 | 10 | protected static int safeLongToInt(long value) { 11 | if (value < Integer.MIN_VALUE || value > Integer.MAX_VALUE) { 12 | throw new IllegalArgumentException 13 | (value + " cannot be cast to int without changing its value."); 14 | } 15 | return (int) value; 16 | } 17 | 18 | protected static String formatCollectionName(final String collection) { 19 | // Note: The maximum size of a collection name is 128 characters 20 | // (including the name of the db and indexes). 21 | // It is probably best to keep it under 80/90 chars. 22 | // http://www.mongodb.org/display/DOCS/Collections 23 | final int maxCollectionNameLength = 70; 24 | 25 | if(collection.length() > maxCollectionNameLength) { 26 | return collection.replace('/', '.').substring(0, maxCollectionNameLength); 27 | } else { 28 | return collection.replace('/', '.'); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /test-util/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | 6 | org.atomhopper 7 | parent 8 | 1.2.35-SNAPSHOT 9 | 10 | 11 | org.atomhopper 12 | test-util 13 | jar 14 | 15 | ATOM Hopper - Test Utility 16 | 17 | 18 | UTF-8 19 | 20 | 21 | 22 | 23 | junit 24 | junit 25 | compile 26 | 27 | 28 | 29 | 30 | 31 | 32 | org.apache.maven.plugins 33 | maven-compiler-plugin 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /adapters/jdbc/src/main/resources/ddl/jdbc/atomhopper-database-schema-ddl-postgres.sql: -------------------------------------------------------------------------------- 1 | SET statement_timeout = 0; 2 | SET client_encoding = 'UTF8'; 3 | SET standard_conforming_strings = on; 4 | SET check_function_bodies = false; 5 | SET client_min_messages = warning; 6 | 7 | CREATE EXTENSION IF NOT EXISTS plpgsql WITH SCHEMA pg_catalog; 8 | 9 | COMMENT ON EXTENSION plpgsql IS 'PL/pgSQL procedural language'; 10 | 11 | SET search_path = public, pg_catalog; 12 | SET default_tablespace = ''; 13 | SET default_with_oids = false; 14 | 15 | CREATE TABLE entries ( 16 | entryid text NOT NULL, 17 | creationdate timestamp without time zone NOT NULL, 18 | datelastupdated timestamp without time zone NOT NULL, 19 | entrybody text, 20 | feed text, 21 | categories character varying[], 22 | PRIMARY KEY(datelastupdated, entryid) 23 | ); 24 | ALTER TABLE public.entries OWNER TO atomschema; 25 | CREATE INDEX entryid_idx on entries(entryid); 26 | CREATE INDEX categories_idx on entries(categories); 27 | CREATE INDEX feed_idx on entries(feed); 28 | CREATE INDEX feed_entryid_idx on entries(feed, entryid); 29 | 30 | REVOKE ALL ON SCHEMA public FROM PUBLIC; 31 | REVOKE ALL ON SCHEMA public FROM postgres; 32 | GRANT ALL ON SCHEMA public TO postgres; 33 | GRANT ALL ON SCHEMA public TO PUBLIC; 34 | -------------------------------------------------------------------------------- /hopper/src/test/java/org/atomhopper/adapter/impl/DisabledFeedInformationTest.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.adapter.impl; 2 | 3 | import static junit.framework.Assert.assertEquals; 4 | import org.atomhopper.adapter.request.feed.FeedRequest; 5 | import org.junit.Before; 6 | import org.junit.Test; 7 | import org.junit.experimental.runners.Enclosed; 8 | import org.junit.runner.RunWith; 9 | import static org.mockito.Mockito.mock; 10 | 11 | /** 12 | * User: sbrayman 13 | * Date: Sep 26, 2011 14 | */ 15 | 16 | @RunWith(Enclosed.class) 17 | public class DisabledFeedInformationTest { 18 | 19 | public static class WhenAccessingDisabledFeedInformation { 20 | 21 | private DisabledFeedInformation disabledFeedInformation; 22 | private FeedRequest feedRequest; 23 | private String noId; 24 | 25 | @Before 26 | public void setUp() throws Exception { 27 | disabledFeedInformation = DisabledFeedInformation.getInstance(); 28 | feedRequest = mock(FeedRequest.class); 29 | noId = "atomhopper:no-id"; 30 | } 31 | 32 | @Test 33 | public void shouldNotReturnId() throws Exception { 34 | assertEquals("Get Id should return " + noId, disabledFeedInformation.getId(feedRequest), noId); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /tests/jetty-killer/src/main/java/org/atomhopper/jettykiller/AtomHopperServerControl.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.jettykiller; 2 | 3 | 4 | 5 | 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | // 9 | import java.io.IOException; 10 | import java.io.OutputStream; 11 | import java.net.InetAddress; 12 | import java.net.Socket; 13 | 14 | 15 | public class AtomHopperServerControl { 16 | private static final Logger LOG = LoggerFactory.getLogger(AtomHopperServerControl.class); 17 | 18 | private final CommandLineArguments commandLineArgs; 19 | private static final String LOCALHOST_IP = "127.0.0.1"; 20 | 21 | public AtomHopperServerControl(CommandLineArguments commandLineArgs) { 22 | this.commandLineArgs = commandLineArgs; 23 | } 24 | 25 | public void stopAtomHopper() { 26 | try { 27 | Socket s = new Socket(InetAddress.getByName(LOCALHOST_IP), commandLineArgs.stopport); 28 | OutputStream out = s.getOutputStream(); 29 | LOG.info("Sending Atom Hopper stop request"); 30 | out.write(("\r\n").getBytes()); 31 | out.flush(); 32 | s.close(); 33 | } catch (IOException ioex) { 34 | LOG.error("An error occured while attempting to stop Atom Hopper: " + ioex.getMessage()); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /hopper/src/main/java/org/atomhopper/abdera/WorkspaceManager.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.abdera; 2 | 3 | import org.apache.abdera.protocol.server.CollectionAdapter; 4 | import org.apache.abdera.protocol.server.RequestContext; 5 | import org.apache.abdera.protocol.server.WorkspaceInfo; 6 | 7 | import java.util.Collection; 8 | import java.util.LinkedList; 9 | import java.util.List; 10 | 11 | public class WorkspaceManager implements org.apache.abdera.protocol.server.WorkspaceManager { 12 | 13 | private final List handlers; 14 | 15 | public WorkspaceManager() { 16 | handlers = new LinkedList(); 17 | } 18 | 19 | public void addWorkspaces(List workspaces) { 20 | handlers.addAll(workspaces); 21 | } 22 | 23 | @Override 24 | public Collection getWorkspaces(RequestContext request) { 25 | return (Collection) handlers; 26 | } 27 | 28 | @Override 29 | public CollectionAdapter getCollectionAdapter(RequestContext request) { 30 | for (WorkspaceHandler workspace : handlers) { 31 | final CollectionAdapter adapter = workspace.getAnsweringAdapter(request); 32 | 33 | if (adapter != null) { 34 | return adapter; 35 | } 36 | } 37 | 38 | return null; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /adapters/postgres-adapter/src/main/resources/ddl/postgres/atomhopper-database-schema-ddl-postgres.sql: -------------------------------------------------------------------------------- 1 | SET statement_timeout = 0; 2 | SET client_encoding = 'UTF8'; 3 | SET standard_conforming_strings = on; 4 | SET check_function_bodies = false; 5 | SET client_min_messages = warning; 6 | 7 | CREATE EXTENSION IF NOT EXISTS plpgsql WITH SCHEMA pg_catalog; 8 | 9 | COMMENT ON EXTENSION plpgsql IS 'PL/pgSQL procedural language'; 10 | 11 | SET search_path = public, pg_catalog; 12 | SET default_tablespace = ''; 13 | SET default_with_oids = false; 14 | 15 | CREATE TABLE entries ( 16 | entryid character varying(255) CONSTRAINT entries_pkey PRIMARY KEY, 17 | creationdate timestamp without time zone NOT NULL, 18 | datelastupdated timestamp without time zone NOT NULL, 19 | entrybody character varying, 20 | feed character varying(255), 21 | categories character varying[] 22 | ); 23 | ALTER TABLE public.entries OWNER TO atomschema; 24 | CREATE INDEX datelastupdated_idx on entries(datelastupdated); 25 | CREATE INDEX categories_idx on entries(categories); 26 | CREATE INDEX feed_idx on entries(feed); 27 | CREATE INDEX feed_datelastupdated_idx on entries(feed, datelastupdated); 28 | 29 | REVOKE ALL ON SCHEMA public FROM PUBLIC; 30 | REVOKE ALL ON SCHEMA public FROM postgres; 31 | GRANT ALL ON SCHEMA public TO postgres; 32 | GRANT ALL ON SCHEMA public TO PUBLIC; 33 | -------------------------------------------------------------------------------- /hopper/src/test/java/org/atomhopper/adapter/request/impl/PostEntryRequestImplTest.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.adapter.request.impl; 2 | 3 | import org.apache.abdera.protocol.server.RequestContext; 4 | import org.atomhopper.adapter.request.adapter.impl.PostEntryRequestImpl; 5 | import org.atomhopper.adapter.request.adapter.impl.RequestParsingException; 6 | import org.junit.Before; 7 | import org.junit.Test; 8 | import org.junit.experimental.runners.Enclosed; 9 | import org.junit.runner.RunWith; 10 | 11 | import java.io.IOException; 12 | 13 | import static org.mockito.Mockito.mock; 14 | import static org.mockito.Mockito.when; 15 | 16 | @RunWith(Enclosed.class) 17 | public class PostEntryRequestImplTest { 18 | 19 | public static class WhenParsingAdberaRequestContexts { 20 | 21 | private RequestContext requestContextMock; 22 | 23 | @Before 24 | public void standUp() throws Exception { 25 | requestContextMock = mock(RequestContext.class); 26 | 27 | when(requestContextMock.getDocument()).thenThrow(new IOException("Unable to read stream")); 28 | } 29 | 30 | @Test (expected = RequestParsingException.class) 31 | public void shouldWrapExceptionCasesGracefully() { 32 | new PostEntryRequestImpl(requestContextMock).getEntry(); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /hopper/src/main/java/org/atomhopper/util/config/AbstractConfigurationParser.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.util.config; 2 | 3 | import org.atomhopper.util.config.resource.ConfigurationResource; 4 | 5 | public abstract class AbstractConfigurationParser implements ConfigurationParser { 6 | 7 | private final Class configurationClassDefinition; 8 | private ConfigurationResource configurationResource; 9 | 10 | public AbstractConfigurationParser(Class configurationClassDefinition) { 11 | this.configurationClassDefinition = configurationClassDefinition; 12 | } 13 | 14 | @Override 15 | public Class getConfigurationClass() { 16 | return configurationClassDefinition; 17 | } 18 | 19 | @Override 20 | public void setConfigurationResource(ConfigurationResource resource) { 21 | configurationResource = resource; 22 | } 23 | 24 | @Override 25 | public ConfigurationResource getConfigurationResource() { 26 | return configurationResource; 27 | } 28 | 29 | @Override 30 | public final T read() { 31 | if (configurationResource == null) { 32 | throw new IllegalStateException("A configuration resource must be set first before reading from it."); 33 | } 34 | 35 | return readConfiguration(); 36 | } 37 | 38 | protected abstract T readConfiguration(); 39 | } 40 | -------------------------------------------------------------------------------- /documentation/src/resources/samples/ah-web.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | Atom Hopper Server 6 | ATOM 7 | 8 | 9 | 10 | 24 | 25 | atomhopper-url-pattern 26 | /atom/ 27 | 28 | 29 | 30 | 31 | Atom-Hopper 32 | /atom/* 33 | 34 | 35 | -------------------------------------------------------------------------------- /hopper/src/main/java/org/atomhopper/servlet/ServletSpringContext.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.servlet; 2 | 3 | import org.springframework.context.ApplicationContext; 4 | import org.springframework.context.ApplicationContextAware; 5 | import org.springframework.web.context.support.WebApplicationContextUtils; 6 | 7 | import javax.servlet.ServletContext; 8 | 9 | /* 10 | * @author John Hopper 11 | */ 12 | public class ServletSpringContext implements ApplicationContextAware, ApplicationContextAdapter { 13 | 14 | private ApplicationContext applicationContext; 15 | 16 | @Override 17 | public synchronized void setApplicationContext(ApplicationContext ac) { 18 | if (applicationContext == null) { 19 | applicationContext = ac; 20 | } 21 | } 22 | 23 | @Override 24 | public synchronized void usingServletContext(ServletContext context) { 25 | if (applicationContext == null) { 26 | applicationContext = WebApplicationContextUtils.getRequiredWebApplicationContext(context); 27 | } 28 | } 29 | 30 | @Override 31 | public T fromContext(Class classToCastTo) { 32 | return applicationContext.getBean(classToCastTo); 33 | } 34 | 35 | @Override 36 | public T fromContext(String refName, Class classToCastTo) { 37 | return applicationContext.getBean(refName, classToCastTo); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /server/src/main/java/org/atomhopper/server/CommandLineArguments.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.server; 2 | 3 | import org.kohsuke.args4j.Argument; 4 | import org.kohsuke.args4j.Option; 5 | 6 | class CommandLineArguments { 7 | 8 | private static final String DEFAULT_PORT_INFO = "(Default listen port is 8080, default listen for shutdown port is 8818 (acceptable range is 1024 to 49150)"; 9 | static final String ACTION_START = "start"; 10 | static final String ACTION_STOP = "stop"; 11 | private static final int PORT = 8080; 12 | private static final int STOPPORT = 8818; 13 | 14 | @Option(name = "-p", aliases = {"--port"}, 15 | usage = "Atom Hopper port number " + DEFAULT_PORT_INFO) 16 | public final Integer port = PORT; 17 | 18 | @Option(name = "-s", aliases = {"--shutdown-port"}, 19 | usage = "The port used to communicate a shutdown to Atom Hopper " + DEFAULT_PORT_INFO) 20 | public final Integer stopport = STOPPORT; 21 | 22 | @Option(name = "-c", aliases = {"--config-file"}, 23 | usage = "The location and name of the Atom Hopper configuration file") 24 | public String configFile; 25 | 26 | //Note: I recommend keeping this an argument to stay inline with what people expect from a daemon script 27 | @Argument(usage = "Action to take - start | stop", required = true) 28 | public String action = ACTION_START; 29 | } 30 | -------------------------------------------------------------------------------- /hopper/src/test/java/org/atomhopper/util/uri/ClasspathSchemeMapperTest.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.util.uri; 2 | 3 | import org.junit.Before; 4 | import org.junit.Test; 5 | import org.junit.experimental.runners.Enclosed; 6 | import org.junit.runner.RunWith; 7 | 8 | import java.net.URI; 9 | 10 | import static org.junit.Assert.assertFalse; 11 | import static org.junit.Assert.assertTrue; 12 | 13 | @RunWith(Enclosed.class) 14 | public class ClasspathSchemeMapperTest { 15 | 16 | public static class WhenMappingClasspathURIs { 17 | 18 | private URISchemeMapper classpathSchemeMapper; 19 | private URI classpathURI, httpURI; 20 | 21 | @Before 22 | public void standUp() throws Exception { 23 | classpathSchemeMapper = new ClasspathSchemeMapper(); 24 | 25 | classpathURI = new URI("classpath:/META-INF/schema/config/bindings.xjb"); 26 | httpURI = new URI("http://localhost"); 27 | } 28 | 29 | @Test 30 | public void shouldMatchClasspathURIs() { 31 | assertTrue("ClasspathSchemeMapper should match on valid classpath URI", classpathSchemeMapper.canMap(classpathURI)); 32 | } 33 | 34 | @Test 35 | public void shouldNotMatchNonClasspathURIs() { 36 | assertFalse("ClasspathSchemeMapper should not match on invalid classpath URI", classpathSchemeMapper.canMap(httpURI)); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /hopper/src/main/resources/META-INF/schema/examples/config/feed-server-config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /adapters/hibernate/src/main/java/org/atomhopper/hibernate/HibernateSessionManager.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.hibernate; 2 | 3 | import java.util.Map; 4 | import org.atomhopper.adapter.jpa.PersistedCategory; 5 | import org.atomhopper.adapter.jpa.PersistedEntry; 6 | import org.atomhopper.adapter.jpa.PersistedFeed; 7 | import org.hibernate.Session; 8 | import org.hibernate.SessionFactory; 9 | import org.hibernate.cfg.Configuration; 10 | 11 | public class HibernateSessionManager { 12 | 13 | private final SessionFactory sessionFactory; 14 | 15 | public HibernateSessionManager(Map parameters) { 16 | sessionFactory = buildSessionFactory(parameters); 17 | } 18 | 19 | private static SessionFactory buildSessionFactory(Map parameters) { 20 | final Configuration hibernateConfiguration = new Configuration() 21 | .addAnnotatedClass(PersistedFeed.class) 22 | .addAnnotatedClass(PersistedEntry.class) 23 | .addAnnotatedClass(PersistedCategory.class); 24 | 25 | for (Map.Entry userParameter : parameters.entrySet()) { 26 | hibernateConfiguration.setProperty(userParameter.getKey(), userParameter.getValue()); 27 | } 28 | 29 | return hibernateConfiguration.buildSessionFactory(); 30 | } 31 | 32 | public Session getSession() { 33 | return sessionFactory.openSession(); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /documentation/src/resources/samples/ah-application-context-db-h2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 17 | 18 | 19 | 20 | 27 | -------------------------------------------------------------------------------- /documentation/src/resources/samples/ah-application-context-db-mysql.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 27 | -------------------------------------------------------------------------------- /adapters/hibernate/src/main/java/org/atomhopper/dbal/FeedRepository.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.dbal; 2 | 3 | import org.atomhopper.adapter.jpa.PersistedCategory; 4 | import org.atomhopper.adapter.jpa.PersistedEntry; 5 | import org.atomhopper.adapter.jpa.PersistedFeed; 6 | import org.atomhopper.hibernate.query.CategoryCriteriaGenerator; 7 | 8 | import java.util.Collection; 9 | import java.util.List; 10 | import java.util.Set; 11 | 12 | public interface FeedRepository { 13 | 14 | Set getCategoriesForFeed(final String feedName); 15 | 16 | Collection getAllFeeds(); 17 | 18 | PersistedFeed getFeed(String resourceName); 19 | 20 | void saveFeed(PersistedFeed feed); 21 | 22 | List getFeedHead(String feedName, CategoryCriteriaGenerator categoryCriteria, int pageSize); 23 | 24 | List getFeedPage(String feedName, PersistedEntry markerEntry, PageDirection direction, 25 | CategoryCriteriaGenerator categoryCriteria, int pageSize); 26 | 27 | PersistedEntry getEntry(String entryId, String feedName); 28 | 29 | List getLastPage(String feedName, int pageSize, CategoryCriteriaGenerator criteriaGenerator); 30 | 31 | void saveEntry(PersistedEntry entry); 32 | 33 | Set updateCategories(Set categories); 34 | 35 | PersistedEntry getNextMarker(PersistedEntry persistedEntry, String feedName, CategoryCriteriaGenerator criteriaGenerator); 36 | } 37 | -------------------------------------------------------------------------------- /hopper/src/main/java/org/atomhopper/util/uri/CustomSchemeResolver.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.util.uri; 2 | 3 | import java.net.MalformedURLException; 4 | import java.net.URI; 5 | import java.net.URL; 6 | import java.util.HashSet; 7 | import java.util.Set; 8 | 9 | /** 10 | * Due to some strange restriction placed on the URLStreamHandlerFactory (see: 11 | * http://download.oracle.com/javase/6/docs/api/index.html?java/net/URL.html) this 12 | * little mapper was introduced to allow for custom scheme matching. 13 | */ 14 | public class CustomSchemeResolver implements UriToUrlResolver { 15 | 16 | public static UriToUrlResolver newDefaultInstance() { 17 | final CustomSchemeResolver resolverInstance = new CustomSchemeResolver(); 18 | resolverInstance.addMapper(new ClasspathSchemeMapper()); 19 | 20 | return resolverInstance; 21 | } 22 | 23 | 24 | private final Set schemeMapperSet; 25 | 26 | public CustomSchemeResolver() { 27 | schemeMapperSet = new HashSet(); 28 | } 29 | 30 | public void addMapper(URISchemeMapper mapper) { 31 | schemeMapperSet.add(mapper); 32 | } 33 | 34 | @Override 35 | public URL toURL(URI uri) throws MalformedURLException { 36 | for (URISchemeMapper mapper : schemeMapperSet) { 37 | if (mapper.canMap(uri)) { 38 | return mapper.toURL(uri); 39 | } 40 | } 41 | 42 | //Default fallback 43 | return uri.toURL(); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /test-suite/src/test/java/org/atomhopper/GetVersionPathTest.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper; 2 | 3 | import static junit.framework.Assert.assertEquals; 4 | import static junit.framework.Assert.assertTrue; 5 | import org.apache.commons.httpclient.HttpClient; 6 | import org.apache.commons.httpclient.HttpMethod; 7 | import org.apache.commons.httpclient.HttpStatus; 8 | import org.apache.commons.httpclient.methods.GetMethod; 9 | import org.junit.Test; 10 | import org.junit.experimental.runners.Enclosed; 11 | import org.junit.runner.RunWith; 12 | 13 | @RunWith(Enclosed.class) 14 | public class GetVersionPathTest extends JettyIntegrationTestHarness { 15 | 16 | private static final HttpClient httpClient = new HttpClient(); 17 | private static final String urlAndPort = "http://localhost:" + getPort(); 18 | 19 | public static GetMethod getVersionPathMethod() { 20 | return new GetMethod(urlAndPort + "/buildinfo"); 21 | } 22 | 23 | public static class WhenGettingVersionInfo { 24 | 25 | private final HttpMethod getVersionMethod = getVersionPathMethod(); 26 | 27 | @Test 28 | public void shouldReturnHTTP200AndEmptyJSON() throws Exception { 29 | assertEquals("Getting the version should return a 200", HttpStatus.SC_OK, httpClient.executeMethod(getVersionMethod)); 30 | // Since the test doesn't have the Maven version info, empty JSON will come back 31 | assertTrue(new String(getVersionMethod.getResponseBody()).contains("{}")); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /adapters/hibernate/src/test/java/org/atomhopper/hibernate/HibernateFeedRepositoryTestMain.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.hibernate; 2 | 3 | import org.atomhopper.adapter.jpa.PersistedEntry; 4 | import org.atomhopper.adapter.jpa.PersistedFeed; 5 | import org.atomhopper.hibernate.actions.SimpleSessionAction; 6 | import org.hibernate.Session; 7 | import org.hibernate.criterion.Restrictions; 8 | 9 | import java.util.Collections; 10 | 11 | public class HibernateFeedRepositoryTestMain { 12 | 13 | public static void main(String[] args) { 14 | final HibernateFeedRepository feedRepository = new HibernateFeedRepository(Collections.EMPTY_MAP); 15 | 16 | feedRepository.saveFeed(new PersistedFeed("testing", "uuid:not-really")); 17 | PersistedFeed f = feedRepository.getFeed("testing"); 18 | 19 | System.out.println(f != null ? f.getName() : "null"); 20 | 21 | PersistedEntry entry = new PersistedEntry("some-random-uuid"); 22 | entry.setFeed(new PersistedFeed("testing", "uuid:not-really")); 23 | 24 | feedRepository.saveEntry(entry); 25 | 26 | feedRepository.performSimpleAction(new SimpleSessionAction() { 27 | 28 | @Override 29 | public void perform(Session liveSession) { 30 | PersistedFeed f = (PersistedFeed) liveSession.createCriteria(PersistedFeed.class).add(Restrictions.idEq("testing")).list().get(0); 31 | 32 | System.out.println("Entries: " + (f != null ? f.getEntries().size() : 0)); 33 | } 34 | }); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /docker/README.md: -------------------------------------------------------------------------------- 1 | # docker build image and run the conatiner 2 | Your current direcotry should be pointing to ***atom-hopper/docker***. 3 | Run the following command to build an image. 4 | ``` 5 | $docker build -t atomhopper:latest-alpine . 6 | ``` 7 | You can use the following command to run a container by provinding the appropriate values to the variables. 8 | ``` 9 | $docker run -d --name [Conatiner_Name] -p 8080:8080 -e DB_TYPE=[Database_Type (PostgreSQL, MySQL)] -e DB_USER=[Database_Username] -e DB_PASSWORD=[Database_Password] -e DB_HOST=[IP:PORT] atomhopper:latest-alpine 10 | ``` 11 | 12 | To run atomhopper with default database configuration (H2) and port 8080 13 | ``` 14 | $docker run -d --name atomhopper -p 8080:8080 atomhopper:latest-alpine 15 | ``` 16 | Test the sample feed at http://localhost:8080/namespace/feed 17 | 18 | H2 is the default databse configured to be used. The databse file for this is present under */opt/atomhopper* 19 | 20 | Following environment variables are set by default 21 | ``` 22 | JAVA_HOME "/opt/java/openjdk8/jre" 23 | CATALINA_HOME "/opt/tomcat" 24 | AH_HOME "/opt/atomhopper" 25 | AH_VERSION "1.2.33" 26 | ``` 27 | For specific databse configuration of your choice (PostgreSQL,MySQL) provide values for the variables DB_TYPE, DB_USER, DB_PASSWORD and DB_HOST 28 | 29 | Example of running with a PostgreSQL databse hosted externally. 30 | ``` 31 | $docker run -d --name atomhopper -p 8080:8080 -e DB_TYPE=PostgreSQL -e DB_USER=postgresql -e DB_PASSWORD=postgresql -e DB_HOST=10.0.0.1:5432 atomhopper:latest-alpine 32 | ``` 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /server/src/main/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | /var/log/atomhopper/atomhopper-logback.log 14 | 15 | %d{yyyy-MM-dd HH:mm:s} %-4r [%t] %-5p %c %x - %m%n 16 | 17 | 18 | 100 19 | /var/log/atomhopper/atomhopper-logback.log.%i 20 | 21 | 22 | 100MB 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /hopper/src/main/resources/template-logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | /var/log/atomhopper/atomhopper-logback.log 14 | 15 | %d{yyyy-MM-dd HH:mm:s} %-4r [%t] %-5p %c %x - %m%n 16 | 17 | 18 | 100 19 | /var/log/atomhopper/atomhopper-logback.log.%i 20 | 21 | 22 | 100MB 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /atomhopper/src/main/webapp/META-INF/template-logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | /var/log/atomhopper/atomhopper-logback.log 14 | 15 | %d{yyyy-MM-dd HH:mm:s} %-4r [%t] %-5p %c %x - %m%n 16 | 17 | 18 | 100 19 | /var/log/atomhopper/atomhopper-logback.log.%i 20 | 21 | 22 | 100MB 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /hopper/src/main/java/org/atomhopper/abdera/filter/FeedEntityTagProcessor.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.abdera.filter; 2 | 3 | import org.apache.abdera.model.Feed; 4 | import org.apache.abdera.protocol.server.RequestContext; 5 | import org.apache.abdera.util.EntityTag; 6 | import org.atomhopper.response.AdapterResponse; 7 | 8 | /** 9 | * AdapterResponseProcessor for a Feed that adds a weak entity tag to the feed using 10 | * the entry id of the first entry in the feed. 11 | */ 12 | public class FeedEntityTagProcessor implements AdapterResponseInterceptor { 13 | 14 | @Override 15 | public void process(RequestContext rc, AdapterResponse adapterResponse) { 16 | final Feed f = adapterResponse.getBody(); 17 | 18 | if(f == null) { 19 | return; 20 | } 21 | 22 | final int totalEntries = f.getEntries().size(); 23 | 24 | // If there are no entries in the feed 25 | if (totalEntries == 0) { 26 | return; 27 | } 28 | 29 | // Get the id of the first entry on this page 30 | String id = f.getEntries().get(0).getId().toString(); 31 | // Get the id of the last entry on this page 32 | String lastId = f.getEntries().get(totalEntries-1).getId().toString(); 33 | 34 | EntityTag feedEtag = createEntityTag(rc, id, lastId); 35 | adapterResponse.setEntityTag(feedEtag); 36 | } 37 | 38 | protected EntityTag createEntityTag(RequestContext rc, String firstId, String lastId) { 39 | return new EntityTag(firstId + ":" + lastId, true); 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /hopper/src/main/java/org/atomhopper/adapter/impl/DisabledPublisher.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.adapter.impl; 2 | 3 | import org.apache.abdera.model.Entry; 4 | import org.atomhopper.adapter.FeedPublisher; 5 | import org.atomhopper.adapter.ResponseBuilder; 6 | import org.atomhopper.adapter.request.adapter.DeleteEntryRequest; 7 | import org.atomhopper.adapter.request.adapter.PostEntryRequest; 8 | import org.atomhopper.adapter.request.adapter.PutEntryRequest; 9 | import org.atomhopper.response.AdapterResponse; 10 | import org.atomhopper.response.EmptyBody; 11 | 12 | public final class DisabledPublisher extends AbstractDisabledAdapter implements FeedPublisher { 13 | 14 | private static final DisabledPublisher INSTANCE = new DisabledPublisher(); 15 | private static final String OP_NOT_SUPPORTED_MESSAGE = "Operation not supported"; 16 | 17 | public static DisabledPublisher getInstance() { 18 | return INSTANCE; 19 | } 20 | 21 | private DisabledPublisher() { 22 | } 23 | 24 | @Override 25 | public AdapterResponse deleteEntry(DeleteEntryRequest deleteEntryRequest) { 26 | return ResponseBuilder.notImplemented(OP_NOT_SUPPORTED_MESSAGE); 27 | } 28 | 29 | @Override 30 | public AdapterResponse postEntry(PostEntryRequest postEntryRequest) { 31 | return ResponseBuilder.notImplemented(OP_NOT_SUPPORTED_MESSAGE); 32 | } 33 | 34 | @Override 35 | public AdapterResponse putEntry(PutEntryRequest putEntryRequest) { 36 | return ResponseBuilder.notImplemented(OP_NOT_SUPPORTED_MESSAGE); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /adapters/migration/src/main/java/org/atomhopper/migration/adapter/MigrationFeedInformation.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.migration.adapter; 2 | 3 | import org.apache.abdera.model.Categories; 4 | import org.atomhopper.adapter.FeedInformation; 5 | import org.atomhopper.adapter.NotImplemented; 6 | import org.atomhopper.adapter.request.adapter.GetCategoriesRequest; 7 | import org.atomhopper.adapter.request.feed.FeedRequest; 8 | import org.atomhopper.migration.domain.MigrationReadFrom; 9 | 10 | public class MigrationFeedInformation implements FeedInformation { 11 | 12 | private FeedInformation oldFeedInformation; 13 | private FeedInformation newFeedInformation; 14 | private MigrationReadFrom readFrom; 15 | 16 | public void setOldFeedInformation(FeedInformation oldFeedInformation) { 17 | this.oldFeedInformation = oldFeedInformation; 18 | } 19 | 20 | public void setNewFeedInformation(FeedInformation newFeedInformation) { 21 | this.newFeedInformation = newFeedInformation; 22 | } 23 | 24 | public void setReadFrom(MigrationReadFrom readFrom) { 25 | this.readFrom = readFrom; 26 | } 27 | 28 | @Override 29 | public String getId(FeedRequest getFeedRequest) { 30 | return readFrom == MigrationReadFrom.NEW ? newFeedInformation.getId(getFeedRequest) 31 | : oldFeedInformation.getId(getFeedRequest); 32 | } 33 | 34 | @Override 35 | @NotImplemented 36 | public Categories getCategories(GetCategoriesRequest getCategoriesRequest) { 37 | throw new UnsupportedOperationException("Not supported yet."); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /docker/Dockerfile: -------------------------------------------------------------------------------- 1 | #base tomcat 9 with openjdk 8 2 | FROM tomcat:9.0.41-jdk8 as tomcat 3 | 4 | FROM adoptopenjdk/openjdk8:alpine-slim 5 | 6 | LABEL maintainer="AtomHopperTeam@rackspace.com" \ 7 | #Atom Hopper version 8 | version="1.2.33" \ 9 | description="Docker image for Atom Hopper" 10 | 11 | #The database type 12 | ENV DB_TYPE=H2 \ 13 | #Database username 14 | DB_USER=sa \ 15 | #Database password 16 | DB_PASSWORD= \ 17 | #Database Host:Port 18 | DB_HOST=h2 \ 19 | AH_VERSION=1.2.33 \ 20 | CATALINA_HOME=/opt/tomcat \ 21 | AH_HOME=/opt/atomhopper \ 22 | PATH=${PATH}:${CATALINA_HOME}/bin:${AH_HOME} 23 | 24 | RUN mkdir -p "${CATALINA_HOME}" "${AH_HOME}" /etc/atomhopper/ /var/log/atomhopper/ 25 | 26 | WORKDIR ${AH_HOME} 27 | 28 | COPY --from=tomcat /usr/local/tomcat ${CATALINA_HOME} 29 | COPY start.sh . 30 | 31 | RUN apk --no-cache add curl \ 32 | && curl -o atomhopper.war https://maven.research.rackspacecloud.com/content/repositories/releases/org/atomhopper/atomhopper/${AH_VERSION}/atomhopper-${AH_VERSION}.war \ 33 | && unzip atomhopper.war META-INF/application-context.xml META-INF/template-logback.xml WEB-INF/classes/META-INF/atom-server.cfg.xml -d . \ 34 | && mv META-INF/application-context.xml WEB-INF/classes/META-INF/atom-server.cfg.xml /etc/atomhopper/ \ 35 | && mv META-INF/template-logback.xml /etc/atomhopper/logback.xml \ 36 | && mv atomhopper.war ${CATALINA_HOME}/webapps/ROOT.war \ 37 | && rm -rf META-INF WEB-INF \ 38 | && chmod +x ${AH_HOME}/start.sh 39 | 40 | EXPOSE 8080 41 | 42 | CMD ["start.sh"] 43 | 44 | -------------------------------------------------------------------------------- /atomhopper/src/main/resources/META-INF/atom-server.cfg.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 10 | 11 | 12 | 18 | 19 | 20 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /hopper/src/main/java/org/atomhopper/abdera/response/EmptyBodyResponseHandler.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.abdera.response; 2 | 3 | import org.apache.abdera.protocol.server.ProviderHelper; 4 | import org.apache.abdera.protocol.server.RequestContext; 5 | import org.apache.abdera.protocol.server.ResponseContext; 6 | import org.atomhopper.abdera.filter.AdapterResponseInterceptor; 7 | import org.atomhopper.response.AdapterResponse; 8 | import org.atomhopper.response.EmptyBody; 9 | 10 | public class EmptyBodyResponseHandler extends AbstractResponseHandler { 11 | 12 | public EmptyBodyResponseHandler(String[] allowedMethods, AdapterResponseInterceptor... interceptors) { 13 | super(allowedMethods, interceptors); 14 | } 15 | 16 | @Override 17 | protected ResponseContext handleAdapterResponse(RequestContext rc, AdapterResponse adapterResponse) { 18 | switch (adapterResponse.getResponseStatus()) { 19 | case NOT_FOUND: 20 | return ProviderHelper.notfound(rc, adapterResponse.getMessage()); 21 | 22 | case INTERNAL_SERVER_ERROR: 23 | return ProviderHelper.servererror(rc, adapterResponse.getMessage(), new InternalServerException()); 24 | 25 | case METHOD_NOT_ALLOWED: 26 | return ProviderHelper.notallowed(rc, adapterResponse.getMessage(), getAllowedHttpMethods()); 27 | 28 | case BAD_REQUEST: 29 | return ProviderHelper.badrequest(rc, adapterResponse.getMessage()); 30 | 31 | default: 32 | return ProviderHelper.nocontent(); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /hopper/src/main/java/org/atomhopper/adapter/jpa/PersistedFeed.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.adapter.jpa; 2 | 3 | import javax.persistence.Column; 4 | import javax.persistence.Entity; 5 | import javax.persistence.FetchType; 6 | import javax.persistence.Id; 7 | import javax.persistence.OneToMany; 8 | import javax.persistence.Table; 9 | import java.io.Serializable; 10 | import java.util.Collections; 11 | import java.util.HashSet; 12 | import java.util.Set; 13 | 14 | @Entity 15 | @Table(name = "Feeds") 16 | public class PersistedFeed implements Serializable { 17 | 18 | @Id 19 | @Column(name = "Name") 20 | private String name; 21 | 22 | @Column(name = "FeedID") 23 | private String feedId; 24 | 25 | @OneToMany(mappedBy = "feed", fetch = FetchType.LAZY) 26 | private Set entries; 27 | 28 | public PersistedFeed() { 29 | entries = Collections.EMPTY_SET; 30 | } 31 | 32 | public PersistedFeed(String name, String feedId) { 33 | entries = new HashSet(); 34 | 35 | this.feedId = feedId; 36 | this.name = name; 37 | } 38 | 39 | public Set getEntries() { 40 | return entries; 41 | } 42 | 43 | public void setEntries(Set entries) { 44 | this.entries = entries; 45 | } 46 | 47 | public String getFeedId() { 48 | return feedId; 49 | } 50 | 51 | public void setFeedId(String feedId) { 52 | this.feedId = feedId; 53 | } 54 | 55 | public String getName() { 56 | return name; 57 | } 58 | 59 | public void setName(String name) { 60 | this.name = name; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /hopper/src/main/java/org/atomhopper/util/config/resource/uri/URIConfigurationResource.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.util.config.resource.uri; 2 | 3 | import org.atomhopper.util.config.resource.ConfigurationResource; 4 | import org.atomhopper.util.config.resource.ConfigurationResourceException; 5 | import org.atomhopper.util.uri.CustomSchemeResolver; 6 | import org.atomhopper.util.uri.UriToUrlResolver; 7 | 8 | import java.io.IOException; 9 | import java.io.InputStream; 10 | import java.net.MalformedURLException; 11 | import java.net.URI; 12 | import java.net.URL; 13 | 14 | public class URIConfigurationResource implements ConfigurationResource { 15 | 16 | private final URI resourceLocation; 17 | private final UriToUrlResolver uriSchemeResolver; 18 | 19 | public URIConfigurationResource(URI resourceLocation) { 20 | this(resourceLocation, CustomSchemeResolver.newDefaultInstance()); 21 | } 22 | 23 | public URIConfigurationResource(URI resourceLocation, UriToUrlResolver customUriSchemeResolver) { 24 | this.resourceLocation = resourceLocation; 25 | this.uriSchemeResolver = customUriSchemeResolver; 26 | } 27 | 28 | @Override 29 | public InputStream getInputStream() throws IOException { 30 | try { 31 | final URL location = uriSchemeResolver.toURL(resourceLocation); 32 | return location.openStream(); 33 | } catch (MalformedURLException murle) { 34 | throw new ConfigurationResourceException("URI produces a malformed URL. URI, \"" 35 | + resourceLocation.toString() 36 | + "\" may be unsupported. Reason: " + murle.getMessage(), murle); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /hopper/src/main/java/org/atomhopper/adapter/request/AbstractClientRequest.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.adapter.request; 2 | 3 | import org.apache.abdera.Abdera; 4 | import org.apache.abdera.protocol.server.RequestContext; 5 | import org.atomhopper.util.uri.template.TemplateParameters; 6 | 7 | import java.util.List; 8 | 9 | /** 10 | * Base class that contains the wrapped RequestContext from Abdera. All domain 11 | * specific request classes should inherit from this class. 12 | */ 13 | public abstract class AbstractClientRequest implements ClientRequest { 14 | 15 | private final RequestContext abderaRequestContext; 16 | 17 | protected AbstractClientRequest(RequestContext abderaRequestContext) { 18 | this.abderaRequestContext = abderaRequestContext; 19 | } 20 | 21 | protected RequestContext getRequestContext() { 22 | return abderaRequestContext; 23 | } 24 | 25 | @Override 26 | public String urlFor(TemplateParameters param) { 27 | return abderaRequestContext.urlFor(param.getTargetTemplateKey(), param); 28 | } 29 | 30 | @Override 31 | public Abdera getAbdera() { 32 | return abderaRequestContext.getAbdera(); 33 | } 34 | 35 | @Override 36 | public String getTargetParameter(String parameter) { 37 | return abderaRequestContext.getTarget().getParameter(parameter); 38 | } 39 | 40 | @Override 41 | public String getRequestParameter(String parameter) { 42 | return abderaRequestContext.getParameter(parameter); 43 | } 44 | 45 | @Override 46 | public List getRequestParameters(String parameter) { 47 | return abderaRequestContext.getParameters(parameter); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /hopper/src/test/java/org/atomhopper/util/config/resource/uri/URIConfigurationResourceTest.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.util.config.resource.uri; 2 | 3 | import org.atomhopper.util.uri.CustomSchemeResolver; 4 | import org.junit.Before; 5 | import org.junit.Test; 6 | import org.junit.experimental.runners.Enclosed; 7 | import org.junit.runner.RunWith; 8 | 9 | import java.io.InputStream; 10 | import java.net.URI; 11 | 12 | import static org.junit.Assert.assertNotNull; 13 | import static org.junit.Assert.assertTrue; 14 | 15 | @RunWith(Enclosed.class) 16 | public class URIConfigurationResourceTest { 17 | 18 | private static final String VALID_CLASSPATH_URI = "classpath:/META-INF/schema/config/bindings.xjb"; 19 | 20 | public static class WhenGettingInputStreamsFromURILocations { 21 | 22 | private URIConfigurationResource configurationResource; 23 | 24 | @Before 25 | public void standUp() throws Exception { 26 | configurationResource = new URIConfigurationResource( 27 | new URI(VALID_CLASSPATH_URI), 28 | CustomSchemeResolver.newDefaultInstance()); 29 | } 30 | 31 | @Test 32 | public void shouldGenerateInputStreamWithValidLocatableURIs() throws Exception { 33 | final InputStream result = configurationResource.getInputStream(); 34 | 35 | assertNotNull("InputStream returned by the static classpath URI, \"" 36 | + VALID_CLASSPATH_URI 37 | + "\" must not be null", result); 38 | 39 | assertTrue("InputStream should have at least one byte readable", result.read() > 0); 40 | 41 | result.close(); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /test-suite/src/test/java/org/atomhopper/abdera/filter/SelectiveURIJSONFilterTest.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.abdera.filter; 2 | 3 | import org.apache.commons.httpclient.HttpClient; 4 | import org.apache.commons.httpclient.HttpMethod; 5 | import org.apache.commons.httpclient.methods.GetMethod; 6 | import org.atomhopper.JettyIntegrationTestHarness; 7 | import org.junit.Test; 8 | import org.junit.experimental.runners.Enclosed; 9 | import org.junit.runner.RunWith; 10 | 11 | import static junit.framework.Assert.assertEquals; 12 | 13 | /** 14 | * User: shin4590 15 | * Date: 7/3/14 16 | */ 17 | @RunWith(Enclosed.class) 18 | public class SelectiveURIJSONFilterTest extends JettyIntegrationTestHarness { 19 | 20 | private static final HttpClient httpClient = new HttpClient(); 21 | private static final String urlAndPort = "http://localhost:" + getPort(); 22 | 23 | public static class WhenGettingFeedsWithFormatJsonWhereJsonIsAllowed { 24 | 25 | @Test 26 | public void shouldReturnOK() throws Exception { 27 | final HttpMethod getFeedMethod = new GetMethod(urlAndPort + "/namespace2/feed2/?format=json"); 28 | httpClient.executeMethod(getFeedMethod); 29 | assertEquals("Response code: ", 200, getFeedMethod.getStatusCode()); 30 | } 31 | } 32 | 33 | public static class WhenGettingFeedsWithFormatJsonWhereJsonIsNotAllowed { 34 | 35 | @Test 36 | public void shouldReturnOK() throws Exception { 37 | final HttpMethod getFeedMethod = new GetMethod(urlAndPort + "/namespace6/feed6/?format=json"); 38 | httpClient.executeMethod(getFeedMethod); 39 | assertEquals("Response code: ", 400, getFeedMethod.getStatusCode()); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /tests/jetty-killer/src/main/java/org/atomhopper/jettykiller/JettyKiller.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.jettykiller; 2 | 3 | 4 | 5 | 6 | import org.kohsuke.args4j.CmdLineException; 7 | import org.kohsuke.args4j.CmdLineParser; 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | 11 | 12 | public class JettyKiller { 13 | private static final Logger LOG = LoggerFactory.getLogger(JettyKiller.class); 14 | private static final int MAX_PORT_NUMBER = 49150; 15 | private static final int MIN_PORT_NUMBER = 1024; 16 | 17 | private JettyKiller(){} 18 | 19 | public static void main(String[] args) { 20 | CommandLineArguments commandLineArgs = new CommandLineArguments(); 21 | CmdLineParser cmdLineParser = new CmdLineParser(commandLineArgs); 22 | AtomHopperServerControl serverControl = new AtomHopperServerControl(commandLineArgs); 23 | 24 | try { 25 | cmdLineParser.parseArgument(args); 26 | } catch (CmdLineException e) { 27 | displayUsage(cmdLineParser, e); 28 | return; 29 | } 30 | 31 | if (!(portIsInRange(commandLineArgs.stopport))) { 32 | LOG.info("Invalid Atom Hopper port setting, use a value between 1 and 65535"); 33 | return; 34 | } 35 | 36 | serverControl.stopAtomHopper(); 37 | } 38 | 39 | private static void displayUsage(CmdLineParser cmdLineParser, Exception e) { 40 | System.err.println(e.getMessage()); 41 | System.err.println("java -jar AtomHopperServer.jar [options...] arguments..."); 42 | cmdLineParser.printUsage(System.err); 43 | } 44 | 45 | private static boolean portIsInRange(int portNum) { 46 | return (portNum <= MAX_PORT_NUMBER) && (portNum >= MIN_PORT_NUMBER); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /docker/start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | export APP_CTX_PATH=/etc/atomhopper 3 | echo "using APP_CTX_PATH="$APP_CTX_PATH 4 | 5 | if [[ -e $APP_CTX_PATH/application-context.xml.orig ]] 6 | then 7 | echo "Replacing application-context.xml with original config." 8 | mv $APP_CTX_PATH/application-context.xml.orig $APP_CTX_PATH/application-context.xml 9 | fi 10 | echo "Database type selected:"$DB_TYPE 11 | 12 | #DB configuration 13 | if [[ $DB_TYPE != 'H2' ]] ; then 14 | #Comment default H2 Database and backup the original file 15 | sed -i.orig -e '/Start H2 Config/a ' $APP_CTX_PATH/application-context.xml 16 | 17 | #Enable databse based on the DB_TYPE value 18 | sed -i "/Start $DB_TYPE Config/{n;;d}" $APP_CTX_PATH/application-context.xml && sed -i "/Start $DB_TYPE Config/{n;n;n;n;n;n;;d}" $APP_CTX_PATH/application-context.xml 19 | 20 | #Remove databse username and password lines 21 | sed -i "/Start $DB_TYPE Config/{n;n;n;n;N;;d}" $APP_CTX_PATH/application-context.xml 22 | 23 | #Replace username and passowrd lines with env variable value 24 | sed -i -e "/End $DB_TYPE Config/i " -e "/End ${DB_TYPE} Config/i " $APP_CTX_PATH/application-context.xml 25 | 26 | #DB_HOST configuration 27 | if [ "$DB_TYPE" = 'MySQL' ] ; then 28 | sed -i -e "s/:mysql:\/\/localhost:8889/:mysql:\/\/$DB_HOST/g" $APP_CTX_PATH/application-context.xml 29 | fi 30 | if [ "$DB_TYPE" = 'PostgreSQL' ] ; then 31 | sed -i -e "s/:postgresql:\/\/localhost:5432/:postgresql:\/\/$DB_HOST/g" $APP_CTX_PATH/application-context.xml 32 | fi 33 | fi 34 | 35 | #Start tomcat server 36 | sh /opt/tomcat/bin/catalina.sh run -------------------------------------------------------------------------------- /adapters/jdbc/src/main/resources/ddl/jdbc/atomhopper-fresh-schema-ddl-postgres.sql: -------------------------------------------------------------------------------- 1 | -- This file can be used to create fresh AH DB schema. 2 | -- It is useful when we create new feeds. 3 | -- 4 | -- This file needs to be kept up-to-date, everytime we change 5 | -- AH schema. 6 | -- 7 | -- You will have to run this SQL as the schema user. 8 | -- For example, if you are adding a new feed called 'new_feed' 9 | -- you will have to run psql like this: 10 | -- psql -h localhost -U new_feed -d new_feed -f 11 | -- 12 | 13 | SET statement_timeout = 0; 14 | SET client_encoding = 'UTF8'; 15 | SET standard_conforming_strings = on; 16 | SET check_function_bodies = false; 17 | SET client_min_messages = warning; 18 | 19 | CREATE TABLE entries ( 20 | id bigserial, 21 | entryid text NOT NULL UNIQUE, 22 | creationdate timestamp without time zone NOT NULL DEFAULT current_timestamp, 23 | datelastupdated timestamp without time zone NOT NULL DEFAULT current_timestamp, 24 | entrybody text, 25 | feed text, 26 | -- categories which are mapped to specific columns 27 | -- remove if you aren't configuring your FeedSource & FeedPublisher accordingly 28 | eventtype text, 29 | tenantid text, 30 | -- --------------------- 31 | categories character varying[], 32 | PRIMARY KEY(datelastupdated, id) 33 | ); 34 | CREATE INDEX entryid_idx on entries(entryid); 35 | CREATE INDEX categories_idx on entries(categories); 36 | CREATE INDEX feed_idx on entries(feed); 37 | CREATE INDEX feed_entryid_idx on entries(feed, entryid); 38 | 39 | -- categories which are mapped to specific columns 40 | -- remove if you aren't configuring your FeedSource & FeedPublisher accordingly 41 | CREATE INDEX eventtype_idx on entries( eventtype ); 42 | CREATE INDEX tenantid_idx on entries( tenantid ); 43 | -- --------------------- 44 | -------------------------------------------------------------------------------- /hopper/src/main/java/org/atomhopper/adapter/impl/DisabledFeedSource.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.adapter.impl; 2 | 3 | import org.apache.abdera.model.Entry; 4 | import org.apache.abdera.model.Feed; 5 | import org.atomhopper.adapter.FeedInformation; 6 | import org.atomhopper.adapter.FeedSource; 7 | import org.atomhopper.adapter.ResponseBuilder; 8 | import org.atomhopper.adapter.request.adapter.GetEntryRequest; 9 | import org.atomhopper.adapter.request.adapter.GetFeedRequest; 10 | import org.atomhopper.response.AdapterResponse; 11 | 12 | import java.net.URL; 13 | 14 | public final class DisabledFeedSource extends AbstractDisabledAdapter implements FeedSource { 15 | 16 | private static final DisabledFeedSource INSTANCE = new DisabledFeedSource(); 17 | private static final String OP_NOT_SUPPORTED_MESSAGE = "Operation not supported"; 18 | 19 | public static DisabledFeedSource getInstance() { 20 | return INSTANCE; 21 | } 22 | 23 | private DisabledFeedSource() { 24 | } 25 | 26 | @Override 27 | public FeedInformation getFeedInformation() { 28 | return DisabledFeedInformation.getInstance(); 29 | } 30 | 31 | @Override 32 | public AdapterResponse getEntry(GetEntryRequest getEntryRequest) { 33 | return ResponseBuilder.notImplemented(OP_NOT_SUPPORTED_MESSAGE); 34 | } 35 | 36 | @Override 37 | public void setCurrentUrl( URL urlCurrent ) { 38 | // do nothing since this feed is disabled 39 | } 40 | 41 | @Override 42 | public void setArchiveUrl( URL url ) { 43 | // do nothing since this feed is disabled 44 | } 45 | 46 | @Override 47 | public AdapterResponse getFeed(GetFeedRequest getFeedRequest) { 48 | return ResponseBuilder.notImplemented(OP_NOT_SUPPORTED_MESSAGE); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /tests/customjdbc-regression/src/test/jmeter/postgres-adaptor-override-on-application-context.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | true 32 | 33 | 34 | true 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /tests/customjdbc-regression/src/test/jmeter/postgres-adaptor-override-off-application-context.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | false 32 | 33 | 34 | false 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /hopper/src/test/java/org/atomhopper/util/TargetRegexBuilderFeedTest.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.util; 2 | 3 | import org.junit.Test; 4 | import org.junit.experimental.runners.Enclosed; 5 | import org.junit.runner.RunWith; 6 | 7 | import java.util.regex.Pattern; 8 | 9 | import static org.junit.Assert.assertTrue; 10 | 11 | /** 12 | * 13 | */ 14 | @RunWith(Enclosed.class) 15 | public class TargetRegexBuilderFeedTest { 16 | 17 | public static class WhenBuildingFeedRegexes extends TargetRegexBuilderTestParent { 18 | 19 | @Test 20 | public void shouldMatchAllFeedVariations() { 21 | final TargetRegexBuilder target = feedRegexBuilder(); 22 | final Pattern targetRegex = Pattern.compile(target.toFeedPattern()); 23 | 24 | assertTrue("Should match plain feed URI - regex is: " + targetRegex.pattern(), 25 | targetRegex.matcher(FEED).matches()); 26 | assertTrue("Should match plain feed URI with a slash - regex is: " + targetRegex.pattern(), 27 | targetRegex.matcher(addTrailingSlash(FEED)).matches()); 28 | } 29 | 30 | @Test 31 | public void shouldMatchWithNonRootContextPath() { 32 | final TargetRegexBuilder target = feedRegexBuilder(); 33 | target.setContextPath(CONTEXT_PATH); 34 | 35 | final Pattern targetRegex = Pattern.compile(target.toFeedPattern()); 36 | 37 | assertTrue("Should match feed URI with a context root - regex is: " + targetRegex.pattern(), 38 | targetRegex.matcher(addContextRoot(FEED)).matches()); 39 | } 40 | 41 | @Test(expected = IllegalStateException.class) 42 | public void shouldFailToBuildRegexWhenFeedIsNotSet() { 43 | new TargetRegexBuilder().toFeedPattern(); 44 | } 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /hopper/src/test/java/org/atomhopper/util/TargetRegexBuilderTestParent.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.util; 2 | 3 | public class TargetRegexBuilderTestParent { 4 | 5 | public TargetRegexBuilder workspaceRegexBuilder() { 6 | final TargetRegexBuilder target = new TargetRegexBuilder(); 7 | target.setWorkspace("workspace"); 8 | 9 | return target; 10 | } 11 | 12 | public TargetRegexBuilder feedRegexBuilder() { 13 | final TargetRegexBuilder target = workspaceRegexBuilder(); 14 | target.setFeed("feed"); 15 | 16 | return target; 17 | } 18 | 19 | 20 | public static final String[] DEFAULT_CATEGORIES_SHORT = new String[]{"category_1", "category_2"}, 21 | DEFAULT_CATEGORIES_LONG = new String[]{"category_a", "category_b", "category_c", "category_d", "category_e"}; 22 | public static final String CONTEXT_PATH = "/approot", 23 | WORKSPACE = "/workspace", 24 | CATEGORIES = "/workspace/feed/categories", 25 | FEED = "/workspace/feed", 26 | ENTRY = "/workspace/feed/entries/tag:domain.com,2011-01-01:entry-id"; 27 | 28 | public static String withCategories(String base, String[] categories) { 29 | final StringBuilder uri = new StringBuilder(base); 30 | uri.append("?categories="); 31 | 32 | if (categories.length >= 1) { 33 | uri.append(categories[0]); 34 | 35 | for (int i = 1; i < categories.length; i++) { 36 | uri.append(";").append(categories[i]); 37 | } 38 | } 39 | 40 | return uri.toString(); 41 | } 42 | 43 | public static String addContextRoot(String base) { 44 | return CONTEXT_PATH + base; 45 | } 46 | 47 | public static String addTrailingSlash(String base) { 48 | return base + "/"; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /hopper/src/main/java/org/atomhopper/LogBackConfigLoader.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | 9 | import ch.qos.logback.classic.LoggerContext; 10 | import ch.qos.logback.classic.joran.JoranConfigurator; 11 | import ch.qos.logback.core.joran.spi.JoranException; 12 | 13 | /** 14 | * Simple Utility class for loading an external config file for logback 15 | * 16 | * This code is modified from https://bowerstudios.com/node/896 17 | */ 18 | public class LogBackConfigLoader { 19 | 20 | private Logger logger = LoggerFactory.getLogger(LogBackConfigLoader.class); 21 | 22 | public LogBackConfigLoader(String externalConfigFileLocation) throws IOException, JoranException { 23 | LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory(); 24 | 25 | File externalConfigFile = new File(externalConfigFileLocation); 26 | if (!externalConfigFile.exists()) { 27 | throw new IOException("Logback External Config File Parameter does not reference a file that exists"); 28 | } else if (!externalConfigFile.isFile()) { 29 | throw new IOException("Logback External Config File Parameter exists, but does not reference a file"); 30 | } else if (!externalConfigFile.canRead()) { 31 | throw new IOException("Logback External Config File exists and is a file, but cannot be read."); 32 | } else { 33 | JoranConfigurator configurator = new JoranConfigurator(); 34 | configurator.setContext(lc); 35 | lc.reset(); 36 | configurator.doConfigure(externalConfigFileLocation); 37 | 38 | logger.info("Configured Logback with config file from: " + externalConfigFileLocation); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /server/src/main/java/org/atomhopper/server/MonitorThread.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.server; 2 | 3 | 4 | import org.eclipse.jetty.server.Server; 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | 8 | import java.io.BufferedReader; 9 | import java.io.InputStreamReader; 10 | import java.net.InetAddress; 11 | import java.net.ServerSocket; 12 | import java.net.Socket; 13 | 14 | 15 | public class MonitorThread extends Thread { 16 | private static final Logger LOG = LoggerFactory.getLogger(AtomHopperServer.class); 17 | 18 | private ServerSocket socket; 19 | private final Server serverInstance; 20 | private static final String MONITOR_NAME = "StopMonitor"; 21 | 22 | public MonitorThread(Server serverInstance, final int stopPort, final String ipAddress) { 23 | this.serverInstance = serverInstance; 24 | 25 | setDaemon(true); 26 | setName(MONITOR_NAME); 27 | 28 | try { 29 | socket = new ServerSocket(stopPort, 1, InetAddress.getByName(ipAddress)); 30 | } catch (Exception e) { 31 | LOG.error("Fatal error while monitoring or trying to stop: " + e.getMessage(), e); 32 | } 33 | } 34 | 35 | @Override 36 | public void run() { 37 | Socket accept; 38 | 39 | try { 40 | accept = socket.accept(); 41 | BufferedReader reader = new BufferedReader(new InputStreamReader(accept.getInputStream())); 42 | reader.readLine(); 43 | LOG.info("Stopping Atom Hopper..."); 44 | serverInstance.stop(); 45 | LOG.info("Atom Hopper has been stopped"); 46 | accept.close(); 47 | socket.close(); 48 | } catch (Exception e) { 49 | LOG.error("Fatal error while monitoring or trying to stop: " + e.getMessage(), e); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /adapters/jdbc/src/test/java/org/atomhopper/jdbc/adapter/JdbcFeedInformationTest.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.jdbc.adapter; 2 | 3 | import org.atomhopper.adapter.request.adapter.GetCategoriesRequest; 4 | import org.atomhopper.adapter.request.feed.FeedRequest; 5 | 6 | import org.junit.Before; 7 | import org.junit.Test; 8 | import org.junit.experimental.runners.Enclosed; 9 | import org.junit.runner.RunWith; 10 | import org.springframework.jdbc.core.JdbcTemplate; 11 | 12 | import static org.junit.Assert.assertNotNull; 13 | import static org.mockito.Mockito.mock; 14 | 15 | @RunWith(Enclosed.class) 16 | public class JdbcFeedInformationTest { 17 | 18 | public static class WhenGettingPostgresFeedInformation { 19 | 20 | private JdbcTemplate jbdcTemplate; 21 | private FeedRequest feedRequest; 22 | private GetCategoriesRequest getCategoriesRequest; 23 | private JdbcFeedInformation postgresFeedInformation; 24 | 25 | @Before 26 | public void setUp() throws Exception { 27 | jbdcTemplate = mock(JdbcTemplate.class); 28 | feedRequest = mock(FeedRequest.class); 29 | getCategoriesRequest = mock(GetCategoriesRequest.class); 30 | 31 | postgresFeedInformation = new JdbcFeedInformation(); 32 | } 33 | 34 | @Test 35 | public void shouldCreatePostgresFeedInformation() throws Exception { 36 | assertNotNull(postgresFeedInformation); 37 | } 38 | 39 | @Test(expected=UnsupportedOperationException.class) 40 | public void shouldReturnId() throws Exception { 41 | postgresFeedInformation.getId(feedRequest); 42 | } 43 | 44 | @Test(expected=UnsupportedOperationException.class) 45 | public void shouldReturnCategories() throws Exception { 46 | postgresFeedInformation.getCategories(getCategoriesRequest); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /hopper/src/test/java/org/atomhopper/util/TargetRegexBuilderWorkspaceTest.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.util; 2 | 3 | import org.junit.Test; 4 | import org.junit.experimental.runners.Enclosed; 5 | import org.junit.runner.RunWith; 6 | 7 | import java.util.regex.Pattern; 8 | 9 | import static org.junit.Assert.assertTrue; 10 | 11 | /** 12 | * 13 | */ 14 | @RunWith(Enclosed.class) 15 | public class TargetRegexBuilderWorkspaceTest { 16 | 17 | public static class WhenBuildingWorkspaceRegexes extends TargetRegexBuilderTestParent { 18 | 19 | @Test 20 | public void shouldMatchAllWorkspaceVariations() { 21 | final TargetRegexBuilder target = workspaceRegexBuilder(); 22 | final Pattern targetRegex = Pattern.compile(target.toWorkspacePattern()); 23 | 24 | assertTrue("Should match plain workspace URI - regex is: " + targetRegex.pattern(), 25 | targetRegex.matcher(WORKSPACE).matches()); 26 | assertTrue("Should match plain workspace URI with a slash - regex is: " + targetRegex.pattern(), 27 | targetRegex.matcher(addTrailingSlash(WORKSPACE)).matches()); 28 | } 29 | 30 | @Test 31 | public void shouldMatchWithNonRootContextPath() { 32 | final TargetRegexBuilder target = workspaceRegexBuilder(); 33 | target.setContextPath(CONTEXT_PATH); 34 | 35 | final Pattern targetRegex = Pattern.compile(target.toWorkspacePattern()); 36 | 37 | assertTrue("Should match workspace URI with a context root - regex is: " + targetRegex.pattern(), 38 | targetRegex.matcher(addContextRoot(WORKSPACE)).matches()); 39 | } 40 | 41 | @Test(expected = IllegalStateException.class) 42 | public void shouldFailToBuildRegexWhenWorkspaceIsNotSet() { 43 | new TargetRegexBuilder().toWorkspacePattern(); 44 | } 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /adapters/migration/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | 6 | org.atomhopper 7 | parent 8 | 1.2.35-SNAPSHOT 9 | ./../../pom.xml 10 | 11 | 12 | org.atomhopper.adapter 13 | migration-adapter 14 | jar 15 | 16 | ATOM Hopper - Migration Adapter 17 | 18 | 19 | 20 | org.atomhopper 21 | core 22 | 23 | 24 | 25 | org.javassist 26 | javassist 27 | 28 | 29 | 30 | org.apache.abdera 31 | abdera-core 32 | 33 | 34 | 35 | org.slf4j 36 | slf4j-api 37 | 38 | 39 | 40 | org.springframework.data 41 | spring-data-mongodb 42 | 43 | 44 | 45 | org.mockito 46 | mockito-all 47 | 48 | 49 | 50 | junit 51 | junit 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /adapters/hibernate/src/test/java/org/atomhopper/hibernate/adapter/HibernateFeedPublisherTest.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.hibernate.adapter; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | import org.atomhopper.adapter.request.adapter.DeleteEntryRequest; 6 | import org.atomhopper.adapter.request.adapter.PutEntryRequest; 7 | import org.junit.Before; 8 | import org.junit.Test; 9 | import org.junit.experimental.runners.Enclosed; 10 | import org.junit.runner.RunWith; 11 | import static org.mockito.Mockito.mock; 12 | 13 | 14 | @RunWith(Enclosed.class) 15 | public class HibernateFeedPublisherTest { 16 | 17 | public static class WhenCallingNonImplementedFunctionality { 18 | 19 | private HibernateFeedPublisher hibernateFeedPublisher; 20 | private PutEntryRequest putEntryRequest; 21 | private DeleteEntryRequest deleteEntryRequest; 22 | 23 | @Before 24 | public void setUp() throws Exception { 25 | putEntryRequest = mock(PutEntryRequest.class); 26 | deleteEntryRequest = mock(DeleteEntryRequest.class); 27 | 28 | hibernateFeedPublisher = new HibernateFeedPublisher(); 29 | } 30 | 31 | @Test(expected=UnsupportedOperationException.class) 32 | public void shouldPutEntry() throws Exception { 33 | hibernateFeedPublisher.putEntry(putEntryRequest); 34 | } 35 | 36 | @Test(expected=UnsupportedOperationException.class) 37 | public void shouldDeleteEntry() throws Exception { 38 | hibernateFeedPublisher.deleteEntry(deleteEntryRequest); 39 | } 40 | 41 | @Test(expected = UnsupportedOperationException.class) 42 | public void shouldSetParameters() throws Exception { 43 | Map map = new HashMap(); 44 | map.put("test1", "test2"); 45 | hibernateFeedPublisher.setParameters(map); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /adapters/mongodb/src/test/java/org/atomhopper/mongodb/adapter/MongodbFeedInformationTest.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.mongodb.adapter; 2 | 3 | import org.atomhopper.adapter.request.adapter.GetCategoriesRequest; 4 | import org.atomhopper.adapter.request.feed.FeedRequest; 5 | import org.springframework.data.mongodb.core.MongoTemplate; 6 | 7 | import org.junit.Before; 8 | import org.junit.Test; 9 | import org.junit.experimental.runners.Enclosed; 10 | import org.junit.runner.RunWith; 11 | 12 | import static org.junit.Assert.assertNotNull; 13 | import static org.mockito.Mockito.mock; 14 | 15 | 16 | @RunWith(Enclosed.class) 17 | public class MongodbFeedInformationTest { 18 | 19 | public static class WhenGettingMongodbFeedInformation { 20 | 21 | private MongoTemplate mongoTemplate; 22 | private FeedRequest feedRequest; 23 | private GetCategoriesRequest getCategoriesRequest; 24 | private MongodbFeedInformation mongodbFeedInformation; 25 | 26 | @Before 27 | public void setUp() throws Exception { 28 | mongoTemplate = mock(MongoTemplate.class); 29 | feedRequest = mock(FeedRequest.class); 30 | getCategoriesRequest = mock(GetCategoriesRequest.class); 31 | 32 | mongodbFeedInformation = new MongodbFeedInformation(); 33 | } 34 | 35 | @Test 36 | public void shouldCreateHibernateFeedInformation() throws Exception { 37 | assertNotNull(mongodbFeedInformation); 38 | } 39 | 40 | @Test(expected=UnsupportedOperationException.class) 41 | public void shouldReturnId() throws Exception { 42 | mongodbFeedInformation.getId(feedRequest); 43 | } 44 | 45 | @Test(expected=UnsupportedOperationException.class) 46 | public void shouldReturnCategories() throws Exception { 47 | mongodbFeedInformation.getCategories(getCategoriesRequest); 48 | } 49 | } 50 | } -------------------------------------------------------------------------------- /adapters/postgres-adapter/src/test/java/org/atomhopper/postgres/adapter/PostgresFeedInformationTest.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.postgres.adapter; 2 | 3 | import org.atomhopper.adapter.request.adapter.GetCategoriesRequest; 4 | import org.atomhopper.adapter.request.feed.FeedRequest; 5 | 6 | import org.junit.Before; 7 | import org.junit.Test; 8 | import org.junit.experimental.runners.Enclosed; 9 | import org.junit.runner.RunWith; 10 | import org.springframework.jdbc.core.JdbcTemplate; 11 | 12 | import static org.junit.Assert.assertNotNull; 13 | import static org.mockito.Mockito.mock; 14 | 15 | @RunWith(Enclosed.class) 16 | public class PostgresFeedInformationTest { 17 | 18 | public static class WhenGettingPostgresFeedInformation { 19 | 20 | private JdbcTemplate jbdcTemplate; 21 | private FeedRequest feedRequest; 22 | private GetCategoriesRequest getCategoriesRequest; 23 | private PostgresFeedInformation postgresFeedInformation; 24 | 25 | @Before 26 | public void setUp() throws Exception { 27 | jbdcTemplate = mock(JdbcTemplate.class); 28 | feedRequest = mock(FeedRequest.class); 29 | getCategoriesRequest = mock(GetCategoriesRequest.class); 30 | 31 | postgresFeedInformation = new PostgresFeedInformation(); 32 | } 33 | 34 | @Test 35 | public void shouldCreatePostgresFeedInformation() throws Exception { 36 | assertNotNull(postgresFeedInformation); 37 | } 38 | 39 | @Test(expected=UnsupportedOperationException.class) 40 | public void shouldReturnId() throws Exception { 41 | postgresFeedInformation.getId(feedRequest); 42 | } 43 | 44 | @Test(expected=UnsupportedOperationException.class) 45 | public void shouldReturnCategories() throws Exception { 46 | postgresFeedInformation.getCategories(getCategoriesRequest); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /hopper/src/main/java/org/atomhopper/adapter/FeedPublisher.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.adapter; 2 | 3 | import org.apache.abdera.model.Entry; 4 | import org.atomhopper.adapter.request.adapter.DeleteEntryRequest; 5 | import org.atomhopper.adapter.request.adapter.PostEntryRequest; 6 | import org.atomhopper.adapter.request.adapter.PutEntryRequest; 7 | import org.atomhopper.response.AdapterResponse; 8 | import org.atomhopper.response.EmptyBody; 9 | 10 | /** 11 | * A feed publisher, as defined by this interface, is responsible for committing 12 | * client change requests to the feed it represents. 13 | * 14 | * Note: this interface is required for ATOMpub functionality 15 | */ 16 | public interface FeedPublisher extends AtomHopperAdapter { 17 | 18 | /** 19 | * Requests a single entry be added to the feed. 20 | * 21 | * @param postEntryRequest 22 | * @see PostEntryRequest 23 | * 24 | * @return 25 | * The returned entry should contain all of the information a client would 26 | * need to then request the newly added entry. This should include linking 27 | * and identifying the new entry in the response 28 | */ 29 | AdapterResponse postEntry(PostEntryRequest postEntryRequest); 30 | 31 | /** 32 | * Requests that an entry be updated. This request is scoped by the unique 33 | * string ID of the entry the update is being requested for. 34 | * 35 | * @param putEntryRequest 36 | * @see PutEntryRequest 37 | * 38 | * @return 39 | */ 40 | AdapterResponse putEntry(PutEntryRequest putEntryRequest); 41 | 42 | /** 43 | * Requests that an entry be deleted. This request is scoped by the unique 44 | * string ID of the entry the delete is being requested for. 45 | * 46 | * @param deleteEntryRequest 47 | * @see DeleteEntryRequest 48 | * 49 | * @return 50 | */ 51 | AdapterResponse deleteEntry(DeleteEntryRequest deleteEntryRequest); 52 | } 53 | -------------------------------------------------------------------------------- /hopper/src/test/java/org/atomhopper/config/SchemaTest.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.config; 2 | 3 | import org.atomhopper.config.v1_0.Configuration; 4 | import org.junit.Before; 5 | import org.junit.Test; 6 | import org.junit.experimental.runners.Enclosed; 7 | import org.junit.runner.RunWith; 8 | 9 | import javax.xml.XMLConstants; 10 | import javax.xml.bind.JAXBContext; 11 | import javax.xml.bind.Unmarshaller; 12 | import javax.xml.transform.stream.StreamSource; 13 | import javax.xml.validation.SchemaFactory; 14 | 15 | import static org.junit.Assert.assertFalse; 16 | 17 | /** 18 | * 19 | */ 20 | @RunWith(Enclosed.class) 21 | public class SchemaTest { 22 | 23 | private static final SchemaFactory SCHEMA_FACTORY = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); 24 | 25 | public static class WhenValidating { 26 | 27 | private JAXBContext jaxbContext; 28 | private Unmarshaller jaxbUnmarshaller; 29 | 30 | @Before 31 | public void standUp() throws Exception { 32 | jaxbContext = JAXBContext.newInstance( 33 | org.atomhopper.config.v1_0.ObjectFactory.class); 34 | 35 | jaxbUnmarshaller = jaxbContext.createUnmarshaller(); 36 | 37 | jaxbUnmarshaller.setSchema(SCHEMA_FACTORY.newSchema( 38 | new StreamSource[]{ 39 | new StreamSource(SchemaTest.class.getResourceAsStream("/META-INF/schema/config/atom-hopper-config.xsd")) 40 | })); 41 | } 42 | 43 | @Test 44 | public void staticExampleShouldMatchSchema() throws Exception { 45 | final Configuration cfg = jaxbUnmarshaller.unmarshal( 46 | new StreamSource(SchemaTest.class.getResourceAsStream("/META-INF/schema/examples/config/feed-server-config.xml")), Configuration.class).getValue(); 47 | 48 | assertFalse("Configured workspace list should have at least one element", cfg.getWorkspace().isEmpty()); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /hopper/src/test/java/org/atomhopper/util/TargetRegexBuilderTest.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.util; 2 | 3 | import static org.junit.Assert.assertTrue; 4 | import org.junit.Test; 5 | import org.junit.experimental.runners.Enclosed; 6 | import org.junit.runner.RunWith; 7 | 8 | import static org.junit.Assert.assertEquals; 9 | 10 | /** 11 | * 12 | */ 13 | @RunWith(Enclosed.class) 14 | public class TargetRegexBuilderTest { 15 | 16 | public static class WhenCopyingTargetRegexBuilders extends TargetRegexBuilderTestParent { 17 | 18 | @Test 19 | public void shouldCopyBuilderElements() { 20 | final TargetRegexBuilder expected = feedRegexBuilder(); 21 | final TargetRegexBuilder actual = new TargetRegexBuilder(expected); 22 | 23 | assertEquals("TargetRegexBuilder copy must populate the context path", expected.getContextPath(), actual.getContextPath()); 24 | assertEquals("TargetRegexBuilder copy must populate the workspace path", expected.getWorkspaceResource(), actual.getWorkspaceResource()); 25 | assertEquals("TargetRegexBuilder copy must populate the feed path", expected.getFeedResource(), actual.getFeedResource()); 26 | } 27 | } 28 | 29 | public static class WhenResourceAttributeHasBackslashesInRegex { 30 | 31 | private TargetRegexBuilder targetRegexBuilder; 32 | 33 | @Test 34 | public void shouldRetainTheBackslashInRegex() { 35 | String workspaceResource = "usagetest\\d{1,2}"; 36 | String feedResource = "events"; 37 | String contextPath = ""; 38 | 39 | targetRegexBuilder = new TargetRegexBuilder(); 40 | targetRegexBuilder.setContextPath(contextPath); 41 | targetRegexBuilder.setWorkspace(workspaceResource); 42 | targetRegexBuilder.setFeed(feedResource); 43 | 44 | assertTrue("should retain the backslash in the workspace resource regex",targetRegexBuilder.toEntryPattern().contains(workspaceResource)); 45 | 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /adapters/hibernate/src/main/resources/ddl/postgres/atomhopper-database-schema-ddl-postgres.sql: -------------------------------------------------------------------------------- 1 | SET statement_timeout = 0; 2 | SET client_encoding = 'UTF8'; 3 | SET standard_conforming_strings = on; 4 | SET check_function_bodies = false; 5 | SET client_min_messages = warning; 6 | 7 | CREATE EXTENSION IF NOT EXISTS plpgsql WITH SCHEMA pg_catalog; 8 | 9 | COMMENT ON EXTENSION plpgsql IS 'PL/pgSQL procedural language'; 10 | 11 | SET search_path = public, pg_catalog; 12 | SET default_tablespace = ''; 13 | SET default_with_oids = false; 14 | 15 | CREATE TABLE feeds ( 16 | name character varying(255) CONSTRAINT feeds_pkey PRIMARY KEY, 17 | feedid character varying(255) 18 | ); 19 | ALTER TABLE public.feeds OWNER TO atomschema; 20 | 21 | CREATE TABLE entries ( 22 | entryid character varying(255) CONSTRAINT entries_pkey PRIMARY KEY, 23 | creationdate timestamp without time zone NOT NULL, 24 | datelastupdated timestamp without time zone NOT NULL, 25 | entrybody text, 26 | feed character varying(255), 27 | CONSTRAINT fk_feed_feeds_name FOREIGN KEY (feed) REFERENCES feeds(name) ON DELETE CASCADE 28 | ); 29 | ALTER TABLE public.entries OWNER TO atomschema; 30 | CREATE INDEX datelastupdated_idx on entries(datelastupdated); 31 | 32 | CREATE TABLE categories ( 33 | term character varying(255) CONSTRAINT categories_pkey PRIMARY KEY 34 | ); 35 | ALTER TABLE public.categories OWNER TO atomschema; 36 | 37 | CREATE TABLE categoryentryreferences ( 38 | entryid character varying(255) CONSTRAINT fk_entryid_entries_entryid REFERENCES entries(entryid) ON DELETE CASCADE, 39 | category character varying(255) CONSTRAINT fk_category_categories_term REFERENCES categories(term) ON DELETE CASCADE, 40 | CONSTRAINT categoryentryreferences_pkey PRIMARY KEY (entryid, category) 41 | ); 42 | ALTER TABLE public.categoryentryreferences OWNER TO atomschema; 43 | 44 | REVOKE ALL ON SCHEMA public FROM PUBLIC; 45 | REVOKE ALL ON SCHEMA public FROM postgres; 46 | GRANT ALL ON SCHEMA public TO postgres; 47 | GRANT ALL ON SCHEMA public TO PUBLIC; 48 | -------------------------------------------------------------------------------- /hopper/src/main/java/org/atomhopper/adapter/jpa/PersistedCategory.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.adapter.jpa; 2 | 3 | import javax.persistence.Column; 4 | import javax.persistence.Entity; 5 | import javax.persistence.FetchType; 6 | import javax.persistence.Id; 7 | import javax.persistence.ManyToMany; 8 | import javax.persistence.Table; 9 | import java.io.Serializable; 10 | import java.util.Collections; 11 | import java.util.HashSet; 12 | import java.util.Set; 13 | 14 | @Entity 15 | @Table(name = "Categories") 16 | public class PersistedCategory implements Serializable { 17 | 18 | @Id 19 | @Column(name = "Term") 20 | private String term; 21 | 22 | @ManyToMany(mappedBy = "categories", fetch = FetchType.LAZY) 23 | private Set feedEntries; 24 | 25 | public PersistedCategory() { 26 | feedEntries = Collections.EMPTY_SET; 27 | } 28 | 29 | public PersistedCategory(String term) { 30 | feedEntries = new HashSet(); 31 | 32 | this.term = term; 33 | } 34 | 35 | public Set getFeedEntries() { 36 | return feedEntries; 37 | } 38 | 39 | public void setFeedEntries(Set feedEntries) { 40 | this.feedEntries = feedEntries; 41 | } 42 | 43 | public String getTerm() { 44 | return term; 45 | } 46 | 47 | public void setTerm(String term) { 48 | this.term = term; 49 | } 50 | 51 | @Override 52 | public boolean equals(Object obj) { 53 | if (obj == null) { 54 | return false; 55 | } 56 | if (getClass() != obj.getClass()) { 57 | return false; 58 | } 59 | final PersistedCategory other = (PersistedCategory) obj; 60 | return !((this.term == null) ? (other.term != null) : !this.term.equals(other.term)); 61 | } 62 | 63 | @Override 64 | public int hashCode() { 65 | int hash = 3; 66 | hash = 53 * hash + (this.term != null ? this.term.hashCode() : 0); 67 | return hash; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /hopper/src/main/java/org/atomhopper/adapter/request/adapter/impl/GetFeedRequestImpl.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.adapter.request.adapter.impl; 2 | 3 | import org.apache.abdera.model.Feed; 4 | import org.apache.abdera.protocol.server.RequestContext; 5 | import org.atomhopper.adapter.request.RequestQueryParameter; 6 | import org.atomhopper.adapter.request.adapter.GetFeedRequest; 7 | import org.atomhopper.adapter.request.feed.AbstractFeedRequest; 8 | import org.h2.util.StringUtils; 9 | 10 | import java.util.Collections; 11 | import java.util.List; 12 | 13 | public class GetFeedRequestImpl extends AbstractFeedRequest implements GetFeedRequest { 14 | 15 | public GetFeedRequestImpl(RequestContext abderaRequestContext) { 16 | super(abderaRequestContext); 17 | } 18 | 19 | @Override 20 | public List getCategories() { 21 | final List categoryList = getRequestParameters(RequestQueryParameter.CATEGORIES.toString()); 22 | 23 | return Collections.unmodifiableList(categoryList != null ? categoryList : Collections.EMPTY_LIST); 24 | } 25 | 26 | @Override 27 | public String getSearchQuery() { 28 | return getRequestParameter(RequestQueryParameter.SEARCH.toString()); 29 | } 30 | 31 | @Override 32 | public String getPageSize() { 33 | return getRequestParameter(RequestQueryParameter.PAGE_LIMIT.toString()); 34 | } 35 | 36 | @Override 37 | public String getPageMarker() { 38 | return getRequestParameter(RequestQueryParameter.MARKER.toString()); 39 | } 40 | 41 | @Override 42 | public Feed newFeed() { 43 | return getAbdera().newFeed(); 44 | } 45 | 46 | public String getDirection() { 47 | final String direction = this.getRequestParameter(RequestQueryParameter.PAGE_DIRECTION.toString()); 48 | return !StringUtils.isNullOrEmpty(direction) ? direction : "forward"; 49 | } 50 | 51 | public String getStartingAt() { 52 | return this.getRequestParameter(RequestQueryParameter.STARTING_AT.toString()); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /hopper/src/main/java/org/atomhopper/adapter/FeedSource.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.adapter; 2 | 3 | import org.apache.abdera.model.Entry; 4 | import org.apache.abdera.model.Feed; 5 | import org.atomhopper.adapter.request.adapter.GetEntryRequest; 6 | import org.atomhopper.adapter.request.adapter.GetFeedRequest; 7 | import org.atomhopper.response.AdapterResponse; 8 | 9 | import java.net.URL; 10 | 11 | /** 12 | * A feed source, as defined by this interface, is responsible for retrieving the 13 | * feed and its associated entry data. 14 | * 15 | */ 16 | public interface FeedSource extends AtomHopperAdapter { 17 | 18 | public static final String REL_ARCHIVE_NEXT = "next-archive"; 19 | public static final String REL_ARCHIVE_PREV = "prev-archive"; 20 | 21 | FeedInformation getFeedInformation(); 22 | 23 | /** 24 | * Requests a single feed from the adapter. This request did not contain 25 | * paging information and the adapter may assume that the requester is 26 | * requesting the head of the feed. 27 | * 28 | * @param request 29 | * @see GetEntryRequest 30 | * 31 | * @return 32 | */ 33 | AdapterResponse getFeed(GetFeedRequest getFeedRequest); 34 | 35 | /** 36 | * Requests a single entry from the adapter. 37 | * 38 | * @param request 39 | * @see GetEntryRequest 40 | * 41 | * @return 42 | */ 43 | AdapterResponse getEntry(GetEntryRequest getEntryRequest); 44 | 45 | /** 46 | * Declares this feed as an archive feed as well as sets the current url for the archive feed. 47 | * An archive feed must have a link to the current feed which its the archive for. 48 | * 49 | * @param urlCurrent The URL to the current feed for this archive. This will be displayed 50 | * in the "current" link 51 | */ 52 | public void setCurrentUrl( URL urlCurrent ); 53 | 54 | /** 55 | * If an atom feed has a corresponding archive, 56 | * 57 | * @param url 58 | */ 59 | public void setArchiveUrl( URL url ); 60 | } 61 | -------------------------------------------------------------------------------- /adapters/mongodb/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | 6 | org.atomhopper 7 | parent 8 | 1.2.35-SNAPSHOT 9 | ./../../pom.xml 10 | 11 | 12 | org.atomhopper.adapter 13 | mongodb-adapter 14 | jar 15 | 16 | ATOM Hopper - MongoDB Adapter 17 | 18 | 19 | 20 | org.atomhopper 21 | core 22 | 23 | 24 | 25 | org.javassist 26 | javassist 27 | 28 | 29 | 30 | org.apache.abdera 31 | abdera-core 32 | 33 | 34 | 35 | org.slf4j 36 | slf4j-api 37 | 38 | 39 | 40 | org.springframework.data 41 | spring-data-mongodb 42 | 43 | 44 | 45 | org.mockito 46 | mockito-all 47 | 48 | 49 | 50 | junit 51 | junit 52 | 53 | 54 | 55 | com.yammer.metrics 56 | metrics-core 57 | 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /server/src/main/java/org/atomhopper/server/AtomHopperServer.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.server; 2 | 3 | 4 | import org.kohsuke.args4j.CmdLineException; 5 | import org.kohsuke.args4j.CmdLineParser; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | 9 | 10 | public class AtomHopperServer { 11 | private static final Logger LOG = LoggerFactory.getLogger(AtomHopperServer.class); 12 | private static final int MAX_PORT_NUMBER = 49150; 13 | private static final int MIN_PORT_NUMBER = 1024; 14 | 15 | private AtomHopperServer(){} 16 | 17 | public static void main(String[] args) { 18 | CommandLineArguments commandLineArgs = new CommandLineArguments(); 19 | CmdLineParser cmdLineParser = new CmdLineParser(commandLineArgs); 20 | AtomHopperServerControl serverControl = new AtomHopperServerControl(commandLineArgs); 21 | 22 | try { 23 | cmdLineParser.parseArgument(args); 24 | 25 | } catch (CmdLineException e) { 26 | displayUsage(cmdLineParser, e); 27 | return; 28 | } 29 | 30 | if ((!(portIsInRange(commandLineArgs.port))) || (!(portIsInRange(commandLineArgs.stopport)))) { 31 | LOG.info("Invalid Atom Hopper port setting, use a value between 1024 and 49150"); 32 | return; 33 | } 34 | 35 | if (commandLineArgs.action.equalsIgnoreCase(CommandLineArguments.ACTION_START)) { 36 | serverControl.startAtomHopper(); 37 | } 38 | 39 | if (commandLineArgs.action.equalsIgnoreCase(CommandLineArguments.ACTION_STOP)) { 40 | serverControl.stopAtomHopper(); 41 | } 42 | } 43 | 44 | private static void displayUsage(CmdLineParser cmdLineParser, Exception e) { 45 | System.err.println(e.getMessage()); 46 | System.err.println("java -jar AtomHopperServer.jar [options...] arguments..."); 47 | cmdLineParser.printUsage(System.err); 48 | } 49 | 50 | private static boolean portIsInRange(int portNum) { 51 | return (portNum <= MAX_PORT_NUMBER) && (portNum >= MIN_PORT_NUMBER); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /hopper/src/main/java/org/atomhopper/response/FeedSourceAdapterResponse.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.response; 2 | 3 | import org.apache.abdera.util.EntityTag; 4 | import org.springframework.http.HttpStatus; 5 | 6 | import java.util.EnumMap; 7 | import java.util.Map; 8 | 9 | public class FeedSourceAdapterResponse implements AdapterResponse { 10 | 11 | private static final HttpStatus DEFAULT_HTTP_STATUS_CODE = HttpStatus.OK; 12 | 13 | private final T responseBody; 14 | private final HttpStatus statusCode; 15 | private final String message; 16 | private final Map parameters; 17 | 18 | private EntityTag etag; 19 | 20 | public FeedSourceAdapterResponse(T responseBody) { 21 | this(responseBody, DEFAULT_HTTP_STATUS_CODE, ""); 22 | } 23 | 24 | public FeedSourceAdapterResponse(T responseBody, HttpStatus statusCode, String message) { 25 | this.responseBody = responseBody; 26 | this.statusCode = statusCode; 27 | this.message = message; 28 | 29 | parameters = new EnumMap(ResponseParameter.class); 30 | } 31 | 32 | public Map getParameters() { 33 | return parameters; 34 | } 35 | 36 | @Override 37 | public String getParameter(ResponseParameter key) { 38 | return getParameters().get(key); 39 | } 40 | 41 | @Override 42 | public AdapterResponse withParameter(ResponseParameter key, Object value) { 43 | getParameters().put(key, value.toString()); 44 | 45 | return this; 46 | } 47 | 48 | @Override 49 | public T getBody() { 50 | return responseBody; 51 | } 52 | 53 | @Override 54 | public String getMessage() { 55 | return message; 56 | } 57 | 58 | @Override 59 | public HttpStatus getResponseStatus() { 60 | return statusCode; 61 | } 62 | 63 | @Override 64 | public EntityTag getEntityTag() { 65 | return etag; 66 | } 67 | 68 | @Override 69 | public void setEntityTag(EntityTag etag) { 70 | this.etag = etag; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /adapters/postgres-adapter/src/main/java/org/atomhopper/postgres/model/PersistedEntry.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.postgres.model; 2 | 3 | import java.util.Arrays; 4 | import java.util.Calendar; 5 | import java.util.Date; 6 | import java.util.TimeZone; 7 | 8 | 9 | public class PersistedEntry { 10 | 11 | private String entryId; 12 | private String feed; 13 | private String entryBody; 14 | private Date creationDate; 15 | private Date dateLastUpdated; 16 | private String[] categories; 17 | 18 | public PersistedEntry() { 19 | final Calendar localNow = Calendar.getInstance(TimeZone.getDefault()); 20 | localNow.setTimeInMillis(System.currentTimeMillis()); 21 | 22 | creationDate = localNow.getTime(); 23 | dateLastUpdated = localNow.getTime(); 24 | } 25 | 26 | public String getEntryId() { 27 | return entryId; 28 | } 29 | 30 | public void setEntryId(String entryId) { 31 | this.entryId = entryId; 32 | } 33 | 34 | public String getFeed() { 35 | return feed; 36 | } 37 | 38 | public void setFeed(String feed) { 39 | this.feed = feed; 40 | } 41 | 42 | public String getEntryBody() { 43 | return entryBody; 44 | } 45 | 46 | public void setEntryBody(String entryBody) { 47 | this.entryBody = entryBody; 48 | } 49 | 50 | public Date getCreationDate() { 51 | return (Date) creationDate.clone(); 52 | } 53 | 54 | public void setCreationDate(Date creationDate) { 55 | this.creationDate = (Date) creationDate.clone(); 56 | } 57 | 58 | public Date getDateLastUpdated() { 59 | return (Date) dateLastUpdated.clone(); 60 | } 61 | 62 | public void setDateLastUpdated(Date dateLastUpdated) { 63 | this.dateLastUpdated = (Date) dateLastUpdated.clone(); 64 | } 65 | 66 | public String[] getCategories() { 67 | return categories.clone(); 68 | } 69 | 70 | public void setCategories(String[] categories) { 71 | if (categories != null) { 72 | this.categories = Arrays.copyOf(categories, categories.length); 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /documentation/devops.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Atom Hopper", 3 | "description": "An open source ATOMPub server", 4 | "tags": [ 5 | "java", 6 | "ATOM", 7 | "open source", 8 | "hibernate", 9 | "MongoDB", 10 | "Abdera" 11 | ], 12 | "contacts": [ 13 | { 14 | "team_name": "CLoud Integration", 15 | "members": [ 16 | { 17 | "name": "Theresa Huth", 18 | "sso":"theresa.huth", 19 | "github": "https://github.com/theresaHuth", 20 | "mailto": "theresa.huth@rackspace.com" 21 | }, 22 | { 23 | "name": "Chad Lung", 24 | "sso":"chad.lung", 25 | "github": "https://github.com/chadlung", 26 | "mailto": "chad.lung@rackspace.com" 27 | }, 28 | { 29 | "name": "Seth Brayman", 30 | "sso":"seth.brayman", 31 | "github": "https://github.com/sethbrayman", 32 | "mailto": "seth.brayman@rackspace.com" 33 | }, 34 | { 35 | "name": "Richard Sartor", 36 | "sso":"richard.sartor", 37 | "github": "https://github.com/rich4632", 38 | "mailto": "richard.sartor@rackspace.com" 39 | } 40 | ] 41 | } 42 | ], 43 | "links": { 44 | "Blog":"http://atomhopper.org", 45 | "Email List": "atomhopper@lists.rackspace.com", 46 | "Code": "https://github.com/rackspace/atom-hopper", 47 | "Wiki":"https://github.com/rackspace/atom-hopper/wiki", 48 | "Twitter": "https://twitter.com/#!/atomhopper", 49 | "Implementation Plan":"https://red.rackspace.com:8443/display/misc/Atom+Hopper+Implementation+Plan" 50 | }, 51 | "dependent_services": [ 52 | "Nova (Cloud Servers)", 53 | "Glance", 54 | "Quantum", 55 | "Melange", 56 | "MaaS", 57 | "DBaaS", 58 | "LBaaS", 59 | "Legacy Cloud Servers", 60 | "Cloud Files", 61 | "Cloud Block Storage", 62 | "Usage Service Layer", 63 | "Data Warehouse", 64 | "Runbook Automation (RBA) / Managed Cloud", 65 | "Rack Connect", 66 | "Identity/Auth" 67 | ] 68 | } 69 | -------------------------------------------------------------------------------- /hopper/src/main/java/org/atomhopper/AtomHopperVersionServlet.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper; 2 | 3 | import com.google.gson.Gson; 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | 7 | import javax.servlet.ServletException; 8 | import javax.servlet.http.HttpServlet; 9 | import javax.servlet.http.HttpServletRequest; 10 | import javax.servlet.http.HttpServletResponse; 11 | import java.io.IOException; 12 | import java.io.InputStream; 13 | import java.io.PrintWriter; 14 | import java.util.Properties; 15 | 16 | 17 | public class AtomHopperVersionServlet extends HttpServlet { 18 | 19 | private static final Logger LOG = LoggerFactory.getLogger(AtomHopperVersionServlet.class); 20 | private static final String POM_PROPERTIES_LOCATION = "META-INF/maven/org.atomhopper/atomhopper/pom.properties"; 21 | 22 | private Properties loadProperties() { 23 | Properties properties = new Properties(); 24 | try { 25 | InputStream inStream = getServletContext().getResourceAsStream(POM_PROPERTIES_LOCATION); 26 | properties.load(inStream); 27 | inStream.close(); 28 | } catch (Exception e){ 29 | LOG.error("Unable to load pom.properties", e); 30 | } 31 | return properties; 32 | } 33 | 34 | private Properties getProperties(){ 35 | return loadProperties(); 36 | } 37 | 38 | protected void processRequest(HttpServletRequest request, HttpServletResponse response) 39 | throws ServletException, IOException { 40 | response.setContentType("application/json"); 41 | PrintWriter out = response.getWriter(); 42 | try { 43 | out.println(new Gson().toJson(getProperties())); 44 | } finally { 45 | out.close(); 46 | } 47 | } 48 | 49 | @Override 50 | protected void doGet(HttpServletRequest request, HttpServletResponse response) 51 | throws ServletException, IOException { 52 | processRequest(request, response); 53 | } 54 | 55 | @Override 56 | public String getServletInfo() { 57 | return "Returns the current Atom Hopper pom.properties in JSON format"; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /test-suite/src/main/java/org/atomhopper/jetty/AtomHopperJettyServerBuilder.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.jetty; 2 | 3 | import org.atomhopper.AtomHopperServlet; 4 | import org.atomhopper.AtomHopperVersionServlet; 5 | import org.atomhopper.servlet.ServletInitParameter; 6 | import org.atomhopper.servlet.ServletSpringContext; 7 | import org.eclipse.jetty.server.Server; 8 | import org.eclipse.jetty.servlet.ServletContextHandler; 9 | import org.eclipse.jetty.servlet.ServletHolder; 10 | import org.springframework.web.context.ContextLoaderListener; 11 | 12 | /** 13 | * 14 | * 15 | */ 16 | public class AtomHopperJettyServerBuilder { 17 | 18 | private final int portNumber; 19 | 20 | public AtomHopperJettyServerBuilder(int portNumber) { 21 | this.portNumber = portNumber; 22 | } 23 | 24 | private Server buildNewInstance() { 25 | final Server jettyServerReference = new Server(portNumber); 26 | final ServletContextHandler rootContext = buildRootContext(jettyServerReference); 27 | 28 | final ServletHolder atomHopServer = new ServletHolder(AtomHopperServlet.class); 29 | final ServletHolder versionServlet = new ServletHolder(AtomHopperVersionServlet.class); 30 | atomHopServer.setInitParameter(ServletInitParameter.CONTEXT_ADAPTER_CLASS.toString(), ServletSpringContext.class.getName()); 31 | atomHopServer.setInitParameter(ServletInitParameter.CONFIGURATION_LOCATION.toString(), "classpath:/META-INF/atom-server.cfg.xml"); 32 | 33 | rootContext.addServlet(versionServlet, "/buildinfo"); 34 | rootContext.addServlet(atomHopServer, "/*"); 35 | 36 | return jettyServerReference; 37 | } 38 | 39 | private ServletContextHandler buildRootContext(Server serverReference) { 40 | final ServletContextHandler servletContext = new ServletContextHandler(serverReference, "/"); 41 | servletContext.getInitParams().put("contextConfigLocation", "classpath:/META-INF/application-context.xml"); 42 | servletContext.addEventListener(new ContextLoaderListener()); 43 | 44 | return servletContext; 45 | } 46 | 47 | public Server newServer() { 48 | return buildNewInstance(); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /hopper/src/main/java/org/atomhopper/abdera/response/EntryResponseHandler.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.abdera.response; 2 | 3 | import org.apache.abdera.model.Entry; 4 | import org.apache.abdera.protocol.server.ProviderHelper; 5 | import org.apache.abdera.protocol.server.RequestContext; 6 | import org.apache.abdera.protocol.server.ResponseContext; 7 | import org.atomhopper.abdera.filter.AdapterResponseInterceptor; 8 | import org.atomhopper.response.AdapterResponse; 9 | 10 | import java.util.Date; 11 | 12 | public class EntryResponseHandler extends AbstractResponseHandler { 13 | 14 | private static final String XML = "application/xml"; 15 | 16 | public EntryResponseHandler(String[] allowedMethods, AdapterResponseInterceptor... interceptors) { 17 | super(allowedMethods, interceptors); 18 | } 19 | 20 | @Override 21 | protected ResponseContext handleAdapterResponse(RequestContext rc, AdapterResponse adapterResponse) { 22 | final Date lastUpdated = adapterResponse.getBody() != null ? adapterResponse.getBody().getUpdated() : null; 23 | 24 | switch (adapterResponse.getResponseStatus()) { 25 | case OK: 26 | case CREATED: 27 | return ProviderHelper.returnBase(adapterResponse.getBody(), adapterResponse.getResponseStatus().value(), lastUpdated); 28 | 29 | case NOT_FOUND: 30 | return ProviderHelper.notfound(rc, adapterResponse.getMessage()).setContentType(XML); 31 | 32 | case INTERNAL_SERVER_ERROR: 33 | return ProviderHelper.servererror(rc, adapterResponse.getMessage(), new InternalServerException()).setContentType(XML); 34 | 35 | case METHOD_NOT_ALLOWED: 36 | return ProviderHelper.notallowed(rc, adapterResponse.getMessage(), getAllowedHttpMethods()).setContentType(XML); 37 | 38 | case BAD_REQUEST: 39 | return ProviderHelper.badrequest(rc, adapterResponse.getMessage()).setContentType(XML); 40 | 41 | case CONFLICT: 42 | return ProviderHelper.conflict(rc, adapterResponse.getMessage()).setContentType(XML); 43 | 44 | default: 45 | return ProviderHelper.notfound(rc).setContentType(XML); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /test-suite/src/main/webapp/WEB-INF/web.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Atom Hopper Server 5 | ATOM 6 | 7 | 8 | contextConfigLocation 9 | /META-INF/application-context.xml 10 | 11 | 12 | 13 | Spring Context Loader 14 | org.springframework.web.context.ContextLoaderListener 15 | 16 | 17 | 18 | Atom-Hopper 19 | org.atomhopper.AtomHopperServlet 20 | 21 | 22 | config-directory 23 | /tmp/atom-server 24 | 25 | 26 | 27 | context-adapter-class 28 | org.atomhopper.servlet.context.impl.ServletSpringContext 29 | 30 | 44 | 45 | atomhopper-url-pattern 46 | 47 | 48 | 49 | 50 | 51 | Atom-Hopper 52 | /* 53 | 54 | 55 | -------------------------------------------------------------------------------- /hopper/src/main/java/org/atomhopper/config/AtomHopperConfigurationPreprocessor.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.config; 2 | 3 | import org.apache.commons.lang.StringUtils; 4 | import org.atomhopper.config.v1_0.Author; 5 | import org.atomhopper.config.v1_0.Configuration; 6 | import org.atomhopper.config.v1_0.ConfigurationDefaults; 7 | import org.atomhopper.config.v1_0.FeedConfiguration; 8 | import org.atomhopper.config.v1_0.WorkspaceConfiguration; 9 | 10 | import java.util.List; 11 | 12 | /** 13 | * @author zinic 14 | */ 15 | public class AtomHopperConfigurationPreprocessor { 16 | 17 | private final Configuration configuration; 18 | 19 | public AtomHopperConfigurationPreprocessor(Configuration configuration) { 20 | this.configuration = configuration; 21 | } 22 | 23 | public AtomHopperConfigurationPreprocessor applyDefaults() { 24 | final ConfigurationDefaults configurationDefaults = configuration.getDefaults(); 25 | setDefaultAuthor(configuration.getWorkspace(), configurationDefaults.getAuthor()); 26 | 27 | return new AtomHopperConfigurationPreprocessor(configuration); 28 | } 29 | 30 | public Configuration getConfiguration() { 31 | return configuration; 32 | } 33 | 34 | private void setDefaultAuthor(List workspaces, Author globalAuthorDefault) { 35 | for (WorkspaceConfiguration workspaceConfiguration : workspaces) { 36 | final Author workspaceAuthorDefault = (workspaceConfiguration.getDefaults() == null) 37 | ? null : workspaceConfiguration.getDefaults().getAuthor(); 38 | 39 | final Author authorToApply = isAuthorEmpty(workspaceAuthorDefault) 40 | ? globalAuthorDefault : workspaceAuthorDefault; 41 | 42 | if (!isAuthorEmpty(authorToApply)) { 43 | for (FeedConfiguration feed : workspaceConfiguration.getFeed()) { 44 | if (isAuthorEmpty(feed.getAuthor())) { 45 | feed.setAuthor(authorToApply); 46 | } 47 | } 48 | } 49 | } 50 | } 51 | 52 | private boolean isAuthorEmpty(Author author) { 53 | return author == null || author.getName() == null || StringUtils.isBlank(author.getName()); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /hopper/src/main/java/org/atomhopper/util/context/AdapterGetter.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.util.context; 2 | 3 | import org.apache.commons.lang.StringUtils; 4 | import org.atomhopper.servlet.ApplicationContextAdapter; 5 | import org.atomhopper.util.reflection.ReflectionTools; 6 | 7 | /** 8 | * 9 | * 10 | */ 11 | public class AdapterGetter { 12 | 13 | private final ApplicationContextAdapter contextAdapter; 14 | 15 | public AdapterGetter(ApplicationContextAdapter contextAdapter) { 16 | this.contextAdapter = contextAdapter; 17 | } 18 | 19 | public T getByName(String referenceName, Class classToCastTo) { 20 | if (StringUtils.isBlank(referenceName)) { 21 | throw new IllegalArgumentException("Bean reference for an adapter must not be empty or null"); 22 | } 23 | 24 | final Object reference = contextAdapter.fromContext(referenceName, classToCastTo); 25 | 26 | if (reference == null) { 27 | throw new AdapterNotFoundException("Unable to find adapter by name: " + referenceName); 28 | } else if (!classToCastTo.isInstance(reference)) { 29 | throw new IllegalArgumentException("Class: " 30 | + reference.getClass().getCanonicalName() 31 | + " does not implement " + classToCastTo.getCanonicalName()); 32 | } 33 | 34 | return (T) reference; 35 | } 36 | 37 | public T getByClassDefinition(Class configuredAdapterClass, Class classToCastTo) { 38 | if (!classToCastTo.isAssignableFrom(configuredAdapterClass)) { 39 | throw new IllegalArgumentException("Class: " 40 | + configuredAdapterClass.getCanonicalName() 41 | + " does not implement " + classToCastTo.getCanonicalName()); 42 | } 43 | 44 | try { 45 | final T instance = (T) contextAdapter.fromContext(configuredAdapterClass); 46 | 47 | return instance != null 48 | ? instance 49 | : (T) ReflectionTools.construct(configuredAdapterClass); 50 | } catch (Exception ex) { 51 | throw new AdapterConstructionException("Failed to get and or construct class: " 52 | + configuredAdapterClass.getCanonicalName(), ex); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /adapters/jdbc/src/main/java/org/atomhopper/jdbc/model/PersistedEntry.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.jdbc.model; 2 | 3 | import java.sql.Timestamp; 4 | import java.util.Arrays; 5 | import java.util.Calendar; 6 | import java.util.Date; 7 | import java.util.TimeZone; 8 | 9 | 10 | public class PersistedEntry { 11 | 12 | private long id; 13 | private String entryId; 14 | private String feed; 15 | private String entryBody; 16 | private Date creationDate; 17 | private Date dateLastUpdated; 18 | private String[] categories; 19 | 20 | public PersistedEntry() { 21 | final Calendar localNow = Calendar.getInstance(TimeZone.getDefault()); 22 | localNow.setTimeInMillis(System.currentTimeMillis()); 23 | 24 | creationDate = localNow.getTime(); 25 | dateLastUpdated = creationDate; 26 | } 27 | 28 | public long getId() { 29 | return id; 30 | } 31 | 32 | public void setId(long id) { 33 | this.id = id; 34 | } 35 | 36 | public String getEntryId() { 37 | return entryId; 38 | } 39 | 40 | public void setEntryId(String entryId) { 41 | this.entryId = entryId; 42 | } 43 | 44 | public String getFeed() { 45 | return feed; 46 | } 47 | 48 | public void setFeed(String feed) { 49 | this.feed = feed; 50 | } 51 | 52 | public String getEntryBody() { 53 | return entryBody; 54 | } 55 | 56 | public void setEntryBody(String entryBody) { 57 | this.entryBody = entryBody; 58 | } 59 | 60 | public Date getCreationDate() { 61 | return (Date) creationDate.clone(); 62 | } 63 | 64 | public void setCreationDate(Date creationDate) { 65 | this.creationDate = (Date) creationDate.clone(); 66 | } 67 | 68 | public Date getDateLastUpdated() { 69 | return (Date) dateLastUpdated.clone(); 70 | } 71 | 72 | public void setDateLastUpdated(Date dateLastUpdated) { 73 | this.dateLastUpdated = (Date) dateLastUpdated.clone(); 74 | } 75 | 76 | public String[] getCategories() { 77 | return categories.clone(); 78 | } 79 | 80 | public void setCategories(String[] categories) { 81 | if (categories != null) { 82 | this.categories = Arrays.copyOf(categories, categories.length); 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /adapters/postgres-adapter/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | 6 | org.atomhopper 7 | parent 8 | 1.2.35-SNAPSHOT 9 | ./../../pom.xml 10 | 11 | 12 | org.atomhopper.adapter 13 | postgres-adapter 14 | jar 15 | 16 | ATOM Hopper - Postgres Adapter 17 | 18 | 19 | 20 | org.atomhopper 21 | core 22 | 23 | 24 | 25 | postgresql 26 | postgresql 27 | 28 | 29 | 30 | org.apache.tomcat 31 | tomcat-jdbc 32 | 33 | 34 | 35 | org.javassist 36 | javassist 37 | 38 | 39 | 40 | org.apache.abdera 41 | abdera-core 42 | 43 | 44 | 45 | org.springframework 46 | spring-jdbc 47 | 48 | 49 | 50 | org.slf4j 51 | slf4j-api 52 | 53 | 54 | 55 | org.mockito 56 | mockito-all 57 | 58 | 59 | 60 | junit 61 | junit 62 | 63 | 64 | 65 | com.yammer.metrics 66 | metrics-core 67 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /adapters/mongodb/src/test/java/org/atomhopper/mongodb/adapter/MongodbUtilitiesTest.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.mongodb.adapter; 2 | 3 | import static junit.framework.Assert.assertEquals; 4 | import static org.atomhopper.mongodb.adapter.MongodbUtilities.formatCollectionName; 5 | import static org.atomhopper.mongodb.adapter.MongodbUtilities.safeLongToInt; 6 | import org.junit.Test; 7 | import org.junit.experimental.runners.Enclosed; 8 | import org.junit.runner.RunWith; 9 | 10 | 11 | @RunWith(Enclosed.class) 12 | public class MongodbUtilitiesTest { 13 | 14 | public static class WhenCallingMongodbUtilities { 15 | 16 | private final String SHORT_FORMATTED_COLLECTION_NAME = "namespace.feed"; 17 | private final String SHORT_COLLECTION_NAME = "namespace/feed"; 18 | 19 | private final String LONG_FORMATTED_COLLECTION_NAME = "namespace.feed.1234567890.1234567890.1234567890.1234567890.1234567890."; 20 | private final String LONG_COLLECTION_NAME = "namespace/feed/1234567890/1234567890/1234567890/1234567890/1234567890/1234567890/1234567890"; 21 | 22 | @Test 23 | public void shouldformatShortCollectionName() throws Exception { 24 | assertEquals("Should return formatted collection name (short)", SHORT_FORMATTED_COLLECTION_NAME, formatCollectionName(SHORT_COLLECTION_NAME)); 25 | } 26 | 27 | @Test 28 | public void shouldformatLongCollectionName() throws Exception { 29 | assertEquals("Should return formatted collection name (long)", LONG_FORMATTED_COLLECTION_NAME, formatCollectionName(LONG_COLLECTION_NAME)); 30 | } 31 | 32 | @Test(expected=IllegalArgumentException.class) 33 | public void shouldThrowErrorWhenConvertingWithMaxValuePlusOne() throws Exception { 34 | long maxValueForTest = (long)Integer.MAX_VALUE + (long)1; 35 | safeLongToInt(maxValueForTest); 36 | } 37 | 38 | @Test(expected=IllegalArgumentException.class) 39 | public void shouldThrowErrorWhenConvertingWithMinValueMinusOne() throws Exception { 40 | long minValueForTest = (long)Integer.MIN_VALUE - (long)1; 41 | safeLongToInt(minValueForTest); 42 | } 43 | 44 | @Test 45 | public void shouldReturnConvertedValue() throws Exception { 46 | assertEquals("Should return value", 1, safeLongToInt(1)); 47 | } 48 | } 49 | } -------------------------------------------------------------------------------- /hopper/src/main/java/org/atomhopper/abdera/response/AbstractResponseHandler.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.abdera.response; 2 | 3 | import org.apache.abdera.protocol.server.RequestContext; 4 | import org.apache.abdera.protocol.server.ResponseContext; 5 | import org.atomhopper.abdera.filter.AdapterResponseInterceptor; 6 | import org.atomhopper.response.AdapterResponse; 7 | 8 | import java.util.Arrays; 9 | import java.util.LinkedList; 10 | import java.util.List; 11 | 12 | public abstract class AbstractResponseHandler implements ResponseHandler { 13 | 14 | private final List> responseInterceptors; 15 | private final String[] allowedMethods; 16 | 17 | public AbstractResponseHandler(String[] allowedMethods, AdapterResponseInterceptor... interceptors) { 18 | this.allowedMethods = Arrays.copyOf(allowedMethods, allowedMethods.length); 19 | responseInterceptors = new LinkedList>(Arrays.asList(interceptors)); 20 | } 21 | 22 | public AbstractResponseHandler(String[] allowedMethods, List> adapterResponseInterceptorList) { 23 | this.allowedMethods = Arrays.copyOf(allowedMethods, allowedMethods.length); 24 | responseInterceptors = new LinkedList>(adapterResponseInterceptorList); 25 | } 26 | 27 | @Override 28 | public final ResponseContext handleResponse(RequestContext rc, AdapterResponse adapterResponse) { 29 | processResponse(rc, adapterResponse); 30 | 31 | return handleAdapterResponse(rc, adapterResponse); 32 | } 33 | 34 | @Override 35 | public void addResponseInterceptor(AdapterResponseInterceptor adapterResponseInterceptor) { 36 | responseInterceptors.add(adapterResponseInterceptor); 37 | } 38 | 39 | @Override 40 | public void clearResponseInterceptors() { 41 | responseInterceptors.clear(); 42 | } 43 | 44 | protected abstract ResponseContext handleAdapterResponse(RequestContext rc, AdapterResponse adapterResponse); 45 | 46 | protected String[] getAllowedHttpMethods() { 47 | return allowedMethods; 48 | } 49 | 50 | private void processResponse(RequestContext rc, AdapterResponse adapterResponse) { 51 | for (AdapterResponseInterceptor processor : responseInterceptors) { 52 | processor.process(rc, adapterResponse); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /hopper/src/test/java/org/atomhopper/adapter/impl/DisabledFeedSourceTest.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.adapter.impl; 2 | 3 | 4 | import org.atomhopper.adapter.request.feed.FeedRequest; 5 | import org.junit.Before; 6 | import org.junit.Test; 7 | import org.junit.experimental.runners.Enclosed; 8 | import org.junit.runner.RunWith; 9 | import org.apache.abdera.model.Entry; 10 | import org.apache.abdera.model.Feed; 11 | import org.atomhopper.adapter.request.adapter.GetEntryRequest; 12 | import org.atomhopper.adapter.request.adapter.GetFeedRequest; 13 | import org.atomhopper.response.AdapterResponse; 14 | import org.springframework.http.HttpStatus; 15 | 16 | import static junit.framework.Assert.assertEquals; 17 | import static junit.framework.Assert.assertNotNull; 18 | import static org.mockito.Mockito.mock; 19 | 20 | 21 | @RunWith(Enclosed.class) 22 | public class DisabledFeedSourceTest { 23 | 24 | public static class WhenAccessingDisabledFeedSource { 25 | 26 | private DisabledFeedSource disabledFeedSource; 27 | private GetEntryRequest mockGetEntryRequest; 28 | private GetFeedRequest mockGetFeedRequest; 29 | 30 | @Before 31 | public void setUp() throws Exception { 32 | disabledFeedSource = DisabledFeedSource.getInstance(); 33 | mockGetEntryRequest = mock(GetEntryRequest.class); 34 | mockGetFeedRequest = mock(GetFeedRequest.class); 35 | } 36 | 37 | @Test 38 | public void shouldGetDisabledFeedSource() { 39 | assertNotNull("Should not return null", DisabledFeedSource.getInstance()); 40 | } 41 | 42 | @Test 43 | public void shouldGetFeedInformation() { 44 | assertNotNull("Should not return null", disabledFeedSource.getFeedInformation()); 45 | } 46 | 47 | @Test 48 | public void shouldGetEntry() { 49 | AdapterResponse response = disabledFeedSource.getEntry(mockGetEntryRequest); 50 | assertEquals("Should get entry with METHOD_NOT_ALLOWED", HttpStatus.METHOD_NOT_ALLOWED, response.getResponseStatus()); 51 | } 52 | 53 | @Test 54 | public void shouldGetFeed() { 55 | AdapterResponse response = disabledFeedSource.getFeed(mockGetFeedRequest); 56 | assertEquals("Should get feed with METHOD_NOT_ALLOWED", HttpStatus.METHOD_NOT_ALLOWED, response.getResponseStatus()); 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /hopper/src/main/java/org/atomhopper/abdera/filter/SelectiveURIJSONFilter.java: -------------------------------------------------------------------------------- 1 | package org.atomhopper.abdera.filter; 2 | 3 | import java.util.List; 4 | import org.apache.abdera.ext.json.JSONFilter; 5 | import org.apache.abdera.i18n.iri.IRI; 6 | import org.apache.abdera.protocol.server.FilterChain; 7 | import org.apache.abdera.protocol.server.ProviderHelper; 8 | import org.apache.abdera.protocol.server.RequestContext; 9 | import org.apache.abdera.protocol.server.ResponseContext; 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | import org.springframework.stereotype.Component; 12 | 13 | /** 14 | * This class performs XML -> JSON transformation on Atom Hopper responses. 15 | * It can be configured to only perform this transformation on selective URIs. 16 | * If a particular request URI is in the 'allowedURIs' list (only substring 17 | * check is performed), then the transformation 18 | * of XML -> JSON will be performed. 19 | * 20 | * User: shin4590 21 | * Date: 7/1/14 22 | */ 23 | @Component 24 | public class SelectiveURIJSONFilter extends JSONFilter { 25 | 26 | @Autowired 27 | private List allowedURIs; 28 | 29 | @Override 30 | public ResponseContext filter(RequestContext request, FilterChain chain) { 31 | if ( uriAllowed(request.getUri()) ) { 32 | return super.filter(request, chain); 33 | } else { 34 | String format = request.getParameter("format"); 35 | if ( format != null && format.equalsIgnoreCase("json") ) { 36 | return ProviderHelper.badrequest(request, "format=json is not a valid query parameter for this feed"); 37 | } else { 38 | return chain.next(request); 39 | } 40 | } 41 | } 42 | 43 | public List getAllowedURIs() { 44 | return allowedURIs; 45 | } 46 | 47 | public void setAllowedURIs(List allowedURIs) { 48 | this.allowedURIs = allowedURIs; 49 | } 50 | 51 | private boolean uriAllowed(IRI uri) { 52 | if ( allowedURIs == null ) { 53 | return false; 54 | } 55 | String path = uri.getPath(); 56 | for (String allowedPath : allowedURIs) { 57 | // actual path maybe: /foo/bar/entries/urn:uuid:xxxx, but 58 | // allowedURIs can just be /foo/bar 59 | if ( path.startsWith(allowedPath) ) { 60 | return true; 61 | } 62 | } 63 | return false; 64 | } 65 | } 66 | --------------------------------------------------------------------------------