77
88import gate .*;
99import gate .corpora .export .GATEJsonExporter ;
10- import gate .creole .ExecutionException ;
1110import gate .creole .ResourceInstantiationException ;
1211import gate .util .GateException ;
1312import gate .util .persistence .PersistenceManager ;
13+
14+ import com .jakewharton .disklrucache .DiskLruCache ;
15+
1416import org .apache .log4j .LogManager ;
1517import org .apache .log4j .Logger ;
18+ import org .codehaus .httpcache4j .util .Hex ;
1619
17- import java .io .ByteArrayOutputStream ;
18- import java .io .File ;
19- import java .io .IOException ;
20+ import javax .xml .stream .XMLStreamException ;
21+ import java .io .*;
2022import java .net .URL ;
23+ import java .security .MessageDigest ;
24+ import java .security .NoSuchAlgorithmException ;
2125import java .util .HashMap ;
2226import java .util .HashSet ;
2327import java .util .Objects ;
@@ -36,38 +40,94 @@ public class App implements RequestHandler<APIGatewayProxyRequestEvent, APIGatew
3640 }
3741 }
3842
43+ private static final String GATE_APP_NAME = System .getenv ("GATE_APP_NAME" );
44+ private static final String CACHE_DIR = System .getenv ().getOrDefault (
45+ "CACHE_DIR_PREFIX" , "/tmp/lru/" + GATE_APP_NAME );
46+ private static final double CACHE_DIR_USAGE = .9 ;
47+
3948 private static final Logger logger = LogManager .getLogger (App .class );
4049 private static final CorpusController application = loadApplication ();
4150 private static final GATEJsonExporter gateJsonExporter = new GATEJsonExporter ();
4251
52+
53+ private static final DiskLruCache cache = initializeCache ();
54+
4355 public APIGatewayProxyResponseEvent handleRequest (APIGatewayProxyRequestEvent input , final Context context ) {
4456 final String responseType = input .getHeaders ().getOrDefault ("Accept" , "application/xml" );
4557 final APIGatewayProxyResponseEvent response = new APIGatewayProxyResponseEvent ()
4658 .withHeaders (new HashMap <>());
4759 response .getHeaders ().put ("Content-Type" , "text/plain" );
4860
4961 try {
50- final Document doc = Factory .newDocument (input .getBody ());
51- final Corpus corpus = application .getCorpus ();
52- corpus .add (doc );
62+ final String bodyDigest = computeMessageDigest (input .getBody ());
63+ response .getHeaders ().put ("x-zae-gate-cache" , "HIT" );
64+ final Document doc = cacheComputeIfNull (
65+ bodyDigest ,
66+ () -> {
67+ final Document rv = Factory .newDocument (input .getBody ());
68+ final Corpus corpus = application .getCorpus ();
69+ corpus .add (rv );
70+ try {
71+ application .execute ();
72+ } finally {
73+ corpus .clear ();
74+ }
75+ response .getHeaders ().put ("x-zae-gate-cache" , "MISS" );
76+ return rv ;
77+ }
78+ );
5379 try {
54- application .execute ();
5580 response .getHeaders ().put ("Content-Type" , responseType );
5681 return response .withBody (export (doc , responseType )).withStatusCode (200 );
5782 } finally {
58- corpus .clear ();
5983 Factory .deleteResource (doc );
6084 }
61- } catch (ExecutionException e ) {
85+ } catch (GateException e ) {
6286 logger .error (e );
6387 return response .withBody (e .getMessage ()).withStatusCode (400 );
6488 } catch (IOException e ) {
6589 logger .error (e );
6690 return response .withBody (e .getMessage ()).withStatusCode (406 );
67- } catch (ResourceInstantiationException e ) {
91+ }
92+ }
93+
94+ private Document cacheComputeIfNull (String key , TextProcessor processor ) throws GateException {
95+ try {
96+ final DiskLruCache .Snapshot snapshot = cache .get (key );
97+ if (snapshot == null ) {
98+ final Document doc = processor .process ();
99+ try {
100+ DiskLruCache .Editor editor = cache .edit (key );
101+ editor .set (0 , doc .toXml ());
102+ editor .commit ();
103+ } catch (IOException e ) {
104+ logger .warn (e );
105+ }
106+ return doc ;
107+ } else {
108+ try {
109+ return Utils .xmlToDocument (new InputStreamReader (snapshot .getInputStream (0 )));
110+ } catch (ResourceInstantiationException | XMLStreamException e ) {
111+ logger .warn (e );
112+ cache .remove (key );
113+ return processor .process ();
114+ }
115+ }
116+ } catch (IOException e ) {
68117 logger .warn (e );
69- return response .withBody (e .getMessage ()).withStatusCode (400 );
118+ return processor .process ();
119+ }
120+ }
121+
122+ private String computeMessageDigest (String text ) {
123+ final String sha256 ;
124+ try {
125+ final MessageDigest md = MessageDigest .getInstance ("SHA-256" );
126+ sha256 = Hex .encode (md .digest (text .getBytes ()));
127+ } catch (NoSuchAlgorithmException e ) {
128+ throw new RuntimeException (e );
70129 }
130+ return sha256 ;
71131 }
72132
73133 /**
@@ -97,7 +157,7 @@ private String export(Document doc, String responseType) throws IOException {
97157
98158 private static CorpusController loadApplication () {
99159 try {
100- final String gappResourcePah = System . getenv ( " GATE_APP_NAME" ) + "/application.xgapp" ;
160+ final String gappResourcePah = GATE_APP_NAME + "/application.xgapp" ;
101161 final URL gappUrl = App .class .getClassLoader ().getResource (gappResourcePah );
102162 final File gappFile = new File (Objects .requireNonNull (gappUrl ).getFile ());
103163 final CorpusController rv =
@@ -109,4 +169,26 @@ private static CorpusController loadApplication() {
109169 throw new RuntimeException (e );
110170 }
111171 }
172+
173+ private static DiskLruCache initializeCache () {
174+ File cacheDir = new File (CACHE_DIR );
175+ if (!cacheDir .exists () && !cacheDir .mkdirs ()) {
176+ throw new RuntimeException ("Unable to create cache directory '" + cacheDir .getName () + "'." );
177+ }
178+ for (File file : Objects .requireNonNull (cacheDir .listFiles ())) file .delete ();
179+ try {
180+ long usableSpace = (long ) (cacheDir .getUsableSpace ()*CACHE_DIR_USAGE );
181+ return DiskLruCache .open (cacheDir ,
182+ 1 ,
183+ 1 ,
184+ usableSpace );
185+ } catch (IOException e ) {
186+ throw new RuntimeException (e );
187+ }
188+ }
189+
190+ private interface TextProcessor {
191+ Document process () throws GateException ;
192+ }
193+
112194}
0 commit comments