[Tynstep-svn] r226 - in trunk/step: step-core step-core/src/main/java/com/tyndalehouse/step/core/data step-core/src/main/java/com/tyndalehouse/step/core/data/caches step-core/src/main/java/com/tyndalehouse/step/core/data/entities step-core/src/main/java/com/tyndalehouse/step/core/guice step-core/src/main/java/com/tyndalehouse/step/core/guice/providers step-core/src/main/java/com/tyndalehouse/step/core/models step-core/src/main/java/com/tyndalehouse/step/core/service step-core/src/main/java/com/tyndalehouse/step/core/service/impl step-core/src/main/java/com/tyndalehouse/step/core/utils step-core/src/main/java/com/tyndalehouse/step/core/utils/cache step-core/src/main/resources step-core/src/test/java/com/tyndalehouse/step/core/data/create step-core/src/test/java/com/tyndalehouse/step/core/guice step-core/src/test/java/com/tyndalehouse/step/core/guice/providers step-core/src/test/java/com/tyndalehouse/step/core/service step-core/src/test/java/com/tyndalehouse/step/core/service/impl step-core/src/test/java/com/tyndalehouse/step/core/utils step-parent step-web step-web/src/main/java/com/tyndalehouse/step/rest/controllers step-web/src/main/java/com/tyndalehouse/step/rest/framework step-web/src/main/webapp step-web/src/main/webapp/WEB-INF step-web/src/main/webapp/libs step-web/src/test/java/com/tyndalehouse/step/rest/controllers step-web/src/test/java/com/tyndalehouse/step/rest/framework
ChrisBurrell at crosswire.org
ChrisBurrell at crosswire.org
Tue Mar 29 14:43:38 MST 2011
Author: ChrisBurrell
Date: 2011-03-29 14:43:38 -0700 (Tue, 29 Mar 2011)
New Revision: 226
Added:
trunk/step/step-core/db-create.sql
trunk/step/step-core/db-drop.sql
trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/data/caches/
trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/data/caches/AbstractCacheListener.java
trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/data/caches/AbstractDefaultCache.java
trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/data/caches/SessionCache.java
trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/data/caches/SessionCacheListener.java
trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/utils/cache/
trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/utils/cache/SimpleCache.java
trunk/step/step-core/src/test/java/com/tyndalehouse/step/core/guice/providers/
trunk/step/step-core/src/test/java/com/tyndalehouse/step/core/guice/providers/ServerSessionProviderTest.java
trunk/step/step-web/db-create.sql
trunk/step/step-web/db-drop.sql
trunk/step/step-web/src/main/java/com/tyndalehouse/step/rest/framework/Cacheable.java
trunk/step/step-web/src/main/java/com/tyndalehouse/step/rest/framework/EbeanServletContextListener.java
trunk/step/step-web/src/main/java/com/tyndalehouse/step/rest/framework/ResponseCache.java
trunk/step/step-web/src/main/webapp/libs/jquery_include.js
Modified:
trunk/step/step-core/pom.xml
trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/data/entities/Session.java
trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/guice/StepCoreModule.java
trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/guice/providers/DatabaseConfigProvider.java
trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/guice/providers/DefaultInstallersProvider.java
trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/guice/providers/ServerSessionProvider.java
trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/models/LookupOption.java
trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/service/UserDataService.java
trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/service/impl/JSwordServiceImpl.java
trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/service/impl/UserDataServiceImpl.java
trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/utils/PassageReferenceUtils.java
trunk/step/step-core/src/main/resources/step.core.properties
trunk/step/step-core/src/test/java/com/tyndalehouse/step/core/data/create/DataTest.java
trunk/step/step-core/src/test/java/com/tyndalehouse/step/core/data/create/DataTestModule.java
trunk/step/step-core/src/test/java/com/tyndalehouse/step/core/service/JSwordServiceImplTest.java
trunk/step/step-core/src/test/java/com/tyndalehouse/step/core/service/impl/UserDataServiceImplTest.java
trunk/step/step-core/src/test/java/com/tyndalehouse/step/core/utils/PassageReferenceUtilsTest.java
trunk/step/step-parent/pom.xml
trunk/step/step-web/pom.xml
trunk/step/step-web/src/main/java/com/tyndalehouse/step/rest/controllers/BibleController.java
trunk/step/step-web/src/main/java/com/tyndalehouse/step/rest/controllers/FrontController.java
trunk/step/step-web/src/main/java/com/tyndalehouse/step/rest/controllers/ModuleController.java
trunk/step/step-web/src/main/java/com/tyndalehouse/step/rest/controllers/TimelineController.java
trunk/step/step-web/src/main/java/com/tyndalehouse/step/rest/controllers/UserController.java
trunk/step/step-web/src/main/java/com/tyndalehouse/step/rest/framework/StepRequest.java
trunk/step/step-web/src/main/webapp/WEB-INF/web.xml
trunk/step/step-web/src/main/webapp/index.html
trunk/step/step-web/src/test/java/com/tyndalehouse/step/rest/controllers/FrontControllerTest.java
trunk/step/step-web/src/test/java/com/tyndalehouse/step/rest/framework/StepRequestTest.java
Log:
fixing poms and added a whole load of caching on request responses, as well as on server side sessions to reduce number of DB calls
Added: trunk/step/step-core/db-create.sql
===================================================================
--- trunk/step/step-core/db-create.sql (rev 0)
+++ trunk/step/step-core/db-create.sql 2011-03-29 21:43:38 UTC (rev 226)
@@ -0,0 +1,106 @@
+create table bookmark (
+ id integer not null,
+ bookmark_reference varchar(255) not null,
+ user_id integer,
+ constraint pk_bookmark primary key (id))
+;
+
+create table history (
+ id integer not null,
+ history_reference varchar(255) not null,
+ user_id integer,
+ last_updated timestamp not null,
+ constraint pk_history primary key (id))
+;
+
+create table hot_spot (
+ id integer not null,
+ description varchar(255),
+ code varchar(255),
+ scale integer,
+ timeband_id integer,
+ constraint ck_hot_spot_scale check (scale in (0,1,2,3,4,5,6)),
+ constraint pk_hot_spot primary key (id))
+;
+
+create table scripture_reference (
+ scripture_reference_id integer not null,
+ target_id integer,
+ target_type integer,
+ start_verse_id integer,
+ end_verse_id integer,
+ constraint ck_scripture_reference_target_type check (target_type in (0)),
+ constraint pk_scripture_reference primary key (scripture_reference_id))
+;
+
+create table scripture_target (
+ targetTypeId integer(31) not null,
+ id integer not null,
+ summary varchar(255),
+ from_date bigint,
+ to_date bigint,
+ from_precision integer,
+ to_precision integer,
+ hot_spot_id integer,
+ constraint ck_scripture_target_from_precision check (from_precision in (0,1,2,3)),
+ constraint ck_scripture_target_to_precision check (to_precision in (0,1,2,3)),
+ constraint pk_scripture_target primary key (id))
+;
+
+create table session (
+ id integer not null,
+ j_session_id varchar(255),
+ user_id integer,
+ ip_address varchar(255),
+ expires_on timestamp,
+ constraint pk_session primary key (id))
+;
+
+create table timeband (
+ id integer not null,
+ code varchar(255),
+ scale integer,
+ description varchar(255),
+ constraint ck_timeband_scale check (scale in (0,1,2,3,4,5,6)),
+ constraint pk_timeband primary key (id))
+;
+
+create table users (
+ id integer not null,
+ name varchar(255),
+ password varchar(255),
+ email_address varchar(255),
+ country varchar(255),
+ constraint pk_users primary key (id))
+;
+
+create sequence bookmark_seq;
+
+create sequence history_seq;
+
+create sequence hot_spot_seq;
+
+create sequence scripture_reference_seq;
+
+create sequence scripture_target_seq;
+
+create sequence session_seq;
+
+create sequence timeband_seq;
+
+create sequence users_seq;
+
+alter table bookmark add constraint fk_bookmark_user_1 foreign key (user_id) references users (id) on delete restrict on update restrict;
+create index ix_bookmark_user_1 on bookmark (user_id);
+alter table history add constraint fk_history_user_2 foreign key (user_id) references users (id) on delete restrict on update restrict;
+create index ix_history_user_2 on history (user_id);
+alter table hot_spot add constraint fk_hot_spot_timeband_3 foreign key (timeband_id) references timeband (id) on delete restrict on update restrict;
+create index ix_hot_spot_timeband_3 on hot_spot (timeband_id);
+alter table scripture_reference add constraint fk_scripture_reference_target_4 foreign key (target_id) references scripture_target (id) on delete restrict on update restrict;
+create index ix_scripture_reference_target_4 on scripture_reference (target_id);
+alter table scripture_target add constraint fk_scripture_target_hotSpot_5 foreign key (hot_spot_id) references hot_spot (id) on delete restrict on update restrict;
+create index ix_scripture_target_hotSpot_5 on scripture_target (hot_spot_id);
+alter table session add constraint fk_session_user_6 foreign key (user_id) references users (id) on delete restrict on update restrict;
+create index ix_session_user_6 on session (user_id);
+
+
Added: trunk/step/step-core/db-drop.sql
===================================================================
--- trunk/step/step-core/db-drop.sql (rev 0)
+++ trunk/step/step-core/db-drop.sql 2011-03-29 21:43:38 UTC (rev 226)
@@ -0,0 +1,36 @@
+SET REFERENTIAL_INTEGRITY FALSE;
+
+drop table if exists bookmark;
+
+drop table if exists history;
+
+drop table if exists hot_spot;
+
+drop table if exists scripture_reference;
+
+drop table if exists scripture_target;
+
+drop table if exists session;
+
+drop table if exists timeband;
+
+drop table if exists users;
+
+SET REFERENTIAL_INTEGRITY TRUE;
+
+drop sequence if exists bookmark_seq;
+
+drop sequence if exists history_seq;
+
+drop sequence if exists hot_spot_seq;
+
+drop sequence if exists scripture_reference_seq;
+
+drop sequence if exists scripture_target_seq;
+
+drop sequence if exists session_seq;
+
+drop sequence if exists timeband_seq;
+
+drop sequence if exists users_seq;
+
Modified: trunk/step/step-core/pom.xml
===================================================================
--- trunk/step/step-core/pom.xml 2011-03-26 17:01:58 UTC (rev 225)
+++ trunk/step/step-core/pom.xml 2011-03-29 21:43:38 UTC (rev 226)
@@ -136,6 +136,12 @@
<artifactId>h2</artifactId>
</dependency>
+ <dependency>
+ <groupId>net.sf.ehcache</groupId>
+ <artifactId>ehcache</artifactId>
+ <type>pom</type>
+ </dependency>
+
<!-- transitive dependencies not picked up by jsword -->
<dependency>
<groupId>javatar</groupId>
Added: trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/data/caches/AbstractCacheListener.java
===================================================================
--- trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/data/caches/AbstractCacheListener.java (rev 0)
+++ trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/data/caches/AbstractCacheListener.java 2011-03-29 21:43:38 UTC (rev 226)
@@ -0,0 +1,58 @@
+package com.tyndalehouse.step.core.data.caches;
+
+import net.sf.ehcache.Ehcache;
+import net.sf.ehcache.Element;
+import net.sf.ehcache.event.CacheEventListener;
+
+/**
+ * This is a simple implementation where the listener does nothing. It is meant to be inherited to provide
+ * overiddes for just what is required.
+ *
+ * @author Chris
+ *
+ */
+ at SuppressWarnings("PMD.EmptyMethodInAbstractClassShouldBeAbstract")
+public abstract class AbstractCacheListener implements CacheEventListener {
+
+ @Override
+ public void notifyElementRemoved(final Ehcache cache, final Element element) {
+ // No implementation
+ }
+
+ @Override
+ public void notifyElementPut(final Ehcache cache, final Element element) {
+ // No implementation
+ }
+
+ @Override
+ public void notifyElementUpdated(final Ehcache cache, final Element element) {
+ // No implementation
+ }
+
+ @Override
+ public void notifyElementExpired(final Ehcache cache, final Element element) {
+ // No implementation
+ }
+
+ @Override
+ public void notifyElementEvicted(final Ehcache cache, final Element element) {
+ // No implementation
+ }
+
+ @Override
+ public void notifyRemoveAll(final Ehcache cache) {
+ // No implementation
+ }
+
+ @Override
+ public void dispose() {
+ // No implementation
+ }
+
+ // CHECKSTYLE:OFF We are forced to override here sadly
+ @Override
+ public Object clone() throws CloneNotSupportedException {
+ throw new CloneNotSupportedException();
+ }
+ // CHECKSTYLE:ON
+}
Property changes on: trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/data/caches/AbstractCacheListener.java
___________________________________________________________________
Added: svn:mime-type
+ text/plain
Added: trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/data/caches/AbstractDefaultCache.java
===================================================================
--- trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/data/caches/AbstractDefaultCache.java (rev 0)
+++ trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/data/caches/AbstractDefaultCache.java 2011-03-29 21:43:38 UTC (rev 226)
@@ -0,0 +1,57 @@
+package com.tyndalehouse.step.core.data.caches;
+
+import net.sf.ehcache.Cache;
+import net.sf.ehcache.CacheManager;
+import net.sf.ehcache.Element;
+import net.sf.ehcache.config.CacheConfiguration;
+
+import com.tyndalehouse.step.core.utils.cache.SimpleCache;
+
+/**
+ * An abstract implementation of the cache
+ *
+ * @author Chris
+ * @param <T> the type of element that will be stored in the cache
+ */
+public abstract class AbstractDefaultCache<T> implements SimpleCache<String, T> {
+ private final Cache cache;
+
+ /**
+ * creates a default cache
+ *
+ * @param cacheManager the cache manager
+ * @param config the config to create it with
+ */
+ public AbstractDefaultCache(final CacheManager cacheManager, final CacheConfiguration config) {
+ this.cache = new Cache(config);
+ cacheManager.addCache(this.cache);
+ }
+
+ @Override
+ public void put(final String key, final T obj) {
+ final Element e = new Element(key, obj);
+ this.cache.put(e);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public T get(final String key) {
+ if (key == null) {
+ return null;
+ }
+
+ final Element element = this.cache.get(key);
+ if (element == null) {
+ return null;
+ }
+
+ return (T) element.getObjectValue();
+ }
+
+ /**
+ * @return the cache
+ */
+ public Cache getCache() {
+ return this.cache;
+ }
+}
Property changes on: trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/data/caches/AbstractDefaultCache.java
___________________________________________________________________
Added: svn:mime-type
+ text/plain
Added: trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/data/caches/SessionCache.java
===================================================================
--- trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/data/caches/SessionCache.java (rev 0)
+++ trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/data/caches/SessionCache.java 2011-03-29 21:43:38 UTC (rev 226)
@@ -0,0 +1,44 @@
+package com.tyndalehouse.step.core.data.caches;
+
+import net.sf.ehcache.CacheManager;
+import net.sf.ehcache.config.CacheConfiguration;
+import net.sf.ehcache.store.MemoryStoreEvictionPolicy;
+
+import com.google.inject.Inject;
+import com.google.inject.name.Named;
+import com.tyndalehouse.step.core.data.entities.Session;
+
+/**
+ * Session cache is configured to only contain elements for a certain while
+ *
+ * The session cache automatically expires elements after a while and will clean up dead sessions on exit
+ *
+ * @author Chris
+ *
+ */
+public class SessionCache extends AbstractDefaultCache<Session> {
+
+ /**
+ * creates the session cache
+ *
+ * @param cacheManager the cache manager
+ * @param cacheListener the listener that will expire or update elements in the database
+ * @param maxElementsInMemory the number of alive sessions
+ * @param timeToLiveSeconds the time in seconds for each element to live
+ * @param timeToIdleSeconds the number of seconds that after an element has been accessed for the last
+ * time.
+ */
+ @Inject
+ public SessionCache(final CacheManager cacheManager, final SessionCacheListener cacheListener,
+ @Named("app.cache.session.maxElements") final int maxElementsInMemory,
+ @Named("app.cache.session.timeToLive") final int timeToLiveSeconds,
+ @Named("app.cache.session.timeToIdle") final int timeToIdleSeconds) {
+ super(cacheManager, new CacheConfiguration("sessionCache", maxElementsInMemory)
+ .memoryStoreEvictionPolicy(MemoryStoreEvictionPolicy.LFU).overflowToDisk(false)
+ .eternal(false).timeToLiveSeconds(timeToLiveSeconds).timeToIdleSeconds(timeToIdleSeconds)
+ .diskPersistent(false).diskExpiryThreadIntervalSeconds(0));
+
+ // add two listeners, one to expire sessions, one to maintain sessions:
+ getCache().getCacheEventNotificationService().registerListener(cacheListener);
+ }
+}
Property changes on: trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/data/caches/SessionCache.java
___________________________________________________________________
Added: svn:mime-type
+ text/plain
Added: trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/data/caches/SessionCacheListener.java
===================================================================
--- trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/data/caches/SessionCacheListener.java (rev 0)
+++ trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/data/caches/SessionCacheListener.java 2011-03-29 21:43:38 UTC (rev 226)
@@ -0,0 +1,65 @@
+package com.tyndalehouse.step.core.data.caches;
+
+import java.sql.Timestamp;
+import java.util.Calendar;
+
+import net.sf.ehcache.Ehcache;
+import net.sf.ehcache.Element;
+
+import com.avaje.ebean.EbeanServer;
+import com.google.inject.Inject;
+import com.google.inject.name.Named;
+import com.tyndalehouse.step.core.data.entities.Session;
+
+/**
+ * provides simple implementation for expiring and updating session
+ *
+ * @author Chris
+ *
+ */
+public class SessionCacheListener extends AbstractCacheListener {
+ private final EbeanServer ebean;
+ private final int expiryPeriod;
+
+ /**
+ * @param ebean ebean server
+ * @param expiryPeriod time to let go by before we expire the session after no activity
+ *
+ */
+ @Inject
+ public SessionCacheListener(final EbeanServer ebean,
+ @Named("app.cache.session.timeToIdle") final int expiryPeriod) {
+ this.ebean = ebean;
+ this.expiryPeriod = expiryPeriod;
+ }
+
+ @Override
+ public void notifyElementUpdated(final Ehcache cache, final Element element) {
+ final Calendar expiryDate = Calendar.getInstance();
+ expiryDate.add(Calendar.DAY_OF_YEAR, this.expiryPeriod);
+
+ final Session session = getSessionFromElement(element);
+ session.setExpiresOn(new Timestamp(expiryDate.getTimeInMillis()));
+ this.ebean.update(session);
+ }
+
+ @Override
+ public void notifyElementEvicted(final Ehcache cache, final Element element) {
+ this.ebean.delete(getSessionFromElement(element));
+ }
+
+ /**
+ * Retrieves the session by casting the object value
+ *
+ * @param element the cache element
+ * @return the value from the cache
+ */
+ private Session getSessionFromElement(final Element element) {
+ return (Session) element.getObjectValue();
+ }
+
+ @Override
+ public void notifyRemoveAll(final Ehcache cache) {
+ this.ebean.delete(Session.class);
+ }
+}
Property changes on: trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/data/caches/SessionCacheListener.java
___________________________________________________________________
Added: svn:mime-type
+ text/plain
Modified: trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/data/entities/Session.java
===================================================================
--- trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/data/entities/Session.java 2011-03-26 17:01:58 UTC (rev 225)
+++ trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/data/entities/Session.java 2011-03-29 21:43:38 UTC (rev 226)
@@ -1,7 +1,7 @@
package com.tyndalehouse.step.core.data.entities;
import java.io.Serializable;
-import java.util.Date;
+import java.sql.Timestamp;
import javax.persistence.CascadeType;
import javax.persistence.Column;
@@ -13,8 +13,8 @@
import javax.persistence.TemporalType;
/**
- * TODO add job to clean up old sessions that have expired A session is associated with a user and may or may
- * not be active (expiresOn value) A user may be logged in multiple times and hence have several sessions.
+ * A session is associated with a user and may or may not be active (expiresOn value) A user may be logged in
+ * multiple times and hence have several sessions.
*
* @author Chris
*
@@ -39,7 +39,7 @@
@Temporal(TemporalType.TIMESTAMP)
@Column
- private Date expiresOn;
+ private Timestamp expiresOn;
/**
* @return the id
@@ -86,14 +86,14 @@
/**
* @return the expiresOn
*/
- public Date getExpiresOn() {
+ public Timestamp getExpiresOn() {
return this.expiresOn;
}
/**
* @param expiresOn the expiresOn to set
*/
- public void setExpiresOn(final Date expiresOn) {
+ public void setExpiresOn(final Timestamp expiresOn) {
this.expiresOn = expiresOn;
}
Modified: trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/guice/StepCoreModule.java
===================================================================
--- trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/guice/StepCoreModule.java 2011-03-26 17:01:58 UTC (rev 225)
+++ trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/guice/StepCoreModule.java 2011-03-29 21:43:38 UTC (rev 226)
@@ -6,10 +6,15 @@
import java.util.Map;
import java.util.Properties;
+import net.sf.ehcache.CacheManager;
+import net.sf.ehcache.config.CacheConfiguration;
+import net.sf.ehcache.config.Configuration;
+
import org.crosswire.jsword.book.install.Installer;
import com.avaje.ebean.EbeanServer;
import com.google.inject.AbstractModule;
+import com.google.inject.Provides;
import com.google.inject.TypeLiteral;
import com.google.inject.name.Names;
import com.tyndalehouse.step.core.data.create.Loader;
@@ -48,6 +53,9 @@
final Properties stepProperties = readProperties();
bind(Properties.class).annotatedWith(Names.named("StepCoreProperties")).toInstance(stepProperties);
+ // for now just have a method that statically initialises the cache
+ initialiseCacheManager();
+
bind(JSwordService.class).to(JSwordServiceImpl.class).asEagerSingleton();
bind(BibleInformationService.class).to(BibleInformationServiceImpl.class).asEagerSingleton();
bind(ModuleService.class).to(ModuleServiceImpl.class).asEagerSingleton();
@@ -67,6 +75,8 @@
bind(EbeanServer.class).toProvider(DatabaseConfigProvider.class).asEagerSingleton();
+ // bind a cache
+
bindDaos();
// now bind the test data
@@ -76,6 +86,26 @@
}
/**
+ * we return the singleton instance here
+ *
+ * @return the singleton cache manager
+ */
+ @Provides
+ public CacheManager getCacheManager() {
+ return CacheManager.getInstance();
+ }
+
+ /**
+ * initialises the cache manager. e.g. disables update checker
+ */
+ private void initialiseCacheManager() {
+ final Configuration config = new Configuration();
+ config.setUpdateCheck(false);
+ config.defaultCache(new CacheConfiguration());
+ CacheManager.create(config);
+ }
+
+ /**
* helper method that binds the DAOs
*/
private void bindDaos() {
Modified: trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/guice/providers/DatabaseConfigProvider.java
===================================================================
--- trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/guice/providers/DatabaseConfigProvider.java 2011-03-26 17:01:58 UTC (rev 225)
+++ trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/guice/providers/DatabaseConfigProvider.java 2011-03-29 21:43:38 UTC (rev 226)
@@ -63,7 +63,7 @@
this.password = password;
this.validationQuery = validationQuery;
- // TODO add exception handling when i know how
+ // TODO: add exception handling when i know how
this.maxActive = Integer.parseInt(maxActive);
this.maxIdle = Integer.parseInt(maxIdle);
this.maxOpenStatements = Integer.parseInt(maxOpenStatements);
Modified: trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/guice/providers/DefaultInstallersProvider.java
===================================================================
--- trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/guice/providers/DefaultInstallersProvider.java 2011-03-26 17:01:58 UTC (rev 225)
+++ trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/guice/providers/DefaultInstallersProvider.java 2011-03-29 21:43:38 UTC (rev 226)
@@ -57,7 +57,7 @@
final Integer p = Integer.parseInt(proxyPort);
installer.setProxyPort(p.intValue());
} catch (final NumberFormatException e) {
- // TODO work out how this should be thrown
+ // TODO: work out how this should be thrown
throw new StepInternalException("Unable to parse port number " + proxyPort, e);
}
}
Modified: trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/guice/providers/ServerSessionProvider.java
===================================================================
--- trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/guice/providers/ServerSessionProvider.java 2011-03-26 17:01:58 UTC (rev 225)
+++ trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/guice/providers/ServerSessionProvider.java 2011-03-29 21:43:38 UTC (rev 226)
@@ -1,16 +1,22 @@
package com.tyndalehouse.step.core.guice.providers;
+import java.sql.Timestamp;
+import java.util.Calendar;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
import com.avaje.ebean.EbeanServer;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
+import com.google.inject.name.Named;
+import com.tyndalehouse.step.core.data.caches.SessionCache;
import com.tyndalehouse.step.core.data.entities.Session;
import com.tyndalehouse.step.core.models.ClientSession;
-import com.tyndalehouse.step.core.service.UserDataService;
/**
- * A server session provider TODO should use CACHE here since we query almost everytime there is a request for
- * bookmarks, etc.
+ * A server session provider
*
* @author Chris
*
@@ -19,36 +25,99 @@
public class ServerSessionProvider implements Provider<Session> {
// we store the provider, since the provider is request-scoped and therefore
// values vary
+ private static final Logger LOG = LoggerFactory.getLogger(ServerSessionProvider.class);
private final Provider<ClientSession> clientSessionProvider;
private final EbeanServer ebean;
- private final UserDataService userDataService;
+ private final SessionCache sessionCache;
+ private final int expiryPeriod;
/**
* Sets up a singleton provider that relies upon a request-scoped clientSessionProvider.
*
* @param ebean the ebean server used to retrieve data
* @param clientSessionProvider the client session provider, giving us an id to reference
- * @param userDataService a service for user and session management
+ * @param sessionCache which will hold in memory to avoid too many DB calls
+ * @param expiryPeriod the time a session will remain alive for the session
*/
@Inject
public ServerSessionProvider(final EbeanServer ebean,
- final Provider<ClientSession> clientSessionProvider, final UserDataService userDataService) {
+ final Provider<ClientSession> clientSessionProvider, final SessionCache sessionCache,
+ @Named("app.cache.session.timeToIdle") final int expiryPeriod) {
this.ebean = ebean;
this.clientSessionProvider = clientSessionProvider;
- this.userDataService = userDataService;
+ this.sessionCache = sessionCache;
+ this.expiryPeriod = expiryPeriod;
}
@Override
public Session get() {
+ LOG.debug("Retrieving session");
final String clientSessionId = this.clientSessionProvider.get().getSessionId();
+
+ Session session = this.sessionCache.get(clientSessionId);
+ if (session == null) {
+ LOG.debug("Session was not in cache, retrieving or creating from database");
+ // cache miss, or no session in server
+ session = getSessionFromDb(clientSessionId);
+ this.sessionCache.put(clientSessionId, session);
+ }
+ return session;
+ }
+
+ /**
+ * checks the database for the server session, and creates one if not present
+ *
+ * @param clientSessionId the key identifying the session
+ * @return the sesion that was or has now been persisted
+ */
+ private Session getSessionFromDb(final String clientSessionId) {
final Session serverSession = this.ebean.find(Session.class).fetch("user", "id, name").where()
.eq("jSessionId", clientSessionId).findUnique();
+ // we create a server session
if (serverSession == null) {
- // we create a server session
- return this.userDataService.createSession();
+ LOG.debug("Server session was not present");
+ return createSession();
}
+ // deal with expired sessions... This should never happen has a cache miss should have cleared out,
+ // but all the same
+ if (Calendar.getInstance().after(serverSession.getExpiresOn())) {
+ // session has expired, so we remove the reference to the user and update the expiry date
+ serverSession.setUser(null);
+ serverSession.setExpiresOn(getExpiryTime());
+ this.ebean.update(serverSession);
+ }
+
return serverSession;
}
+
+ /**
+ * Creates a session in the database
+ *
+ * @return the session that was created
+ */
+ Session createSession() {
+ final Session session = new Session();
+ final ClientSession clientSession = this.clientSessionProvider.get();
+ session.setJSessionId(clientSession.getSessionId());
+ session.setIpAddress(clientSession.getIpAddress());
+ session.setExpiresOn(getExpiryTime());
+
+ LOG.debug("Persisting session (jSessionId=[{}],ip=[{}])", session.getJSessionId(),
+ session.getIpAddress());
+ this.ebean.save(session);
+ return session;
+ }
+
+ /**
+ * Simply add today to the expiry period and return
+ *
+ * @return the time at which the session will expire
+ */
+ private Timestamp getExpiryTime() {
+ final Calendar expiryDate = Calendar.getInstance();
+ expiryDate.add(Calendar.DAY_OF_YEAR, this.expiryPeriod);
+ return new Timestamp(expiryDate.getTimeInMillis());
+ }
}
Modified: trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/models/LookupOption.java
===================================================================
--- trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/models/LookupOption.java 2011-03-26 17:01:58 UTC (rev 225)
+++ trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/models/LookupOption.java 2011-03-29 21:43:38 UTC (rev 226)
@@ -11,7 +11,7 @@
*
*/
public enum LookupOption {
- // CHECKSTYLE:OFF TODO change the values in the XSL file
+ // CHECKSTYLE:OFF TODO: change the values in the XSL file
/**
* Showing headings
*/
Modified: trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/service/UserDataService.java
===================================================================
--- trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/service/UserDataService.java 2011-03-26 17:01:58 UTC (rev 225)
+++ trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/service/UserDataService.java 2011-03-29 21:43:38 UTC (rev 226)
@@ -1,6 +1,5 @@
package com.tyndalehouse.step.core.service;
-import com.tyndalehouse.step.core.data.entities.Session;
import com.tyndalehouse.step.core.data.entities.User;
/**
@@ -34,14 +33,6 @@
User register(String emailAddress, String name, String country, String password);
/**
- * TODO move this to session provider This method is called to create a session for the user. This will
- * associate the jsession id with a new row in the Session table.
- *
- * @return the server session that was created
- */
- Session createSession();
-
- /**
* Associates the current session with the username assuming password and username authenticates
*
* @param emailAddress the email address is used as the login token
Modified: trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/service/impl/JSwordServiceImpl.java
===================================================================
--- trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/service/impl/JSwordServiceImpl.java 2011-03-26 17:01:58 UTC (rev 225)
+++ trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/service/impl/JSwordServiceImpl.java 2011-03-29 21:43:38 UTC (rev 226)
@@ -114,7 +114,7 @@
return getOsisText(version, reference, options, null);
}
- // TODO remove synchronisation once book is fixed
+ // TODO: remove synchronisation once book is fixed
@Override
public synchronized String getOsisText(final String version, final String reference,
final List<LookupOption> options, final String interlinearVersion) {
@@ -228,7 +228,7 @@
if (isNotBlank(interlinearVersion)) {
tsep.setParameter("interlinearVersion", interlinearVersion);
}
- // TODO else depending on OT or NT, we select a default interlinear version
+ // TODO: else depending on OT or NT, we select a default interlinear version
}
}
Modified: trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/service/impl/UserDataServiceImpl.java
===================================================================
--- trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/service/impl/UserDataServiceImpl.java 2011-03-26 17:01:58 UTC (rev 225)
+++ trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/service/impl/UserDataServiceImpl.java 2011-03-29 21:43:38 UTC (rev 226)
@@ -2,9 +2,6 @@
import static com.avaje.ebean.Expr.eq;
-import java.util.Calendar;
-import java.util.Date;
-
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -15,7 +12,6 @@
import com.tyndalehouse.step.core.data.entities.Session;
import com.tyndalehouse.step.core.data.entities.User;
import com.tyndalehouse.step.core.exceptions.StepInternalException;
-import com.tyndalehouse.step.core.models.ClientSession;
import com.tyndalehouse.step.core.service.UserDataService;
/**
@@ -28,41 +24,31 @@
*/
@Singleton
public class UserDataServiceImpl implements UserDataService {
- private static final int EXPIRY_SESSION_INTERVAL = 30;
private static final Logger LOG = LoggerFactory.getLogger(UserDataServiceImpl.class);
private final Provider<Session> sessionProvider;
private final EbeanServer ebean;
- private final Provider<ClientSession> clientSessionProvider;
/**
* sessions change at runtime based on which request we are serving
*
* @param ebean the ebean server to persist and load data
* @param sessionProvider the session provider
- * @param clientSessionProvider the client session (cookie information + ip address)
*/
@Inject
- public UserDataServiceImpl(final EbeanServer ebean, final Provider<Session> sessionProvider,
- final Provider<ClientSession> clientSessionProvider) {
+ public UserDataServiceImpl(final EbeanServer ebean, final Provider<Session> sessionProvider) {
this.ebean = ebean;
this.sessionProvider = sessionProvider;
- this.clientSessionProvider = clientSessionProvider;
}
@Override
public User register(final String emailAddress, final String name, final String country,
final String password) {
- // first check that are we not logged in!
- Session session = this.sessionProvider.get();
- // check we have a session, otherwise create one for ourselves (this should be catered
- if (session == null || Calendar.getInstance().after(session.getExpiresOn())) {
- // the session is either non-existent or exists but has expired, so recreate one:
- createSession();
- session = this.sessionProvider.get();
- assert session != null;
- }
+ LOG.debug("Registering user [{}] with email address [{}]", name, emailAddress);
+ // first check that are we not logged in!
+ final Session session = this.sessionProvider.get();
+
if (session.getUser() != null) {
throw new IllegalArgumentException("You cannot register, as you are already logged in.");
}
@@ -77,47 +63,26 @@
this.ebean.save(u);
// next, we just associate the current session with the user by logging in
- return this.login(emailAddress, password);
+ return this.login(u);
}
@Override
- public Session createSession() {
- final Session session = new Session();
- final ClientSession clientSession = this.clientSessionProvider.get();
-
- // TODO we ensure that we expire the sessions after a while of inactivity
- // so we will need to add a filter to ensure this actually happens.
- // i.e. update the time of the session asynchronously if possible
- // write a job that deletes expired sessions
- // this also needs to match up with the value in the client session really
- final Calendar expiryDate = Calendar.getInstance();
- expiryDate.add(Calendar.DAY_OF_YEAR, EXPIRY_SESSION_INTERVAL);
-
- session.setJSessionId(clientSession.getSessionId());
- session.setIpAddress(clientSession.getIpAddress());
- session.setExpiresOn(new Date(expiryDate.getTimeInMillis()));
-
- LOG.debug("Persisting session (jSessionId=[{}],ip=[{}]", session.getJSessionId(),
- session.getIpAddress());
- this.ebean.save(session);
- return session;
- }
-
- @Override
public User getLoggedInUser() {
final Session session = this.sessionProvider.get();
if (session == null) {
+ LOG.debug("There was no user no logged in");
return null;
}
+ LOG.debug("Returning a user ");
return session.getUser();
}
@Override
public User login(final String emailAddress, final String password) {
- // logging in basically means associating the user with the session
- final Session serverSession = this.sessionProvider.get();
+ LOG.debug("Logging [{}] in to the system", emailAddress);
+ // logging in basically means associating the user with the session
final User user = this.ebean.find(User.class).select("id, name").where()
.and(eq("emailAddress", emailAddress), eq("password", password)).findUnique();
@@ -126,11 +91,21 @@
throw new StepInternalException("Unable to login with username/password provided");
}
+ return login(user);
+ }
+
+ /**
+ * A way of logging in without authenticating
+ *
+ * @param user the user that requires loggin in
+ * @return the user once logged in
+ */
+ private User login(final User user) {
+ final Session serverSession = this.sessionProvider.get();
serverSession.setUser(user);
// saving the session
this.ebean.save(serverSession);
-
return user;
}
Modified: trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/utils/PassageReferenceUtils.java
===================================================================
--- trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/utils/PassageReferenceUtils.java 2011-03-26 17:01:58 UTC (rev 225)
+++ trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/utils/PassageReferenceUtils.java 2011-03-29 21:43:38 UTC (rev 226)
@@ -63,7 +63,7 @@
LOG.trace("Found reference [{}] to [{}]", valueOf(startVerseId), valueOf(endVerseId));
final ScriptureReference sr = new ScriptureReference();
- // TODO fix this:
+ // TODO: fix this:
sr.setTarget(target);
sr.setStartVerseId(startVerseId);
Added: trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/utils/cache/SimpleCache.java
===================================================================
--- trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/utils/cache/SimpleCache.java (rev 0)
+++ trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/utils/cache/SimpleCache.java 2011-03-29 21:43:38 UTC (rev 226)
@@ -0,0 +1,28 @@
+package com.tyndalehouse.step.core.utils.cache;
+
+
+/**
+ * a simple cache interface
+ *
+ * @param <T> the key class
+ * @param <S> the item class
+ * @author Chris
+ *
+ */
+public interface SimpleCache<T, S> {
+ /**
+ * puts an object in the cache
+ *
+ * @param key the key of the object
+ * @param obj the object to be cached
+ */
+ void put(T key, S obj);
+
+ /**
+ * retrieves an object from the cache
+ *
+ * @param key the key to the cache
+ * @return the object that was keyed
+ */
+ S get(T key);
+}
Property changes on: trunk/step/step-core/src/main/java/com/tyndalehouse/step/core/utils/cache/SimpleCache.java
___________________________________________________________________
Added: svn:mime-type
+ text/plain
Modified: trunk/step/step-core/src/main/resources/step.core.properties
===================================================================
--- trunk/step/step-core/src/main/resources/step.core.properties 2011-03-26 17:01:58 UTC (rev 225)
+++ trunk/step/step-core/src/main/resources/step.core.properties 2011-03-29 21:43:38 UTC (rev 226)
@@ -1,5 +1,5 @@
# Front controller properties
-cache.enabled=false
+cache.enabled=true
#list of installers in the format: host,package,catalog
installer.1=www.crosswire.org,/ftpmirror/pub/sword/packages/rawzip,/ftpmirror/pub/sword/raw
@@ -9,6 +9,8 @@
app.proxy.host=
app.proxy.port=
+
+
#Test data related questions
test.data.load=true
@@ -22,3 +24,14 @@
app.db.maxOpenStatement=20
app.db.poolableStatements=true
app.db.validationQuery=select 1
+
+#######################
+# cache settings
+#######################
+
+# session cache storing currently logged in people
+app.cache.session.maxElements=250
+app.cache.session.timeToLive=300
+app.cache.session.timeToIdle=300
+
+
Modified: trunk/step/step-core/src/test/java/com/tyndalehouse/step/core/data/create/DataTest.java
===================================================================
--- trunk/step/step-core/src/test/java/com/tyndalehouse/step/core/data/create/DataTest.java 2011-03-26 17:01:58 UTC (rev 225)
+++ trunk/step/step-core/src/test/java/com/tyndalehouse/step/core/data/create/DataTest.java 2011-03-29 21:43:38 UTC (rev 226)
@@ -41,7 +41,9 @@
}
/**
- * testing the loading process TODO don't want to test the whole timeline component every build
+ * testing the loading process
+ * <p />
+ * TODO: don't want to test the whole timeline component every build
*/
@Test
public void tryLoadingProcess() {
Modified: trunk/step/step-core/src/test/java/com/tyndalehouse/step/core/data/create/DataTestModule.java
===================================================================
--- trunk/step/step-core/src/test/java/com/tyndalehouse/step/core/data/create/DataTestModule.java 2011-03-26 17:01:58 UTC (rev 225)
+++ trunk/step/step-core/src/test/java/com/tyndalehouse/step/core/data/create/DataTestModule.java 2011-03-29 21:43:38 UTC (rev 226)
@@ -25,7 +25,7 @@
}
/**
- * TODO share this code with main code reads the core properties from the file
+ * TODO: share this code with main code reads the core properties from the file
*
* @return a list of properties read from file
*/
Added: trunk/step/step-core/src/test/java/com/tyndalehouse/step/core/guice/providers/ServerSessionProviderTest.java
===================================================================
--- trunk/step/step-core/src/test/java/com/tyndalehouse/step/core/guice/providers/ServerSessionProviderTest.java (rev 0)
+++ trunk/step/step-core/src/test/java/com/tyndalehouse/step/core/guice/providers/ServerSessionProviderTest.java 2011-03-29 21:43:38 UTC (rev 226)
@@ -0,0 +1,64 @@
+package com.tyndalehouse.step.core.guice.providers;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.util.Date;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.runners.MockitoJUnitRunner;
+
+import com.google.inject.Provider;
+import com.tyndalehouse.step.core.data.DataDrivenTestExtension;
+import com.tyndalehouse.step.core.data.caches.SessionCache;
+import com.tyndalehouse.step.core.data.entities.Session;
+import com.tyndalehouse.step.core.models.ClientSession;
+
+/**
+ * Testing various methods in the server-side session provider
+ *
+ * @author Chris
+ *
+ */
+ at RunWith(MockitoJUnitRunner.class)
+public class ServerSessionProviderTest extends DataDrivenTestExtension {
+ @Mock
+ private Provider<ClientSession> clientSessionProvider;
+ @Mock
+ private SessionCache sessionCache;
+ private ServerSessionProvider serverSessionProvider;
+
+ /**
+ * sets up the user service under test
+ */
+ @Before
+ public void setUp() {
+ // MockitoAnnotations.initMocks(this);
+ this.serverSessionProvider = new ServerSessionProvider(super.getEbean(), this.clientSessionProvider,
+ this.sessionCache, 10);
+ }
+
+ /**
+ * we check that we can create a session
+ */
+ @Test
+ public void testCreateSession() {
+ final String testClientSessionId = "999";
+ final String testIpAddress = "xx.xxx.xx.xx";
+ final ClientSession clientSession = mock(ClientSession.class);
+ when(this.clientSessionProvider.get()).thenReturn(clientSession);
+ when(clientSession.getSessionId()).thenReturn(testClientSessionId);
+ when(clientSession.getIpAddress()).thenReturn(testIpAddress);
+ this.serverSessionProvider.createSession();
+
+ final Session persistedSession = getEbean().find(Session.class).where()
+ .eq("jSessionId", testClientSessionId).findUnique();
+ assertEquals(persistedSession.getIpAddress(), testIpAddress);
+ assertTrue(persistedSession.getExpiresOn().after(new Date()));
+ }
+}
Property changes on: trunk/step/step-core/src/test/java/com/tyndalehouse/step/core/guice/providers/ServerSessionProviderTest.java
___________________________________________________________________
Added: svn:mime-type
+ text/plain
Modified: trunk/step/step-core/src/test/java/com/tyndalehouse/step/core/service/JSwordServiceImplTest.java
===================================================================
--- trunk/step/step-core/src/test/java/com/tyndalehouse/step/core/service/JSwordServiceImplTest.java 2011-03-26 17:01:58 UTC (rev 225)
+++ trunk/step/step-core/src/test/java/com/tyndalehouse/step/core/service/JSwordServiceImplTest.java 2011-03-29 21:43:38 UTC (rev 226)
@@ -105,7 +105,7 @@
* @throws BookException a book exception
* @throws InterruptedException when the thread is interrupted
*/
- // TODO currently disabled
+ // TODO: currently disabled
@Test
public void testConcurrencyIssueOnBookData() throws NoSuchKeyException, BookException,
InterruptedException {
Modified: trunk/step/step-core/src/test/java/com/tyndalehouse/step/core/service/impl/UserDataServiceImplTest.java
===================================================================
--- trunk/step/step-core/src/test/java/com/tyndalehouse/step/core/service/impl/UserDataServiceImplTest.java 2011-03-26 17:01:58 UTC (rev 225)
+++ trunk/step/step-core/src/test/java/com/tyndalehouse/step/core/service/impl/UserDataServiceImplTest.java 2011-03-29 21:43:38 UTC (rev 226)
@@ -1,12 +1,9 @@
package com.tyndalehouse.step.core.service.impl;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
-import java.util.Date;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -40,8 +37,7 @@
@Before
public void setUp() {
// MockitoAnnotations.initMocks(this);
- this.userService = new UserDataServiceImpl(super.getEbean(), this.serverSessionProvider,
- this.clientSessionProvider);
+ this.userService = new UserDataServiceImpl(super.getEbean(), this.serverSessionProvider);
}
/**
@@ -127,23 +123,4 @@
// check that the user is logged in
assertEquals(currentServerSession.getUser().getName(), testName);
}
-
- /**
- * we check that we can create a session
- */
- @Test
- public void testCreateSession() {
- final String testClientSessionId = "999";
- final String testIpAddress = "xx.xxx.xx.xx";
- final ClientSession clientSession = mock(ClientSession.class);
- when(this.clientSessionProvider.get()).thenReturn(clientSession);
- when(clientSession.getSessionId()).thenReturn(testClientSessionId);
- when(clientSession.getIpAddress()).thenReturn(testIpAddress);
- this.userService.createSession();
-
- final Session persistedSession = getEbean().find(Session.class).where()
- .eq("jSessionId", testClientSessionId).findUnique();
- assertEquals(persistedSession.getIpAddress(), testIpAddress);
- assertTrue(persistedSession.getExpiresOn().after(new Date()));
- }
}
Modified: trunk/step/step-core/src/test/java/com/tyndalehouse/step/core/utils/PassageReferenceUtilsTest.java
===================================================================
--- trunk/step/step-core/src/test/java/com/tyndalehouse/step/core/utils/PassageReferenceUtilsTest.java 2011-03-26 17:01:58 UTC (rev 225)
+++ trunk/step/step-core/src/test/java/com/tyndalehouse/step/core/utils/PassageReferenceUtilsTest.java 2011-03-29 21:43:38 UTC (rev 226)
@@ -8,8 +8,8 @@
import org.junit.Test;
+import com.tyndalehouse.step.core.data.entities.ScriptureReference;
import com.tyndalehouse.step.core.data.entities.ScriptureTarget;
-import com.tyndalehouse.step.core.data.entities.ScriptureReference;
/**
* testing the passage reference utils class
Modified: trunk/step/step-parent/pom.xml
===================================================================
--- trunk/step/step-parent/pom.xml 2011-03-26 17:01:58 UTC (rev 225)
+++ trunk/step/step-parent/pom.xml 2011-03-29 21:43:38 UTC (rev 226)
@@ -35,8 +35,9 @@
<guice-servlet.version>2.9.1</guice-servlet.version>
<opencsv.version>2.1</opencsv.version>
<jetty.version>6.1.26</jetty.version>
- <!-- <ormlite.version>4.6</ormlite.version> -->
+
<ebean.version>2.7.2</ebean.version>
+ <ehcache.version>2.3.1</ehcache.version>
<lucene.version>3.0.3</lucene.version>
<!-- Commons -->
@@ -172,7 +173,7 @@
<version>${pjl-comp-filter.version}</version>
</dependency>
- <!-- TODO build against google released artifacts -->
+ <!-- TODO: build against google released artifacts -->
<dependency>
<groupId>com.jolira</groupId>
<artifactId>guice</artifactId>
@@ -299,6 +300,12 @@
<artifactId>junit</artifactId>
<version>${junit.version}</version>
</dependency>
+ <dependency>
+ <groupId>net.sf.ehcache</groupId>
+ <artifactId>ehcache</artifactId>
+ <version>${ehcache.version}</version>
+ <type>pom</type>
+ </dependency>
</dependencies>
</dependencyManagement>
Added: trunk/step/step-web/db-create.sql
===================================================================
--- trunk/step/step-web/db-create.sql (rev 0)
+++ trunk/step/step-web/db-create.sql 2011-03-29 21:43:38 UTC (rev 226)
@@ -0,0 +1,106 @@
+create table bookmark (
+ id integer not null,
+ bookmark_reference varchar(255) not null,
+ user_id integer,
+ constraint pk_bookmark primary key (id))
+;
+
+create table history (
+ id integer not null,
+ history_reference varchar(255) not null,
+ user_id integer,
+ last_updated timestamp not null,
+ constraint pk_history primary key (id))
+;
+
+create table hot_spot (
+ id integer not null,
+ description varchar(255),
+ code varchar(255),
+ scale integer,
+ timeband_id integer,
+ constraint ck_hot_spot_scale check (scale in (0,1,2,3,4,5,6)),
+ constraint pk_hot_spot primary key (id))
+;
+
+create table scripture_reference (
+ scripture_reference_id integer not null,
+ target_id integer,
+ target_type integer,
+ start_verse_id integer,
+ end_verse_id integer,
+ constraint ck_scripture_reference_target_type check (target_type in (0)),
+ constraint pk_scripture_reference primary key (scripture_reference_id))
+;
+
+create table scripture_target (
+ targetTypeId integer(31) not null,
+ id integer not null,
+ summary varchar(255),
+ from_date bigint,
+ to_date bigint,
+ from_precision integer,
+ to_precision integer,
+ hot_spot_id integer,
+ constraint ck_scripture_target_from_precision check (from_precision in (0,1,2,3)),
+ constraint ck_scripture_target_to_precision check (to_precision in (0,1,2,3)),
+ constraint pk_scripture_target primary key (id))
+;
+
+create table session (
+ id integer not null,
+ j_session_id varchar(255),
+ user_id integer,
+ ip_address varchar(255),
+ expires_on timestamp,
+ constraint pk_session primary key (id))
+;
+
+create table timeband (
+ id integer not null,
+ code varchar(255),
+ scale integer,
+ description varchar(255),
+ constraint ck_timeband_scale check (scale in (0,1,2,3,4,5,6)),
+ constraint pk_timeband primary key (id))
+;
+
+create table users (
+ id integer not null,
+ name varchar(255),
+ password varchar(255),
+ email_address varchar(255),
+ country varchar(255),
+ constraint pk_users primary key (id))
+;
+
+create sequence bookmark_seq;
+
+create sequence history_seq;
+
+create sequence hot_spot_seq;
+
+create sequence scripture_reference_seq;
+
+create sequence scripture_target_seq;
+
+create sequence session_seq;
+
+create sequence timeband_seq;
+
+create sequence users_seq;
+
+alter table bookmark add constraint fk_bookmark_user_1 foreign key (user_id) references users (id) on delete restrict on update restrict;
+create index ix_bookmark_user_1 on bookmark (user_id);
+alter table history add constraint fk_history_user_2 foreign key (user_id) references users (id) on delete restrict on update restrict;
+create index ix_history_user_2 on history (user_id);
+alter table hot_spot add constraint fk_hot_spot_timeband_3 foreign key (timeband_id) references timeband (id) on delete restrict on update restrict;
+create index ix_hot_spot_timeband_3 on hot_spot (timeband_id);
+alter table scripture_reference add constraint fk_scripture_reference_target_4 foreign key (target_id) references scripture_target (id) on delete restrict on update restrict;
+create index ix_scripture_reference_target_4 on scripture_reference (target_id);
+alter table scripture_target add constraint fk_scripture_target_hotSpot_5 foreign key (hot_spot_id) references hot_spot (id) on delete restrict on update restrict;
+create index ix_scripture_target_hotSpot_5 on scripture_target (hot_spot_id);
+alter table session add constraint fk_session_user_6 foreign key (user_id) references users (id) on delete restrict on update restrict;
+create index ix_session_user_6 on session (user_id);
+
+
Added: trunk/step/step-web/db-drop.sql
===================================================================
--- trunk/step/step-web/db-drop.sql (rev 0)
+++ trunk/step/step-web/db-drop.sql 2011-03-29 21:43:38 UTC (rev 226)
@@ -0,0 +1,36 @@
+SET REFERENTIAL_INTEGRITY FALSE;
+
+drop table if exists bookmark;
+
+drop table if exists history;
+
+drop table if exists hot_spot;
+
+drop table if exists scripture_reference;
+
+drop table if exists scripture_target;
+
+drop table if exists session;
+
+drop table if exists timeband;
+
+drop table if exists users;
+
+SET REFERENTIAL_INTEGRITY TRUE;
+
+drop sequence if exists bookmark_seq;
+
+drop sequence if exists history_seq;
+
+drop sequence if exists hot_spot_seq;
+
+drop sequence if exists scripture_reference_seq;
+
+drop sequence if exists scripture_target_seq;
+
+drop sequence if exists session_seq;
+
+drop sequence if exists timeband_seq;
+
+drop sequence if exists users_seq;
+
Modified: trunk/step/step-web/pom.xml
===================================================================
--- trunk/step/step-web/pom.xml 2011-03-26 17:01:58 UTC (rev 225)
+++ trunk/step/step-web/pom.xml 2011-03-29 21:43:38 UTC (rev 226)
@@ -102,5 +102,30 @@
<build>
<finalName>step-web</finalName>
+
+ <plugins>
+ <plugin>
+ <groupId>com.samaxes.maven</groupId>
+ <artifactId>maven-minify-plugin</artifactId>
+ <version>1.3.3</version>
+ <configuration>
+ <cssSourceDir>css</cssSourceDir>
+ <cssSourceIncludes>
+ <cssSourceInclude>ui-lightness/*.css</cssSourceInclude>
+ <cssSourceInclude>*.css</cssSourceInclude>
+ <cssSourceInclude>libs/menu/*.css</cssSourceInclude>
+ </cssSourceIncludes>
+ <cssFinalFile>step.css</cssFinalFile>
+ </configuration>
+ <executions>
+ <execution>
+ <phase>process-resources</phase>
+ <goals>
+ <goal>minify</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
</build>
</project>
Modified: trunk/step/step-web/src/main/java/com/tyndalehouse/step/rest/controllers/BibleController.java
===================================================================
--- trunk/step/step-web/src/main/java/com/tyndalehouse/step/rest/controllers/BibleController.java 2011-03-26 17:01:58 UTC (rev 225)
+++ trunk/step/step-web/src/main/java/com/tyndalehouse/step/rest/controllers/BibleController.java 2011-03-29 21:43:38 UTC (rev 226)
@@ -16,6 +16,7 @@
import com.tyndalehouse.step.core.models.EnrichedLookupOption;
import com.tyndalehouse.step.core.models.LookupOption;
import com.tyndalehouse.step.core.service.BibleInformationService;
+import com.tyndalehouse.step.rest.framework.Cacheable;
import com.tyndalehouse.step.rest.wrappers.HtmlWrapper;
/**
@@ -46,6 +47,7 @@
*
* @return all versions of modules that are considered to be Bibles.
*/
+ @Cacheable(true)
public List<BibleVersion> getBibleVersions() {
return this.bibleInformation.getAvailableBibleVersions();
}
@@ -57,6 +59,7 @@
* @param reference the reference to lookup
* @return the text to be displayed, formatted as HTML
*/
+ @Cacheable(true)
public HtmlWrapper getBibleText(final String version, final String reference) {
return getBibleText(version, reference, null, null);
}
@@ -69,6 +72,7 @@
* @param options the list of options to be passed through and affect the retrieval process
* @return the text to be displayed, formatted as HTML
*/
+ @Cacheable(true)
public HtmlWrapper getBibleText(final String version, final String reference, final String options) {
return getBibleText(version, reference, options, null);
}
@@ -82,6 +86,7 @@
* @param interlinearVersion the interlinear version if provided adds lines under the text
* @return the text to be displayed, formatted as HTML
*/
+ @Cacheable(true)
public HtmlWrapper getBibleText(final String version, final String reference, final String options,
final String interlinearVersion) {
Validate.notEmpty(version, "You need to provide a version");
@@ -109,6 +114,7 @@
* @param version the version initials or full version name to retrieve the versions for
* @return all versions of modules that are considered to be Bibles.
*/
+ @Cacheable(true)
public List<LookupOption> getFeatures(final String version) {
return this.bibleInformation.getFeaturesForVersion(version);
}
@@ -118,6 +124,7 @@
*
* @return a list of features currently supported by the application
*/
+ @Cacheable(true)
public List<EnrichedLookupOption> getAllFeatures() {
return this.bibleInformation.getAllFeatures();
}
Modified: trunk/step/step-web/src/main/java/com/tyndalehouse/step/rest/controllers/FrontController.java
===================================================================
--- trunk/step/step-web/src/main/java/com/tyndalehouse/step/rest/controllers/FrontController.java 2011-03-26 17:01:58 UTC (rev 225)
+++ trunk/step/step-web/src/main/java/com/tyndalehouse/step/rest/controllers/FrontController.java 2011-03-29 21:43:38 UTC (rev 226)
@@ -26,9 +26,10 @@
import com.google.inject.Singleton;
import com.google.inject.name.Named;
import com.tyndalehouse.step.core.exceptions.StepInternalException;
+import com.tyndalehouse.step.rest.framework.Cacheable;
import com.tyndalehouse.step.rest.framework.ClientErrorResolver;
import com.tyndalehouse.step.rest.framework.ClientHandledIssue;
-import com.tyndalehouse.step.rest.framework.ControllerCacheKey;
+import com.tyndalehouse.step.rest.framework.ResponseCache;
import com.tyndalehouse.step.rest.framework.StepRequest;
/**
@@ -49,35 +50,38 @@
private static final char PACKAGE_SEPARATOR = '.';
private static final long serialVersionUID = 7898656504631346047L;
private static final String CONTROLLER_SUFFIX = "Controller";
- // TODO EH cache here too?
+ // TODO: EH cache here too?
private final Map<String, String> contextPath = new HashMap<String, String>();
- private final Map<String, byte[]> resultsCache = new HashMap<String, byte[]>();
private final transient Injector guiceInjector;
- // TODO but also check thread safety and whether we should share this object
+ // TODO: but also check thread safety and whether we should share this object
private final transient ObjectMapper jsonMapper = new ObjectMapper();
- // TODO check if this is thread safe, and if so, then make private field
+ // TODO: check if this is thread safe, and if so, then make private field
private final transient JsonContext ebeanJson;
- // TODO investigate EH cache here
+ // TODO: investigate EH cache here
private final Map<String, Method> methodNames = new HashMap<String, Method>();
private final Map<String, Object> controllers = new HashMap<String, Object>();
private final boolean isCacheEnabled;
private final transient ClientErrorResolver errorResolver;
+ private final transient ResponseCache responseCache;
/**
* creates the front controller which will dispatch all the requests
+ * <p />
+ * TODO: rename all ebeans to DB
*
* @param guiceInjector the injector used to call the relevant controllers
- * @param isCacheEnabled indicates whether responses should be cached for fast retrieval TODO rename all
- * ebeans to DB
+ * @param isCacheEnabled indicates whether responses should be cached for fast retrieval
* @param ebean the db access/persisitence object
* @param errorResolver the error resolver is the object that helps us translate errors for the client
+ * @param responseCache cache in which are put any number of responses to speed up processing
*/
@Inject
public FrontController(final Injector guiceInjector,
@Named("cache.enabled") final Boolean isCacheEnabled, final EbeanServer ebean,
- final ClientErrorResolver errorResolver) {
+ final ClientErrorResolver errorResolver, final ResponseCache responseCache) {
this.guiceInjector = guiceInjector;
+ this.responseCache = responseCache;
this.ebeanJson = ebean.createJsonContext();
this.errorResolver = errorResolver;
@@ -86,31 +90,24 @@
@Override
protected void doGet(final HttpServletRequest request, final HttpServletResponse response) {
+ // first of all check cache against URI: (only cached responses go here)
+ byte[] jsonEncoded = this.responseCache.get(request.getRequestURI());
+
StepRequest sr = null;
try {
- sr = parseRequest(request);
- byte[] jsonEncoded = null;
-
- // in here we want to retrieve from the cache.
- final ControllerCacheKey cacheKey = sr.getCacheKey();
-
- // check results cache here -- TODO - use servlet caching instead?
- if (this.isCacheEnabled) {
- LOGGER.debug("Checking cache...");
- jsonEncoded = this.resultsCache.get(cacheKey.getResultsKey());
- }
-
// cache miss?
- if (jsonEncoded == null) {
- LOGGER.debug("The cache was missed so invoking method now...");
- jsonEncoded = invokeMethod(sr);
+ if (jsonEncoded == null || jsonEncoded.length == 0) {
+ sr = parseRequest(request);
+ if (jsonEncoded == null) {
+ LOGGER.debug("The cache was missed so invoking method now...");
+ jsonEncoded = invokeMethod(sr);
+ }
+ } else {
+ LOGGER.debug("Returning answer from cache [{}]", request.getRequestURI());
}
setupHeaders(response, jsonEncoded.length);
response.getOutputStream().write(jsonEncoded);
-
- // TODO move cache down
- cache(jsonEncoded, sr.getControllerName(), sr.getMethodName(), sr.getArgs());
// CHECKSTYLE:OFF We allow catching errors here, since we are at the top of the structure
} catch (final Exception e) {
// CHECKSTYLE:ON
@@ -137,11 +134,14 @@
Object returnVal;
try {
returnVal = controllerMethod.invoke(controllerInstance, (Object[]) sr.getArgs());
+
// CHECKSTYLE:OFF
} catch (final Exception e) {
returnVal = convertExceptionToJson(e);
}
- return getEncodedJsonResponse(returnVal);
+ final byte[] encodedJsonResponse = getEncodedJsonResponse(returnVal);
+ cache(encodedJsonResponse, sr, controllerMethod);
+ return encodedJsonResponse;
// CHECKSTYLE:ON
}
@@ -225,20 +225,21 @@
final int endOfMethodName = startOfMethodName + methodName.length();
LOGGER.debug("Request parsed as controller: [{}], method [{}]", controllerName, methodName);
- return new StepRequest(controllerName, methodName, getArgs(requestURI, endOfMethodName + 1));
+ return new StepRequest(requestURI, controllerName, methodName, getArgs(requestURI,
+ endOfMethodName + 1));
}
/**
- * TODO caches the results for future use
+ * TODO: caches the results for future use
*
* @param jsonEncoded json encoding of the response
- * @param controllerName the name of the controller that was ino
- * @param methodName the method name that was called
- * @param args the arguments that were passed
+ * @param sr the processed request URI containg the the cache key
+ * @param controllerMethod the method so that we can inspect whether an annotation is present
*/
- void cache(final byte[] jsonEncoded, final String controllerName, final String methodName,
- final Object[] args) {
- // TODO using EH Cache
+ void cache(final byte[] jsonEncoded, final StepRequest sr, final Method controllerMethod) {
+ if (this.isCacheEnabled && controllerMethod.isAnnotationPresent(Cacheable.class)) {
+ this.responseCache.put(sr.getCacheKey().getResultsKey(), jsonEncoded);
+ }
}
/**
Modified: trunk/step/step-web/src/main/java/com/tyndalehouse/step/rest/controllers/ModuleController.java
===================================================================
--- trunk/step/step-web/src/main/java/com/tyndalehouse/step/rest/controllers/ModuleController.java 2011-03-26 17:01:58 UTC (rev 225)
+++ trunk/step/step-web/src/main/java/com/tyndalehouse/step/rest/controllers/ModuleController.java 2011-03-29 21:43:38 UTC (rev 226)
@@ -8,6 +8,7 @@
import com.google.inject.Inject;
import com.tyndalehouse.step.core.models.BibleVersion;
import com.tyndalehouse.step.core.service.ModuleService;
+import com.tyndalehouse.step.rest.framework.Cacheable;
/**
* The Module Controller servicing requests for module information
@@ -50,6 +51,7 @@
* @param reference a reference for a module to lookup
* @return the definition(s) that can be resolved from the reference provided
*/
+ @Cacheable(true)
public String getDefinition(final String reference) {
LOGGER.debug("Getting definition for {}", reference);
return this.moduleDefintions.getDefinition(reference).getExplanation();
Modified: trunk/step/step-web/src/main/java/com/tyndalehouse/step/rest/controllers/TimelineController.java
===================================================================
--- trunk/step/step-web/src/main/java/com/tyndalehouse/step/rest/controllers/TimelineController.java 2011-03-26 17:01:58 UTC (rev 225)
+++ trunk/step/step-web/src/main/java/com/tyndalehouse/step/rest/controllers/TimelineController.java 2011-03-29 21:43:38 UTC (rev 226)
@@ -10,6 +10,7 @@
import com.google.inject.Singleton;
import com.tyndalehouse.step.core.data.entities.Timeband;
import com.tyndalehouse.step.core.service.TimelineService;
+import com.tyndalehouse.step.rest.framework.Cacheable;
/**
* The timeline controller retrieves information about past events
@@ -40,8 +41,11 @@
* @param timebandId the timeband ids
* @param from the from dates
* @param to the to dates
- * @return all versions of modules that are considered to be Bibles. TODO work out UK date format mappings
+ * @return all versions of modules that are considered to be Bibles.
+ * <p />
+ * TODO: work out UK date format mappings
*/
+ @Cacheable(true)
public String getEvents(final String[] timebandId, final Date from, final Date to) {
LOGGER.debug("Retrieving events between [{}] and [{}]", from, to);
return timebandId[0];
@@ -56,6 +60,7 @@
* @return a list of events to be shown on a timeline, including the origin of the timeline and the scale
* of the timeline
*/
+ @Cacheable(true)
public String getEventsFromReference(final String bibleReference) {
return null;
@@ -66,6 +71,7 @@
*
* @return the timebands
*/
+ @Cacheable(true)
public List<Timeband> getTimelineConfiguration() {
return this.timelineService.getTimelineConfiguration();
}
Modified: trunk/step/step-web/src/main/java/com/tyndalehouse/step/rest/controllers/UserController.java
===================================================================
--- trunk/step/step-web/src/main/java/com/tyndalehouse/step/rest/controllers/UserController.java 2011-03-26 17:01:58 UTC (rev 225)
+++ trunk/step/step-web/src/main/java/com/tyndalehouse/step/rest/controllers/UserController.java 2011-03-29 21:43:38 UTC (rev 226)
@@ -32,7 +32,9 @@
* @param name the name of the person [optional]
* @param country his country [optional]
* @param password the password he has chosen, which we should SHA-1 and salt
- * @return the registered user TODO salt
+ * @return the registered user
+ *
+ * TODO: salt
*/
public User register(final String emailAddress, final String name, final String country,
final String password) {
Added: trunk/step/step-web/src/main/java/com/tyndalehouse/step/rest/framework/Cacheable.java
===================================================================
--- trunk/step/step-web/src/main/java/com/tyndalehouse/step/rest/framework/Cacheable.java (rev 0)
+++ trunk/step/step-web/src/main/java/com/tyndalehouse/step/rest/framework/Cacheable.java 2011-03-29 21:43:38 UTC (rev 226)
@@ -0,0 +1,21 @@
+package com.tyndalehouse.step.rest.framework;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * An annotation that indicates the results of a particular method can be cached
+ *
+ * @author Chris
+ *
+ */
+ at Retention(RetentionPolicy.RUNTIME)
+ at Target(ElementType.METHOD)
+public @interface Cacheable {
+ /**
+ * true to indicate the results of a method can be cached
+ */
+ boolean value() default false;
+}
Property changes on: trunk/step/step-web/src/main/java/com/tyndalehouse/step/rest/framework/Cacheable.java
___________________________________________________________________
Added: svn:mime-type
+ text/plain
Added: trunk/step/step-web/src/main/java/com/tyndalehouse/step/rest/framework/EbeanServletContextListener.java
===================================================================
--- trunk/step/step-web/src/main/java/com/tyndalehouse/step/rest/framework/EbeanServletContextListener.java (rev 0)
+++ trunk/step/step-web/src/main/java/com/tyndalehouse/step/rest/framework/EbeanServletContextListener.java 2011-03-29 21:43:38 UTC (rev 226)
@@ -0,0 +1,20 @@
+package com.tyndalehouse.step.rest.framework;
+
+import javax.servlet.ServletContextEvent;
+
+import com.avaje.ebeaninternal.server.core.ServletContextListener;
+
+/**
+ * Overrides the creation to avoid creation of Ebean - this is handled by Guice
+ *
+ * TODO: remove this in favour of context listener? if so refactor of tests required
+ *
+ * @author Chris
+ *
+ */
+public class EbeanServletContextListener extends ServletContextListener {
+ @Override
+ public void contextInitialized(final ServletContextEvent event) {
+ // DO NOTHING
+ }
+}
Property changes on: trunk/step/step-web/src/main/java/com/tyndalehouse/step/rest/framework/EbeanServletContextListener.java
___________________________________________________________________
Added: svn:mime-type
+ text/plain
Added: trunk/step/step-web/src/main/java/com/tyndalehouse/step/rest/framework/ResponseCache.java
===================================================================
--- trunk/step/step-web/src/main/java/com/tyndalehouse/step/rest/framework/ResponseCache.java (rev 0)
+++ trunk/step/step-web/src/main/java/com/tyndalehouse/step/rest/framework/ResponseCache.java 2011-03-29 21:43:38 UTC (rev 226)
@@ -0,0 +1,30 @@
+package com.tyndalehouse.step.rest.framework;
+
+import net.sf.ehcache.CacheManager;
+import net.sf.ehcache.config.CacheConfiguration;
+import net.sf.ehcache.store.MemoryStoreEvictionPolicy;
+
+import com.google.inject.Inject;
+import com.tyndalehouse.step.core.data.caches.AbstractDefaultCache;
+
+/**
+ * This caches responses - should be varied for Android and mobiles since there would be less memory
+ *
+ * @author Chris
+ *
+ */
+public class ResponseCache extends AbstractDefaultCache<byte[]> {
+ /**
+ * A simple cache for the responses to be sent to the client..
+ *
+ * @param cacheManager the cache manager with which to register the cache
+ */
+ @Inject
+ public ResponseCache(final CacheManager cacheManager) {
+ super(cacheManager, new CacheConfiguration("httpResponseCache", 500)
+ .memoryStoreEvictionPolicy(MemoryStoreEvictionPolicy.LFU).overflowToDisk(false)
+ .eternal(false).timeToLiveSeconds(3600).timeToIdleSeconds(3600).diskPersistent(false)
+ .diskExpiryThreadIntervalSeconds(0));
+ }
+
+}
Property changes on: trunk/step/step-web/src/main/java/com/tyndalehouse/step/rest/framework/ResponseCache.java
___________________________________________________________________
Added: svn:mime-type
+ text/plain
Modified: trunk/step/step-web/src/main/java/com/tyndalehouse/step/rest/framework/StepRequest.java
===================================================================
--- trunk/step/step-web/src/main/java/com/tyndalehouse/step/rest/framework/StepRequest.java 2011-03-26 17:01:58 UTC (rev 225)
+++ trunk/step/step-web/src/main/java/com/tyndalehouse/step/rest/framework/StepRequest.java 2011-03-29 21:43:38 UTC (rev 226)
@@ -2,25 +2,30 @@
/**
* A simple class that hold request information, provides various cache keys
+ * <p />
+ * TODO: move parse method from FrontController to here.
*
* @author Chris
*
*/
public class StepRequest {
- private static final Object ARG_SEPARATOR = '-';
-
private final String controllerName;
private final String methodName;
private final String[] args;
+ private final String requestURI;
+
/**
* Creates a request holder object containing the relevant information about a request
*
+ * @param requestURI the request URI that determines the controller, method name, etc.
* @param controllerName the controller name
* @param methodName the method name
* @param args the arguments that should be passed to the method
*/
- public StepRequest(final String controllerName, final String methodName, final String[] args) {
+ public StepRequest(final String requestURI, final String controllerName, final String methodName,
+ final String[] args) {
+ this.requestURI = requestURI;
this.controllerName = controllerName;
this.methodName = methodName;
this.args = args == null ? new String[] {} : args;
@@ -33,7 +38,6 @@
*/
public ControllerCacheKey getCacheKey() {
final String methodKey;
- final String resultsKey;
// generate the shorter key
final StringBuilder cacheKeyBuffer = new StringBuilder(this.controllerName.length()
@@ -44,14 +48,7 @@
// get the shorter key now
methodKey = cacheKeyBuffer.toString();
-
- // now get a slightly longer key by extending it
- for (int ii = 0; ii < this.args.length; ii++) {
- cacheKeyBuffer.append(this.args[ii]);
- cacheKeyBuffer.append(ARG_SEPARATOR);
- }
- resultsKey = cacheKeyBuffer.toString();
- return new ControllerCacheKey(methodKey, resultsKey);
+ return new ControllerCacheKey(methodKey, this.requestURI);
}
/**
Modified: trunk/step/step-web/src/main/webapp/WEB-INF/web.xml
===================================================================
--- trunk/step/step-web/src/main/webapp/WEB-INF/web.xml 2011-03-26 17:01:58 UTC (rev 225)
+++ trunk/step/step-web/src/main/webapp/WEB-INF/web.xml 2011-03-29 21:43:38 UTC (rev 226)
@@ -29,6 +29,14 @@
<listener-class>com.tyndalehouse.step.guice.StepServletConfig</listener-class>
</listener>
+ <listener>
+ <listener-class>com.tyndalehouse.step.rest.framework.EbeanServletContextListener</listener-class>
+ </listener>
+
+ <listener>
+ <listener-class>net.sf.ehcache.constructs.web.ShutdownListener</listener-class>
+ </listener>
+
<welcome-file-list>
<welcome-file>index.html</welcome-file>
</welcome-file-list>
Modified: trunk/step/step-web/src/main/webapp/index.html
===================================================================
--- trunk/step/step-web/src/main/webapp/index.html 2011-03-26 17:01:58 UTC (rev 225)
+++ trunk/step/step-web/src/main/webapp/index.html 2011-03-29 21:43:38 UTC (rev 226)
@@ -5,11 +5,12 @@
<META http-equiv="Content-Type" content="text/html; charset=utf-8">
<TITLE>STEP :: Scripture Tools for Every Pastor</TITLE>
+
+
<link rel="stylesheet" type="text/css" href="css/ui-lightness/jquery-ui-1.8.5.custom.css" />
<link rel="stylesheet" type="text/css" href="css/initial-layout.css" />
<link rel="stylesheet" type="text/css" href="css/initial-fonts.css" />
<link rel="stylesheet" type="text/css" href="css/passage.css" />
-
<link rel="stylesheet" type="text/css" href="libs/menu/ddsmoothmenu.css" />
<link rel="stylesheet" type="text/css" href="libs/menu/ddsmoothmenu-v.css" />
@@ -17,10 +18,11 @@
<script src="libs/timeline_js/timeline-api.js?bundle=true" type="text/javascript"></script>
<script src="libs/jquery-1.4.2.min.js" type="text/javascript"></script>
<script src="libs/jquery-ui-1.8.5.custom.min.js" type="text/javascript"></script>
+
+
<script src="libs/jquery-shout.js" type="text/javascript"></script>
<script src="libs/menu/ddsmoothmenu.js" type="text/javascript"></script>
<script src="libs/cookies/jquery_cookie.js" type="text/javascript"></script>
-
<script src="js/util.js" type="text/javascript"></script>
<script src="js/passage.js" type="text/javascript"></script>
<script src="js/bookmark.js" type="text/javascript"></script>
Added: trunk/step/step-web/src/main/webapp/libs/jquery_include.js
===================================================================
--- trunk/step/step-web/src/main/webapp/libs/jquery_include.js (rev 0)
+++ trunk/step/step-web/src/main/webapp/libs/jquery_include.js 2011-03-29 21:43:38 UTC (rev 226)
@@ -0,0 +1,199 @@
+/*!*
+ * @filename include.jquery.js
+ * @name jQuery Include File
+ * @type jQuery
+ * @projectDescription Include a file (css and js) in a head of the document and execute
+ * @date 08/07/2008
+ * @version 1.0
+ * @cat Ajax
+ * @require
+ * @author Alex
+ * @param required none url String|Array The address of the plugin that will be inserted.
+ * You can pass a indexed array of url
+ * @param optional none callback Function The function to be executed after the file has loaded
+ * @example
+ * $.include('/foo/test/file.js');
+ * @desc load the current script
+ * @example
+ * var files = ['test.js','another.js','onemore.js'];
+ * $.include(files,function(){
+ * //execute some code after all scripts are completed
+ * });
+ * @desc load all the script inside the array
+ * @return false | Element (object)
+ */
+
+(function($) {
+ $.extend({
+ // You can change the base path to be applied in all imports
+ ImportBasePath: '',
+ // Associative array storing wating tasks and their callback
+ __WaitingTasks: new Object(),
+ // Called when a single file is loaded successfully - update and check WaitingTasks to see if it's ok to load callback
+ __loadedSuccessfully: function(taskId){
+ if (taskId in $.__WaitingTasks){
+ if (($.__WaitingTasks[taskId].loading -= 1) < 1){
+ var callback = $.__WaitingTasks[taskId].task;
+ if (typeof callback == 'function') {
+ callback();
+ }
+ delete $.__WaitingTasks[taskId];
+ }
+ }
+ },
+ //pass a file name and return a array with file name and extension
+ fileinfo: function(data){
+ data = data.replace(/^\s|\s$/g, "");
+ var m;
+ if (/\.\w+$/.test(data)) {
+ m = data.match(/([^\/\\]+)\.(\w+)$/);
+ if (m) {
+ if (m[2] == 'js') {
+ return {
+ filename: m[1],
+ ext: m[2],
+ tag: 'script'
+ };
+ }
+ else
+ if (m[2] == 'css') {
+ return {
+ filename: m[1],
+ ext: m[2],
+ tag: 'link'
+ };
+ }
+ else {
+ return {
+ filename: m[1],
+ ext: m[2],
+ tag: null
+ };
+ }
+ }
+ else {
+ return {
+ filename: null,
+ ext: null
+ };
+ }
+ } else {
+ m = data.match(/([^\/\\]+)$/);
+ if (m) {
+ return {
+ filename: m[1],
+ ext: null,
+ tag: null
+ };
+ }
+ else {
+ return {
+ filename: null,
+ ext: null,
+ tag: null
+ };
+ }
+ }
+ },
+ //Check if the file that is been included already exist and return a Boolean value
+ fileExist: function(filename,filetype,attrCheck) {
+ var elementsArray = document.getElementsByTagName(filetype);
+ for(var i=0;i<elementsArray.length;i++) {
+ if(elementsArray[i].getAttribute(attrCheck)==$.ImportBasePath+filename) {
+ return true;
+ }
+ }
+ return false;
+ },
+ //Create the element depending of the file type and return the element (Object)
+ createElement: function(filename,filetype) {
+ switch(filetype) {
+ case 'script' :
+ if (!$.fileExist(filename, filetype, 'src')) {
+ var scriptTag = document.createElement(filetype);
+ scriptTag.setAttribute('language', 'javascript');
+ scriptTag.setAttribute('type', 'text/javascript');
+ scriptTag.setAttribute('src', $.ImportBasePath + filename);
+ return scriptTag;
+ } else {
+ return false;
+ }
+ break;
+ case 'link' :
+ if (!$.fileExist(filename, filetype, 'href')) {
+ var styleTag = document.createElement(filetype);
+ styleTag.setAttribute('type', 'text/css');
+ styleTag.setAttribute('rel', 'stylesheet');
+ styleTag.setAttribute('href', $.ImportBasePath + filename);
+ return styleTag;
+ } else {
+ return false;
+ }
+ break;
+
+ default :
+ return false;
+ break;
+ }
+ },
+ cssReady: function(index, taskId) {
+ function check() {
+ if(document.styleSheets[index]){
+ window.clearInterval(checkInterval);
+ $.__loadedSuccessfully(taskId);
+ }
+ }
+ var checkInterval = window.setInterval(check,200);
+ },
+ //The main function to insert the file
+ include: function(file,callback) {
+ var headerTag = document.getElementsByTagName('head')[0];
+ var fileArray = [];
+ //if file is string, give a single index element
+ typeof file=='string' ? fileArray[0] = file : fileArray = file;
+ // Create a unique id using the current time
+ var taskId = new Date().getTime().toString();
+ $.__WaitingTasks[taskId] = {'loading': fileArray.length, 'task': callback};
+ //go through all the files
+ for (var i = 0; i < fileArray.length; i++) {
+ var elementTag = $.fileinfo(fileArray[i]).tag;
+ var el = [];
+ if (elementTag !== null) {
+ el[i] = $.createElement(fileArray[i], elementTag);
+ if (el[i]) {
+ headerTag.appendChild(el[i]);
+ if ($.browser.msie) {
+ el[i].onreadystatechange = function(){
+ if (this.readyState === 'loaded' || this.readyState === 'complete') {
+ $.__loadedSuccessfully(taskId);
+ }
+ };
+ }
+ else {
+ if (elementTag == 'link') {
+ $.cssReady(i, taskId);
+ }
+ else {
+ if (/WebKit/i.test(navigator.userAgent)) {
+ var _timer = setInterval(function(){
+ if (/loaded|complete/.test(document.readyState)) {
+ $.__loadedSuccessfully(taskId); // call of the call
+ }
+ }, 100);
+ }
+ el[i].onload = function(){
+ $.__loadedSuccessfully(taskId);
+ };
+ }
+ }
+ }else{
+ $.__loadedSuccessfully(taskId);
+ }
+ } else {
+ return false;
+ }
+ }
+ }
+ });
+
+})(jQuery);
Property changes on: trunk/step/step-web/src/main/webapp/libs/jquery_include.js
___________________________________________________________________
Added: svn:mime-type
+ text/plain
Modified: trunk/step/step-web/src/test/java/com/tyndalehouse/step/rest/controllers/FrontControllerTest.java
===================================================================
--- trunk/step/step-web/src/test/java/com/tyndalehouse/step/rest/controllers/FrontControllerTest.java 2011-03-26 17:01:58 UTC (rev 225)
+++ trunk/step/step-web/src/test/java/com/tyndalehouse/step/rest/controllers/FrontControllerTest.java 2011-03-29 21:43:38 UTC (rev 226)
@@ -24,12 +24,18 @@
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
+import org.junit.Before;
import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.runners.MockitoJUnitRunner;
import com.avaje.ebean.EbeanServer;
import com.google.inject.Injector;
import com.tyndalehouse.step.core.exceptions.StepInternalException;
import com.tyndalehouse.step.core.service.BibleInformationService;
+import com.tyndalehouse.step.rest.framework.ClientErrorResolver;
+import com.tyndalehouse.step.rest.framework.ResponseCache;
import com.tyndalehouse.step.rest.framework.StepRequest;
/**
@@ -39,9 +45,31 @@
*
*/
@SuppressWarnings("PMD.TooManyMethods")
+ at RunWith(MockitoJUnitRunner.class)
public class FrontControllerTest {
+ private FrontController fcUnderTest;
+ @Mock
+ private Injector guiceInjector;
+ private final Boolean isCacheEnabled = Boolean.FALSE;
+
+ @Mock
+ private EbeanServer ebean;
+ @Mock
+ private ClientErrorResolver errorResolver;
+ @Mock
+ private ResponseCache responseCache;
+
/**
+ * Simply setting up the FrontController under test
+ */
+ @Before
+ public void setUp() {
+ this.fcUnderTest = new FrontController(this.guiceInjector, this.isCacheEnabled, this.ebean,
+ this.errorResolver, this.responseCache);
+ }
+
+ /**
* Tests normal operation of a GET method
*
* @throws IOException uncaught exception
@@ -51,9 +79,9 @@
final HttpServletRequest request = mock(HttpServletRequest.class);
final HttpServletResponse response = mock(HttpServletResponse.class);
- final FrontController fc = spy(new FrontController(null, false, mock(EbeanServer.class), null));
- final StepRequest parsedRequest = new StepRequest("SomeController", "someMethod", new String[] {
- "arg1", "arg2" });
+ final FrontController fc = spy(this.fcUnderTest);
+ final StepRequest parsedRequest = new StepRequest("blah", "SomeController", "someMethod",
+ new String[] { "arg1", "arg2" });
final ServletOutputStream mockOutputStream = mock(ServletOutputStream.class);
doReturn(parsedRequest).when(fc).parseRequest(request);
@@ -75,9 +103,9 @@
final HttpServletResponse response = mock(HttpServletResponse.class);
final StepInternalException testException = new StepInternalException("A test exception");
- final FrontController fc = spy(new FrontController(null, false, mock(EbeanServer.class), null));
- final StepRequest parsedRequest = new StepRequest("SomeController", "someMethod", new String[] {
- "arg1", "arg2" });
+ final FrontController fc = spy(this.fcUnderTest);
+ final StepRequest parsedRequest = new StepRequest("blah", "SomeController", "someMethod",
+ new String[] { "arg1", "arg2" });
doThrow(testException).when(fc).parseRequest(request);
doNothing().when(fc).handleError(response, testException, parsedRequest);
@@ -95,11 +123,8 @@
// index starts at ...........0123456789-123456789-123456
final String sampleRequest = "step-web/rest/bible/get/1K2/2K2";
- final FrontController fc = new FrontController(mock(Injector.class), Boolean.FALSE,
- mock(EbeanServer.class), null);
-
// when
- final Object[] args = fc.getArgs(sampleRequest, 24);
+ final Object[] args = this.fcUnderTest.getArgs(sampleRequest, 24);
// then
assertEquals(2, args.length);
@@ -115,11 +140,8 @@
// index starts at ...........0123456789-123456789-123456
final String sampleRequest = "step-web/rest/bible/get/1K2/2K2/";
- final FrontController fc = new FrontController(mock(Injector.class), Boolean.FALSE,
- mock(EbeanServer.class), null);
-
// when
- final Object[] args = fc.getArgs(sampleRequest, 24);
+ final Object[] args = this.fcUnderTest.getArgs(sampleRequest, 24);
// then
assertEquals(2, args.length);
@@ -134,8 +156,7 @@
*/
@Test
public void testGetPath() throws ServletException {
- final FrontController fc = new FrontController(null, null, mock(EbeanServer.class), null);
- final FrontController spy = spy(fc);
+ final FrontController spy = spy(this.fcUnderTest);
final ServletContext mockServletContext = mock(ServletContext.class);
final HttpServletRequest mockRequest = mock(HttpServletRequest.class);
@@ -161,8 +182,7 @@
final HttpServletResponse response = mock(HttpServletResponse.class);
final int sampleRequestLength = 10;
- new FrontController(null, null, mock(EbeanServer.class), null).setupHeaders(response,
- sampleRequestLength);
+ this.fcUnderTest.setupHeaders(response, sampleRequestLength);
verify(response).addDateHeader(eq("Date"), anyLong());
verify(response).setCharacterEncoding("UTF-8");
@@ -179,13 +199,11 @@
*/
@Test
public void testGetControllerMethod() throws IllegalAccessException, InvocationTargetException {
- final FrontController frontController = new FrontController(mock(Injector.class), Boolean.FALSE,
- mock(EbeanServer.class), null);
final BibleInformationService bibleInfo = mock(BibleInformationService.class);
final BibleController controllerInstance = new BibleController(bibleInfo);
// when
- final Method controllerMethod = frontController.getControllerMethod("getBibleVersions",
+ final Method controllerMethod = this.fcUnderTest.getControllerMethod("getBibleVersions",
controllerInstance, null, null);
// then
@@ -199,15 +217,11 @@
@Test
public void testGetController() {
final String controllerName = "Bible";
- final Injector mockInjector = mock(Injector.class);
- final FrontController frontController = new FrontController(mockInjector, Boolean.FALSE,
- mock(EbeanServer.class), null);
-
final BibleController mockController = mock(BibleController.class);
- when(mockInjector.getInstance(BibleController.class)).thenReturn(mockController);
+ when(this.guiceInjector.getInstance(BibleController.class)).thenReturn(mockController);
// when
- final Object controller = frontController.getController(controllerName);
+ final Object controller = this.fcUnderTest.getController(controllerName);
// then
assertEquals(controller.getClass(), mockController.getClass());
@@ -218,12 +232,10 @@
*/
@Test
public void testGetClasses() {
- final FrontController fc = new FrontController(null, Boolean.FALSE, mock(EbeanServer.class), null);
-
- assertEquals(0, fc.getClasses(null).length);
- assertEquals(0, fc.getClasses(new Object[0]).length);
+ assertEquals(0, this.fcUnderTest.getClasses(null).length);
+ assertEquals(0, this.fcUnderTest.getClasses(new Object[0]).length);
assertArrayEquals(new Class<?>[] { String.class, Integer.class },
- fc.getClasses(new Object[] { "hello", Integer.valueOf(1) }));
+ this.fcUnderTest.getClasses(new Object[] { "hello", Integer.valueOf(1) }));
}
@@ -232,8 +244,7 @@
*/
@Test
public void testJsonEncoding() {
- final byte[] encodedJsonResponse = new FrontController(null, null, mock(EbeanServer.class), null)
- .getEncodedJsonResponse("abc");
+ final byte[] encodedJsonResponse = this.fcUnderTest.getEncodedJsonResponse("abc");
// this reprensents the string "{abc}"
final byte[] expectedValues = new byte[] { 34, 97, 98, 99, 34 };
@@ -248,15 +259,14 @@
*/
@Test
public void testDoErrorHandlesCorrectly() throws IOException {
- final FrontController fc = new FrontController(null, null, mock(EbeanServer.class), null);
final HttpServletResponse response = mock(HttpServletResponse.class);
- final StepRequest stepRequest = new StepRequest("controller", "method", null);
+ final StepRequest stepRequest = new StepRequest("blah", "controller", "method", null);
final ServletOutputStream outputStream = mock(ServletOutputStream.class);
final Throwable exception = new Exception();
when(response.getOutputStream()).thenReturn(outputStream);
// do test
- fc.handleError(response, exception, stepRequest);
+ this.fcUnderTest.handleError(response, exception, stepRequest);
// check
verify(outputStream).write(any(byte[].class));
@@ -283,17 +293,16 @@
contextName + requestSeparator + servletName + requestSeparator + controllerName
+ requestSeparator + methodName + requestSeparator + arg1 + requestSeparator + arg2);
- final FrontController frontController = new FrontController(null, null, mock(EbeanServer.class), null);
- frontController.init(mock(ServletConfig.class));
+ this.fcUnderTest.init(mock(ServletConfig.class));
- final FrontController spy = spy(frontController);
+ final FrontController spy = spy(this.fcUnderTest);
final ServletContext mockServletContext = mock(ServletContext.class);
when(spy.getServletContext()).thenReturn(mockServletContext);
when(mockServletContext.getContextPath()).thenReturn(contextName + "/");
// do test
- final StepRequest parseRequest = frontController.parseRequest(request);
+ final StepRequest parseRequest = this.fcUnderTest.parseRequest(request);
// check controller name, method name and arguments
assertEquals(controllerName, parseRequest.getControllerName());
@@ -305,10 +314,10 @@
*/
@Test
public void testInvokeMethod() {
- final StepRequest sr = new StepRequest("bible", "getAllFeatures", new String[] {});
+ final StepRequest sr = new StepRequest("blah", "bible", "getAllFeatures", new String[] {});
final BibleController testController = mock(BibleController.class);
- final FrontController fc = spy(new FrontController(null, null, mock(EbeanServer.class), null));
+ final FrontController fc = spy(this.fcUnderTest);
doReturn(testController).when(fc).getController("bible");
// do test
Modified: trunk/step/step-web/src/test/java/com/tyndalehouse/step/rest/framework/StepRequestTest.java
===================================================================
--- trunk/step/step-web/src/test/java/com/tyndalehouse/step/rest/framework/StepRequestTest.java 2011-03-26 17:01:58 UTC (rev 225)
+++ trunk/step/step-web/src/test/java/com/tyndalehouse/step/rest/framework/StepRequestTest.java 2011-03-29 21:43:38 UTC (rev 226)
@@ -15,6 +15,7 @@
private static final String[] TEST_ARGS = new String[] { "arg1", "arg2", "arg3" };
private static final String TEST_CONTROLLER_NAME = "Controller";
private static final String TEST_METHOD_NAME = "method";
+ private static final String TEST_URI = "uri/ControllerController/method/arg1/arg2/arg3";
/**
* a method key should not contain arguments, but contain controller name and method name
@@ -51,6 +52,6 @@
* @return a step request
*/
private StepRequest getTestStepRequest() {
- return new StepRequest(TEST_CONTROLLER_NAME, TEST_METHOD_NAME, TEST_ARGS);
+ return new StepRequest(TEST_URI, TEST_CONTROLLER_NAME, TEST_METHOD_NAME, TEST_ARGS);
}
}
More information about the Tynstep-svn
mailing list