diff --git a/api/src/org/labkey/api/assay/AbstractAssayProvider.java b/api/src/org/labkey/api/assay/AbstractAssayProvider.java index d021b65453d..713045df26f 100644 --- a/api/src/org/labkey/api/assay/AbstractAssayProvider.java +++ b/api/src/org/labkey/api/assay/AbstractAssayProvider.java @@ -382,31 +382,37 @@ public static String getDomainURIForPrefixIfExists(ExpProtocol protocol, String return result; } - public static Domain getDomainByPrefix(ExpProtocol protocol, String domainPrefix) + public static Domain getDomainByPrefix(ExpProtocol protocol, String domainPrefix, boolean forUpdate) { Container container = protocol.getContainer(); - return PropertyService.get().getDomain(container, getDomainURIForPrefix(protocol, domainPrefix)); + return PropertyService.get().getDomain(container, getDomainURIForPrefix(protocol, domainPrefix), forUpdate); } @Nullable - public static Domain getDomainByPrefixIfExists(ExpProtocol protocol, String domainPrefix) + public static Domain getDomainByPrefixIfExists(ExpProtocol protocol, String domainPrefix, boolean forUpdate) { String domainURI = getDomainURIForPrefixIfExists(protocol, domainPrefix); if (null == domainURI) return null; Container container = protocol.getContainer(); - return PropertyService.get().getDomain(container, domainURI); + return PropertyService.get().getDomain(container, domainURI, forUpdate); } @Override public Domain getResultsDomain(ExpProtocol protocol) { - return getDomainByPrefix(protocol, ExpProtocol.ASSAY_DOMAIN_DATA); + return getResultsDomain(protocol, false); + } + + @Override + public Domain getResultsDomain(ExpProtocol protocol, boolean forUpdate) + { + return getDomainByPrefix(protocol, ExpProtocol.ASSAY_DOMAIN_DATA, forUpdate); } protected @Nullable Domain getResultsDomainIfExists(ExpProtocol protocol) { - return getDomainByPrefixIfExists(protocol, ExpProtocol.ASSAY_DOMAIN_DATA); + return getDomainByPrefixIfExists(protocol, ExpProtocol.ASSAY_DOMAIN_DATA, false); } @Override @@ -422,13 +428,25 @@ public void afterDomainChange(User user, ExpProtocol protocol, GWTDomain dataMap, Collection types) diff --git a/api/src/org/labkey/api/assay/AbstractAssayTsvDataHandler.java b/api/src/org/labkey/api/assay/AbstractAssayTsvDataHandler.java index d111fb55845..7d394df7c14 100644 --- a/api/src/org/labkey/api/assay/AbstractAssayTsvDataHandler.java +++ b/api/src/org/labkey/api/assay/AbstractAssayTsvDataHandler.java @@ -395,7 +395,7 @@ public void beforeDeleteData(List data, User user) throws ExperimentExc // results/data domain for TSV-style assays try { - domain = AbstractAssayProvider.getDomainByPrefixIfExists(protocol, ExpProtocol.ASSAY_DOMAIN_DATA) ; + domain = AbstractAssayProvider.getDomainByPrefixIfExists(protocol, ExpProtocol.ASSAY_DOMAIN_DATA, false) ; } catch (IllegalStateException ignored) { diff --git a/api/src/org/labkey/api/assay/AssayProvider.java b/api/src/org/labkey/api/assay/AssayProvider.java index 4e74be4acbd..400702bd4b8 100644 --- a/api/src/org/labkey/api/assay/AssayProvider.java +++ b/api/src/org/labkey/api/assay/AssayProvider.java @@ -97,12 +97,19 @@ enum ReRunSupport /** Get a schema that includes queries like Batch, Run, Results, and any additional tables. */ AssayProtocolSchema createProtocolSchema(User user, Container container, @NotNull ExpProtocol protocol, @Nullable Container targetStudy); + /** Get a domain that is not intended to be mutated */ Domain getBatchDomain(ExpProtocol protocol); + Domain getBatchDomain(ExpProtocol protocol, boolean forUpdate); + Domain getRunDomain(ExpProtocol protocol); + Domain getRunDomain(ExpProtocol protocol, boolean forUpdate); + Domain getResultsDomain(ExpProtocol protocol); + Domain getResultsDomain(ExpProtocol protocol, boolean forUpdate); + void beforeDomainChange(User user, ExpProtocol protocol, GWTDomain orig, GWTDomain update) throws ValidationException; void afterDomainChange(User user, ExpProtocol protocol, GWTDomain orig, GWTDomain update) throws ValidationException; diff --git a/api/src/org/labkey/api/audit/AbstractAuditTypeProvider.java b/api/src/org/labkey/api/audit/AbstractAuditTypeProvider.java index 4a50b03a2cf..12bec0441ea 100644 --- a/api/src/org/labkey/api/audit/AbstractAuditTypeProvider.java +++ b/api/src/org/labkey/api/audit/AbstractAuditTypeProvider.java @@ -124,7 +124,7 @@ protected AbstractAuditDomainKind getDomainKind() public void initializeProvider(User user) { AbstractAuditDomainKind domainKind = getDomainKind(); - Domain domain = getDomain(); + Domain domain = getDomain(true); // if the domain doesn't exist, create it if (domain == null) @@ -138,7 +138,7 @@ public void initializeProvider(User user) domain.addPropertyOfPropertyDescriptor(pd); } domain.save(user); - domain = getDomain(); + domain = getDomain(true); } catch (ChangePropertyDescriptorException e) { @@ -299,12 +299,18 @@ private void copyTo(DomainProperty dp, PropertyDescriptor pd, Container c) @Override public final Domain getDomain() + { + return getDomain(false); + } + + @Override + public final Domain getDomain(boolean forUpdate) { DomainKind domainKind = getDomainKind(); String domainURI = domainKind.generateDomainURI(QUERY_SCHEMA_NAME, getEventName(), getDomainContainer(), null); - return PropertyService.get().getDomain(getDomainContainer(), domainURI); + return PropertyService.get().getDomain(getDomainContainer(), domainURI, forUpdate); } diff --git a/api/src/org/labkey/api/audit/AuditTypeProvider.java b/api/src/org/labkey/api/audit/AuditTypeProvider.java index 81f1d8e9ccd..332f1a5d681 100644 --- a/api/src/org/labkey/api/audit/AuditTypeProvider.java +++ b/api/src/org/labkey/api/audit/AuditTypeProvider.java @@ -44,6 +44,8 @@ public interface AuditTypeProvider Domain getDomain(); + Domain getDomain(boolean forUpdate); + TableInfo createTableInfo(UserSchema schema, ContainerFilter cf); Class getEventClass(); diff --git a/api/src/org/labkey/api/audit/query/AbstractAuditDomainKind.java b/api/src/org/labkey/api/audit/query/AbstractAuditDomainKind.java index 7daba72dc54..9add67980cd 100644 --- a/api/src/org/labkey/api/audit/query/AbstractAuditDomainKind.java +++ b/api/src/org/labkey/api/audit/query/AbstractAuditDomainKind.java @@ -241,7 +241,7 @@ public JSONObject getDomainKindProperties(GWTDomain domain, Container container, } @Override - public Domain createDomain(GWTDomain domain, JSONObject arguments, Container container, User user, @Nullable TemplateInfo templateInfo) + public Domain createDomain(GWTDomain domain, JSONObject arguments, Container container, User user, @Nullable TemplateInfo templateInfo, boolean forUpdate) { throw new UnsupportedOperationException(); } diff --git a/api/src/org/labkey/api/audit/query/DefaultAuditTypeTable.java b/api/src/org/labkey/api/audit/query/DefaultAuditTypeTable.java index fc3d4a5441c..61e7497a9f0 100644 --- a/api/src/org/labkey/api/audit/query/DefaultAuditTypeTable.java +++ b/api/src/org/labkey/api/audit/query/DefaultAuditTypeTable.java @@ -142,7 +142,14 @@ protected void initColumn(MutableColumnInfo col) @Override public Domain getDomain() { - return _provider.getDomain(); + return getDomain(false); + } + + @Nullable + @Override + public Domain getDomain(boolean forUpdate) + { + return _provider.getDomain(forUpdate); } @Nullable diff --git a/api/src/org/labkey/api/data/AbstractTableInfo.java b/api/src/org/labkey/api/data/AbstractTableInfo.java index 79aa2ef871f..e8eca0662bd 100644 --- a/api/src/org/labkey/api/data/AbstractTableInfo.java +++ b/api/src/org/labkey/api/data/AbstractTableInfo.java @@ -1769,6 +1769,13 @@ public Domain getDomain() return null; } + @Nullable + @Override + public Domain getDomain(boolean forUpdate) + { + return getDomain(); + } + @Nullable @Override public DomainKind getDomainKind() diff --git a/api/src/org/labkey/api/data/MutableColumnInfo.java b/api/src/org/labkey/api/data/MutableColumnInfo.java index 225a8020ff6..662bbfb697f 100644 --- a/api/src/org/labkey/api/data/MutableColumnInfo.java +++ b/api/src/org/labkey/api/data/MutableColumnInfo.java @@ -97,7 +97,7 @@ default void setSortFieldKeysFromXml(String xml) void setLocked(boolean b); - // return a ColumnInfo that does not suppport MutableColumnInfo or a MutableColumnInfo that is locked + // return a ColumnInfo that does not support MutableColumnInfo or a MutableColumnInfo that is locked default ColumnInfo lock() { setLocked(true); diff --git a/api/src/org/labkey/api/data/NameGenerator.java b/api/src/org/labkey/api/data/NameGenerator.java index 6df428fa11e..bf35e3250c6 100644 --- a/api/src/org/labkey/api/data/NameGenerator.java +++ b/api/src/org/labkey/api/data/NameGenerator.java @@ -15,8 +15,6 @@ */ package org.labkey.api.data; -import org.apache.commons.beanutils.ConversionException; -import org.apache.commons.beanutils.ConvertUtils; import org.apache.commons.lang3.StringUtils; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -26,17 +24,12 @@ import org.junit.Test; import org.labkey.api.collections.CaseInsensitiveHashMap; import org.labkey.api.collections.CaseInsensitiveHashSet; -import org.labkey.api.exp.Identifiable; -import org.labkey.api.exp.LsidManager; import org.labkey.api.exp.PropertyType; import org.labkey.api.exp.api.ExpData; import org.labkey.api.exp.api.ExpDataClass; -import org.labkey.api.exp.api.ExpLineage; import org.labkey.api.exp.api.ExpLineageOptions; -import org.labkey.api.exp.api.ExpLineageService; import org.labkey.api.exp.api.ExpMaterial; import org.labkey.api.exp.api.ExpObject; -import org.labkey.api.exp.api.ExpRunItem; import org.labkey.api.exp.api.ExpSampleType; import org.labkey.api.exp.api.ExperimentService; import org.labkey.api.exp.api.SampleTypeService; @@ -47,7 +40,6 @@ import org.labkey.api.query.FieldKey; import org.labkey.api.query.QueryKey; import org.labkey.api.query.QueryService; -import org.labkey.api.query.RuntimeValidationException; import org.labkey.api.query.UserSchema; import org.labkey.api.query.ValidationException; import org.labkey.api.reader.TabLoader; @@ -59,12 +51,9 @@ import org.labkey.api.util.StringExpressionFactory; import org.labkey.api.util.StringExpressionFactory.AbstractStringExpression.NullValueBehavior; import org.labkey.api.util.StringExpressionFactory.FieldKeyStringExpression; -import org.labkey.api.util.StringUtilsLabKey; import org.labkey.api.util.SubstitutionFormat; -import org.labkey.api.util.Tuple3; import java.io.IOException; -import java.sql.SQLException; import java.sql.Time; import java.text.ParseException; import java.text.SimpleDateFormat; @@ -73,12 +62,10 @@ import java.util.Calendar; import java.util.Collection; import java.util.Collections; -import java.util.Comparator; import java.util.Date; import java.util.GregorianCalendar; import java.util.HashMap; import java.util.HashSet; -import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; import java.util.ListIterator; @@ -88,7 +75,6 @@ import java.util.function.Supplier; import java.util.regex.Matcher; import java.util.regex.Pattern; -import java.util.stream.Collectors; import java.util.stream.Stream; import static org.labkey.api.data.NameGenerator.NameGenerationExpression.findFirstOpenOrCloseTag; diff --git a/api/src/org/labkey/api/data/SchemaTableInfo.java b/api/src/org/labkey/api/data/SchemaTableInfo.java index 1fecb0c4d66..3df40b08008 100644 --- a/api/src/org/labkey/api/data/SchemaTableInfo.java +++ b/api/src/org/labkey/api/data/SchemaTableInfo.java @@ -857,6 +857,15 @@ public Domain getDomain() return null; } + @Nullable + @Override + public Domain getDomain(boolean forUpdate) + { + if (forUpdate) + throw new UnsupportedOperationException("Cannot get domain for update."); + return getDomain(); + } + @Nullable @Override public DomainKind getDomainKind() diff --git a/api/src/org/labkey/api/data/TableInfo.java b/api/src/org/labkey/api/data/TableInfo.java index ea6450d0a1a..d396f31d651 100644 --- a/api/src/org/labkey/api/data/TableInfo.java +++ b/api/src/org/labkey/api/data/TableInfo.java @@ -445,6 +445,12 @@ default List> getValidatedImportTemplates(ViewContext ctx) @Nullable Domain getDomain(); + /** + * Get Domain associated with this TableInfo if any. + */ + @Nullable + Domain getDomain(boolean forUpdate); + /** * Get DomainKind associated with this TableInfo if any. * Domain may or may not exist even if DomainKind is available. diff --git a/api/src/org/labkey/api/data/generator/DataGenerator.java b/api/src/org/labkey/api/data/generator/DataGenerator.java index d45fd29d5a8..760396cb66d 100644 --- a/api/src/org/labkey/api/data/generator/DataGenerator.java +++ b/api/src/org/labkey/api/data/generator/DataGenerator.java @@ -17,7 +17,6 @@ import org.labkey.api.data.dialect.SqlDialect; import org.labkey.api.dataiterator.DataIteratorBuilder; import org.labkey.api.dataiterator.DetailedAuditLogDataIterator; -import org.labkey.api.dataiterator.ListofMapsDataIterator; import org.labkey.api.dataiterator.MapDataIterator; import org.labkey.api.exp.ExperimentException; import org.labkey.api.exp.api.DataClassDomainKindProperties; diff --git a/api/src/org/labkey/api/exp/OntologyManager.java b/api/src/org/labkey/api/exp/OntologyManager.java index 152597bb6b2..2f8e5f75e63 100644 --- a/api/src/org/labkey/api/exp/OntologyManager.java +++ b/api/src/org/labkey/api/exp/OntologyManager.java @@ -2441,25 +2441,55 @@ public DomainDescriptor load(@NotNull Integer key, @Nullable Object argument) } } - public static DomainDescriptor getDomainDescriptor(int id) { + return getDomainDescriptor(id, false); + } + + public static DomainDescriptor getDomainDescriptor(int id, boolean forUpdate) + { + if (forUpdate) + return new DomainDescriptorLoader().load(id, null); + return DOMAIN_DESC_BY_ID_CACHE.get(id); } @Nullable public static DomainDescriptor getDomainDescriptor(String domainURI, Container c) + { + return getDomainDescriptor(domainURI, c, false); + } + + @Nullable + public static DomainDescriptor getDomainDescriptor(String domainURI, Container c, boolean forUpdate) { if (c == null) return null; + if (forUpdate) + return getDomainDescriptorForUpdate(domainURI, c); + // cache lookup by project. if not found at project level, check to see if global - DomainDescriptor dd = DOMAIN_DESCRIPTORS_BY_URI_CACHE.get(getCacheKey(domainURI, c)); + Pair key = getCacheKey(domainURI, c); + DomainDescriptor dd = DOMAIN_DESCRIPTORS_BY_URI_CACHE.get(key); if (null != dd) return dd; // Try in the /Shared container too - return DOMAIN_DESCRIPTORS_BY_URI_CACHE.get(getCacheKey(domainURI, _sharedContainer)); + key = getCacheKey(domainURI, _sharedContainer); + return DOMAIN_DESCRIPTORS_BY_URI_CACHE.get(key); + } + + @Nullable + private static DomainDescriptor getDomainDescriptorForUpdate(String domainURI, Container c) + { + if (c == null) + return null; + + DomainDescriptor dd = fetchDomainDescriptorFromDB(domainURI, c); + if (dd == null) + dd = fetchDomainDescriptorFromDB(domainURI, _sharedContainer); + return dd; } /** diff --git a/api/src/org/labkey/api/exp/api/ExpDataClass.java b/api/src/org/labkey/api/exp/api/ExpDataClass.java index 5d794610259..e785f647e16 100644 --- a/api/src/org/labkey/api/exp/api/ExpDataClass.java +++ b/api/src/org/labkey/api/exp/api/ExpDataClass.java @@ -58,9 +58,11 @@ public interface ExpDataClass extends ExpObject, ExpSearchable void setSampleType(Integer sampleType); + @NotNull Domain getDomain(); - void setDomain(Domain d); + @NotNull + Domain getDomain(boolean forUpdate); String getDescription(); diff --git a/api/src/org/labkey/api/exp/api/ExpSampleType.java b/api/src/org/labkey/api/exp/api/ExpSampleType.java index 547298f64ed..3e746633d00 100644 --- a/api/src/org/labkey/api/exp/api/ExpSampleType.java +++ b/api/src/org/labkey/api/exp/api/ExpSampleType.java @@ -66,6 +66,9 @@ public interface ExpSampleType extends ExpObject, ExpSearchable @NotNull Domain getDomain(); + @NotNull + Domain getDomain(boolean forUpdate); + String getDescription(); /** diff --git a/api/src/org/labkey/api/exp/api/SampleTypeDomainKind.java b/api/src/org/labkey/api/exp/api/SampleTypeDomainKind.java index 4eab9c0a44b..160160a7c16 100644 --- a/api/src/org/labkey/api/exp/api/SampleTypeDomainKind.java +++ b/api/src/org/labkey/api/exp/api/SampleTypeDomainKind.java @@ -62,7 +62,6 @@ import org.labkey.api.security.permissions.AdminPermission; import org.labkey.api.security.permissions.DesignSampleTypePermission; import org.labkey.api.util.PageFlowUtil; -import org.labkey.api.util.Pair; import org.labkey.api.util.StringUtilsLabKey; import org.labkey.api.view.ActionURL; import org.labkey.api.view.NotFoundException; @@ -542,7 +541,7 @@ public void validateOptions(Container container, User user, SampleTypeDomainKind } @Override - public Domain createDomain(GWTDomain domain, @Nullable SampleTypeDomainKindProperties arguments, Container container, User user, @Nullable TemplateInfo templateInfo) + public Domain createDomain(GWTDomain domain, @Nullable SampleTypeDomainKindProperties arguments, Container container, User user, @Nullable TemplateInfo templateInfo, boolean forUpdate) { String name = StringUtils.trimToNull(domain.getName()); if (name == null) @@ -602,7 +601,7 @@ public Domain createDomain(GWTDomain domain, @Nullable SampleTypeDomainKindPrope { throw new RuntimeException(e); } - return st.getDomain(); + return st.getDomain(forUpdate); } @Override diff --git a/api/src/org/labkey/api/exp/list/ListDefinition.java b/api/src/org/labkey/api/exp/list/ListDefinition.java index 29923d0f2a0..af26042e3d4 100644 --- a/api/src/org/labkey/api/exp/list/ListDefinition.java +++ b/api/src/org/labkey/api/exp/list/ListDefinition.java @@ -277,6 +277,8 @@ public static BodySetting getForValue(int value) Container getContainer(); @Nullable Domain getDomain(); + @Nullable Domain getDomain(boolean forUpdate); + String getName(); String getKeyName(); void setKeyName(String name); diff --git a/api/src/org/labkey/api/exp/property/AbstractDomainKind.java b/api/src/org/labkey/api/exp/property/AbstractDomainKind.java index 9b7178a7d56..5966606ecc0 100644 --- a/api/src/org/labkey/api/exp/property/AbstractDomainKind.java +++ b/api/src/org/labkey/api/exp/property/AbstractDomainKind.java @@ -108,7 +108,7 @@ public void deletePropertyDescriptor(Domain domain, User user, PropertyDescripto } @Override - public Domain createDomain(GWTDomain domain, T arguments, Container container, User user, @Nullable TemplateInfo templateInfo) + public Domain createDomain(GWTDomain domain, T arguments, Container container, User user, @Nullable TemplateInfo templateInfo, boolean forUpdate) { return null; } diff --git a/api/src/org/labkey/api/exp/property/Domain.java b/api/src/org/labkey/api/exp/property/Domain.java index 4c0e4da5580..9ea6c5534f2 100644 --- a/api/src/org/labkey/api/exp/property/Domain.java +++ b/api/src/org/labkey/api/exp/property/Domain.java @@ -76,9 +76,11 @@ public interface Domain extends IPropertyType List getColumns(TableInfo sourceTable, ColumnInfo lsidColumn, Container container, User user); + boolean isMutable(); + /* * This returns a lock which will acquire an UPDATE lock on the domain row in the database. - * This can be called at the beginning of a transaction to help reduce the chance of a dead-lock. + * This can be called at the beginning of a transaction to help reduce the chance of a deadlock. * This pattern effectively forces all callers who are trying to manipulate this domain to queue up. */ Lock getDatabaseLock(); diff --git a/api/src/org/labkey/api/exp/property/DomainKind.java b/api/src/org/labkey/api/exp/property/DomainKind.java index 8b96486c2d5..d6bce31c447 100644 --- a/api/src/org/labkey/api/exp/property/DomainKind.java +++ b/api/src/org/labkey/api/exp/property/DomainKind.java @@ -145,13 +145,15 @@ public Set getReservedPropertyNamePrefixes() /** * Create a Domain appropriate for this DomainKind. - * @param domain The domain design. - * @param options Any domain kind specific properties/options. + * + * @param domain The domain design. + * @param options Any domain kind specific properties/options. * @param container Container - * @param user User + * @param user User + * @param forUpdate Whether the returned domain should be mutable or not * @return The newly created Domain. */ - abstract public Domain createDomain(GWTDomain domain, T options, Container container, User user, @Nullable TemplateInfo templateInfo); + abstract public Domain createDomain(GWTDomain domain, T options, Container container, User user, @Nullable TemplateInfo templateInfo, boolean forUpdate); /** * Update a Domain definition appropriate for this DomainKind. diff --git a/api/src/org/labkey/api/exp/property/DomainTemplate.java b/api/src/org/labkey/api/exp/property/DomainTemplate.java index ce25a89b353..00b965e52b8 100644 --- a/api/src/org/labkey/api/exp/property/DomainTemplate.java +++ b/api/src/org/labkey/api/exp/property/DomainTemplate.java @@ -390,7 +390,7 @@ public Domain createAndImport(Container c, User u, @Nullable String domainName, try (DbScope.Transaction tx = ExperimentService.get().getSchema().getScope().ensureTransaction()) { DomainTemplateGroup.LOG.debug("creating domain '" + domainName + "'"); - d = DomainUtil.createDomain(this, c, u, domainName); + d = DomainUtil.createDomain(this, c, u, domainName, true); tx.commit(); } catch (ValidationException ve) diff --git a/api/src/org/labkey/api/exp/property/DomainUtil.java b/api/src/org/labkey/api/exp/property/DomainUtil.java index 74dc6d8b26d..dce5ba484c6 100644 --- a/api/src/org/labkey/api/exp/property/DomainUtil.java +++ b/api/src/org/labkey/api/exp/property/DomainUtil.java @@ -686,7 +686,12 @@ public static GWTPropertyDescriptor getPropertyDescriptor(ColumnType columnXml) public static Domain createDomain(DomainTemplate template, Container container, User user, @Nullable String domainName) throws ValidationException { - return createDomain(template.getDomainKind(), template.getDomain(), template.getOptions(), container, user, domainName, template.getTemplateInfo()); + return createDomain(template, container, user, domainName, false); + } + + public static Domain createDomain(DomainTemplate template, Container container, User user, @Nullable String domainName, boolean forUpdate) throws ValidationException + { + return createDomain(template.getDomainKind(), template.getDomain(), template.getOptions(), container, user, domainName, template.getTemplateInfo(), forUpdate); } public static Domain createDomain( @@ -696,8 +701,8 @@ public static Domain createDomain( Container container, User user, @Nullable String domainName, - @Nullable TemplateInfo templateInfo - ) throws ValidationException + @Nullable TemplateInfo templateInfo, + boolean forUpdate) throws ValidationException { // Create a copy of the GWTDomain to ensure the template's Domain is not modified domain = new GWTDomain(domain); @@ -734,7 +739,7 @@ public static Domain createDomain( arguments = kind.processArguments(container, user, arguments); Object options = JsonUtil.DEFAULT_MAPPER.convertValue(arguments, kind.getTypeClass()); - Domain created = kind.createDomain(domain, options, container, user, templateInfo); + Domain created = kind.createDomain(domain, options, container, user, templateInfo, forUpdate); if (created == null) throw new RuntimeException("Failed to created domain for kind '" + kind.getKindName() + "' using domain name '" + domainName + "'"); @@ -761,7 +766,8 @@ public static ValidationException updateDomainDescriptor(GWTDomain> getDomainKinds(); List> getDomainKinds(Container container, User user, Set domainKinds, boolean includeProjectAndShared); diff --git a/api/src/org/labkey/api/exp/property/TestDomainKind.java b/api/src/org/labkey/api/exp/property/TestDomainKind.java index 47fbf4aeacd..363125723f5 100644 --- a/api/src/org/labkey/api/exp/property/TestDomainKind.java +++ b/api/src/org/labkey/api/exp/property/TestDomainKind.java @@ -165,7 +165,7 @@ public Set getMandatoryPropertyNames(Domain domain) } @Override - public Domain createDomain(GWTDomain domain, JSONObject arguments, Container container, User user, @Nullable TemplateInfo templateInfo) + public Domain createDomain(GWTDomain domain, JSONObject arguments, Container container, User user, @Nullable TemplateInfo templateInfo, boolean forUpdate) { throw new UnsupportedOperationException(); } diff --git a/api/src/org/labkey/api/issues/AbstractIssuesListDefDomainKind.java b/api/src/org/labkey/api/issues/AbstractIssuesListDefDomainKind.java index 96ff421afb4..1fe8ba5fc32 100644 --- a/api/src/org/labkey/api/issues/AbstractIssuesListDefDomainKind.java +++ b/api/src/org/labkey/api/issues/AbstractIssuesListDefDomainKind.java @@ -297,7 +297,7 @@ public Class getTypeClass() } @Override - public Domain createDomain(GWTDomain domain, IssuesDomainKindProperties arguments, Container container, User user, @Nullable TemplateInfo templateInfo) + public Domain createDomain(GWTDomain domain, IssuesDomainKindProperties arguments, Container container, User user, @Nullable TemplateInfo templateInfo, boolean forUpdate) { int issueDefId; try (DbScope.Transaction transaction = ExperimentService.get().getSchema().getScope().ensureTransaction(_lock)) @@ -318,7 +318,7 @@ public Domain createDomain(GWTDomain domain, IssuesDomainKindProperties argument List properties = (List)domain.getFields(); List indices = (List)domain.getIndices(); - Domain newDomain = IssuesListDefService.get().getDomainFromIssueDefId(issueDefId, container, user); + Domain newDomain = IssuesListDefService.get().getDomainFromIssueDefId(issueDefId, container, user, true); if (newDomain != null) { Set reservedNames = getReservedPropertyNames(newDomain, user); @@ -353,7 +353,7 @@ public Domain createDomain(GWTDomain domain, IssuesDomainKindProperties argument { throw new RuntimeException(e); } - return IssuesListDefService.get().getDomainFromIssueDefId(issueDefId, container, user); + return IssuesListDefService.get().getDomainFromIssueDefId(issueDefId, container, user, forUpdate); } public static void setDefaultValues(Domain domain, Collection requiredProps) diff --git a/api/src/org/labkey/api/issues/IssuesListDefService.java b/api/src/org/labkey/api/issues/IssuesListDefService.java index 1cb66dac38a..d3f420d0cfc 100644 --- a/api/src/org/labkey/api/issues/IssuesListDefService.java +++ b/api/src/org/labkey/api/issues/IssuesListDefService.java @@ -107,21 +107,25 @@ ValidationException updateIssueDefinition(Container container, User user, GWTDom /** * Get the Domain for a specific issue list definition based on the issue list definition name. + * * @param issueDefName the name of the issue list definition to look for - * @param container the container to look in - * @param user the user who made the request + * @param container the container to look in + * @param user the user who made the request + * @param forUpdate whether the domain returned should be mutable or not * @return Domain */ - Domain getDomainFromIssueDefName(String issueDefName, Container container, User user); + Domain getDomainFromIssueDefName(String issueDefName, Container container, User user, boolean forUpdate); /** * Get the Domain for a specific issue list definition based on the issue list definition id. + * * @param issueDefId the issue definition row id - * @param container the container to look in - * @param user the user who made the request + * @param container the container to look in + * @param user the user who made the request + * @param forUpdate whether the domain returned should be mutable or not * @return Domain */ - Domain getDomainFromIssueDefId(int issueDefId, Container container, User user); + Domain getDomainFromIssueDefId(int issueDefId, Container container, User user, boolean forUpdate); /** * Register a provider that will add text links to the issue details header link display. diff --git a/api/src/org/labkey/api/query/ExtendedTableDomainKind.java b/api/src/org/labkey/api/query/ExtendedTableDomainKind.java index 5b91f78925c..902d22a06c7 100644 --- a/api/src/org/labkey/api/query/ExtendedTableDomainKind.java +++ b/api/src/org/labkey/api/query/ExtendedTableDomainKind.java @@ -69,7 +69,7 @@ public Domain updateDomain(Container container, User user, GWTDomain gwtDomain, JSONObject arguments, Container container, User user, TemplateInfo templateInfo) + public Domain createDomain(GWTDomain gwtDomain, JSONObject arguments, Container container, User user, TemplateInfo templateInfo, boolean forUpdate) { if (StringUtils.trimToNull(gwtDomain.getName()) == null) throw new IllegalArgumentException("table name is required"); @@ -113,7 +113,7 @@ public Domain createDomain(GWTDomain gwtDomain, JSONObjec throw new RuntimeException(e); } } - return PropertyService.get().getDomain(container, domainURI); + return PropertyService.get().getDomain(container, domainURI, forUpdate); } @Override diff --git a/api/src/org/labkey/api/query/SimpleTableDomainKind.java b/api/src/org/labkey/api/query/SimpleTableDomainKind.java index 82512ad8680..acd7363beb8 100644 --- a/api/src/org/labkey/api/query/SimpleTableDomainKind.java +++ b/api/src/org/labkey/api/query/SimpleTableDomainKind.java @@ -262,7 +262,7 @@ public String apply(ColumnInfo col) } @Override - public Domain createDomain(GWTDomain domain, JSONObject arguments, Container container, User user, TemplateInfo templateInfo) + public Domain createDomain(GWTDomain domain, JSONObject arguments, Container container, User user, TemplateInfo templateInfo, boolean forUpdate) { String schemaName = (String)arguments.get("schemaName"); String tableName = (String)arguments.get("tableName"); diff --git a/api/src/org/labkey/api/query/SimpleUserSchema.java b/api/src/org/labkey/api/query/SimpleUserSchema.java index 9dc3023953b..69d4b9ea265 100644 --- a/api/src/org/labkey/api/query/SimpleUserSchema.java +++ b/api/src/org/labkey/api/query/SimpleUserSchema.java @@ -457,15 +457,21 @@ protected Container getDomainContainer() @Override public Domain getDomain() + { + return getDomain(false); + } + + @Override + public Domain getDomain(boolean forUpdate) { if (_objectUriCol == null) return null; - if (_domain == null) - { - String domainURI = getDomainURI(); - _domain = PropertyService.get().getDomain(getDomainContainer(), domainURI); - } + if (_domain == null || (forUpdate && !_domain.isMutable())) + _domain = PropertyService.get().getDomain(getDomainContainer(), getDomainURI(), forUpdate); + + if (_domain != null && !forUpdate && _domain.isMutable()) + _domain = PropertyService.get().getDomain(getDomainContainer(), getDomainURI(), forUpdate); return _domain; } diff --git a/api/src/org/labkey/api/reports/model/ReportPropsManager.java b/api/src/org/labkey/api/reports/model/ReportPropsManager.java index cd535d89f19..5320a2b9a74 100644 --- a/api/src/org/labkey/api/reports/model/ReportPropsManager.java +++ b/api/src/org/labkey/api/reports/model/ReportPropsManager.java @@ -75,7 +75,7 @@ public static ReportPropsManager get() public List getProperties(Container container) { List properties = new ArrayList<>(); - Domain domain = getDomain(container); + Domain domain = getDomain(container, false); if (domain != null) { @@ -91,7 +91,7 @@ public void createProperty(Container container, User user, String name, String l private Map getPropertyMap(Container container) { - Domain domain = getDomain(container); + Domain domain = getDomain(container, false); Map propsMap = new HashMap<>(); if (domain != null) @@ -104,7 +104,7 @@ private Map getPropertyMap(Container container) public synchronized DomainProperty ensureProperty(Container container, User user, String name, String label, PropertyType type) throws ChangePropertyDescriptorException { - Domain domain = getDomain(container); + Domain domain = getDomain(container, false); if (domain != null) { boolean dirty = false; @@ -114,7 +114,8 @@ public synchronized DomainProperty ensureProperty(Container container, User user if (dp == null) { dirty = true; - + // Get a mutable version of the domain. + domain = getDomain(container, true); DomainProperty prop = domain.addProperty(); prop.setName(name); prop.setLabel(label); @@ -184,13 +185,13 @@ public Object getPropertyValue(String entityId, Container container, String prop } @Nullable - private Domain getDomain(Container container) + private Domain getDomain(Container container, boolean forUpdate) { String uri = getDomainURI(container); try (var ignore = SpringActionController.ignoreSqlUpdates()) { DomainDescriptor dd = OntologyManager.ensureDomainDescriptor(uri, PROPERTIES_DOMAIN, container); - return PropertyService.get().getDomain(dd.getDomainId()); + return PropertyService.get().getDomain(dd.getDomainId(), forUpdate); } catch (RuntimeSQLException e) { diff --git a/api/src/org/labkey/api/study/Dataset.java b/api/src/org/labkey/api/study/Dataset.java index cd13190e51d..4ebb7fa75f8 100644 --- a/api/src/org/labkey/api/study/Dataset.java +++ b/api/src/org/labkey/api/study/Dataset.java @@ -244,6 +244,8 @@ public String getRecallFromStudyAuditMessage(String label, int recordCount) */ @Nullable Domain getDomain(); + @Nullable + Domain getDomain(boolean forUpdate); boolean isShared(); String getName(); diff --git a/assay/api-src/org/labkey/api/assay/AssayDomainKind.java b/assay/api-src/org/labkey/api/assay/AssayDomainKind.java index d4fc6e37e68..111079ea908 100644 --- a/assay/api-src/org/labkey/api/assay/AssayDomainKind.java +++ b/assay/api-src/org/labkey/api/assay/AssayDomainKind.java @@ -201,13 +201,13 @@ public boolean canCreateDefinition(User user, Container container) } @Override - public Domain createDomain(GWTDomain domain, JSONObject arguments, Container container, User user, @Nullable TemplateInfo templateInfo) + public Domain createDomain(GWTDomain domain, JSONObject arguments, Container container, User user, @Nullable TemplateInfo templateInfo, boolean forUpdate) { DomainDescriptor dd = OntologyManager.ensureDomainDescriptor(domain.getDomainURI(), domain.getName(), container); dd = dd.edit().setDescription(domain.getDescription()).build(); OntologyManager.ensureDomainDescriptor(dd); - return PropertyService.get().getDomain(container, dd.getDomainURI()); + return PropertyService.get().getDomain(container, dd.getDomainURI(), forUpdate); } protected Set getAssayReservedPropertyNames() diff --git a/assay/api-src/org/labkey/api/assay/dilution/AbstractDilutionAssayProvider.java b/assay/api-src/org/labkey/api/assay/dilution/AbstractDilutionAssayProvider.java index 885cdee8c1a..6171b374f78 100644 --- a/assay/api-src/org/labkey/api/assay/dilution/AbstractDilutionAssayProvider.java +++ b/assay/api-src/org/labkey/api/assay/dilution/AbstractDilutionAssayProvider.java @@ -135,7 +135,7 @@ protected Map> getRequiredDomainProperties() } @Override - public Domain getResultsDomain(ExpProtocol protocol) + public Domain getResultsDomain(ExpProtocol protocol, boolean forUpdate) { return null; } diff --git a/assay/api-src/org/labkey/api/assay/plate/AbstractPlateBasedAssayProvider.java b/assay/api-src/org/labkey/api/assay/plate/AbstractPlateBasedAssayProvider.java index 25f7628965f..17d2bbc2a87 100644 --- a/assay/api-src/org/labkey/api/assay/plate/AbstractPlateBasedAssayProvider.java +++ b/assay/api-src/org/labkey/api/assay/plate/AbstractPlateBasedAssayProvider.java @@ -159,7 +159,7 @@ public File getSampleMetadataFile(Container container, int runId) @Override public Domain getSampleWellGroupDomain(ExpProtocol protocol) { - return getDomainByPrefix(protocol, ASSAY_DOMAIN_SAMPLE_WELLGROUP); + return getDomainByPrefix(protocol, ASSAY_DOMAIN_SAMPLE_WELLGROUP, false); } @Override diff --git a/assay/api-src/org/labkey/api/assay/plate/PlateService.java b/assay/api-src/org/labkey/api/assay/plate/PlateService.java index 4aa7401735d..711cddabf7c 100644 --- a/assay/api-src/org/labkey/api/assay/plate/PlateService.java +++ b/assay/api-src/org/labkey/api/assay/plate/PlateService.java @@ -252,7 +252,7 @@ static PlateService get() /** * Create the plate metadata domain for this container. */ - @NotNull Domain ensurePlateMetadataDomain(Container container, User user) throws ValidationException; + @NotNull Domain ensurePlateMetadataDomain(Container container, User user, boolean forUpdate) throws ValidationException; /** * Name expressions for plate sets and plates. diff --git a/assay/src/org/labkey/assay/AssayDomainServiceImpl.java b/assay/src/org/labkey/assay/AssayDomainServiceImpl.java index 64eb534d760..eb044936770 100644 --- a/assay/src/org/labkey/assay/AssayDomainServiceImpl.java +++ b/assay/src/org/labkey/assay/AssayDomainServiceImpl.java @@ -450,7 +450,7 @@ public GWTProtocol saveChanges(GWTProtocol assay, boolean replaceIfExisting) thr GWTDomain gwtDomain = DomainUtil.getDomainDescriptor(getUser(), domain.getDomainURI(), getContainer(), true); if (gwtDomain == null) { - Domain newDomain = DomainUtil.createDomain(PropertyService.get().getDomainKind(domain.getDomainURI()).getKindName(), domain, null, getContainer(), getUser(), domain.getName(), null); + Domain newDomain = DomainUtil.createDomain(PropertyService.get().getDomainKind(domain.getDomainURI()).getKindName(), domain, null, getContainer(), getUser(), domain.getName(), null, false); domainURIs.add(newDomain.getTypeURI()); } else diff --git a/assay/src/org/labkey/assay/AssayUpgradeCode.java b/assay/src/org/labkey/assay/AssayUpgradeCode.java index 0b7ec76ed08..093e9c822d2 100644 --- a/assay/src/org/labkey/assay/AssayUpgradeCode.java +++ b/assay/src/org/labkey/assay/AssayUpgradeCode.java @@ -272,7 +272,7 @@ public static void deletePlateVocabDomains(ModuleContext ctx) throws Exception { // ensure the plate metadata domain for the top level biologics projects if (container.isProject()) - PlateManager.get().ensurePlateMetadataDomain(container, User.getAdminServiceUser()); + PlateManager.get().ensurePlateMetadataDomain(container, User.getAdminServiceUser(), false); biologicsFolders.add(container); } } @@ -535,7 +535,7 @@ public static void renameWellMetadataFields(ModuleContext ctx) throws Exception DbScope scope = AssayDbSchema.getInstance().getSchema().getScope(); for (Container container : metadataContainers) { - Domain domain = PlateManager.get().getPlateMetadataDomain(container, User.getAdminServiceUser()); + Domain domain = PlateManager.get().getPlateMetadataDomain(container, User.getAdminServiceUser(), true); if (domain != null) { try (DbScope.Transaction tx = scope.ensureTransaction()) @@ -751,7 +751,7 @@ public static void initializeWellExclusions(ModuleContext ctx) throws Exception if (provider.isPlateMetadataEnabled(protocol)) { // ensure the QC state column exists in the result domain - Domain resultDomain = provider.getResultsDomain(protocol); + Domain resultDomain = provider.getResultsDomain(protocol, true); if (resultDomain.getPropertyByName(AssayResultDomainKind.Column.State.name()) == null) { _log.info(String.format("Adding the %s field to the results domain for assay : %s", AssayResultDomainKind.Column.State.name(), protocol.getName())); @@ -796,7 +796,7 @@ public static void initializeHitSelectionCriteria(ModuleContext ctx) throws Exce if (provider != null && provider.isPlateMetadataEnabled(protocol)) { // ensure the QC state column exists in the result domain - Domain runDomain = provider.getRunDomain(protocol); + Domain runDomain = provider.getRunDomain(protocol, true); if (runDomain != null && runDomain.getPropertyByName(HIT_SELECTION_CRITERIA_COLUMN_NAME) == null) { _log.info("Adding the \"{}\" field to the run domain for assay : {}", HIT_SELECTION_CRITERIA_COLUMN_NAME, protocol.getName()); diff --git a/assay/src/org/labkey/assay/PlateBasedAssaySampleTypeDomainKind.java b/assay/src/org/labkey/assay/PlateBasedAssaySampleTypeDomainKind.java index 74b22a0b62f..95e1f781a9b 100644 --- a/assay/src/org/labkey/assay/PlateBasedAssaySampleTypeDomainKind.java +++ b/assay/src/org/labkey/assay/PlateBasedAssaySampleTypeDomainKind.java @@ -149,10 +149,10 @@ public boolean allowCalculatedFields() } @Override - public Domain createDomain(GWTDomain domain, SampleTypeDomainKindProperties arguments, Container container, User user, @Nullable TemplateInfo templateInfo) + public Domain createDomain(GWTDomain domain, SampleTypeDomainKindProperties arguments, Container container, User user, @Nullable TemplateInfo templateInfo, boolean forUpdate) { JSONObject args = arguments != null ? arguments.toJSONObject() : null; - return _assayDelegate.createDomain(domain, args, container, user, templateInfo); + return _assayDelegate.createDomain(domain, args, container, user, templateInfo, forUpdate); } @NotNull diff --git a/assay/src/org/labkey/assay/PlateController.java b/assay/src/org/labkey/assay/PlateController.java index c5da7fb4ced..333c17dc567 100644 --- a/assay/src/org/labkey/assay/PlateController.java +++ b/assay/src/org/labkey/assay/PlateController.java @@ -904,7 +904,7 @@ public static class EnsurePlateMetadataDomainAction extends MutatingApiAction createPlateMetadataFields(Container container, User user, List fields) throws Exception { - Domain metadataDomain = ensurePlateMetadataDomain(container, user); + Domain metadataDomain = ensurePlateMetadataDomain(container, user, true); DomainKind domainKind = metadataDomain.getDomainKind(); if (!domainKind.canEditDefinition(user, metadataDomain)) @@ -2338,7 +2345,7 @@ public Container getPlateMetadataDomainContainer(Container container) public @NotNull List deletePlateMetadataFields(Container container, User user, List fields) throws Exception { - Domain metadataDomain = getPlateMetadataDomain(container, user); + Domain metadataDomain = getPlateMetadataDomain(container, user, true); if (metadataDomain == null) throw new IllegalArgumentException("Unable to remove fields from the domain, the domain was not found."); diff --git a/assay/src/org/labkey/assay/plate/PlateMetadataDomainKind.java b/assay/src/org/labkey/assay/plate/PlateMetadataDomainKind.java index a3e461423a2..956491f2882 100644 --- a/assay/src/org/labkey/assay/plate/PlateMetadataDomainKind.java +++ b/assay/src/org/labkey/assay/plate/PlateMetadataDomainKind.java @@ -122,7 +122,7 @@ public Set getMandatoryPropertyNames(Domain domain) } @Override - public Domain createDomain(GWTDomain domain, JSONObject arguments, Container container, User user, @Nullable TemplateInfo templateInfo) + public Domain createDomain(GWTDomain domain, JSONObject arguments, Container container, User user, @Nullable TemplateInfo templateInfo, boolean forUpdate) { try { @@ -131,7 +131,7 @@ public Domain createDomain(GWTDomain domain, JSONObject a ensureDomainProperties(metadataDomain, container); metadataDomain.save(user); - return PropertyService.get().getDomain(container, domainURI); + return PropertyService.get().getDomain(container, domainURI, forUpdate); } catch (Exception e) { diff --git a/assay/src/org/labkey/assay/plate/query/WellTable.java b/assay/src/org/labkey/assay/plate/query/WellTable.java index b081d68fea1..1f6f828b898 100644 --- a/assay/src/org/labkey/assay/plate/query/WellTable.java +++ b/assay/src/org/labkey/assay/plate/query/WellTable.java @@ -355,7 +355,7 @@ public QueryUpdateService getUpdateService() { try { - domain = PlateManager.get().ensurePlateMetadataDomain(container, user); + domain = PlateManager.get().ensurePlateMetadataDomain(container, user, false); } catch (ValidationException e) { diff --git a/core/src/org/labkey/core/query/UsersDomainKind.java b/core/src/org/labkey/core/query/UsersDomainKind.java index 4f6664a46f2..b5fcceed910 100644 --- a/core/src/org/labkey/core/query/UsersDomainKind.java +++ b/core/src/org/labkey/core/query/UsersDomainKind.java @@ -148,9 +148,9 @@ public ActionURL urlShowData(Domain domain, ContainerUser containerUser) } @Override - public Domain createDomain(GWTDomain domain, JSONObject arguments, Container container, User user, TemplateInfo templateInfo) + public Domain createDomain(GWTDomain domain, JSONObject arguments, Container container, User user, TemplateInfo templateInfo, boolean forUpdate) { - return super.createDomain(domain, arguments, getDomainContainer(), user, templateInfo); + return super.createDomain(domain, arguments, getDomainContainer(), user, templateInfo, forUpdate); } @Override @@ -206,7 +206,7 @@ public static void ensureDomain(ModuleContext context) user = UserManager.getGuestUser(); String domainURI = UsersDomainKind.getDomainURI("core", CoreQuerySchema.USERS_TABLE_NAME, UsersDomainKind.getDomainContainer(), user); - Domain domain = PropertyService.get().getDomain(UsersDomainKind.getDomainContainer(), domainURI); + Domain domain = PropertyService.get().getDomain(UsersDomainKind.getDomainContainer(), domainURI, true); if (domain == null) { diff --git a/core/src/org/labkey/core/query/UsersTable.java b/core/src/org/labkey/core/query/UsersTable.java index 9f2d0095dfa..29056240011 100644 --- a/core/src/org/labkey/core/query/UsersTable.java +++ b/core/src/org/labkey/core/query/UsersTable.java @@ -321,6 +321,12 @@ else if (getColumn(propColumn.getName()) == null) @Override public Domain getDomain() + { + return getDomain(false); + } + + @Override + public Domain getDomain(boolean forUpdate) { if (getObjectUriColumn() == null) return null; @@ -328,7 +334,7 @@ public Domain getDomain() if (_domain == null) { String domainURI = getDomainURI(); - _domain = PropertyService.get().getDomain(UsersDomainKind.getDomainContainer(), domainURI); + _domain = PropertyService.get().getDomain(UsersDomainKind.getDomainContainer(), domainURI, forUpdate); } return _domain; diff --git a/experiment/src/org/labkey/experiment/ExperimentModule.java b/experiment/src/org/labkey/experiment/ExperimentModule.java index 143dbefe190..58ba260dca8 100644 --- a/experiment/src/org/labkey/experiment/ExperimentModule.java +++ b/experiment/src/org/labkey/experiment/ExperimentModule.java @@ -112,6 +112,7 @@ import org.labkey.experiment.api.ExperimentServiceImpl; import org.labkey.experiment.api.ExperimentStressTest; import org.labkey.experiment.api.GraphAlgorithms; +import org.labkey.experiment.api.property.DomainImpl; import org.labkey.experiment.api.property.StorageNameGenerator; import org.labkey.experiment.lineage.LineagePerfTest; import org.labkey.experiment.api.LineageTest; @@ -947,6 +948,7 @@ public Collection getSummary(Container c) public Set getIntegrationTests() { return Set.of( + DomainImpl.TestCase.class, DomainPropertyImpl.TestCase.class, ExpDataTableImpl.TestCase.class, ExperimentServiceImpl.LineageQueryTestCase.class, diff --git a/experiment/src/org/labkey/experiment/api/DataClassDomainKind.java b/experiment/src/org/labkey/experiment/api/DataClassDomainKind.java index 05e50c23ba0..44c360392ea 100644 --- a/experiment/src/org/labkey/experiment/api/DataClassDomainKind.java +++ b/experiment/src/org/labkey/experiment/api/DataClassDomainKind.java @@ -369,7 +369,7 @@ public void validateOptions(Container container, User user, DataClassDomainKindP } @Override - public Domain createDomain(GWTDomain domain, DataClassDomainKindProperties options, Container container, User user, @Nullable TemplateInfo templateInfo) + public Domain createDomain(GWTDomain domain, DataClassDomainKindProperties options, Container container, User user, @Nullable TemplateInfo templateInfo, boolean forUpdate) { // Issue 45042: Allow for the dataClass description to be set via the create domain API calls if (options.getDescription() == null && domain.getDescription() != null) @@ -387,7 +387,7 @@ public Domain createDomain(GWTDomain domain, DataClassDom templateInfo, domain.getDisabledSystemFields() ); - return dataClass.getDomain(); + return dataClass.getDomain(forUpdate); } catch (ExperimentException e) { diff --git a/experiment/src/org/labkey/experiment/api/ExpDataClassDataTableImpl.java b/experiment/src/org/labkey/experiment/api/ExpDataClassDataTableImpl.java index 2f1fb18e91d..5b0e900da1b 100644 --- a/experiment/src/org/labkey/experiment/api/ExpDataClassDataTableImpl.java +++ b/experiment/src/org/labkey/experiment/api/ExpDataClassDataTableImpl.java @@ -189,7 +189,13 @@ public ExpDataClassDataTableImpl(String name, UserSchema schema, ContainerFilter @Override public Domain getDomain() { - return _dataClass.getDomain(); + return getDomain(false); + } + + @Override + public Domain getDomain(boolean forUpdate) + { + return _dataClass.getDomain(forUpdate); } @Override diff --git a/experiment/src/org/labkey/experiment/api/ExpDataClassDataTestCase.jsp b/experiment/src/org/labkey/experiment/api/ExpDataClassDataTestCase.jsp index 63cef5ef436..b30585d01c2 100644 --- a/experiment/src/org/labkey/experiment/api/ExpDataClassDataTestCase.jsp +++ b/experiment/src/org/labkey/experiment/api/ExpDataClassDataTestCase.jsp @@ -988,7 +988,7 @@ public void testViewSupportForVocabularyDomains() throws Exception domain.setDescription(domainDescription); domain.setFields(List.of(prop1)); - Domain lookUpDomain = DomainUtil.createDomain("Vocabulary", domain, null, c, user, domainName, null); + Domain lookUpDomain = DomainUtil.createDomain("Vocabulary", domain, null, c, user, domainName, null, false); Map vocabularyPropertyURIs = helper.getVocabularyPropertyURIS(lookUpDomain); final String locationPropertyURI = vocabularyPropertyURIs.get(locationPropertyName); diff --git a/experiment/src/org/labkey/experiment/api/ExpDataClassImpl.java b/experiment/src/org/labkey/experiment/api/ExpDataClassImpl.java index abea8c260a8..098a65a5259 100644 --- a/experiment/src/org/labkey/experiment/api/ExpDataClassImpl.java +++ b/experiment/src/org/labkey/experiment/api/ExpDataClassImpl.java @@ -230,19 +230,37 @@ public long getDataCount(Container c, @Nullable ContainerFilter cf) } @Override + @NotNull public Domain getDomain() { - if (_domain == null) - { - _domain = PropertyService.get().getDomain(getContainer(), getLSID()); - } - return _domain; + return getDomain(false); } @Override - public void setDomain(Domain d) + @NotNull + public Domain getDomain(boolean forUpdate) { - _domain = d; + if (_domain == null || (forUpdate && !_domain.isMutable())) + { + _domain = PropertyService.get().getDomain(getContainer(), getLSID(), forUpdate); + if (_domain == null) + { + _domain = PropertyService.get().createDomain(getContainer(), getLSID(), getName()); + try + { + _domain.save(null); + } + catch (ChangePropertyDescriptorException e) + { + throw UnexpectedException.wrap(e); + } + } + if (_domain != null && !forUpdate && _domain.isMutable()) + _domain = PropertyService.get().getDomain(getContainer(), getLSID(), false); + if (_domain == null) // this should really never be true + throw new IllegalStateException("Domain does not exist."); + } + return _domain; } @Nullable diff --git a/experiment/src/org/labkey/experiment/api/ExpMaterialTableImpl.java b/experiment/src/org/labkey/experiment/api/ExpMaterialTableImpl.java index cade33d0b57..bffd6ea89f5 100644 --- a/experiment/src/org/labkey/experiment/api/ExpMaterialTableImpl.java +++ b/experiment/src/org/labkey/experiment/api/ExpMaterialTableImpl.java @@ -866,9 +866,16 @@ private ContainerFilter getSampleStatusLookupContainerFilter() @Override public Domain getDomain() { - return _ss == null ? null : _ss.getDomain(); + return getDomain(false); } + @Override + public Domain getDomain(boolean forUpdate) + { + return _ss == null ? null : _ss.getDomain(forUpdate); + } + + public static String appendNameExpressionDescription(String currentDescription, String nameExpression, String nameExpressionPreview) { if (nameExpression == null) diff --git a/experiment/src/org/labkey/experiment/api/ExpProvisionedTableTestHelper.java b/experiment/src/org/labkey/experiment/api/ExpProvisionedTableTestHelper.java index 699faf27bd2..f1a4fbc64d6 100644 --- a/experiment/src/org/labkey/experiment/api/ExpProvisionedTableTestHelper.java +++ b/experiment/src/org/labkey/experiment/api/ExpProvisionedTableTestHelper.java @@ -60,7 +60,7 @@ public Domain createVocabularyTestDomain(User user, Container c) throws Validati domain.setDescription(domainDescription); domain.setFields(List.of(prop1, prop2, prop3)); - return DomainUtil.createDomain("Vocabulary", domain, null, c, user, domainName, null); + return DomainUtil.createDomain("Vocabulary", domain, null, c, user, domainName, null, false); } public Map getVocabularyPropertyURIS(Domain domain) diff --git a/experiment/src/org/labkey/experiment/api/ExpSampleTypeImpl.java b/experiment/src/org/labkey/experiment/api/ExpSampleTypeImpl.java index f3f31fc9205..10222d086bb 100644 --- a/experiment/src/org/labkey/experiment/api/ExpSampleTypeImpl.java +++ b/experiment/src/org/labkey/experiment/api/ExpSampleTypeImpl.java @@ -805,9 +805,16 @@ public ExpMaterial getEffectiveSample(Container c, String name, Date effectiveDa @NotNull public Domain getDomain() { - if (_domain == null) + return getDomain(false); + } + + @Override + @NotNull + public Domain getDomain(boolean forUpdate) + { + if (_domain == null || (forUpdate && !_domain.isMutable())) { - _domain = PropertyService.get().getDomain(getContainer(), getLSID()); + _domain = PropertyService.get().getDomain(getContainer(), getLSID(), forUpdate); if (_domain == null) { _domain = PropertyService.get().createDomain(getContainer(), getLSID(), getName()); diff --git a/experiment/src/org/labkey/experiment/api/VocabularyDomainKind.java b/experiment/src/org/labkey/experiment/api/VocabularyDomainKind.java index a1654378403..ed94f605992 100644 --- a/experiment/src/org/labkey/experiment/api/VocabularyDomainKind.java +++ b/experiment/src/org/labkey/experiment/api/VocabularyDomainKind.java @@ -144,7 +144,7 @@ public String generateDomainURI(String vocabularyName, Container container) } @Override - public Domain createDomain(GWTDomain domain, JSONObject arguments, Container container, User user, @Nullable TemplateInfo templateInfo) + public Domain createDomain(GWTDomain domain, JSONObject arguments, Container container, User user, @Nullable TemplateInfo templateInfo, boolean forUpdate) { String name = StringUtils.trimToNull(domain.getName()); if (name == null) @@ -174,6 +174,6 @@ public Domain createDomain(GWTDomain domain, JSONObject arguments, Container con throw new RuntimeException(e); } - return PropertyService.get().getDomain(container, domainURI); + return PropertyService.get().getDomain(container, domainURI, forUpdate); } } diff --git a/experiment/src/org/labkey/experiment/api/property/DomainImpl.java b/experiment/src/org/labkey/experiment/api/property/DomainImpl.java index 3d264d3a62b..58362a56c14 100644 --- a/experiment/src/org/labkey/experiment/api/property/DomainImpl.java +++ b/experiment/src/org/labkey/experiment/api/property/DomainImpl.java @@ -25,6 +25,8 @@ import org.json.JSONArray; import org.json.JSONObject; import org.labkey.api.audit.AbstractAuditTypeProvider; +import org.junit.Assert; +import org.junit.Test; import org.labkey.api.audit.AuditLogService; import org.labkey.api.audit.AuditTypeEvent; import org.labkey.api.collections.CaseInsensitiveHashMap; @@ -71,11 +73,14 @@ import org.labkey.api.security.User; import org.labkey.api.security.permissions.Permission; import org.labkey.api.settings.AppProps; +import org.labkey.api.test.TestWhen; import org.labkey.api.util.DateUtil; import org.labkey.api.util.GUID; import org.labkey.api.util.JdbcUtil; +import org.labkey.api.util.JunitUtil; import org.labkey.api.util.Pair; import org.labkey.api.util.StringExpressionFactory; +import org.labkey.api.util.TestContext; import org.labkey.api.view.ActionURL; import org.labkey.api.writer.ContainerUser; @@ -107,14 +112,21 @@ public class DomainImpl implements Domain private Set _propertyForeignKeys = Collections.emptySet(); private Set _propertyIndices = Collections.emptySet(); private boolean _shouldDeleteAllData = false; + private final boolean _isMutable; // NOTE we could put responsibility for generating column names on the StorageProvisioner // But then we'd have the situation of StorageProvisioner knowing about/updating Domains, which seems fraught transient StorageNameGenerator _storageNameGenerator = null; public DomainImpl(DomainDescriptor dd) + { + this(dd, false); + } + + public DomainImpl(DomainDescriptor dd, boolean isMutable) { _dd = dd; + _isMutable = isMutable; List allFormats = DomainPropertyManager.get().getConditionalFormats(getContainer()); List pds = OntologyManager.getPropertiesForType(getTypeURI(), getContainer()); @@ -137,14 +149,15 @@ public DomainImpl(DomainDescriptor dd) } } - public DomainImpl(Container container, String uri, String name) + public DomainImpl(Container container, String uri, String name, boolean isMutable) { - this(container, uri, name, null); + this(container, uri, name, null, isMutable); } - public DomainImpl(Container container, String uri, String name, @Nullable TemplateInfo templateInfo) + public DomainImpl(Container container, String uri, String name, @Nullable TemplateInfo templateInfo, boolean isMutable) { _new = true; + _isMutable = isMutable; _dd = new DomainDescriptor.Builder(uri, container) .setName(name) .setTemplateInfoObject(templateInfo) @@ -207,6 +220,11 @@ public String getLabel(Container container) return ret; } + @Override + public boolean isMutable() + { + return _isMutable; + } @Override @Nullable // null if not provisioned @@ -548,6 +566,9 @@ public void save(User user, boolean allowAddBaseProperty, boolean saveOnlyIfNotE @Nullable Map oldRecordMap, @Nullable Map newRecordMap, @Nullable List oldCalculatedFields, @Nullable List newCalculatedFields) throws ChangePropertyDescriptorException { + if (!_isMutable) + throw new ChangePropertyDescriptorException("Cannot save a domain that is immutable"); + ExperimentService exp = ExperimentService.get(); // NOTE: the synchronization here does not remove the need to add better synchronization in StorageProvisioner, but it helps @@ -1517,4 +1538,26 @@ public DomainTemplate getTemplate() { return DomainTemplate.findTemplate(getTemplateInfo(), getDomainKind().getKindName()); } + + @TestWhen(TestWhen.When.BVT) + public static class TestCase extends Assert + { + @Test + public void saveAfterGetReadOnlyDomain() + { + Container c = JunitUtil.getTestContainer(); + String uri = "StorageProvisionImpl/" + GUID.makeGUID(); + DomainImpl d = new DomainImpl(c, uri, "test", false); + User user = TestContext.get().getUser(); + try + { + d.save(user); + fail("Save of read-only domain should fail."); + } + catch (ChangePropertyDescriptorException e) + { + // expected exception + } + } + } } diff --git a/experiment/src/org/labkey/experiment/api/property/PropertyServiceImpl.java b/experiment/src/org/labkey/experiment/api/property/PropertyServiceImpl.java index 61c1e5b76f7..434faa91fe6 100644 --- a/experiment/src/org/labkey/experiment/api/property/PropertyServiceImpl.java +++ b/experiment/src/org/labkey/experiment/api/property/PropertyServiceImpl.java @@ -128,34 +128,48 @@ public IPropertyType getType(Container container, String typeURI) @Nullable public DomainImpl getDomain(Container container, String domainURI) { - DomainDescriptor dd = OntologyManager.getDomainDescriptor(domainURI, container); + return getDomain(container, domainURI, false); + } + + @Override + @Nullable + public DomainImpl getDomain(Container container, String domainURI, boolean forUpdate) + { + DomainDescriptor dd = OntologyManager.getDomainDescriptor(domainURI, container, forUpdate); if (dd == null) return null; - return new DomainImpl(dd); + return new DomainImpl(dd, forUpdate); } @Override @Nullable public Domain getDomain(int domainId) { - DomainDescriptor dd = OntologyManager.getDomainDescriptor(domainId); + return getDomain(domainId, false); + } + + @Override + @Nullable + public Domain getDomain(int domainId, boolean forUpdate) + { + DomainDescriptor dd = OntologyManager.getDomainDescriptor(domainId, forUpdate); if (dd == null) return null; - return new DomainImpl(dd); + return new DomainImpl(dd, forUpdate); } @Override @NotNull public Domain createDomain(Container container, String typeURI, String name) { - return new DomainImpl(container, typeURI, name); + return new DomainImpl(container, typeURI, name, true); } @Override @NotNull public Domain createDomain(Container container, String typeURI, String name, @Nullable TemplateInfo templateInfo) { - return new DomainImpl(container, typeURI, name, templateInfo); + return new DomainImpl(container, typeURI, name, templateInfo, true); } @Override @@ -176,7 +190,7 @@ public Domain createDomain(Container container, String typeURI, String name, @Nu } // return created domain, will only be null in some sort of race condition - domain = PropertyService.get().getDomain(domain.getTypeId()); + domain = PropertyService.get().getDomain(domain.getTypeId(), false); if (null == domain) throw new OptimisticConflictException("Domain deleted: " + domainURI, "", Table.ERROR_DELETED); return domain; diff --git a/experiment/src/org/labkey/experiment/api/property/StorageProvisionerImpl.java b/experiment/src/org/labkey/experiment/api/property/StorageProvisionerImpl.java index 71062667db2..77f5166736e 100644 --- a/experiment/src/org/labkey/experiment/api/property/StorageProvisionerImpl.java +++ b/experiment/src/org/labkey/experiment/api/property/StorageProvisionerImpl.java @@ -34,13 +34,13 @@ import org.labkey.api.data.ColumnInfo; import org.labkey.api.data.Container; import org.labkey.api.data.CoreSchema; +import org.labkey.api.data.DatabaseIdentifier; import org.labkey.api.data.DatabaseTableType; import org.labkey.api.data.DbSchema; import org.labkey.api.data.DbSchemaType; import org.labkey.api.data.DbScope; import org.labkey.api.data.DbScope.SchemaTableOptions; import org.labkey.api.data.DbScope.Transaction; -import org.labkey.api.data.DatabaseIdentifier; import org.labkey.api.data.JdbcType; import org.labkey.api.data.MVDisplayColumnFactory; import org.labkey.api.data.ParameterMapStatement; @@ -1575,7 +1575,7 @@ public void before() throws Exception domain = PropertyService.get().createDomain(container, lsid.toString(), domainName); domain.save(new User()); StorageProvisioner.createTableInfo(domain); - domain = PropertyService.get().getDomain(domain.getTypeId()); + domain = PropertyService.get().getDomain(domain.getTypeId(), true); } @After @@ -1619,7 +1619,7 @@ public void testRenameProperty() throws Exception propB.setName(newName); domain.save(new User()); - domain = Objects.requireNonNull(PropertyService.get().getDomain(domain.getTypeId())); + domain = Objects.requireNonNull(PropertyService.get().getDomain(domain.getTypeId(), true)); Assert.assertNull("renamed column is not present in old name", getJdbcColumnMetadata(domain, oldColumnName)); @@ -1649,7 +1649,7 @@ public void testEnableMv() throws Exception propB.setMvEnabled(true); domain.save(new User()); - domain = Objects.requireNonNull(PropertyService.get().getDomain(domain.getTypeId())); + domain = Objects.requireNonNull(PropertyService.get().getDomain(domain.getTypeId(), true)); ColumnInfo col = getJdbcColumnMetadata(domain, propBMvColumnName); Assert.assertNotNull("enabled mvindicator causes mvindicator column to be provisioned", col); @@ -1663,13 +1663,13 @@ public void testDisableMv() throws Exception propB.setMvEnabled(true); domain.save(new User()); - domain = Objects.requireNonNull(PropertyService.get().getDomain(domain.getTypeId())); + domain = Objects.requireNonNull(PropertyService.get().getDomain(domain.getTypeId(), true)); propB = domain.getPropertyByName(propNameB); propB.setMvEnabled(false); domain.save(new User()); - domain = Objects.requireNonNull(PropertyService.get().getDomain(domain.getTypeId())); + domain = Objects.requireNonNull(PropertyService.get().getDomain(domain.getTypeId(), true)); Assert.assertNull("property with disabled mvindicator has no mvindicator column", getJdbcColumnMetadata(domain, propBMvColumnName)); } @@ -1806,7 +1806,7 @@ public Set getReservedPropertyNames(Domain domain, User user) DomainImpl d = null; try { - d = new DomainImpl(c, uri, "test") + d = new DomainImpl(c, uri, "test", true) { @Override public DomainKind getDomainKind() @@ -1845,7 +1845,7 @@ private void addPropertyB() throws Exception dp.setName(propNameB); domain.save(new User()); - domain = PropertyService.get().getDomain(domain.getTypeId()); + domain = PropertyService.get().getDomain(domain.getTypeId(), true); } private @Nullable ColumnInfo getJdbcColumnMetadata(Domain domain, String columnName) throws Exception diff --git a/experiment/src/org/labkey/experiment/controllers/exp/ExperimentController.java b/experiment/src/org/labkey/experiment/controllers/exp/ExperimentController.java index 601e1a97e88..ecfa9b3bbaf 100644 --- a/experiment/src/org/labkey/experiment/controllers/exp/ExperimentController.java +++ b/experiment/src/org/labkey/experiment/controllers/exp/ExperimentController.java @@ -4080,7 +4080,7 @@ protected void deleteObjects(DeleteForm deleteForm) for (ExpSampleType source : sampleTypes) { Domain domain = source.getDomain(); - if (domain != null && !domain.getDomainKind().canDeleteDefinition(getUser(), domain)) + if (!domain.getDomainKind().canDeleteDefinition(getUser(), domain)) { throw new UnauthorizedException(); } diff --git a/experiment/src/org/labkey/experiment/controllers/property/PropertyController.java b/experiment/src/org/labkey/experiment/controllers/property/PropertyController.java index 45a7f993041..d887cd41626 100644 --- a/experiment/src/org/labkey/experiment/controllers/property/PropertyController.java +++ b/experiment/src/org/labkey/experiment/controllers/property/PropertyController.java @@ -317,7 +317,7 @@ public ApiResponse execute(DomainApiForm form, BindException errors) throws Exce } else if (kindName != null) { - domain = DomainUtil.createDomain(kindName, newDomain, form.getOptionsProperties(), getContainer(), getUser(), domainName, null); + domain = DomainUtil.createDomain(kindName, newDomain, form.getOptionsProperties(), getContainer(), getUser(), domainName, null, false); } else { @@ -2147,7 +2147,7 @@ Domain createVocabDomain() throws ValidationException gwtDomain.setFields(gwtProps); - return DomainUtil.createDomain(VocabularyDomainKind.KIND_NAME, gwtDomain, null, c, user, domainName, null); + return DomainUtil.createDomain(VocabularyDomainKind.KIND_NAME, gwtDomain, null, c, user, domainName, null, false); } @Test diff --git a/issues/src/org/labkey/issue/model/IssueListDef.java b/issues/src/org/labkey/issue/model/IssueListDef.java index c01636fa04b..270f8acff01 100644 --- a/issues/src/org/labkey/issue/model/IssueListDef.java +++ b/issues/src/org/labkey/issue/model/IssueListDef.java @@ -138,9 +138,14 @@ public Container getDomainContainer(User user) } public Domain getDomain(User user) + { + return getDomain(user, false); + } + + public Domain getDomain(User user, boolean forUpdate) { String uri = generateDomainURI(getDomainContainer(user), user, getName(), getKind()); - return PropertyService.get().getDomain(getDomainContainer(user), uri); + return PropertyService.get().getDomain(getDomainContainer(user), uri, forUpdate); } public AbstractIssuesListDefDomainKind getDomainKind() diff --git a/issues/src/org/labkey/issue/model/IssuesListDefServiceImpl.java b/issues/src/org/labkey/issue/model/IssuesListDefServiceImpl.java index f05abadefd5..739acf89d65 100644 --- a/issues/src/org/labkey/issue/model/IssuesListDefServiceImpl.java +++ b/issues/src/org/labkey/issue/model/IssuesListDefServiceImpl.java @@ -222,24 +222,24 @@ public IssuesListDefProvider getIssuesListDefProvider(String providerName) } @Override - public Domain getDomainFromIssueDefName(String issueDefName, Container container, User user) + public Domain getDomainFromIssueDefName(String issueDefName, Container container, User user, boolean forUpdate) { IssueListDef issueListDef = IssueManager.getIssueListDef(container, issueDefName); if (issueListDef != null) { - return issueListDef.getDomain(user); + return issueListDef.getDomain(user, forUpdate); } return null; } @Override - public Domain getDomainFromIssueDefId(int issueDefId, Container container, User user) + public Domain getDomainFromIssueDefId(int issueDefId, Container container, User user, boolean forUpdate) { IssueListDef issueListDef = IssueManager.getIssueListDef(container, issueDefId); if (issueListDef != null) { - Domain domain = issueListDef.getDomain(user); + Domain domain = issueListDef.getDomain(user, forUpdate); if (domain == null) LOG.warn("Unable to find the domain for issue list definition id: " + issueDefId + " and container: " + container); return domain; diff --git a/issues/src/org/labkey/issue/query/IssuesTable.java b/issues/src/org/labkey/issue/query/IssuesTable.java index 46f73b15083..3413f0fe689 100644 --- a/issues/src/org/labkey/issue/query/IssuesTable.java +++ b/issues/src/org/labkey/issue/query/IssuesTable.java @@ -411,7 +411,14 @@ public SQLFragment getFromSQL(String alias) @Override public Domain getDomain() { - return _issueDef.getDomain(getUserSchema().getUser()); + return getDomain(false); + } + + @Nullable + @Override + public Domain getDomain(boolean forUpdate) + { + return _issueDef.getDomain(getUserSchema().getUser(), forUpdate); } @Nullable diff --git a/list/src/org/labkey/list/model/ListDefinitionImpl.java b/list/src/org/labkey/list/model/ListDefinitionImpl.java index b89a48084e9..9ca3cef6a67 100644 --- a/list/src/org/labkey/list/model/ListDefinitionImpl.java +++ b/list/src/org/labkey/list/model/ListDefinitionImpl.java @@ -138,13 +138,19 @@ public Container getContainer() @Nullable public Domain getDomain() { - if (_domain == null) + return getDomain(false); + } + + @Override + @Nullable + public Domain getDomain(boolean forUpdate) + { + if (_domain == null || (forUpdate && !_domain.isMutable())) // assure we have a mutable domain if needed, but don't ditch a mutable one because it may not have been saved yet { - _domain = PropertyService.get().getDomain(_def.getDomainId()); + _domain = PropertyService.get().getDomain(_def.getDomainId(), forUpdate); } return _domain; } - @Override public String getName() { @@ -391,7 +397,7 @@ public void save(User user, boolean ensureKey, @Nullable Map new if (ensureKey) ensureKey(); - Domain domain = getDomain(); + Domain domain = getDomain(true); if (_new) { diff --git a/list/src/org/labkey/list/model/ListDomainKind.java b/list/src/org/labkey/list/model/ListDomainKind.java index 55e67e2ec2d..486ad5952d3 100644 --- a/list/src/org/labkey/list/model/ListDomainKind.java +++ b/list/src/org/labkey/list/model/ListDomainKind.java @@ -360,7 +360,7 @@ public Class getTypeClass() } @Override - public Domain createDomain(GWTDomain domain, ListDomainKindProperties listProperties, Container container, User user, @Nullable TemplateInfo templateInfo) + public Domain createDomain(GWTDomain domain, ListDomainKindProperties listProperties, Container container, User user, @Nullable TemplateInfo templateInfo, boolean forUpdate) { String name = StringUtils.trimToEmpty(domain.getName()); String keyName = listProperties.getKeyName(); @@ -411,7 +411,7 @@ public Domain createDomain(GWTDomain domain, ListDomainKindProperties listProper try (DbScope.Transaction tx = ExperimentService.get().ensureTransaction()) { - Domain d = list.getDomain(); + Domain d = list.getDomain(forUpdate); Set reservedNames = getReservedPropertyNames(d, user); Set lowerReservedNames = reservedNames.stream().map(String::toLowerCase).collect(Collectors.toSet()); @@ -450,7 +450,7 @@ public Domain createDomain(GWTDomain domain, ListDomainKindProperties listProper throw new RuntimeException(e); } - return list.getDomain(); + return list.getDomain(forUpdate); } @Override diff --git a/list/src/org/labkey/list/model/ListImporter.java b/list/src/org/labkey/list/model/ListImporter.java index a1da940aaa5..68759f56608 100644 --- a/list/src/org/labkey/list/model/ListImporter.java +++ b/list/src/org/labkey/list/model/ListImporter.java @@ -639,7 +639,7 @@ public ValidatorImporter(int typeId, List properties, public void process() throws Exception { boolean hasValidator = false; - Domain domain = PropertyService.get().getDomain(_typeId); + Domain domain = PropertyService.get().getDomain(_typeId, true); if (null != domain) { @@ -673,7 +673,7 @@ private boolean resolveDomainChanges(Container c, User user, DataLoader loader, if (allowUpdates) { - Domain domain = listDef.getDomain(); + Domain domain = listDef.getDomain(true); boolean isDirty = false; if (domain != null) { diff --git a/list/src/org/labkey/list/model/ListTable.java b/list/src/org/labkey/list/model/ListTable.java index 46ea997d22c..cf1a514f86b 100644 --- a/list/src/org/labkey/list/model/ListTable.java +++ b/list/src/org/labkey/list/model/ListTable.java @@ -390,9 +390,15 @@ public List getDefaultVisibleColumns() @Override public Domain getDomain() + { + return getDomain(false); + } + + @Override + public Domain getDomain(boolean forUpdate) { if (null != _list) - return _list.getDomain(); + return _list.getDomain(forUpdate); return null; } diff --git a/query/src/org/labkey/query/QueryTestCase.jsp b/query/src/org/labkey/query/QueryTestCase.jsp index 2001fe6a358..1ff113df5a6 100644 --- a/query/src/org/labkey/query/QueryTestCase.jsp +++ b/query/src/org/labkey/query/QueryTestCase.jsp @@ -1752,7 +1752,7 @@ d,seven,twelve,day,month,date,duration,guid // create a URL expression and MV for suggested columns ListDefinition rDef = ListService.get().getList(c, "R"); - d = rDef.getDomain(); + d = rDef.getDomain(true); d.getPropertyByName("rowid").setURL("https://www.google.com/search?q=${guid}"); d.getPropertyByName("d").setMvEnabled(true); d.getPropertyByName("day").setLookup(new Lookup(d.getContainer(), SchemaKey.fromParts("lists"), "Days$Test")); diff --git a/specimen/src/org/labkey/specimen/SpecimenTableManager.java b/specimen/src/org/labkey/specimen/SpecimenTableManager.java index 116caa3ac91..1e9f4af2a3c 100644 --- a/specimen/src/org/labkey/specimen/SpecimenTableManager.java +++ b/specimen/src/org/labkey/specimen/SpecimenTableManager.java @@ -139,7 +139,7 @@ private Collection determineSpecimenColumns() SpecimenTablesProvider specimenTablesProvider = new SpecimenTablesProvider(_container, _user, null); - Domain vialDomain = specimenTablesProvider.getDomain("Vial", true); + Domain vialDomain = specimenTablesProvider.getDomain("Vial", true, false); if (null == vialDomain) throw new IllegalStateException("Expected Vial domain to already be created."); @@ -147,7 +147,7 @@ private Collection determineSpecimenColumns() for (DomainProperty domainProperty : vialDomain.getNonBaseProperties()) vialProperties.add(domainProperty.getPropertyDescriptor()); - Domain specimenEventDomain = specimenTablesProvider.getDomain("SpecimenEvent", true); + Domain specimenEventDomain = specimenTablesProvider.getDomain("SpecimenEvent", true, false); if (null == specimenEventDomain) throw new IllegalStateException("Expected SpecimenEvent domain to already be created."); diff --git a/specimen/src/org/labkey/specimen/importer/SpecimenSchemaImporter.java b/specimen/src/org/labkey/specimen/importer/SpecimenSchemaImporter.java index 3b4976725bc..7ace1092f9b 100644 --- a/specimen/src/org/labkey/specimen/importer/SpecimenSchemaImporter.java +++ b/specimen/src/org/labkey/specimen/importer/SpecimenSchemaImporter.java @@ -169,7 +169,7 @@ public void process(SimpleStudyImportContext ctx, VirtualFile studyDir, BindExce final String tableName = tableXml.getTableName(); // get the domain of the table we are updating - final Domain domain = tablesProvider.getDomain(tableName, false); + final Domain domain = tablesProvider.getDomain(tableName, false, true); final Container container = ctx.getContainer(); if (domain != null) diff --git a/specimen/src/org/labkey/specimen/view/manageSpecimens.jsp b/specimen/src/org/labkey/specimen/view/manageSpecimens.jsp index ba2b586feea..3f221e660c1 100644 --- a/specimen/src/org/labkey/specimen/view/manageSpecimens.jsp +++ b/specimen/src/org/labkey/specimen/view/manageSpecimens.jsp @@ -56,9 +56,9 @@ if (!study.hasSourceStudy() && !study.isSnapshotStudy()) { SpecimenTablesProvider stp = new SpecimenTablesProvider(c, user, null); - Domain domainEvent = stp.getDomain("specimenevent",false); - Domain domainVial = stp.getDomain("vial",false); - Domain domainSpecimen = stp.getDomain("specimen",false); + Domain domainEvent = stp.getDomain("specimenevent",false, false); + Domain domainVial = stp.getDomain("vial",false, false); + Domain domainSpecimen = stp.getDomain("specimen",false, false); ActionURL specimenEventUrl = null; ActionURL vialUrl = null; diff --git a/specimen/src/org/labkey/specimen/writer/SpecimenArchiveWriter.java b/specimen/src/org/labkey/specimen/writer/SpecimenArchiveWriter.java index b6775986f61..398784a5f9e 100644 --- a/specimen/src/org/labkey/specimen/writer/SpecimenArchiveWriter.java +++ b/specimen/src/org/labkey/specimen/writer/SpecimenArchiveWriter.java @@ -103,7 +103,7 @@ public void write(Study study, SimpleStudyExportContext ctx, VirtualFile root) t { TableType tableXml = tablesXml.addNewTable(); - Domain domain = tablesProvider.getDomain(entry.getKey(), false); + Domain domain = tablesProvider.getDomain(entry.getKey(), false, false); TableInfo table = entry.getValue(); List columns = new ArrayList<>(); diff --git a/study/api-src/org/labkey/api/specimen/importer/RollupHelper.java b/study/api-src/org/labkey/api/specimen/importer/RollupHelper.java index ae82d66f4b0..b8fac29ad76 100644 --- a/study/api-src/org/labkey/api/specimen/importer/RollupHelper.java +++ b/study/api-src/org/labkey/api/specimen/importer/RollupHelper.java @@ -145,11 +145,11 @@ public static RollupMap getEventToVialRollups(Container contain List rollups = RollupHelper.getEventVialRollups(); SpecimenTablesProvider specimenTablesProvider = new SpecimenTablesProvider(container, user, null); - Domain fromDomain = specimenTablesProvider.getDomain("SpecimenEvent", true); + Domain fromDomain = specimenTablesProvider.getDomain("SpecimenEvent", true, false); if (null == fromDomain) throw new IllegalStateException("Expected SpecimenEvent table to already be created."); - Domain toDomain = specimenTablesProvider.getDomain("Vial", true); + Domain toDomain = specimenTablesProvider.getDomain("Vial", true, false); if (null == toDomain) throw new IllegalStateException("Expected Vial table to already be created."); @@ -175,11 +175,11 @@ public static RollupMap getVialToSpecimenRollups(Container c List rollups = RollupHelper.getVialSpecimenRollups(); SpecimenTablesProvider specimenTablesProvider = new SpecimenTablesProvider(container, user, null); - Domain fromDomain = specimenTablesProvider.getDomain("Vial", true); + Domain fromDomain = specimenTablesProvider.getDomain("Vial", true, false); if (null == fromDomain) throw new IllegalStateException("Expected Vial table to already be created."); - Domain toDomain = specimenTablesProvider.getDomain("Specimen", true); + Domain toDomain = specimenTablesProvider.getDomain("Specimen", true, false); if (null == toDomain) throw new IllegalStateException("Expected Specimen table to already be created."); diff --git a/study/api-src/org/labkey/api/specimen/model/AbstractSpecimenDomainKind.java b/study/api-src/org/labkey/api/specimen/model/AbstractSpecimenDomainKind.java index a20e2ed1e1a..7ab93f2d336 100644 --- a/study/api-src/org/labkey/api/specimen/model/AbstractSpecimenDomainKind.java +++ b/study/api-src/org/labkey/api/specimen/model/AbstractSpecimenDomainKind.java @@ -76,7 +76,7 @@ public String getTypeLabel(Domain domain) @Override - public Domain createDomain(GWTDomain domain, JSONObject arguments, Container container, User user, @Nullable TemplateInfo templateInfo) + public Domain createDomain(GWTDomain domain, JSONObject arguments, Container container, User user, @Nullable TemplateInfo templateInfo, boolean forUpdate) { ValidationException validation = checkFieldNameLength(domain); if (validation != null) @@ -84,7 +84,7 @@ public Domain createDomain(GWTDomain domain, JSONObject a throw UnexpectedException.wrap(validation); } - return super.createDomain(domain, arguments, container, user, templateInfo); + return super.createDomain(domain, arguments, container, user, templateInfo, forUpdate); } @Override @@ -190,7 +190,7 @@ protected void setForeignKeyTableInfos(Set forei { if (foreignKey.isProvisioned()) { - Domain domain = provider.getDomain(foreignKey.getTableName(), true); + Domain domain = provider.getDomain(foreignKey.getTableName(), true, false); if (null == domain) throw new IllegalStateException("Expected domain to be created if it didn't already exist."); @@ -218,9 +218,9 @@ protected ValidationException checkRollups( boolean addWarnings) { SpecimenTablesProvider specimenTablesProvider = new SpecimenTablesProvider(container, user, null); - Domain eventDomain = specimenTablesProvider.getDomain("SpecimenEvent", false); - Domain vialDomain = specimenTablesProvider.getDomain("vial", false); - Domain specimenDomain = specimenTablesProvider.getDomain("specimen", false); + Domain eventDomain = specimenTablesProvider.getDomain("SpecimenEvent", false, true); + Domain vialDomain = specimenTablesProvider.getDomain("vial", false, true); + Domain specimenDomain = specimenTablesProvider.getDomain("specimen", false, false); boolean editingSpecimen = null != specimenProps; boolean editingVial = null != vialProps; diff --git a/study/api-src/org/labkey/api/specimen/model/SpecimenDomainKind.java b/study/api-src/org/labkey/api/specimen/model/SpecimenDomainKind.java index b54a91b3ce4..255c67e6411 100644 --- a/study/api-src/org/labkey/api/specimen/model/SpecimenDomainKind.java +++ b/study/api-src/org/labkey/api/specimen/model/SpecimenDomainKind.java @@ -175,8 +175,8 @@ public ActionURL urlEditDefinition(Domain domain, ContainerUser containerUser) { exception = new ValidationException(); SpecimenTablesProvider stp = new SpecimenTablesProvider(container, user, null); - Domain domainVial = stp.getDomain("vial", false); - Domain domainSpecimen = stp.getDomain("specimen", false); + Domain domainVial = stp.getDomain("vial", false, false); + Domain domainSpecimen = stp.getDomain("specimen", false, false); // Check for the same name in Specimen and Vial CaseInsensitiveHashSet vialFields = new CaseInsensitiveHashSet(); diff --git a/study/api-src/org/labkey/api/specimen/model/SpecimenEventDomainKind.java b/study/api-src/org/labkey/api/specimen/model/SpecimenEventDomainKind.java index f2e341d06ae..81d6f8a96c5 100644 --- a/study/api-src/org/labkey/api/specimen/model/SpecimenEventDomainKind.java +++ b/study/api-src/org/labkey/api/specimen/model/SpecimenEventDomainKind.java @@ -241,7 +241,7 @@ public ActionURL urlEditDefinition(Domain domain, ContainerUser containerUser) validationException = new ValidationException(); SpecimenTablesProvider stp = new SpecimenTablesProvider(container, user, null); - Domain domainEvent = stp.getDomain("specimenevent", false); + Domain domainEvent = stp.getDomain("specimenevent", false, false); Set mandatoryPropertyNames = getMandatoryPropertyNames(domainEvent); for (GWTPropertyDescriptor prop : update.getFields()) diff --git a/study/api-src/org/labkey/api/specimen/model/SpecimenTablesProvider.java b/study/api-src/org/labkey/api/specimen/model/SpecimenTablesProvider.java index 868dad573d8..5356db474a6 100644 --- a/study/api-src/org/labkey/api/specimen/model/SpecimenTablesProvider.java +++ b/study/api-src/org/labkey/api/specimen/model/SpecimenTablesProvider.java @@ -80,13 +80,13 @@ public SpecimenTablesProvider(Container container, @Nullable User user, @Nullabl } @Nullable - public final Domain getDomain(String tableName, boolean create) + public final Domain getDomain(String tableName, boolean create, boolean forUpdate) { // if the domain doesn't exist and we're asked to create, create it AbstractSpecimenDomainKind domainKind = getDomainKind(tableName); String domainURI = domainKind.generateDomainURI(SCHEMA_NAME, tableName, _container, _user); - Domain ret = PropertyService.get().getDomain(_container, domainURI); + Domain ret = PropertyService.get().getDomain(_container, domainURI, forUpdate); if (null == ret && create) { // Multiple threads attempting to create the domain and provisioned table may result in a constraint @@ -114,7 +114,7 @@ public final Domain getDomain(String tableName, boolean create) domain.save(_user); // Refresh the domain. save() doesn't populate provisioned schema and table names, e.g. - return PropertyService.get().getDomain(_container, domainURI); + return PropertyService.get().getDomain(_container, domainURI, forUpdate); } catch (ChangePropertyDescriptorException e) { @@ -134,7 +134,7 @@ public final Domain getDomain(String tableName, boolean create) @NotNull public TableInfo createTableInfo(String tableName) { - Domain domain = getDomain(tableName, true); + Domain domain = getDomain(tableName, true, false); if (null == domain) throw new IllegalStateException("Unable to create domain for table '" + tableName + "'"); return createTableInfo(domain); @@ -143,7 +143,7 @@ public TableInfo createTableInfo(String tableName) @Nullable public TableInfo getTableInfoIfExists(String tableName) { - Domain domain = getDomain(tableName, false); + Domain domain = getDomain(tableName, false, false); if (null != domain) return createTableInfo(domain); return null; @@ -157,7 +157,7 @@ public void deleteTables() { try { - Domain domain = getDomain(tableName, false); + Domain domain = getDomain(tableName, false, false); if (null != domain) { domain.delete(_user); diff --git a/study/api-src/org/labkey/api/specimen/model/VialDomainKind.java b/study/api-src/org/labkey/api/specimen/model/VialDomainKind.java index 39e311136b1..1d67a539bed 100644 --- a/study/api-src/org/labkey/api/specimen/model/VialDomainKind.java +++ b/study/api-src/org/labkey/api/specimen/model/VialDomainKind.java @@ -178,8 +178,8 @@ public ActionURL urlEditDefinition(Domain domain, ContainerUser containerUser) { exception = new ValidationException(); SpecimenTablesProvider stp = new SpecimenTablesProvider(container, user, null); - Domain domainSpecimen = stp.getDomain("specimen", false); - Domain domainVial = stp.getDomain("vial", false); + Domain domainSpecimen = stp.getDomain("specimen", false, false); + Domain domainVial = stp.getDomain("vial", false, false); // Check for the same name in Specimen and Vial CaseInsensitiveHashSet specimenFields = new CaseInsensitiveHashSet(); diff --git a/study/src/org/labkey/study/StudyUnionTableInfo.java b/study/src/org/labkey/study/StudyUnionTableInfo.java index bfed76d8cab..31239578e75 100644 --- a/study/src/org/labkey/study/StudyUnionTableInfo.java +++ b/study/src/org/labkey/study/StudyUnionTableInfo.java @@ -105,7 +105,7 @@ public void init(Collection defs) for (DatasetDefinition def : defs) { - TableInfo ti = def.getStorageTableInfo(); + TableInfo ti = def.getStorageTableInfo(false); if (null == ti || (_user != null && !def.canRead(_user))) continue; count++; @@ -268,7 +268,7 @@ private SQLFragment _getFromSQL(String alias, Set cols, boolean distin SQLFragment sqlf = new SQLFragment(); for (DatasetDefinition def : _defs) { - TableInfo ti = def.getStorageTableInfo(); + TableInfo ti = def.getStorageTableInfo(false); if (null == ti || (_user != null && !def.canRead(_user))) continue; count++; diff --git a/study/src/org/labkey/study/assay/StudyPublishManager.java b/study/src/org/labkey/study/assay/StudyPublishManager.java index cee8e3c4972..a9263c3828e 100644 --- a/study/src/org/labkey/study/assay/StudyPublishManager.java +++ b/study/src/org/labkey/study/assay/StudyPublishManager.java @@ -721,7 +721,7 @@ private Map ensurePropertyDescriptors( User user, DatasetDefinition dataset, List> dataMaps, List types, String keyPropertyName) throws ChangePropertyDescriptorException { - Domain domain = dataset.getDomain(); + Domain domain = dataset.getDomain(true); if (domain == null) { domain = PropertyService.get().createDomain(dataset.getContainer(), dataset.getTypeURI(), dataset.getName()); diff --git a/study/src/org/labkey/study/controllers/StudyController.java b/study/src/org/labkey/study/controllers/StudyController.java index 05fa268f0cd..98de5f963cc 100644 --- a/study/src/org/labkey/study/controllers/StudyController.java +++ b/study/src/org/labkey/study/controllers/StudyController.java @@ -5200,14 +5200,14 @@ private Dataset createDataset(StudySnapshotForm form, BindException errors) thro // def may not be provisioned yet, create before we start adding properties if (def.isQueryDataset()) { - def.provisionQueryDataset(); + def.provisionQueryDataset(true); } else { - def.provisionTable(); + def.provisionTable(true); } - Domain d = def.getDomain(); + Domain d = def.getDomain(true); for (ColumnInfo col : columnsToProvision) { @@ -7122,7 +7122,7 @@ public ApiResponse execute(DefineDatasetForm form, BindException errors) .setStudy(_study) .setDemographicData(false) .setCategoryId(categoryId)); - def.provisionTable(); + def.provisionTable(false); ActionURL redirect = new ActionURL(EditTypeAction.class, getContainer()).addParameter(Dataset.DATASET_KEY, def.getDatasetId()); response.put("redirectUrl", redirect.getLocalURIString()); @@ -7133,7 +7133,7 @@ public ApiResponse execute(DefineDatasetForm form, BindException errors) .setDemographicData(false) .setType(Dataset.TYPE_PLACEHOLDER) .setCategoryId(categoryId)); - def.provisionTable(); + def.provisionTable(false); response.put("datasetId", def.getDatasetId()); break; diff --git a/study/src/org/labkey/study/dataset/DatasetSnapshotProvider.java b/study/src/org/labkey/study/dataset/DatasetSnapshotProvider.java index 24723507f4a..5447e687b45 100644 --- a/study/src/org/labkey/study/dataset/DatasetSnapshotProvider.java +++ b/study/src/org/labkey/study/dataset/DatasetSnapshotProvider.java @@ -559,7 +559,7 @@ private void updateDomainProperties(User user, QuerySnapshotDefinition snapshotD { TableInfo sourceTable = sourceDef.getSchema().getTable(sourceDef.getName(), null); Domain sourceDomain = sourceTable != null ? sourceTable.getDomain() : null; - Domain snapshotDomain = dsDef.getDomain(); + Domain snapshotDomain = dsDef.getDomain(true); // source domain may be null if from a query if (sourceDomain != null && snapshotDomain != null) diff --git a/study/src/org/labkey/study/importer/DefaultStudyDesignImporter.java b/study/src/org/labkey/study/importer/DefaultStudyDesignImporter.java index a459ac43536..fad0f767cc7 100644 --- a/study/src/org/labkey/study/importer/DefaultStudyDesignImporter.java +++ b/study/src/org/labkey/study/importer/DefaultStudyDesignImporter.java @@ -116,7 +116,8 @@ protected void importTableinfo(StudyImportContext ctx, VirtualFile root, String if (table != null) { - final Domain domain = schema.getTable(tableName).getDomain(); + // TODO this is possibly inefficient since we'll be bypassing the cache even if there's nothing new to save + final Domain domain = schema.getTable(tableName).getDomain(true); if (domain != null) { diff --git a/study/src/org/labkey/study/model/DatasetDefinition.java b/study/src/org/labkey/study/model/DatasetDefinition.java index bab733391ba..07c29e4300b 100644 --- a/study/src/org/labkey/study/model/DatasetDefinition.java +++ b/study/src/org/labkey/study/model/DatasetDefinition.java @@ -685,7 +685,7 @@ public DatasetSchemaTableInfo getDatasetSchemaTableInfo(User user, boolean check /** why do some datasets have a typeURI, but no domain? */ - private Domain ensureDomain() + private Domain ensureDomain(boolean forUpdate) { assert _lock.isHeldByCurrentThread(); @@ -693,13 +693,13 @@ private Domain ensureDomain() { StudyImpl shared = getDefinitionStudy(); DatasetDefinition ds = shared.getDataset(getDatasetId()); - return null == ds ? null : ds.ensureDomain(); + return null == ds ? null : ds.ensureDomain(forUpdate); } if (null == getTypeURI()) throw new IllegalStateException(); - Domain d = getDomain(); + Domain d = getDomain(forUpdate); if (null != d) return d; @@ -716,7 +716,7 @@ private Domain ensureDomain() } - private TableInfo loadStorageTableInfo() + private TableInfo loadStorageTableInfo(boolean forDomainUpdate) { if (null == getTypeURI()) return null; @@ -725,7 +725,7 @@ private TableInfo loadStorageTableInfo() // consistent order to avoid deadlock try (Transaction t = StudySchema.getInstance().getSchema().getScope().ensureTransaction(_lock)) { - Domain d = ensureDomain(); + Domain d = ensureDomain(forDomainUpdate); // create table may set storageTableName() so uncache _domain if (null == d.getStorageTableName()) @@ -742,28 +742,28 @@ private TableInfo loadStorageTableInfo() /** * just a wrapper for StorageProvisioner.create() */ - public void provisionTable() + public void provisionTable(boolean forDomainUpdate) { try (Transaction t = StudySchema.getInstance().getSchema().getScope().ensureTransaction(_lock)) { - ensureDomainDef(); - loadStorageTableInfo(); + ensureDomainDef(forDomainUpdate); + loadStorageTableInfo(false); StudyManager.getInstance().uncache(this); t.commit(); } } - public void provisionQueryDataset() + public void provisionQueryDataset(boolean forDomainUpdate) { try (Transaction t = StudySchema.getInstance().getSchema().getScope().ensureTransaction(_lock)) { - ensureDomainDef(); + ensureDomainDef(forDomainUpdate); StudyManager.getInstance().uncache(this); t.commit(); } } - private void ensureDomainDef() + private void ensureDomainDef(boolean forUpdate) { _domain = null; if (null == getTypeURI()) @@ -772,11 +772,11 @@ private void ensureDomainDef() d.setTypeURI(DatasetDomainKind.generateDomainURI(getName(), getEntityId(), getContainer())); d.save(null); } - ensureDomain(); + ensureDomain(forUpdate); } @Transient - public TableInfo getStorageTableInfo() throws UnauthorizedException + public TableInfo getStorageTableInfo(boolean forDomainUpdate) throws UnauthorizedException { if (isQueryDataset()) return null; @@ -785,11 +785,11 @@ public TableInfo getStorageTableInfo() throws UnauthorizedException { StudyImpl shared = getDefinitionStudy(); DatasetDefinition ds = shared.getDataset(getDatasetId()); - return null == ds ? null : ds.getStorageTableInfo(); + return null == ds ? null : ds.getStorageTableInfo(forDomainUpdate); } else { - return loadStorageTableInfo(); + return loadStorageTableInfo(forDomainUpdate); } } @@ -808,7 +808,7 @@ public int deleteRows(@Nullable Date cutoff) CPUTimer time = new CPUTimer("purge"); time.start(); - SQLFragment studyDataFrag = new SQLFragment("DELETE FROM ").append(getStorageTableInfo()); + SQLFragment studyDataFrag = new SQLFragment("DELETE FROM ").append(getStorageTableInfo(false)); String and = "\nWHERE "; // only apply a container filter on delete when this is a shared dataset definition @@ -1170,7 +1170,7 @@ public void delete(User user) @Override public void deleteAllRows(User user) { - TableInfo data = getStorageTableInfo(); + TableInfo data = getStorageTableInfo(false); if (null == data) return; DbScope scope = StudySchema.getInstance().getSchema().getScope(); @@ -1416,7 +1416,7 @@ private ColumnInfo getStorageColumn(Domain d, DomainProperty p) } else { - _storage = def.getStorageTableInfo(); + _storage = def.getStorageTableInfo(false); } _template = getTemplateTableInfo(); @@ -1674,7 +1674,13 @@ public SQLFragment getFromSQL(String alias) @Override public Domain getDomain() { - return DatasetDefinition.this.getDomain(); + return getDomain(false); + } + + @Override + public Domain getDomain(boolean forUpdate) + { + return DatasetDefinition.this.getDomain(forUpdate); } @Override @@ -1708,7 +1714,7 @@ public CaseInsensitiveHashMap remapSchemaColumns() // shouldn't getStorageTableInfo().getColumn("date").getPropertyURI() == getVisitDateURI()? if (!getStudy().getTimepointType().isVisitBased()) { - m.put(getStorageTableInfo().getColumn("date").getPropertyURI(), getVisitDateURI()); + m.put(getStorageTableInfo(false).getColumn("date").getPropertyURI(), getVisitDateURI()); } return m; @@ -1917,6 +1923,13 @@ public static Set getStandardPropertiesSet() @Override @Transient public Domain getDomain() + { + return getDomain(false); + } + + @Override + @Transient + public Domain getDomain(boolean forUpdate) { if (isInherited()) { @@ -1929,15 +1942,16 @@ public Domain getDomain() { if (null == getTypeURI()) return null; - if (null != _domain) + if (null != _domain && (!forUpdate || _domain.isMutable())) return _domain; } Domain d=null; + // VERIFY: Transaction here is just for synchronization. No updates are happening. try (Transaction t = StudySchema.getInstance().getSchema().getScope().ensureTransaction(_lock)) { - if (null != getTypeURI() && null == _domain) - d = PropertyService.get().getDomain(getContainer(), getTypeURI()); + if (null != getTypeURI() && null == _domain || forUpdate) + d = PropertyService.get().getDomain(getContainer(), getTypeURI(), forUpdate); t.commit(); } @@ -2109,7 +2123,7 @@ public void deleteRows(Collection allLSIDs) { List> rowLSIDSlices = slice(allLSIDs); - TableInfo data = getStorageTableInfo(); + TableInfo data = getStorageTableInfo(false); try (Transaction transaction = StudySchema.getInstance().getSchema().getScope().ensureTransaction()) { @@ -2404,7 +2418,7 @@ public DataIteratorBuilder getInsertDataIterator(User user, Container container, * */ public SQLFragment generateLSIDSQL() { - if (null == getStorageTableInfo()) + if (null == getStorageTableInfo(false)) return new SQLFragment("''"); List parts = new ArrayList<>(); @@ -2436,7 +2450,7 @@ public SQLFragment generateLSIDSQL() } else if (getKeyPropertyName() != null) { - var key = getStorageTableInfo().getColumn(getKeyPropertyName()); + var key = getStorageTableInfo(false).getColumn(getKeyPropertyName()); if (null != key) { // It's possible for the key value to be null. In SQL, NULL concatenated with any other value is NULL, @@ -2547,7 +2561,7 @@ private HashMap checkTargetDupesAndDelete(final boolean demogr // duplicate keys found that should be deleted final Set deleteSet = new HashSet<>(); - TableInfo tinfo = getStorageTableInfo(); + TableInfo tinfo = getStorageTableInfo(false); SimpleFilter filter = new SimpleFilter(); filter.addWhereClause((demographic ?"ParticipantId":"LSID") + " IN (" + sbIn + ")", new Object[]{}); if (isShared()) @@ -2604,7 +2618,7 @@ private HashMap checkTargetDupesAndDelete(final boolean demogr */ int getMaxKeyValue() { - TableInfo tInfo = getStorageTableInfo(); + TableInfo tInfo = getStorageTableInfo(false); SQLFragment sqlf = new SQLFragment("SELECT COALESCE(MAX(CAST(_key AS INTEGER)), 0) FROM ").append(tInfo.getFromSQL("_")); Integer newKey = new SqlSelector(tInfo.getSchema(),sqlf).getObject(Integer.class); return newKey.intValue(); diff --git a/study/src/org/labkey/study/model/DatasetDomainKind.java b/study/src/org/labkey/study/model/DatasetDomainKind.java index ba213b65f06..773d97c175e 100644 --- a/study/src/org/labkey/study/model/DatasetDomainKind.java +++ b/study/src/org/labkey/study/model/DatasetDomainKind.java @@ -195,7 +195,7 @@ public SQLFragment sqlObjectIdsInDomain(Domain domain) DatasetDefinition def = getDatasetDefinition(domain.getTypeURI()); if (null == def) return new SQLFragment("NULL"); - TableInfo ti = def.getStorageTableInfo(); + TableInfo ti = def.getStorageTableInfo(false); SQLFragment sql = new SQLFragment(); sql.append("SELECT O.ObjectId FROM ").append(ti).append(" SD JOIN exp.Object O ON SD.Lsid=O.ObjectURI WHERE O.container=?"); sql.add(def.getContainer()); @@ -399,7 +399,7 @@ public DatasetDomainKindProperties getDomainKindProperties(GWTDomain domain, Con @Override public Domain createDomain(GWTDomain domain, DatasetDomainKindProperties arguments, Container container, User user, - @Nullable TemplateInfo templateInfo) + @Nullable TemplateInfo templateInfo, boolean forUpdate) { arguments.setName(StringUtils.trimToNull(domain.getName())); String name = arguments.getName(); @@ -494,7 +494,7 @@ else if (!timepointType.isVisitBased() && getKindName().equals(VisitDatasetDomai { List properties = (List)domain.getFields(); - Domain newDomain = def.getDomain(); + Domain newDomain = def.getDomain(true); if (newDomain != null) { Set reservedNames = getReservedPropertyNames(newDomain, user); @@ -527,7 +527,7 @@ else if (!timepointType.isVisitBased() && getKindName().equals(VisitDatasetDomai } transaction.commit(); - return study.getDataset(def.getDatasetId()).getDomain(); + return study.getDataset(def.getDatasetId()).getDomain(forUpdate); } catch (Exception e) { diff --git a/study/src/org/labkey/study/model/DatasetImportTestCase.jsp b/study/src/org/labkey/study/model/DatasetImportTestCase.jsp index 6801849ec80..c6c58c6570c 100644 --- a/study/src/org/labkey/study/model/DatasetImportTestCase.jsp +++ b/study/src/org/labkey/study/model/DatasetImportTestCase.jsp @@ -197,7 +197,7 @@ Dataset createDataset(Study study, String name, DatasetType type) throws Excepti pvLessThan100.setExpressionValue("~lte=100.0"); // define columns - Domain domain = dd.getDomain(); + Domain domain = dd.getDomain(true); DomainProperty measure = domain.addProperty(); measure.setName("Measure"); diff --git a/study/src/org/labkey/study/model/StudyManager.java b/study/src/org/labkey/study/model/StudyManager.java index 6ab7f6b640d..ae50f2837de 100644 --- a/study/src/org/labkey/study/model/StudyManager.java +++ b/study/src/org/labkey/study/model/StudyManager.java @@ -665,7 +665,7 @@ public void createDatasetDefinition(User user, DatasetDefinition datasetDefiniti // This method call has the side effect of ensuring that we have a domain. If we don't create it here, // 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(); + datasetDefinition.getStorageTableInfo(true); QueryService.get().updateLastModified(); transaction.commit(); @@ -752,7 +752,7 @@ public boolean updateDatasetDefinition(User user, final DatasetDefinition datase if (isProvisioned && isSharedChanged) { // let's not change the shared setting if there are existing rows - if (new TableSelector(datasetDefinition.getStorageTableInfo()).exists()) + if (new TableSelector(datasetDefinition.getStorageTableInfo(false)).exists()) { throw new IllegalArgumentException("Can't change data sharing setting if there are existing data rows."); } @@ -760,7 +760,7 @@ public boolean updateDatasetDefinition(User user, final DatasetDefinition datase if (isProvisioned && isKeyChanged) { - TableInfo storageTableInfo = datasetDefinition.getStorageTableInfo(); + TableInfo storageTableInfo = datasetDefinition.getStorageTableInfo(false); // If so, we need to update the _key column and the LSID @@ -1016,7 +1016,7 @@ private boolean deleteDatasetPropertyOverrides(User user, final DatasetDefinitio public boolean isDataUniquePerParticipant(DatasetDefinition dataset) { // don't use dataset.getTableInfo() since this method is called during updateDatasetDefinition`() and may be in an inconsistent state - TableInfo t = dataset.getStorageTableInfo(); + TableInfo t = dataset.getStorageTableInfo(false); SQLFragment sql = new SQLFragment(); sql.append("SELECT MAX(n) FROM (SELECT COUNT(*) AS n FROM ").append(t.getFromSQL("DS")).append(" GROUP BY ParticipantId) x"); Integer maxCount = new SqlSelector(StudySchema.getInstance().getSchema(), sql).getObject(Integer.class); @@ -1621,7 +1621,7 @@ public void deleteVisits(StudyImpl study, Collection visits, User use { for (DatasetDefinition def : study.getDatasets()) { - TableInfo t = def.getStorageTableInfo(); + TableInfo t = def.getStorageTableInfo(false); if (null == t) continue; @@ -1917,7 +1917,7 @@ public void updateDataQCState(Container container, User user, int datasetId, Col try (Transaction transaction = scope.ensureTransaction()) { // TODO fix updating across study data - SQLFragment sql = new SQLFragment("UPDATE " ).append(def.getStorageTableInfo()); + SQLFragment sql = new SQLFragment("UPDATE " ).append(def.getStorageTableInfo(false)); sql.append(" SET QCState = "); // do string concatenation, rather that using a parameter, for the new state id because Postgres null // parameters are typed which causes a cast exception trying to set the value back to null (bug 6370) @@ -3645,7 +3645,7 @@ public _DatasetDomainChange(Domain domain) private _DatasetDomainChange createDomainChange(String domainURI, String domainName, DatasetDefinitionEntry def, boolean existingDomainsOnly) { _DatasetDomainChange domainChange = new _DatasetDomainChange(); - domainChange.domain = PropertyService.get().getDomain(def.datasetDefinition.getDefinitionContainer(), domainURI); + domainChange.domain = PropertyService.get().getDomain(def.datasetDefinition.getDefinitionContainer(), domainURI, true); if (domainChange.domain == null && existingDomainsOnly) return null; else if (domainChange.domain == null) @@ -3742,7 +3742,8 @@ private void buildPropertySaveAndDeleteLists(Map Domain domain = PropertyService.get().getDomain( datasetDefinitionEntry.datasetDefinition.getDefinitionContainer(), - datasetDefinitionEntry.datasetDefinition.getTypeURI()); + datasetDefinitionEntry.datasetDefinition.getTypeURI(), + true); if (domain != null) domainChangeMap.put(datasetDefinitionEntry.datasetDefinition.getTypeURI(), new _DatasetDomainChange(domain)); } diff --git a/study/src/org/labkey/study/pipeline/DatasetImportRunnable.java b/study/src/org/labkey/study/pipeline/DatasetImportRunnable.java index 5067cabb45c..dfda1b7c774 100644 --- a/study/src/org/labkey/study/pipeline/DatasetImportRunnable.java +++ b/study/src/org/labkey/study/pipeline/DatasetImportRunnable.java @@ -110,7 +110,7 @@ public void validate(List errors) errors.add("Dataset not defined"); else if (_datasetDefinition.getTypeURI() == null) errors.add("Dataset " + (null != _datasetDefinition.getName() ? _datasetDefinition.getName() + ": " : "") + "type is not defined"); - else if (null == _datasetDefinition.getStorageTableInfo()) + else if (null == _datasetDefinition.getStorageTableInfo(false)) errors.add("No database table found for dataset " + _datasetDefinition.getName()); if (_action == AbstractDatasetImportTask.Action.DELETE) diff --git a/study/src/org/labkey/study/query/CohortTable.java b/study/src/org/labkey/study/query/CohortTable.java index d18395ef08c..755d9c77df1 100644 --- a/study/src/org/labkey/study/query/CohortTable.java +++ b/study/src/org/labkey/study/query/CohortTable.java @@ -110,6 +110,17 @@ public Domain getDomain() return _domain; } + @Override + public Domain getDomain(boolean forUpdate) + { + if (_domain != null && forUpdate && !_domain.isMutable()) + { + String domainURI = CohortImpl.DOMAIN_INFO.getDomainURI(getContainer()); + _domain = PropertyService.get().getDomain(getContainer(), domainURI, forUpdate); + } + return _domain; + } + @Override public QueryUpdateService getUpdateService() { diff --git a/study/src/org/labkey/study/query/DatasetTableImpl.java b/study/src/org/labkey/study/query/DatasetTableImpl.java index 74e23885118..f223cd1de16 100644 --- a/study/src/org/labkey/study/query/DatasetTableImpl.java +++ b/study/src/org/labkey/study/query/DatasetTableImpl.java @@ -67,7 +67,6 @@ import org.labkey.api.query.SchemaKey; import org.labkey.api.query.UserIdQueryForeignKey; import org.labkey.api.query.UserSchema; -import org.labkey.api.query.ValidationException; import org.labkey.api.security.User; import org.labkey.api.security.UserPrincipal; import org.labkey.api.security.permissions.DeletePermission; @@ -528,7 +527,7 @@ protected Set getContextualRoles() public Map>> getUniqueIndices() { // Get indices from underlying storage table - Map>> ret = new HashMap<>(wrapTableIndices(getDatasetDefinition().getStorageTableInfo())); + Map>> ret = new HashMap<>(wrapTableIndices(getDatasetDefinition().getStorageTableInfo(false))); String subjectColName = StudyService.get().getSubjectColumnName(getContainer()); // Index enforced in code not on actual database for demographic datasets only @@ -663,7 +662,13 @@ protected ColumnInfo resolveColumn(String name) @Override public Domain getDomain() { - return _dsd.getDomain(); + return getDomain(false); + } + + @Override + public Domain getDomain(boolean forUpdate) + { + return _dsd.getDomain(forUpdate); } @Override diff --git a/study/src/org/labkey/study/query/DatasetUpdateService.java b/study/src/org/labkey/study/query/DatasetUpdateService.java index 653c6854b99..356ea4cce1c 100644 --- a/study/src/org/labkey/study/query/DatasetUpdateService.java +++ b/study/src/org/labkey/study/query/DatasetUpdateService.java @@ -144,7 +144,7 @@ public enum Config public DatasetUpdateService(DatasetTableImpl table) { - super(table, table.getDatasetDefinition().getStorageTableInfo(), createMVMapping(table.getDatasetDefinition().getDomain())); + super(table, table.getDatasetDefinition().getStorageTableInfo(false), createMVMapping(table.getDatasetDefinition().getDomain())); _dataset = table.getDatasetDefinition(); } @@ -801,7 +801,7 @@ public void updateRowTest() throws Exception dsd = _manager.getDatasetDefinition(_junitStudy, 1001); dsd.refreshDomain(); { - var domain = dsd.getDomain(); + var domain = dsd.getDomain(true); DomainProperty p; p = domain.addProperty(); diff --git a/study/src/org/labkey/study/query/SpecimenDetailTable.java b/study/src/org/labkey/study/query/SpecimenDetailTable.java index 4caa8d5dccc..942aea591e4 100644 --- a/study/src/org/labkey/study/query/SpecimenDetailTable.java +++ b/study/src/org/labkey/study/query/SpecimenDetailTable.java @@ -374,11 +374,11 @@ public static void getOptionalSpecimenAndVialProperties(Container container, Lis List optionalVialProperties) { SpecimenTablesProvider specimenTablesProvider = new SpecimenTablesProvider(container, null, null); - Domain specimenDomain = specimenTablesProvider.getDomain("Specimen", true); + Domain specimenDomain = specimenTablesProvider.getDomain("Specimen", true, false); if (null == specimenDomain) throw new IllegalStateException("Expected Specimen table to already be created."); - Domain vialDomain = specimenTablesProvider.getDomain("Vial", true); + Domain vialDomain = specimenTablesProvider.getDomain("Vial", true, false); if (null == vialDomain) throw new IllegalStateException("Expected Vial table to already be created."); diff --git a/study/src/org/labkey/study/query/SpecimenEventTable.java b/study/src/org/labkey/study/query/SpecimenEventTable.java index 179785eb3bd..0809da6d5bf 100644 --- a/study/src/org/labkey/study/query/SpecimenEventTable.java +++ b/study/src/org/labkey/study/query/SpecimenEventTable.java @@ -80,7 +80,7 @@ public SpecimenEventTable(StudyQuerySchema schema, ContainerFilter cf) // Add optional fields SpecimenTablesProvider specimenTablesProvider = new SpecimenTablesProvider(schema.getContainer(), null, null); - Domain specimenEventDomain = specimenTablesProvider.getDomain("SpecimenEvent", false); + Domain specimenEventDomain = specimenTablesProvider.getDomain("SpecimenEvent", false, false); if (null == specimenEventDomain) throw new IllegalStateException("Expected SpecimenEvent table to already be created."); diff --git a/study/src/org/labkey/study/query/SpecimenSummaryTable.java b/study/src/org/labkey/study/query/SpecimenSummaryTable.java index 1701a9bff14..ccb2a15b22f 100644 --- a/study/src/org/labkey/study/query/SpecimenSummaryTable.java +++ b/study/src/org/labkey/study/query/SpecimenSummaryTable.java @@ -151,7 +151,7 @@ public DisplayColumn createRenderer(ColumnInfo colInfo) addColumn(new ExprColumn(this, "QualityControlFlag", sqlFragConflicts, JdbcType.BOOLEAN)); SpecimenTablesProvider specimenTablesProvider = new SpecimenTablesProvider(schema.getContainer(), null, null); - Domain specimenDomain = specimenTablesProvider.getDomain("Specimen", false); + Domain specimenDomain = specimenTablesProvider.getDomain("Specimen", false, false); if (null == specimenDomain) throw new IllegalStateException("Expected Specimen table to already be created."); addOptionalColumns(specimenDomain.getNonBaseProperties(), false, null); diff --git a/study/src/org/labkey/study/query/StudyPropertiesTable.java b/study/src/org/labkey/study/query/StudyPropertiesTable.java index 787c3afc726..faf4382c38e 100644 --- a/study/src/org/labkey/study/query/StudyPropertiesTable.java +++ b/study/src/org/labkey/study/query/StudyPropertiesTable.java @@ -174,6 +174,18 @@ public Domain getDomain() return _domain; } + + @Override + public Domain getDomain(boolean forUpdate) + { + if (_domain != null && forUpdate && !_domain.isMutable()) + { + String domainURI = StudyImpl.DOMAIN_INFO.getDomainURI(getContainer()); + _domain = PropertyService.get().getDomain(getContainer(), domainURI, forUpdate); + } + return _domain; + } + @Override public boolean hasPermission(@NotNull UserPrincipal user, @NotNull Class perm) { diff --git a/study/src/org/labkey/study/view/participantAll.jsp b/study/src/org/labkey/study/view/participantAll.jsp index 1e3f6ad5adb..1477319de67 100644 --- a/study/src/org/labkey/study/view/participantAll.jsp +++ b/study/src/org/labkey/study/view/participantAll.jsp @@ -106,7 +106,7 @@ for (DatasetDefinition def : allDatasets) { - if (!def.isShowByDefault() || null == def.getStorageTableInfo() || def.isDemographicData()) + if (!def.isShowByDefault() || null == def.getStorageTableInfo(false) || def.isDemographicData()) continue; TableInfo t = querySchema.getDatasetTableForLookup(def, null); if (null==t || !t.hasPermission(user, ReadPermission.class)) diff --git a/study/src/org/labkey/study/visitmanager/VisitManager.java b/study/src/org/labkey/study/visitmanager/VisitManager.java index 6e406aaea65..1c11ea19d8a 100644 --- a/study/src/org/labkey/study/visitmanager/VisitManager.java +++ b/study/src/org/labkey/study/visitmanager/VisitManager.java @@ -581,7 +581,7 @@ private static SQLFragment studyDataPtids(Collection defs) TableInfo sti = null; try { - sti = d.getStorageTableInfo(); + sti = d.getStorageTableInfo(false); } catch (IllegalArgumentException x) { @@ -611,7 +611,7 @@ protected void updateStartDates() DatasetDefinition startDateDataset = getStartDateDataset(getStudy()); if (null != startDateDataset) { - TableInfo tInfo = startDateDataset.getStorageTableInfo(); + TableInfo tInfo = startDateDataset.getStorageTableInfo(false); ColumnInfo col = tInfo.getColumn(START_DATE_COLUMN_NAME); Container c = startDateDataset.getContainer(); SQLFragment expr = new SQLFragment() diff --git a/survey/src/org/labkey/survey/query/SurveyTableDomainKind.java b/survey/src/org/labkey/survey/query/SurveyTableDomainKind.java index 2f18e4aa006..83bb9a20907 100644 --- a/survey/src/org/labkey/survey/query/SurveyTableDomainKind.java +++ b/survey/src/org/labkey/survey/query/SurveyTableDomainKind.java @@ -62,9 +62,9 @@ public ActionURL urlCreateDefinition(String schemaName, String queryName, Contai } @Override - public Domain createDomain(GWTDomain domain, JSONObject arguments, Container container, User user, TemplateInfo templateInfo) + public Domain createDomain(GWTDomain domain, JSONObject arguments, Container container, User user, TemplateInfo templateInfo, boolean forUpdate) { - return super.createDomain(domain, arguments, getDomainContainer(container), user, templateInfo); + return super.createDomain(domain, arguments, getDomainContainer(container), user, templateInfo, forUpdate); } @Override diff --git a/survey/src/org/labkey/survey/query/SurveysTable.java b/survey/src/org/labkey/survey/query/SurveysTable.java index a624d57dfef..f7c1b727377 100644 --- a/survey/src/org/labkey/survey/query/SurveysTable.java +++ b/survey/src/org/labkey/survey/query/SurveysTable.java @@ -103,6 +103,12 @@ public SimpleTableDomainKind getDomainKind() @Override public Domain getDomain() + { + return getDomain(false); + } + + @Override + public Domain getDomain(boolean forUpdate) { if (getObjectUriColumn() == null) return null; @@ -110,7 +116,7 @@ public Domain getDomain() if (_domain == null) { String domainURI = getDomainURI(); - _domain = PropertyService.get().getDomain(SurveyTableDomainKind.getDomainContainer(getContainer()), domainURI); + _domain = PropertyService.get().getDomain(SurveyTableDomainKind.getDomainContainer(getContainer()), domainURI, forUpdate); } return _domain; }