Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package org.gusdb.wdk.service.init;

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

import org.apache.log4j.Logger;
import org.gusdb.wdk.controller.ContextLookup;
import org.gusdb.wdk.model.WdkModel;
import org.gusdb.wdk.service.service.RecordService;

/**
* Servlet context listener that initializes service-layer caches after the WdkModel has been created.
* This must run after ApplicationInitListener has set up the model.
*
* To use this listener, add it to web.xml AFTER ApplicationInitListener:
*/
public class ServiceCacheInitializer implements ServletContextListener {

private static final Logger LOG = Logger.getLogger(ServiceCacheInitializer.class);

@Override
public void contextInitialized(ServletContextEvent sce) {
try {
LOG.info("Initializing service caches...");

WdkModel wdkModel = ContextLookup.getWdkModel(sce.getServletContext());

if (wdkModel == null) {
throw new RuntimeException("WdkModel not found in context. Ensure ApplicationInitListener runs before ServiceCacheInitializer.");
}

// Generate expanded record classes cache for /record-types?format=expanded endpoint
LOG.info("Generating expanded record classes cache...");
RecordService.generateExpandedRecordClassesCache(wdkModel);
LOG.info("Service cache initialization complete.");

} catch (Exception e) {
throw new RuntimeException("Unable to initialize WDK service caches.", e);
}
}

@Override
public void contextDestroyed(ServletContextEvent sce) {
// Nothing to clean up
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,15 @@

import static org.gusdb.fgputil.FormatUtil.NL;

import java.io.BufferedWriter;
import java.io.ByteArrayInputStream;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
Expand All @@ -12,7 +19,6 @@
import javax.ws.rs.GET;
import javax.ws.rs.NotFoundException;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
Expand Down Expand Up @@ -48,11 +54,12 @@ public class RecordService extends AbstractWdkService {
public static final String RECORD_TYPE_PARAM_SEGMENT = "{" + RECORD_TYPE_PATH_PARAM + "}";
public static final String NAMED_RECORD_TYPE_SEGMENT_PAIR = RECORD_TYPES_PATH + "/" + RECORD_TYPE_PARAM_SEGMENT;

@SuppressWarnings("unused")
private static final Logger LOG = Logger.getLogger(RecordService.class);

private static final String RECORDCLASS_RESOURCE = "RecordClass with name ";

private static final String EXPANDED_RECORD_CLASSES_CACHE_FILE = "expanded-record-classes.json";

private static final Counter TABLE_REQUEST_COUNTER = Counter.build()
.name("wdk_table_requests")
.help("Times individual tables are requested at the /records endpoint")
Expand All @@ -75,8 +82,55 @@ public Response getRecordClassList(@QueryParam("format") String format) {
}

protected InputStream getExpandedRecordClassesJsonStream(WdkModel wdkModel) {
JSONArray allRecordClassesJson = RecordClassFormatter.getExpandedRecordClassesJson(wdkModel.getAllRecordClasses(), wdkModel.getRecordClassQuestionMap());
return new ByteArrayInputStream(allRecordClassesJson.toString().getBytes());
try {
Path cacheFile = getExpandedRecordClassesCacheFile(wdkModel);

if (Files.exists(cacheFile)) {
LOG.debug("Serving expanded record classes from cache file: " + cacheFile);
return new FileInputStream(cacheFile.toFile());
} else {
LOG.warn("Cache file does not exist at: " + cacheFile + ". Falling back to in-memory generation.");
// Fallback to in-memory generation if cache doesn't exist
JSONArray allRecordClassesJson = RecordClassFormatter.getExpandedRecordClassesJson(
wdkModel.getAllRecordClasses(), wdkModel.getRecordClassQuestionMap());
return new ByteArrayInputStream(allRecordClassesJson.toString().getBytes());
}
} catch (IOException e) {
LOG.error("Failed to read cache file, falling back to in-memory generation", e);
// Fallback to in-memory generation if cache read fails
JSONArray allRecordClassesJson = RecordClassFormatter.getExpandedRecordClassesJson(
wdkModel.getAllRecordClasses(), wdkModel.getRecordClassQuestionMap());
return new ByteArrayInputStream(allRecordClassesJson.toString().getBytes());
}
}

/**
* Gets the path to the expanded record classes cache file.
*/
public static Path getExpandedRecordClassesCacheFile(WdkModel wdkModel) {
Path wdkTempDir = wdkModel.getModelConfig().getWdkTempDir();
return Paths.get(wdkTempDir.toString(), EXPANDED_RECORD_CLASSES_CACHE_FILE);
}

/**
* Generates the expanded record classes cache file.
* This should be called during application initialization.
*/
public static void generateExpandedRecordClassesCache(WdkModel wdkModel) throws IOException {
Path cacheFile = getExpandedRecordClassesCacheFile(wdkModel);

LOG.info("Generating expanded record classes cache file at: " + cacheFile);

// Generate JSON
JSONArray allRecordClassesJson = RecordClassFormatter.getExpandedRecordClassesJson(
wdkModel.getAllRecordClasses(), wdkModel.getRecordClassQuestionMap());

// Write to file
try (BufferedWriter writer = new BufferedWriter(new FileWriter(cacheFile.toFile()))) {
writer.write(allRecordClassesJson.toString());
}

LOG.info("Cache file generation complete. File size: " + Files.size(cacheFile) + " bytes");
}

@GET
Expand Down