Skip to content

Commit

Permalink
Fixed Ehcache
Browse files Browse the repository at this point in the history
Removed async
  • Loading branch information
Nick Kallen committed Mar 2, 2009
1 parent 811528b commit 979b813
Show file tree
Hide file tree
Showing 11 changed files with 119 additions and 46 deletions.
8 changes: 4 additions & 4 deletions TODO
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
The various DateHeader/IntHeaders in CacheEntry are vulnerable to header fragmentation

The responseWrapper object is untested and still a little janky when mocked out in tests
CPSF/Receive should only do it's business if the request is initial

Learn . vs ' ' method invocation convention
Hop-by-hop headers
Cache-Control pragmas like no-cache

ResponseTime stuff is weird
Learn . vs ' ' method invocation convention

Locking / Grace Period - prevent dog pile by locking around fetch.

Expand Down
6 changes: 4 additions & 2 deletions src/main/scala/com/twitter/service/cachet/Server.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,20 @@ package com.twitter.service.cachet

import org.mortbay.jetty
import org.mortbay.jetty.bio.SocketConnector
import org.mortbay.jetty.servlet.{ServletHolder, Context}
import servlet.ProxyServlet
import org.mortbay.jetty.servlet.{FilterHolder, ServletHolder, Context}
import servlet.{CacheProxyServletFilter, ProxyServlet}

class Server {
val server = new jetty.Server
val connector = new SocketConnector
val root = new Context(server, "/", Context.SESSIONS)
val servlet = new ProxyServlet
val filter = new CacheProxyServletFilter

connector.setPort(1234)
server.setConnectors(Array(connector))

root.addFilter(new FilterHolder(filter), "/", 1)
root.addServlet(new ServletHolder(servlet), "/")

def start() {
Expand Down
8 changes: 5 additions & 3 deletions src/main/scala/com/twitter/service/cachet/cache/Ehcache.scala
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package com.twitter.service.cachet.cache

import net.sf.ehcache._
import net.sf._

object Ehcache extends Cache {
val cache = CacheManager.getInstance.getEhcache("Name")
val cacheManager = ehcache.CacheManager.getInstance
val cache = new ehcache.Cache("Cache", 5000, false, true, 5000, 5000)
cacheManager.addCache(cache)

def fetch(key: String) = {
val element = cache.get(key)
Expand All @@ -15,6 +17,6 @@ object Ehcache extends Cache {
}

def put(key: String, value: CacheEntry) {
cache.put(new Element(key, value))
cache.put(new ehcache.Element(key, value))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,19 @@ import javax.servlet.http.HttpServletResponse

class ClientRequest(client: HttpClient, Exchange: (HttpServletRequest, HttpServletResponse) => HttpExchange) {
def apply(request: HttpServletRequest, response: HttpServletResponse) {
if (request.isInitial) {
val exchange = Exchange(request, response)
val headers = request.getHeaderNames
while (headers.hasMoreElements) {
var headerName = headers.nextElement.asInstanceOf[String]
exchange.setRequestHeader(headerName, request.getHeader(headerName))
}
exchange.setMethod(request.getMethod)
exchange.setURL("http://localhost:3000" + request.getRequestURI)
exchange.setRequestContentSource(request.getInputStream)

client.send(exchange)
// if (request.isInitial) {
val exchange = Exchange(request, response)
val headers = request.getHeaderNames
while (headers.hasMoreElements) {
var headerName = headers.nextElement.asInstanceOf[String]
exchange.setRequestHeader(headerName, request.getHeader(headerName))
}
exchange.setMethod(request.getMethod)
exchange.setURL("http://localhost:3000" + request.getRequestURI)
exchange.setRequestContentSource(request.getInputStream)

client.send(exchange)
exchange.waitForDone()
// }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import org.mortbay.io.Buffer
import org.mortbay.jetty.client.{HttpClient, HttpExchange}

class CopyExchange(request: HttpServletRequest, response: HttpServletResponse) extends HttpExchange {
request.suspend()
// request.suspend()

override def onResponseHeader(name: Buffer, value: Buffer) {
response.setHeader(name.toString, value.toString)
Expand All @@ -19,7 +19,7 @@ class CopyExchange(request: HttpServletRequest, response: HttpServletResponse) e
response.setStatus(status)
}

override def onResponseComplete {
request.resume()
}
// override def onResponseComplete {
// request.resume()
// }
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,17 @@ import strategy.{Receive, Fetch}

class CacheProxyServletFilter extends Filter {
var config = null: FilterConfig
var get = null: Receive
var receive = null: Receive

def init(c: FilterConfig) {
config = c
val cache = new TransparentCache(Ehcache)
val fetch = new Fetch(cache, ResponseCapturer, FreshResponseCacheEntry)
get = new Receive(cache, fetch)
receive = new Receive(cache, fetch)
}

def doFilter(request: ServletRequest, response: ServletResponse, chain: FilterChain) {
get(request.asInstanceOf[HttpServletRequest], response.asInstanceOf[HttpServletResponse], chain)
receive(request.asInstanceOf[HttpServletRequest], response.asInstanceOf[HttpServletResponse], chain)
}

def destroy {}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ class ResponseCapturer(response: HttpServletResponse, servletOutputStreamCapture
def maxAge =
for (cacheControl <- stringHeaders get "Cache-Control";
maxAge <- MaxAge findFirstMatchIn cacheControl)
yield maxAge group (1) toLong
yield convertFromSecondsToMillis(maxAge group 1 toInt)

def age = intHeaders get "Age" map (_.toLong) orElse
(stringHeaders get "Age" map (_.toLong))
Expand Down Expand Up @@ -167,4 +167,6 @@ class ResponseCapturer(response: HttpServletResponse, servletOutputStreamCapture
dateHeaders get name orElse
(stringHeaders get name map (Date.parse(_)))
}

private def convertFromSecondsToMillis(s: Int) = s.toLong * 1000
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@ import _root_.javax.servlet.http._
*
* @author Steve Jenson (stevej@pobox.com)
*/
class FakeHttpServletResponse extends HttpServletResponse {
var statusCode : Int = 200
var statusString : String = "OK"
abstract class FakeHttpServletResponse extends HttpServletResponse {
var statusCode: Int = 200
var statusString: String = "OK"
var contentType = "text/html"
var contentLength = 0
val headers: HashMap[String, String] = new HashMap[String, String]
Expand All @@ -49,19 +49,23 @@ class FakeHttpServletResponse extends HttpServletResponse {
def addIntHeader(s: String, i: Int) {
headers += (s -> i.toString)
}

def setIntHeader(s: String, i: Int) {
headers += (s -> i.toString)
}

def addHeader(s1: String, s2: String) {
headers += (s1 -> s2)
}

def setHeader(s1: String, s2: String) {
headers += (s1 -> s2)
}

def addDateHeader(s: String, l: Long) {
headers += (s -> (new Date(l)).toString)
}

def setDateHeader(s: String, l: Long) {
addDateHeader(s, l)
}
Expand All @@ -82,40 +86,60 @@ class FakeHttpServletResponse extends HttpServletResponse {
}

def encodeRedirectURL(url: String): String = encodeRedirectUrl(url)

def encodeRedirectUrl(url: String): String = {
// do something fancy encoding on uri, return that.
url
}

def encodeURL(url: String): String = encodeUrl(url)

def encodeUrl(url: String): String = {
// use the same encoder as encodeRedirectUrl
url
}

def containsHeader(header: String): Boolean = {
headers.contains(header)
}

def addCookie(cookie: Cookie) = {
cookies = cookie :: cookies
cookies = cookie :: cookies
}

def getLocale: Locale = locale

def setLocale(l: Locale) = locale = l

def reset {
// well, reset all the state to it's original values. yikes. later.
}

def isCommitted = false

def resetBuffer {
// reset the buffer.
}

def flushBuffer {
// flush the buffer
}

def getBufferSize = bufferSize

def setBufferSize(i: Int) = bufferSize = i

def setContentType(t: String) = contentType = t

def setContentLength(l: Int) = contentLength = l

def setCharacterEncoding(e: String) = charEncoding = e

def getWriter = null

def getOutputStream = null

def getContentType = contentType

def getCharacterEncoding = charEncoding
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import _root_.javax.servlet.http._
/**
* An example of how to use these mock classes in your unit tests:
*
* def testLiftCore = {
* def testLiftCore = {
* val output = new ByteArrayOutputStream
* val outputStream = new MockServletOutputStream(output)
* val writer = new PrintWriter(outputStream)
Expand All @@ -34,33 +34,46 @@ import _root_.javax.servlet.http._
* filter.init(new MockFilterConfig(new MockServletContext("target/test1-1.0-SNAPSHOT")))
* filter.doFilter(req, res,new DoNothingFilterChain)
* assertTrue(output.toString.startsWith("<?xml"))
* }
* }
*/



/**
* A Mock ServletContext. LiftFilter expects a ServletContext inside a FilterConfig
*
* @param target the target directory where your template files live
*
* @author Steve Jenson (stevej@pobox.com)
*/
class MockServletContext(var target: String) extends ServletContext {
abstract class MockServletContext(var target: String) extends ServletContext {
def getInitParameter(f: String) = null

def getInitParameterNames = new Vector[AnyRef]().elements

def getAttribute(f: String) = null

def getAttributeNames = new Vector[AnyRef]().elements

def removeAttribute(name: String) {}

def setAttribute(name: String, o: Object) {}

def getContext(path: String) = this

def getMajorVersion = 2

def getMimeType(file: String) = null

def getMinorVersion = 3

def getRealPath(path: String) = null

def getNamedDispatcher(name: String) = null

def getRequestDispatcher(path: String) = null

def getResource(path: String) = null

def getResourceAsStream(path: String) = {
val file = new File(target + path)
if (file.exists) {
Expand All @@ -71,20 +84,29 @@ class MockServletContext(var target: String) extends ServletContext {
}

def getResourcePaths(path: String) = null

def getServerInfo = null

def getServlet(name: String) = null

def getServletContextName = null

def getServletNames = new Vector[AnyRef]().elements

def getServlets = new Vector[AnyRef]().elements

def log(msg: String, t: Throwable) {
t.printStackTrace
log(msg)
}

def log(e: Exception, msg: String) {
e.printStackTrace
log(msg)
}

def log(msg: String) = println("MockServletContext.log: " + msg)

def getContextPath = null
}

Expand All @@ -96,7 +118,9 @@ class MockServletContext(var target: String) extends ServletContext {
class MockFilterConfig(servletContext: ServletContext) extends FilterConfig {
def getFilterName = "LiftFilter" // as in lift's default web.xml
def getInitParameter(key: String) = null
def getInitParameterNames = new Vector[AnyRef]().elements

def getInitParameterNames = new Vector[AnyRef]().elements

def getServletContext = servletContext
}

Expand Down Expand Up @@ -141,27 +165,44 @@ class MockHttpSession extends HttpSession {
var maxii = 0
var servletContext = null
var creationTime = System.currentTimeMillis

def isNew = false

def invalidate {}

def getValue(key: String) = values.get(key) match {
case Some(v) => v.asInstanceOf[Object]
case None => Nil
}

def removeValue(key: String) = values -= key

def putValue(key: String, value: Any) = values += (key -> value)

def getAttribute(key: String) = attr.get(key) match {
case Some(v) => v.asInstanceOf[Object]
case None => Nil
}
case Some(v) => v.asInstanceOf[Object]
case None => Nil
}

def removeAttribute(key: String) = attr -= key

def setAttribute(key: String, value: Any) = attr += (key -> value)

def getValueNames: Array[String] = values.keySet.toArray

def getAttributeNames = new Vector[AnyRef](attr.underlying.keySet).elements

def getSessionContext = sessionContext

def getMaxInactiveInterval = maxii

def setMaxInactiveInterval(i: Int) = maxii = i

def getServletContext = servletContext

def getLastAccessedTime = 0L

def getId = null

def getCreationTime = creationTime
}
Loading

0 comments on commit 979b813

Please sign in to comment.