diff --git a/api/src/org/labkey/api/action/BaseApiAction.java b/api/src/org/labkey/api/action/BaseApiAction.java index 6bce922f6b4..525de642ab4 100644 --- a/api/src/org/labkey/api/action/BaseApiAction.java +++ b/api/src/org/labkey/api/action/BaseApiAction.java @@ -19,12 +19,14 @@ import com.fasterxml.jackson.databind.JsonMappingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectReader; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import org.json.JSONTokener; -import org.jetbrains.annotations.NotNull; import org.labkey.api.miniprofiler.MiniProfiler; import org.labkey.api.miniprofiler.Timing; import org.labkey.api.query.BatchValidationException; @@ -37,7 +39,6 @@ import org.labkey.api.util.HttpUtil; import org.labkey.api.util.JsonUtil; import org.labkey.api.util.MimeMap; -import org.labkey.api.util.ResponseHelper; import org.labkey.api.util.StringUtilsLabKey; import org.labkey.api.view.BadRequestException; import org.labkey.api.view.NotFoundException; @@ -50,13 +51,10 @@ import org.springframework.validation.Errors; import org.springframework.web.servlet.ModelAndView; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.InputStreamReader; import java.io.Reader; import java.net.SocketTimeoutException; -import java.time.Duration; import java.util.Map; /** @@ -201,37 +199,6 @@ public ModelAndView handlePost() throws Exception } else { - boolean cachable = false; - - // ETag header - String eTag = getETag(form); - if (eTag != null) - { - getViewContext().getResponse().setHeader("ETag", eTag); - cachable = true; - } - - // Last-Modified header - long lastModified = getLastModified(form); - if (lastModified != Long.MIN_VALUE) - { - getViewContext().getResponse().addDateHeader("Last-Modified", lastModified); - cachable = true; - } - - if (cachable) - { - // Include max-age to tell the browser to cache for a short duration before making another request to check "If-Modified-Since" - ResponseHelper.setPrivate(getViewContext().getResponse(), Duration.ofSeconds(10)); - } - - // Check if the conditions specified in the optional If headers are satisfied. - if (!ResponseHelper.checkIfHeaders(getViewContext(), eTag, lastModified)) - { - assert getViewContext().getResponse().getStatus() != HttpServletResponse.SC_OK; - return null; - } - Object response; try (Timing ignored = MiniProfiler.step("execute")) { diff --git a/api/src/org/labkey/api/action/BaseViewAction.java b/api/src/org/labkey/api/action/BaseViewAction.java index 7d4f3020b85..e42ad5205c2 100644 --- a/api/src/org/labkey/api/action/BaseViewAction.java +++ b/api/src/org/labkey/api/action/BaseViewAction.java @@ -770,20 +770,4 @@ public String getCommandName() { return _commandName; } - - /** - * Cacheable resources can calculate a last modified timestamp to send to the browser. - */ - protected long getLastModified(FORM form) - { - return Long.MIN_VALUE; - } - - /** - * Cacheable resources can calculate an ETag header to send to the browser. - */ - protected String getETag(FORM form) - { - return null; - } } diff --git a/api/src/org/labkey/api/data/DbScope.java b/api/src/org/labkey/api/data/DbScope.java index e8bfbf7b492..f020ec50d63 100644 --- a/api/src/org/labkey/api/data/DbScope.java +++ b/api/src/org/labkey/api/data/DbScope.java @@ -1624,7 +1624,6 @@ public void invalidateSchema(DbSchema schema) */ public void invalidateSchema(String schemaName, DbSchemaType type) { - QueryService.get().updateLastModified(); _schemaCache.remove(schemaName, type); invalidateAllTables(schemaName, type); } @@ -1641,7 +1640,6 @@ private void invalidateAllTables(String schemaName, DbSchemaType type) // DbSchema. public void invalidateTable(String schemaName, String tableName, DbSchemaType type) { - QueryService.get().updateLastModified(); getTableInfoCache(type).remove(schemaName, tableName, type); _schemaCache.remove(schemaName, type); } diff --git a/api/src/org/labkey/api/query/QueryService.java b/api/src/org/labkey/api/query/QueryService.java index d54e7b9e044..f90e5c90704 100644 --- a/api/src/org/labkey/api/query/QueryService.java +++ b/api/src/org/labkey/api/query/QueryService.java @@ -69,7 +69,6 @@ public interface QueryService { - String EXPERIMENTAL_LAST_MODIFIED = "queryMetadataLastModified"; String EXPERIMENTAL_DISABLE_MANAGED_TRIGGER_COLUMNS = "queryDisableManagedTriggerColumns"; String EXPERIMENTAL_PRODUCT_ALL_FOLDER_LOOKUPS = "queryProductAllFolderLookups"; String EXPERIMENTAL_PRODUCT_PROJECT_DATA_LISTING_SCOPED = "queryProductProjectDataListingScoped"; @@ -135,12 +134,6 @@ static void setInstance(QueryService impl) // TODO: These probably need to change to support data source qualified schema names - /** Get the value used for the "Last-Modified" time stamp in query metadata API responses. */ - long metadataLastModified(); - - /** Invalidate the value used for the "Last-Modified" time stamp. */ - void updateLastModified(); - /** Get schema for SchemaKey encoded path. */ UserSchema getUserSchema(User user, Container container, String schemaPath); /** Get schema for SchemaKey path. */ diff --git a/api/src/org/labkey/api/util/ResponseHelper.java b/api/src/org/labkey/api/util/ResponseHelper.java index 2fa0939e385..d50313e513e 100644 --- a/api/src/org/labkey/api/util/ResponseHelper.java +++ b/api/src/org/labkey/api/util/ResponseHelper.java @@ -18,12 +18,11 @@ // place to centralize some common usages +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import org.jetbrains.annotations.NotNull; -import org.labkey.api.view.ViewContext; import org.springframework.http.ContentDisposition; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.time.Duration; @@ -166,21 +165,6 @@ public static void setContentDisposition(HttpServletResponse response, ContentDi response.setHeader("Content-Disposition", type.toHeaderValue(filename)); } - - /** - * Check if the conditions specified in the optional If headers are - * satisfied. - * - * @return boolean true if the resource meets all the specified conditions, - * and false if any of the conditions is not satisfied, in which case - * request processing is stopped - */ - public static boolean checkIfHeaders(ViewContext context, String eTag, long lastModified) - throws IOException - { - return checkIfHeaders(context.getRequest(), context.getResponse(), eTag, lastModified); - } - /** * Check if the conditions specified in the optional If headers are * satisfied. diff --git a/assay/src/org/labkey/assay/AssayDomainServiceImpl.java b/assay/src/org/labkey/assay/AssayDomainServiceImpl.java index 819d96d410a..2b9b35e902f 100644 --- a/assay/src/org/labkey/assay/AssayDomainServiceImpl.java +++ b/assay/src/org/labkey/assay/AssayDomainServiceImpl.java @@ -660,7 +660,6 @@ public GWTProtocol saveChanges(GWTProtocol assay, boolean replaceIfExisting) thr QueryService.get().saveCalculatedFieldsMetadata(domainDescriptor.getSchemaName(), domainDescriptor.getQueryName(), null, domain.getCalculatedFields(), hasExistingCalcFields, getUser(), protocol.getContainer()); } - QueryService.get().updateLastModified(); transaction.commit(); AssayManager.get().clearProtocolCache(); return getAssayDefinition(assay.getProtocolId(), false); diff --git a/assay/src/org/labkey/assay/ModuleAssayCache.java b/assay/src/org/labkey/assay/ModuleAssayCache.java index e9518e05e9c..beaa92e45a2 100644 --- a/assay/src/org/labkey/assay/ModuleAssayCache.java +++ b/assay/src/org/labkey/assay/ModuleAssayCache.java @@ -26,7 +26,6 @@ import org.labkey.api.module.ModuleResourceCaches; import org.labkey.api.module.ResourceRootProvider; import org.labkey.api.pipeline.PipelineProvider; -import org.labkey.api.query.QueryService; import org.labkey.api.util.Path; import java.util.Collection; @@ -72,7 +71,6 @@ void clearModuleAssayCollections() synchronized (PROVIDER_LOCK) { _moduleAssayCollections = null; - QueryService.get().updateLastModified(); } } diff --git a/experiment/src/org/labkey/experiment/api/property/DomainImpl.java b/experiment/src/org/labkey/experiment/api/property/DomainImpl.java index 55491df7b4a..c17a1bebd45 100644 --- a/experiment/src/org/labkey/experiment/api/property/DomainImpl.java +++ b/experiment/src/org/labkey/experiment/api/property/DomainImpl.java @@ -922,8 +922,6 @@ else if (!isDomainNew) getDomainKind().invalidate(this); }; transaction.addCommitTask(afterDomainCommitOrRollback, DbScope.CommitTaskOption.POSTCOMMIT, DbScope.CommitTaskOption.POSTROLLBACK); - - QueryService.get().updateLastModified(); transaction.commit(); } } diff --git a/query/src/org/labkey/query/QueryModule.java b/query/src/org/labkey/query/QueryModule.java index 3a03809f167..4419ac69ef5 100644 --- a/query/src/org/labkey/query/QueryModule.java +++ b/query/src/org/labkey/query/QueryModule.java @@ -237,9 +237,6 @@ public QuerySchema createSchema(DefaultSchema schema, Module module) DataViewService.get().registerProvider(QueryDataViewProvider.TYPE, new QueryDataViewProvider()); DataViewService.get().registerProvider(InheritedQueryDataViewProvider.TYPE, new InheritedQueryDataViewProvider()); - OptionalFeatureService.get().addExperimentalFeatureFlag(QueryServiceImpl.EXPERIMENTAL_LAST_MODIFIED, "Include Last-Modified header on query metadata requests", - "For schema, query, and view metadata requests include a Last-Modified header such that the browser can cache the response. " + - "The metadata is invalidated when performing actions such as creating a new List or modifying the columns on a custom view", false); OptionalFeatureService.get().addExperimentalFeatureFlag(USE_ROW_BY_ROW_UPDATE, "Use row-by-row update", "For Query.updateRows api, do row-by-row update, instead of using a prepared statement that updates rows in batches.", false); OptionalFeatureService.get().addExperimentalFeatureFlag(QueryServiceImpl.EXPERIMENTAL_PRODUCT_ALL_FOLDER_LOOKUPS, "Less restrictive product folder lookups", @@ -308,7 +305,6 @@ public void doStartup(ModuleContext moduleContext) // Note: DailyMessageDigest timer is initialized by the AnnouncementModule CacheManager.addListener(new ServerManager.CacheListener()); - CacheManager.addListener(new QueryServiceImpl.CacheListener()); AdminLinkManager.getInstance().addListener((adminNavTree, container, user) -> { if (container.hasPermission(user, ReadPermission.class)) diff --git a/query/src/org/labkey/query/QueryServiceImpl.java b/query/src/org/labkey/query/QueryServiceImpl.java index 80eab8d4a51..294d7499590 100644 --- a/query/src/org/labkey/query/QueryServiceImpl.java +++ b/query/src/org/labkey/query/QueryServiceImpl.java @@ -83,7 +83,6 @@ import org.labkey.api.module.ModuleLoader; import org.labkey.api.module.ModuleResourceCache; import org.labkey.api.module.ModuleResourceCacheHandler; -import org.labkey.api.module.ModuleResourceCacheListener; import org.labkey.api.module.ModuleResourceCaches; import org.labkey.api.module.ResourceRootProvider; import org.labkey.api.pipeline.PipelineJob; @@ -191,7 +190,6 @@ import java.util.Arrays; import java.util.Collection; import java.util.Collections; -import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; @@ -207,7 +205,6 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.CopyOnWriteArrayList; -import java.util.concurrent.atomic.AtomicLong; import java.util.function.Consumer; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -235,46 +232,11 @@ public void fillResourceRoots(@NotNull Resource topRoot, @NotNull Collection> MODULE_QUERY_DEF_CACHE = ModuleResourceCaches.create("Module query definitions", new QueryDefResourceCacheHandler(), QUERY_AND_ASSAY_PROVIDER); private static final ModuleResourceCache> MODULE_QUERY_METADATA_DEF_CACHE = ModuleResourceCaches.create("Module query meta data", new QueryMetaDataDefResourceCacheHandler(), QUERY_AND_ASSAY_PROVIDER); private static final ModuleResourceCache> MODULE_CUSTOM_VIEW_CACHE = ModuleResourceCaches.create("Module custom view definitions", new CustomViewResourceCacheHandler(), QUERY_AND_ASSAY_PROVIDER); - - private static final ModuleResourceCacheListener INVALIDATE_QUERY_METADATA_HANDLER = new ModuleResourceCacheListener() - { - @Override - public void entryCreated(java.nio.file.Path directory, java.nio.file.Path entry) - { - QueryService.get().updateLastModified(); - } - - @Override - public void entryDeleted(java.nio.file.Path directory, java.nio.file.Path entry) - { - QueryService.get().updateLastModified(); - } - - @Override - public void entryModified(java.nio.file.Path directory, java.nio.file.Path entry) - { - QueryService.get().updateLastModified(); - } - - @Override - public void overflow() - { - } - - @Override - public void moduleChanged(Module module) - { - QueryService.get().updateLastModified(); - } - }; - private final ConcurrentMap, Pair> _schemaLinkActions = new ConcurrentHashMap<>(); private QueryAnalysisService _queryAnalysisService; private final List _queryIconURLProviders = new CopyOnWriteArrayList<>(); - private final AtomicLong _metadataLastModified = new AtomicLong(new Date().getTime()); - private final List COMPARE_TYPES = new CopyOnWriteArrayList<>(Arrays.asList( CompareType.EQUAL, CompareType.DATE_EQUAL, @@ -696,30 +658,6 @@ static public QueryServiceImpl get() return (QueryServiceImpl) QueryService.get(); } - static class CacheListener implements org.labkey.api.cache.CacheListener - { - @Override - public void clearCaches() - { - QueryServiceImpl.get().updateLastModified(); - } - } - - /** Get the value used for the "Last-Modified" time stamp in query metadata API responses. */ - @Override - public long metadataLastModified() - { - return AppProps.getInstance().isOptionalFeatureEnabled(EXPERIMENTAL_LAST_MODIFIED) ? - _metadataLastModified.get() : Long.MIN_VALUE; - } - - /** Invalidate the value used for the "Last-Modified" time stamp. */ - @Override - public void updateLastModified() - { - _metadataLastModified.set(new Date().getTime()); - } - @Override public UserSchema getUserSchema(User user, Container container, String schemaPath) { @@ -984,7 +922,6 @@ public void uncacheModuleResources(Module module) MODULE_QUERY_DEF_CACHE.onModuleChanged(module); MODULE_QUERY_METADATA_DEF_CACHE.onModuleChanged(module); MODULE_CUSTOM_VIEW_CACHE.onModuleChanged(module); - INVALIDATE_QUERY_METADATA_HANDLER.moduleChanged(module); } private static class QueryDefResourceCacheHandler implements ModuleResourceCacheHandler> @@ -997,12 +934,6 @@ public MultiValuedMap load(Stream reso .map(resource -> new ModuleQueryDef(module, resource)) .collect(LabKeyCollectors.toMultiValuedMap(def -> def.getPath().getParent(), def -> def))); } - - @Override - public @Nullable ModuleResourceCacheListener createChainedListener(Module module) - { - return INVALIDATE_QUERY_METADATA_HANDLER; - } } @Override @@ -1434,12 +1365,6 @@ public MultiValuedMap load(Stream .map(ModuleCustomViewDef::new) .collect(LabKeyCollectors.toMultiValuedMap(def -> def.getPath().getParent(), def -> def))); } - - @Override - public @Nullable ModuleResourceCacheListener createChainedListener(Module module) - { - return INVALIDATE_QUERY_METADATA_HANDLER; - } } @Override @@ -2555,12 +2480,6 @@ public MultiValuedMap load(Stream def.getPath().getParent(), def -> def))); } - - @Override - public @Nullable ModuleResourceCacheListener createChainedListener(Module module) - { - return INVALIDATE_QUERY_METADATA_HANDLER; - } } @Override diff --git a/query/src/org/labkey/query/controllers/GetQueryDetailsAction.java b/query/src/org/labkey/query/controllers/GetQueryDetailsAction.java index 8e148f40b40..5394da6b60f 100644 --- a/query/src/org/labkey/query/controllers/GetQueryDetailsAction.java +++ b/query/src/org/labkey/query/controllers/GetQueryDetailsAction.java @@ -87,12 +87,6 @@ public class GetQueryDetailsAction extends ReadOnlyApiAction { - @Override - protected long getLastModified(Form form) - { - return QueryService.get().metadataLastModified(); - } - @Override public ApiResponse execute(Form form, BindException errors) throws Exception { diff --git a/query/src/org/labkey/query/controllers/QueryController.java b/query/src/org/labkey/query/controllers/QueryController.java index b4ebe9d8ed5..202951cc808 100644 --- a/query/src/org/labkey/query/controllers/QueryController.java +++ b/query/src/org/labkey/query/controllers/QueryController.java @@ -6603,12 +6603,6 @@ public void setIncludeHidden(boolean includeHidden) @ApiVersion(12.3) public static class GetSchemasAction extends ReadOnlyApiAction { - @Override - protected long getLastModified(GetSchemasForm form) - { - return QueryService.get().metadataLastModified(); - } - @Override public ApiResponse execute(GetSchemasForm form, BindException errors) { @@ -6755,12 +6749,6 @@ public void setIncludeTitle(boolean includeTitle) @Action(ActionType.SelectMetaData.class) public static class GetQueriesAction extends ReadOnlyApiAction { - @Override - protected long getLastModified(GetQueriesForm form) - { - return QueryService.get().metadataLastModified(); - } - @Override public ApiResponse execute(GetQueriesForm form, BindException errors) { @@ -6963,12 +6951,6 @@ public void setExcludeSessionView(boolean excludeSessionView) @Action(ActionType.SelectMetaData.class) public static class GetQueryViewsAction extends ReadOnlyApiAction { - @Override - protected long getLastModified(GetQueryViewsForm form) - { - return QueryService.get().metadataLastModified(); - } - @Override public ApiResponse execute(GetQueryViewsForm form, BindException errors) { diff --git a/query/src/org/labkey/query/persist/QueryManager.java b/query/src/org/labkey/query/persist/QueryManager.java index 3717b6dff61..30b0df1af29 100644 --- a/query/src/org/labkey/query/persist/QueryManager.java +++ b/query/src/org/labkey/query/persist/QueryManager.java @@ -429,7 +429,6 @@ public void deleteLinkedSchema(Container container, String userSchemaName) // changes in any way (insert/update/delete). public void updateExternalSchemas(Container c) { - QueryService.get().updateLastModified(); if (null != c) { ExternalSchemaDefCache.uncache(c); @@ -559,14 +558,12 @@ public void removeQueryListener(QueryChangeListener listener) public void fireQueryCreated(User user, Container container, ContainerFilter scope, SchemaKey schema, @NotNull Collection queries) { - QueryService.get().updateLastModified(); for (QueryChangeListener l : QUERY_LISTENERS) l.queryCreated(user, container, scope, schema, queries); } public void fireQueryChanged(User user, Container container, ContainerFilter scope, SchemaKey schema, @NotNull QueryChangeListener.QueryProperty property, @NotNull Collection> changes) { - QueryService.get().updateLastModified(); assert checkChanges(property, changes); for (QueryChangeListener l : QUERY_LISTENERS) l.queryChanged(user, container, scope, schema, property, changes); @@ -605,7 +602,6 @@ private boolean checkChanges(QueryChangeListener.QueryProperty property, Collect public void fireQueryDeleted(User user, Container container, ContainerFilter scope, SchemaKey schema, Collection queries) { - QueryService.get().updateLastModified(); for (QueryChangeListener l : QUERY_LISTENERS) l.queryDeleted(user, container, scope, schema, queries); } @@ -630,21 +626,18 @@ public void removeCustomViewListener(CustomViewChangeListener listener) public void fireViewCreated(CustomView view) { - QueryService.get().updateLastModified(); for (CustomViewChangeListener l : VIEW_LISTENERS) l.viewCreated(view); } public void fireViewChanged(CustomView view) { - QueryService.get().updateLastModified(); for (CustomViewChangeListener l : VIEW_LISTENERS) l.viewChanged(view); } public void fireViewDeleted(CustomView view) { - QueryService.get().updateLastModified(); for (CustomViewChangeListener l : VIEW_LISTENERS) l.viewDeleted(view); } diff --git a/study/src/org/labkey/study/model/StudyManager.java b/study/src/org/labkey/study/model/StudyManager.java index bdf0424e68c..77e59c501f4 100644 --- a/study/src/org/labkey/study/model/StudyManager.java +++ b/study/src/org/labkey/study/model/StudyManager.java @@ -626,7 +626,6 @@ public StudyImpl createStudy(User user, StudyImpl study) transaction.commit(); } StudyDesignManager.get().ensureStudyDesignDomains(container, user); - QueryService.get().updateLastModified(); ContainerManager.notifyContainerChange(container.getId(), ContainerManager.Property.StudyChange); return study; } @@ -656,7 +655,6 @@ public ValidationException updateStudy(@Nullable User user, StudyImpl study) String comment = "Dataset security type changed from " + oldStudy.getSecurityType() + " to " + study.getSecurityType(); StudyService.get().addStudyAuditEvent(study.getContainer(), user, comment); } - QueryService.get().updateLastModified(); return errors; } @@ -688,8 +686,6 @@ public void createDatasetDefinition(User user, DatasetDefinition datasetDefiniti // we're open to a race condition if another thread tries to do something with the dataset's table // and ends up attempting to create the domain as well datasetDefinition.getStorageTableInfo(true); - - QueryService.get().updateLastModified(); transaction.commit(); } indexDataset(SearchService.get().defaultTask().getQueue(datasetDefinition.getContainer(), SearchService.PRIORITY.modified), datasetDefinition); @@ -872,7 +868,6 @@ else if (datasetDefinition.getKeyPropertyName() == null) // NOTE: not redundant with uncache() in commit task, there may be an active outer transaction uncache(datasetDefinition); - QueryService.get().updateLastModified(); transaction.commit(); } datasetDefinition.refreshDomain();