From 067a6b95ccf507f1d40f159730db5dd534ef4f13 Mon Sep 17 00:00:00 2001 From: Georgy Litvinov Date: Fri, 8 Mar 2024 09:02:40 +0100 Subject: [PATCH 01/14] Split: ConfigurationBeanLoader fixes Proof of concept for dynapi february sprint (#253) * Dynamic API RPC starting point * Use String to store dateTime until a better type defined * instantinate objects independently in configuration bean loader * Modified ontology files * Replace requireXXX with requresXXX in dynamic API ontology * Parse boolean type while loading instances with ConfigurationBeanLoader * Ontology modifications, refactoring. * Added n3 template rdf class * Renamed rpc name set method * Implemented basic support of sparql queries * Current state for discussion * Multiple fixes, added n3 action example * Added typed parameter example, fixed localization tags * Defined rdf class RPC to represent RPC call, defined class to represent REST Resources * Initialize pools with StartupManager * Added draft of REST Endpoint * Added default http method to define a method should be used for rpc execution and also REST custom action * fix: a mistake, Action should have firstStep, next should be an option of OperationalStep * fix: REST custom action names shouldn't be equal to RPC names --- .../ConfigurationBeanLoader.java | 37 ++++++++++++--- .../utils/configuration/PropertyType.java | 47 ++++++++++++++++++- 2 files changed, 77 insertions(+), 7 deletions(-) diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/ConfigurationBeanLoader.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/ConfigurationBeanLoader.java index 8c45a2fca4..515269b5e4 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/ConfigurationBeanLoader.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/ConfigurationBeanLoader.java @@ -13,6 +13,8 @@ import javax.servlet.ServletContext; import javax.servlet.http.HttpServletRequest; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.apache.jena.rdf.model.Model; import org.apache.jena.rdf.model.Resource; import org.apache.jena.vocabulary.RDF; @@ -24,6 +26,9 @@ * Load one or more Configuration beans from a specified model. */ public class ConfigurationBeanLoader { + + private static final Log log = LogFactory.getLog(ConfigurationBeanLoader.class); + private static final String JAVA_URI_PREFIX = "java:"; @@ -161,6 +166,32 @@ public T loadInstance(String uri, Class resultClass) public Set loadAll(Class resultClass) throws ConfigurationBeanLoaderException { Set uris = new HashSet<>(); + findUris(resultClass, uris); + Set instances = new HashSet<>(); + for (String uri : uris) { + instances.add(loadInstance(uri, resultClass)); + } + return instances; + } + + /** + * Find all of the resources with the specified class, and instantiate them. + */ + public Set loadEach(Class resultClass){ + Set uris = new HashSet<>(); + findUris(resultClass, uris); + Set instances = new HashSet<>(); + for (String uri : uris) { + try { + instances.add(loadInstance(uri, resultClass)); + } catch (ConfigurationBeanLoaderException e) { + e.printStackTrace(); + } + } + return instances; + } + + private void findUris(Class resultClass, Set uris) { try (LockedModel m = locking.read()) { for (String typeUri : toPossibleJavaUris(resultClass)) { List resources = m.listResourcesWithProperty(RDF.type, @@ -172,11 +203,5 @@ public Set loadAll(Class resultClass) } } } - - Set instances = new HashSet<>(); - for (String uri : uris) { - instances.add(loadInstance(uri, resultClass)); - } - return instances; } } diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/PropertyType.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/PropertyType.java index 7bae32b366..07de259cd4 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/PropertyType.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/PropertyType.java @@ -4,6 +4,8 @@ import static org.apache.jena.datatypes.xsd.XSDDatatype.XSDfloat; import static org.apache.jena.datatypes.xsd.XSDDatatype.XSDstring; +import static org.apache.jena.datatypes.xsd.XSDDatatype.XSDdateTime; +import static org.apache.jena.datatypes.xsd.XSDDatatype.XSDboolean; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; @@ -61,6 +63,19 @@ protected PropertyMethod buildPropertyMethod(Method method, Property annotation) { return new FloatPropertyMethod(method, annotation); } + }, + BOOLEAN{ + @Override + public PropertyStatement buildPropertyStatement(Statement s) { + return new BooleanPropertyStatement(s.getPredicate().getURI(), s + .getObject().asLiteral().getBoolean()); + } + + @Override + protected PropertyMethod buildPropertyMethod(Method method, + Property annotation) { + return new BooleanPropertyMethod(method, annotation); + } }; public static PropertyType typeForObject(RDFNode object) @@ -71,12 +86,19 @@ public static PropertyType typeForObject(RDFNode object) if (object.isLiteral()) { Literal literal = object.asLiteral(); RDFDatatype datatype = literal.getDatatype(); - if (datatype == null || datatype.equals(XSDstring) || datatype.equals(RDFLangString.rdfLangString)) { + if (datatype == null || + datatype.equals(XSDstring) || + datatype.equals(RDFLangString.rdfLangString) || + //TODO: Until more suitable type defined + datatype.equals(XSDdateTime)) { return STRING; } if (datatype.equals(XSDfloat)) { return FLOAT; } + if (datatype.equals(XSDboolean)) { + return BOOLEAN; + } } throw new PropertyTypeException("Unsupported datatype on object: " + object); @@ -87,6 +109,9 @@ public static PropertyType typeForParameterType(Class parameterType) if (Float.TYPE.equals(parameterType)) { return FLOAT; } + if (Boolean.TYPE.equals(parameterType)) { + return BOOLEAN; + } if (String.class.equals(parameterType)) { return STRING; } @@ -176,6 +201,20 @@ public Float getValue() { return f; } } + + public static class BooleanPropertyStatement extends PropertyStatement { + private final Boolean bool; + + public BooleanPropertyStatement(String predicateUri, Boolean b) { + super(BOOLEAN, predicateUri); + this.bool = b; + } + + @Override + public Boolean getValue() { + return bool; + } + } public static abstract class PropertyMethod { protected final PropertyType type; @@ -257,6 +296,12 @@ public FloatPropertyMethod(Method method, Property annotation) { super(FLOAT, method, annotation); } } + + public static class BooleanPropertyMethod extends PropertyMethod { + public BooleanPropertyMethod(Method method, Property annotation) { + super(BOOLEAN, method, annotation); + } + } public static class PropertyTypeException extends Exception { public PropertyTypeException(String message) { From 75ba2b36543d2abfeca2ca201f54e638bf5b153b Mon Sep 17 00:00:00 2001 From: Georgy Litvinov Date: Fri, 8 Mar 2024 09:11:33 +0100 Subject: [PATCH 02/14] Split: extracted ConfigurationBeanLoader Co-authored-by: William Welling <8352733+wwelling@users.noreply.github.com> Co-authored-by: Kevin Day Co-authored-by: Dragan Ivanovic Co-authored-by: chenejac correct context path vs servlet path Refactor Integration Tests to use the full word Test. This allows for the tests to be properly picked up and executed. Merge pull request #281 from kaladay/dynapi-rpc_endpoint_it_to_itest [3678] Refactor Integration Tests to use the full word Test. Merge branch 'sprint-dynapi-2022-feb-staging' of github.com:vivo-project/Vitro into servlet-path Merge pull request #280 from wwelling/servlet-path Correct context path vs servlet path Update of the ontology. Introducing structured parameter types (object and array) rest endpoint corrections and integration tests Updating binding of the ontology to Java classes and n3 test examples.To be in accordance with changes in the ontology. Adding files for primitive types and APIInformation java bean. Adding new line at the end of file and retrieving history of deleted files by using git mv Adding new line at the end of file Merge pull request #283 from chenejac/3653_definition_of_the_ontology 3653 definition of the ontology Merge branch 'sprint-dynapi-2022-feb-staging' of github.com:vivo-project/Vitro into 3667-rest-it --- .../ConfigurationBeanLoader.java | 25 ++++++- .../utils/configuration/PropertyType.java | 75 ++++++++++++++----- .../utils/configuration/WrappedInstance.java | 3 +- .../ConfigurationBeanLoaderTest.java | 37 +++++++-- 4 files changed, 111 insertions(+), 29 deletions(-) diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/ConfigurationBeanLoader.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/ConfigurationBeanLoader.java index 515269b5e4..10b4510db7 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/ConfigurationBeanLoader.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/ConfigurationBeanLoader.java @@ -4,8 +4,10 @@ import static org.apache.jena.rdf.model.ResourceFactory.createResource; +import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.TreeSet; @@ -32,6 +34,9 @@ public class ConfigurationBeanLoader { private static final String JAVA_URI_PREFIX = "java:"; + Map instancesMap = new HashMap(); + + // ---------------------------------------------------------------------- // utility methods // ---------------------------------------------------------------------- @@ -135,7 +140,14 @@ private ConfigurationBeanLoader(LockableModel locking, ServletContext ctx, /** * Load the instance with this URI, if it is assignable to this class. */ - public T loadInstance(String uri, Class resultClass) + public T loadInstance(String uri, Class resultClass) throws ConfigurationBeanLoaderException { + instancesMap.clear(); + T result = loadSubordinateInstance(uri, resultClass); + instancesMap.clear(); + return result; + } + + protected T loadSubordinateInstance(String uri, Class resultClass) throws ConfigurationBeanLoaderException { if (uri == null) { throw new NullPointerException("uri may not be null."); @@ -143,7 +155,15 @@ public T loadInstance(String uri, Class resultClass) if (resultClass == null) { throw new NullPointerException("resultClass may not be null."); } - + if (instancesMap.containsKey(uri)) { + try { + T t = (T) instancesMap.get(uri); + return t; + } catch (ClassCastException e) { + throw new ConfigurationBeanLoaderException(uri, e); + } + } + try { ConfigurationRdf parsedRdf = ConfigurationRdfParser .parse(locking, uri, resultClass); @@ -151,6 +171,7 @@ public T loadInstance(String uri, Class resultClass) .wrap(parsedRdf.getConcreteClass()); wrapper.satisfyInterfaces(ctx, req); wrapper.checkCardinality(parsedRdf.getPropertyStatements()); + instancesMap.put(uri, wrapper.getInstance()); wrapper.setProperties(this, parsedRdf.getPropertyStatements()); wrapper.validate(); return wrapper.getInstance(); diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/PropertyType.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/PropertyType.java index 07de259cd4..4c69e3b9e6 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/PropertyType.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/PropertyType.java @@ -2,11 +2,6 @@ package edu.cornell.mannlib.vitro.webapp.utils.configuration; -import static org.apache.jena.datatypes.xsd.XSDDatatype.XSDfloat; -import static org.apache.jena.datatypes.xsd.XSDDatatype.XSDstring; -import static org.apache.jena.datatypes.xsd.XSDDatatype.XSDdateTime; -import static org.apache.jena.datatypes.xsd.XSDDatatype.XSDboolean; - import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; @@ -16,6 +11,8 @@ import org.apache.jena.rdf.model.RDFNode; import org.apache.jena.rdf.model.Statement; +import static org.apache.jena.datatypes.xsd.XSDDatatype.*; + /** * An enumeration of the types of properties that the ConfigurationBeanLoader * will support. @@ -32,8 +29,7 @@ public PropertyStatement buildPropertyStatement(Statement s) { } @Override - protected PropertyMethod buildPropertyMethod(Method method, - Property annotation) { + protected PropertyMethod buildPropertyMethod(Method method, Property annotation) { return new ResourcePropertyMethod(method, annotation); } @@ -46,8 +42,7 @@ public PropertyStatement buildPropertyStatement(Statement s) { } @Override - protected PropertyMethod buildPropertyMethod(Method method, - Property annotation) { + protected PropertyMethod buildPropertyMethod(Method method, Property annotation) { return new StringPropertyMethod(method, annotation); } }, @@ -59,11 +54,22 @@ public PropertyStatement buildPropertyStatement(Statement s) { } @Override - protected PropertyMethod buildPropertyMethod(Method method, - Property annotation) { + protected PropertyMethod buildPropertyMethod(Method method, Property annotation) { return new FloatPropertyMethod(method, annotation); } - }, + }, + INTEGER { + @Override + public PropertyStatement buildPropertyStatement(Statement s) { + return new IntegerPropertyStatement(s.getPredicate().getURI(), s + .getObject().asLiteral().getInt()); + } + + @Override + protected PropertyMethod buildPropertyMethod(Method method, Property annotation) { + return new IntegerPropertyMethod(method, annotation); + } + }, BOOLEAN{ @Override public PropertyStatement buildPropertyStatement(Statement s) { @@ -72,8 +78,7 @@ public PropertyStatement buildPropertyStatement(Statement s) { } @Override - protected PropertyMethod buildPropertyMethod(Method method, - Property annotation) { + protected PropertyMethod buildPropertyMethod(Method method, Property annotation) { return new BooleanPropertyMethod(method, annotation); } }; @@ -93,9 +98,14 @@ public static PropertyType typeForObject(RDFNode object) datatype.equals(XSDdateTime)) { return STRING; } - if (datatype.equals(XSDfloat)) { + if (datatype.equals(XSDfloat) || + datatype.equals(XSDdecimal)){ return FLOAT; } + if (datatype.equals(XSDint) || + datatype.equals(XSDinteger)) { + return INTEGER; + } if (datatype.equals(XSDboolean)) { return BOOLEAN; } @@ -109,6 +119,9 @@ public static PropertyType typeForParameterType(Class parameterType) if (Float.TYPE.equals(parameterType)) { return FLOAT; } + if (Integer.TYPE.equals(parameterType)) { + return INTEGER; + } if (Boolean.TYPE.equals(parameterType)) { return BOOLEAN; } @@ -128,8 +141,7 @@ public static PropertyStatement createPropertyStatement(Statement s) return type.buildPropertyStatement(s); } - public static PropertyMethod createPropertyMethod(Method method, - Property annotation) throws PropertyTypeException { + public static PropertyMethod createPropertyMethod(Method method, Property annotation) throws PropertyTypeException { Class parameterType = method.getParameterTypes()[0]; PropertyType type = PropertyType.typeForParameterType(parameterType); return type.buildPropertyMethod(method, annotation); @@ -201,6 +213,20 @@ public Float getValue() { return f; } } + + public static class IntegerPropertyStatement extends PropertyStatement { + private final int i; + + public IntegerPropertyStatement(String predicateUri, int i) { + super(INTEGER, predicateUri); + this.i = i; + } + + @Override + public Integer getValue() { + return i; + } + } public static class BooleanPropertyStatement extends PropertyStatement { private final Boolean bool; @@ -260,13 +286,20 @@ public int getMaxOccurs() { public void confirmCompatible(PropertyStatement ps) throws PropertyTypeException { - if (type != ps.getType()) { + if (type != ps.getType() && + ! (isSubtype(ps.getType(), type))){ throw new PropertyTypeException( "Can't apply statement of type " + ps.getType() + " to a method of type " + type); } } + private boolean isSubtype(PropertyType subType, PropertyType superType){ + if (subType.equals(INTEGER) && superType.equals(FLOAT)) + return true; + return false; + } + public void invoke(Object instance, Object value) throws PropertyTypeException { try { @@ -296,6 +329,12 @@ public FloatPropertyMethod(Method method, Property annotation) { super(FLOAT, method, annotation); } } + + public static class IntegerPropertyMethod extends PropertyMethod { + public IntegerPropertyMethod(Method method, Property annotation) { + super(INTEGER, method, annotation); + } + } public static class BooleanPropertyMethod extends PropertyMethod { public BooleanPropertyMethod(Method method, Property annotation) { diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/WrappedInstance.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/WrappedInstance.java index 9e889426ce..df04470014 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/WrappedInstance.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/WrappedInstance.java @@ -130,8 +130,7 @@ public void setProperties(ConfigurationBeanLoader loader, if (ps instanceof ResourcePropertyStatement) { ResourcePropertyStatement rps = (ResourcePropertyStatement) ps; - Object subordinate = loader.loadInstance(rps.getValue(), - pm.getParameterType()); + Object subordinate = loader.loadSubordinateInstance(rps.getValue(), pm.getParameterType()); pm.invoke(instance, subordinate); } else { pm.invoke(instance, ps.getValue()); diff --git a/api/src/test/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/ConfigurationBeanLoaderTest.java b/api/src/test/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/ConfigurationBeanLoaderTest.java index bfd88db4a4..dc6552cb94 100644 --- a/api/src/test/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/ConfigurationBeanLoaderTest.java +++ b/api/src/test/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/ConfigurationBeanLoaderTest.java @@ -9,6 +9,7 @@ import static org.apache.jena.datatypes.xsd.XSDDatatype.XSDfloat; import static org.apache.jena.datatypes.xsd.XSDDatatype.XSDstring; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; @@ -24,6 +25,7 @@ import org.junit.Ignore; import org.junit.Test; +import edu.cornell.mannlib.vitro.webapp.dynapi.components.OperationalStep; import edu.cornell.mannlib.vitro.webapp.modelaccess.ContextModelAccess; import edu.cornell.mannlib.vitro.webapp.modelaccess.RequestModelAccess; import edu.cornell.mannlib.vitro.webapp.utils.configuration.ConfigurationRdfParser.InvalidConfigurationRdfException; @@ -31,13 +33,6 @@ import edu.cornell.mannlib.vitro.webapp.utils.configuration.PropertyType.PropertyTypeException; import edu.cornell.mannlib.vitro.webapp.utils.configuration.WrappedInstance.ResourceUnavailableException; -/** - * TODO - * - * Circularity prevention. Before setting properties, create a WeakMap of - * instances by URIs, so if a property refers to a created instance, we just - * pass it in. - */ public class ConfigurationBeanLoaderTest extends ConfigurationBeanLoaderTestBase { @@ -394,6 +389,19 @@ public void simpleSuccessIgnoringExtraProperties() throws ConfigurationBeanLoade public static class SimpleSuccess { // Nothing of interest. } + + public static class Friend { + + public Friend() { + + } + Friend friend; + + @Property(uri = "http://set.friend/property") + public void setFriend(Friend friend) { + this.friend = friend; + } + } // -------------------------------------------- @@ -673,6 +681,21 @@ public void loadAll_oneResult_success() Set instances = loader.loadAll(SimpleSuccess.class); assertEquals(1, instances.size()); } + + // -------------------------------------------- + + @Test + public void loop_test() throws ConfigurationBeanLoaderException { + model.add(new Statement[] { + typeStatement("http://friend.instance/one", toJavaUri(Friend.class)), + typeStatement("http://friend.instance/two", toJavaUri(Friend.class)), + objectProperty("http://friend.instance/one", "http://set.friend/property", "http://friend.instance/two"), + objectProperty("http://friend.instance/two", "http://set.friend/property", "http://friend.instance/one") }); + + Friend friend = loader.loadInstance("http://friend.instance/one", Friend.class); + assertNotEquals(friend, friend.friend); + assertEquals(friend, friend.friend.friend); + } // -------------------------------------------- From 1f2ee82255c675527a49b840e8caceef37387e17 Mon Sep 17 00:00:00 2001 From: Georgy Litvinov Date: Fri, 8 Mar 2024 09:46:56 +0100 Subject: [PATCH 03/14] Split: extracted ConfigurationRdfParser modifications use dynapi model instead of full union moved dynamic-api-implementation.n3 to dynapi model --- .../webapp/utils/configuration/ConfigurationRdfParser.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/ConfigurationRdfParser.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/ConfigurationRdfParser.java index 4153d22953..b931bed7a1 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/ConfigurationRdfParser.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/ConfigurationRdfParser.java @@ -120,6 +120,9 @@ private static Class determineConcreteClass( for (RDFNode node : m .listObjectsOfProperty(createResource(uri), RDF.type) .toSet()) { + if (node.isAnon()) { + continue; + } if (!node.isURIResource()) { throw typeMustBeUriResource(node); } From 73161b389e3ccdc9354dfc64832f90ce19361b03 Mon Sep 17 00:00:00 2001 From: Georgy Litvinov Date: Fri, 8 Mar 2024 09:13:59 +0100 Subject: [PATCH 04/14] Split: extracted ConfigurationBeanLoader modification AbstractPool improvements fix configuration bean loader log errors --- .../utils/configuration/ConfigurationBeanLoader.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/ConfigurationBeanLoader.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/ConfigurationBeanLoader.java index 10b4510db7..d78d37ecc8 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/ConfigurationBeanLoader.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/ConfigurationBeanLoader.java @@ -198,15 +198,15 @@ public Set loadAll(Class resultClass) /** * Find all of the resources with the specified class, and instantiate them. */ - public Set loadEach(Class resultClass){ + public Map loadEach(Class resultClass){ Set uris = new HashSet<>(); findUris(resultClass, uris); - Set instances = new HashSet<>(); + HashMap instances = new HashMap<>(); for (String uri : uris) { try { - instances.add(loadInstance(uri, resultClass)); + instances.put(uri, loadInstance(uri, resultClass)); } catch (ConfigurationBeanLoaderException e) { - e.printStackTrace(); + log.error(e, e); } } return instances; From 067f4a23b4b3087e0e164f1072f8d04e8ca62c17 Mon Sep 17 00:00:00 2001 From: Georgy Litvinov Date: Fri, 8 Mar 2024 09:36:01 +0100 Subject: [PATCH 05/14] Split: extracted ConfigurationRdfParser refact: extracted variables to simplify debugging --- .../utils/configuration/ConfigurationRdfParser.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/ConfigurationRdfParser.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/ConfigurationRdfParser.java index b931bed7a1..6c6ecf5656 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/ConfigurationRdfParser.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/ConfigurationRdfParser.java @@ -57,7 +57,8 @@ private static void confirmExistenceInModel(LockableModel locking, Selector s = new SimpleSelector(createResource(uri), null, (RDFNode) null); try (LockedModel m = locking.read()) { - if (m.listStatements(s).toList().isEmpty()) { + final List subjectStatements = m.listStatements(s).toList(); + if (subjectStatements.isEmpty()) { throw individualDoesNotAppearInModel(uri); } } @@ -117,9 +118,10 @@ private static Class determineConcreteClass( Set> concreteClasses = new HashSet<>(); try (LockedModel m = locking.read()) { - for (RDFNode node : m + final Set objectsWithProperty = m .listObjectsOfProperty(createResource(uri), RDF.type) - .toSet()) { + .toSet(); + for (RDFNode node : objectsWithProperty) { if (node.isAnon()) { continue; } From 40d16a0cc3fac2911efbb2893eabf22e3d72b01d Mon Sep 17 00:00:00 2001 From: Georgy Litvinov Date: Tue, 3 Jan 2023 14:12:45 +0100 Subject: [PATCH 06/14] added asString Property annotation attribute to provide resource uris as String into methods --- .../webapp/utils/configuration/Property.java | 1 + .../utils/configuration/PropertyType.java | 27 ++++++++++++------- .../utils/configuration/WrappedInstance.java | 2 +- 3 files changed, 19 insertions(+), 11 deletions(-) diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/Property.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/Property.java index da531f5984..32eb339136 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/Property.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/Property.java @@ -17,4 +17,5 @@ String uri(); int minOccurs() default 0; int maxOccurs() default Integer.MAX_VALUE; + boolean asString() default false; } diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/PropertyType.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/PropertyType.java index 4c69e3b9e6..ce80b6b2c6 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/PropertyType.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/PropertyType.java @@ -246,6 +246,7 @@ public static abstract class PropertyMethod { protected final PropertyType type; protected final Method method; protected final String propertyUri; + protected final boolean asString; protected final int minOccurs; protected final int maxOccurs; @@ -257,6 +258,7 @@ public PropertyMethod(PropertyType type, Method method, this.propertyUri = annotation.uri(); this.minOccurs = annotation.minOccurs(); this.maxOccurs = annotation.maxOccurs(); + this.asString = annotation.asString(); checkCardinalityBounds(); } @@ -283,16 +285,21 @@ public int getMinOccurs() { public int getMaxOccurs() { return maxOccurs; } - - public void confirmCompatible(PropertyStatement ps) - throws PropertyTypeException { - if (type != ps.getType() && - ! (isSubtype(ps.getType(), type))){ - throw new PropertyTypeException( - "Can't apply statement of type " + ps.getType() - + " to a method of type " + type); - } - } + + public boolean getAsString() { + return asString; + } + + public void confirmCompatible(PropertyStatement ps) throws PropertyTypeException { + final PropertyType psType = ps.getType(); + if (asString && psType.equals(PropertyType.RESOURCE) && type.equals(PropertyType.STRING)) { + return; + } + if (type != psType && !(isSubtype(psType, type))) { + throw new PropertyTypeException( + "Can't apply statement of type " + psType + " to a method of type " + type); + } + } private boolean isSubtype(PropertyType subType, PropertyType superType){ if (subType.equals(INTEGER) && superType.equals(FLOAT)) diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/WrappedInstance.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/WrappedInstance.java index df04470014..fc1b9998af 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/WrappedInstance.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/WrappedInstance.java @@ -128,7 +128,7 @@ public void setProperties(ConfigurationBeanLoader loader, } pm.confirmCompatible(ps); - if (ps instanceof ResourcePropertyStatement) { + if (!pm.asString && ps instanceof ResourcePropertyStatement) { ResourcePropertyStatement rps = (ResourcePropertyStatement) ps; Object subordinate = loader.loadSubordinateInstance(rps.getValue(), pm.getParameterType()); pm.invoke(instance, subordinate); From acb0c12cc2411a4f3b76944ea64fa9daa4b12d57 Mon Sep 17 00:00:00 2001 From: Georgy Litvinov Date: Fri, 8 Mar 2024 09:17:56 +0100 Subject: [PATCH 07/14] Split: extracted ConfigurationBeanLoader modifications endpoint reorganization in progress tests are passing test fixes, refactorings, fixed concurrent use of action in DynamicAPIDocumentation fix model provider removed unused imports removed debug statements remamings --- .../configuration/ConfigurationBeanLoader.java | 1 - .../configuration/ConfigurationRdfParser.java | 1 + .../webapp/utils/configuration/PropertyType.java | 2 +- .../ConfigurationBeanLoaderTestBase.java | 14 ++++++-------- 4 files changed, 8 insertions(+), 10 deletions(-) diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/ConfigurationBeanLoader.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/ConfigurationBeanLoader.java index d78d37ecc8..a21ffd2fdc 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/ConfigurationBeanLoader.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/ConfigurationBeanLoader.java @@ -163,7 +163,6 @@ protected T loadSubordinateInstance(String uri, Class resultClass) throw new ConfigurationBeanLoaderException(uri, e); } } - try { ConfigurationRdf parsedRdf = ConfigurationRdfParser .parse(locking, uri, resultClass); diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/ConfigurationRdfParser.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/ConfigurationRdfParser.java index 6c6ecf5656..9de5f9c58f 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/ConfigurationRdfParser.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/ConfigurationRdfParser.java @@ -121,6 +121,7 @@ private static Class determineConcreteClass( final Set objectsWithProperty = m .listObjectsOfProperty(createResource(uri), RDF.type) .toSet(); + for (RDFNode node : objectsWithProperty) { if (node.isAnon()) { continue; diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/PropertyType.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/PropertyType.java index ce80b6b2c6..0d29850ee1 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/PropertyType.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/PropertyType.java @@ -297,7 +297,7 @@ public void confirmCompatible(PropertyStatement ps) throws PropertyTypeException } if (type != psType && !(isSubtype(psType, type))) { throw new PropertyTypeException( - "Can't apply statement of type " + psType + " to a method of type " + type); + "Can't apply statement of type " + psType + " to a method of type " + type + ".\n Class " + method.getDeclaringClass().getCanonicalName() + ". Method:" + method.getName()); } } diff --git a/api/src/test/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/ConfigurationBeanLoaderTestBase.java b/api/src/test/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/ConfigurationBeanLoaderTestBase.java index 66db7d49f7..8ca5bbaa34 100644 --- a/api/src/test/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/ConfigurationBeanLoaderTestBase.java +++ b/api/src/test/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/ConfigurationBeanLoaderTestBase.java @@ -63,15 +63,13 @@ public void setup() { // Helper methods for simple failure // ---------------------------------------------------------------------- - protected void expectSimpleFailure(Class failureClass, - ExpectedThrowable expected, ExpectedThrowable cause) - throws ConfigurationBeanLoaderException { - expectException(expected.getClazz(), expected.getMessageSubstring(), - cause.getClazz(), cause.getMessageSubstring()); + protected void expectSimpleFailure(Class failureClass, ExpectedThrowable expected, ExpectedThrowable cause) + throws ConfigurationBeanLoaderException { + expectException(expected.getClazz(), expected.getMessageSubstring(), cause.getClazz(), cause.getMessageSubstring()); - @SuppressWarnings("unused") - Object unused = loader.loadInstance(GENERIC_INSTANCE_URI, failureClass); - } + @SuppressWarnings("unused") + Object unused = loader.loadInstance(GENERIC_INSTANCE_URI, failureClass); + } protected ExpectedThrowable throwable(Class clazz, String messageSubstring) { From a1473cb46c7abcdd8d426a84129cf6eba3409c03 Mon Sep 17 00:00:00 2001 From: Georgy Litvinov Date: Mon, 30 Jan 2023 07:53:10 +0100 Subject: [PATCH 08/14] unhide configuration bean loader error log --- .../webapp/utils/configuration/ConfigurationBeanLoader.java | 1 + 1 file changed, 1 insertion(+) diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/ConfigurationBeanLoader.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/ConfigurationBeanLoader.java index a21ffd2fdc..edffab3885 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/ConfigurationBeanLoader.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/ConfigurationBeanLoader.java @@ -175,6 +175,7 @@ protected T loadSubordinateInstance(String uri, Class resultClass) wrapper.validate(); return wrapper.getInstance(); } catch (Exception e) { + log.error(e,e); throw new ConfigurationBeanLoaderException( "Failed to load '" + uri + "'", e); } From 3b7ed538402fb1cd01d5f15fbdce6f4f6adc0500 Mon Sep 17 00:00:00 2001 From: Georgy Litvinov Date: Fri, 8 Mar 2024 10:51:35 +0100 Subject: [PATCH 09/14] Revert "unhide configuration bean loader error log" This reverts commit a1473cb46c7abcdd8d426a84129cf6eba3409c03. --- .../webapp/utils/configuration/ConfigurationBeanLoader.java | 1 - 1 file changed, 1 deletion(-) diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/ConfigurationBeanLoader.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/ConfigurationBeanLoader.java index edffab3885..a21ffd2fdc 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/ConfigurationBeanLoader.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/ConfigurationBeanLoader.java @@ -175,7 +175,6 @@ protected T loadSubordinateInstance(String uri, Class resultClass) wrapper.validate(); return wrapper.getInstance(); } catch (Exception e) { - log.error(e,e); throw new ConfigurationBeanLoaderException( "Failed to load '" + uri + "'", e); } From 056c4d7b8e4bd3ff598b3aff0a061a845fc62f6f Mon Sep 17 00:00:00 2001 From: Georgy Litvinov Date: Fri, 8 Mar 2024 10:26:38 +0100 Subject: [PATCH 10/14] remove not needed import --- .../webapp/utils/configuration/ConfigurationBeanLoaderTest.java | 1 - 1 file changed, 1 deletion(-) diff --git a/api/src/test/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/ConfigurationBeanLoaderTest.java b/api/src/test/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/ConfigurationBeanLoaderTest.java index dc6552cb94..c4aa766c05 100644 --- a/api/src/test/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/ConfigurationBeanLoaderTest.java +++ b/api/src/test/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/ConfigurationBeanLoaderTest.java @@ -25,7 +25,6 @@ import org.junit.Ignore; import org.junit.Test; -import edu.cornell.mannlib.vitro.webapp.dynapi.components.OperationalStep; import edu.cornell.mannlib.vitro.webapp.modelaccess.ContextModelAccess; import edu.cornell.mannlib.vitro.webapp.modelaccess.RequestModelAccess; import edu.cornell.mannlib.vitro.webapp.utils.configuration.ConfigurationRdfParser.InvalidConfigurationRdfException; From 7f9f29df11c448f648fff15d7681275526e0a211 Mon Sep 17 00:00:00 2001 From: Georgy Litvinov Date: Fri, 8 Mar 2024 11:18:34 +0100 Subject: [PATCH 11/14] fixed PropertyType imports --- .../vitro/webapp/utils/configuration/PropertyType.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/PropertyType.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/PropertyType.java index 0d29850ee1..7dfa8ac553 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/PropertyType.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/PropertyType.java @@ -2,6 +2,14 @@ package edu.cornell.mannlib.vitro.webapp.utils.configuration; +import static org.apache.jena.datatypes.xsd.XSDDatatype.XSDboolean; +import static org.apache.jena.datatypes.xsd.XSDDatatype.XSDdateTime; +import static org.apache.jena.datatypes.xsd.XSDDatatype.XSDdecimal; +import static org.apache.jena.datatypes.xsd.XSDDatatype.XSDfloat; +import static org.apache.jena.datatypes.xsd.XSDDatatype.XSDint; +import static org.apache.jena.datatypes.xsd.XSDDatatype.XSDinteger; +import static org.apache.jena.datatypes.xsd.XSDDatatype.XSDstring; + import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; @@ -11,8 +19,6 @@ import org.apache.jena.rdf.model.RDFNode; import org.apache.jena.rdf.model.Statement; -import static org.apache.jena.datatypes.xsd.XSDDatatype.*; - /** * An enumeration of the types of properties that the ConfigurationBeanLoader * will support. From b776311e1594c74d6bafccb550132af026f80d0e Mon Sep 17 00:00:00 2001 From: Georgy Litvinov Date: Tue, 12 Mar 2024 17:44:57 +0100 Subject: [PATCH 12/14] Added tests to check loading integers, booleans and Resource as a string. --- .../ConfigurationBeanLoaderTest.java | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/api/src/test/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/ConfigurationBeanLoaderTest.java b/api/src/test/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/ConfigurationBeanLoaderTest.java index c4aa766c05..9652a6603e 100644 --- a/api/src/test/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/ConfigurationBeanLoaderTest.java +++ b/api/src/test/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/ConfigurationBeanLoaderTest.java @@ -6,7 +6,9 @@ import static edu.cornell.mannlib.vitro.testing.ModelUtilitiesTestHelper.objectProperty; import static edu.cornell.mannlib.vitro.testing.ModelUtilitiesTestHelper.typeStatement; import static edu.cornell.mannlib.vitro.webapp.utils.configuration.ConfigurationBeanLoader.toJavaUri; +import static org.apache.jena.datatypes.xsd.XSDDatatype.XSDboolean; import static org.apache.jena.datatypes.xsd.XSDDatatype.XSDfloat; +import static org.apache.jena.datatypes.xsd.XSDDatatype.XSDinteger; import static org.apache.jena.datatypes.xsd.XSDDatatype.XSDstring; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; @@ -395,11 +397,29 @@ public Friend() { } Friend friend; + String uri; + Integer intNumber; + boolean booleanValue; @Property(uri = "http://set.friend/property") public void setFriend(Friend friend) { this.friend = friend; } + + @Property(uri = "http://set.friend/asString", asString = true) + public void setUri(String uri) { + this.uri = uri; + } + + @Property(uri = "http://set.friend/setInteger") + public void setIntNumber(int intNumber) { + this.intNumber = intNumber; + } + + @Property(uri = "http://set.friend/setBoolean") + public void setBooleanValue(boolean booleanValue) { + this.booleanValue = booleanValue; + } } // -------------------------------------------- @@ -696,6 +716,37 @@ public void loop_test() throws ConfigurationBeanLoaderException { assertEquals(friend, friend.friend.friend); } + @Test + public void loadUriAsString() throws ConfigurationBeanLoaderException { + model.add(new Statement[] { + typeStatement("http://friend.instance/one", toJavaUri(Friend.class)), + typeStatement("http://friend.instance/two", toJavaUri(Friend.class)), + objectProperty("http://friend.instance/one", "http://set.friend/asString", + "http://friend.instance/two"), }); + + Friend friend = loader.loadInstance("http://friend.instance/one", Friend.class); + assertEquals(friend.uri, "http://friend.instance/two"); + } + + @Test + public void loadInteger() throws ConfigurationBeanLoaderException { + model.add(new Statement[] { + typeStatement("http://friend.instance/one", toJavaUri(Friend.class)), + dataProperty("http://friend.instance/one", "http://set.friend/setInteger", 42, XSDinteger), }); + + Friend friend = loader.loadInstance("http://friend.instance/one", Friend.class); + assertEquals(new Integer(42), friend.intNumber); + } + + @Test + public void loadBoolean() throws ConfigurationBeanLoaderException { + model.add(new Statement[] { + typeStatement("http://friend.instance/one", toJavaUri(Friend.class)), + dataProperty("http://friend.instance/one", "http://set.friend/setBoolean", true, XSDboolean), }); + + Friend friend = loader.loadInstance("http://friend.instance/one", Friend.class); + assertEquals(Boolean.TRUE, friend.booleanValue); + } // -------------------------------------------- @Test From 365cfae29f0cf443f19aad7f45504688e3fc0198 Mon Sep 17 00:00:00 2001 From: Georgy Litvinov Date: Thu, 7 Mar 2024 15:54:31 +0100 Subject: [PATCH 13/14] refact: remove not needed ServletContext dependency --- .../filtering/filters/VitroFilterUtils.java | 6 +-- .../webapp/dao/jena/VClassGroupCache.java | 15 ++----- .../webapp/searchindex/SearchIndexerImpl.java | 9 ++--- .../ConfigurationBeanLoader.java | 39 ++++--------------- .../utils/configuration/WrappedInstance.java | 21 +++------- .../ConfigurationBeanLoaderTest.java | 25 ------------ .../ConfigurationBeanLoaderTestBase.java | 2 +- 7 files changed, 22 insertions(+), 95 deletions(-) diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/filtering/filters/VitroFilterUtils.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/filtering/filters/VitroFilterUtils.java index 169a7e09b4..33ee73f5c5 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/filtering/filters/VitroFilterUtils.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/filtering/filters/VitroFilterUtils.java @@ -6,8 +6,6 @@ import java.util.Comparator; import java.util.List; -import javax.servlet.ServletContext; - import net.sf.jga.fn.UnaryFunctor; import net.sf.jga.fn.adaptor.ChainUnary; import net.sf.jga.fn.property.GetProperty; @@ -16,12 +14,12 @@ /** * Static methods to help create commonly used filters. */ -public class VitroFilterUtils { +public class VitroFilterUtils { /** * Gets a filter that hides any property or resource that is restricted from * public view. */ - public static VitroFilters getPublicFilter(ServletContext ctx) { + public static VitroFilters getPublicFilter() { return new FilterByDisplayPermission(); } diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/jena/VClassGroupCache.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/jena/VClassGroupCache.java index 96c125f04c..82cff3bda0 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/jena/VClassGroupCache.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/jena/VClassGroupCache.java @@ -89,14 +89,7 @@ public class VClassGroupCache implements SearchIndexer.Listener { private final RebuildGroupCacheThread _cacheRebuildThread; - /** - * Need a pointer to the context to get DAOs and models. - */ - private final ServletContext context; - - private VClassGroupCache(ServletContext context) { - this.context = context; this._groupList = null; if (StartupStatus.getBean(context).isStartupAborted()) { @@ -199,7 +192,7 @@ protected void requestStop() { } protected VClassGroupDao getVCGDao() { - return ModelAccess.on(context).getWebappDaoFactory().getVClassGroupDao(); + return ModelAccess.getInstance().getWebappDaoFactory().getVClassGroupDao(); } public void doSynchronousRebuild(){ @@ -263,11 +256,11 @@ public static VClassGroupsForRequest getVClassGroups(HttpServletRequest req) { */ protected static void rebuildCacheUsingSearch( VClassGroupCache cache ) throws SearchEngineException{ long start = System.currentTimeMillis(); - WebappDaoFactory wdFactory = ModelAccess.on(cache.context).getWebappDaoFactory(); + WebappDaoFactory wdFactory = ModelAccess.getInstance().getWebappDaoFactory(); SearchEngine searchEngine = ApplicationUtils.instance().getSearchEngine(); - VitroFilters vFilters = VitroFilterUtils.getPublicFilter(cache.context); + VitroFilters vFilters = VitroFilterUtils.getPublicFilter(); VClassGroupDao vcgDao = new WebappDaoFactoryFiltering(wdFactory, vFilters).getVClassGroupDao(); List groups = vcgDao.getPublicGroupsWithVClasses(ORDER_BY_DISPLAYRANK, @@ -488,7 +481,7 @@ protected void checkAndDoUpdate(Statement stmt) { } else if(VitroVocabulary.DISPLAY_RANK.equals(stmt.getPredicate().getURI())){ requestCacheUpdate(); } else if (RDFS.label.equals(stmt.getPredicate())){ - OntModel jenaOntModel = ModelAccess.on(context).getOntModelSelector().getTBoxModel(); + OntModel jenaOntModel = ModelAccess.getInstance().getOntModelSelector().getTBoxModel(); if( isClassNameChange(stmt, jenaOntModel) ) { requestCacheUpdate(); } diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/searchindex/SearchIndexerImpl.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/searchindex/SearchIndexerImpl.java index c1f1f0a611..78cef9ac62 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/searchindex/SearchIndexerImpl.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/searchindex/SearchIndexerImpl.java @@ -27,7 +27,6 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; -import javax.servlet.ServletContext; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -93,7 +92,6 @@ public class SearchIndexerImpl implements SearchIndexer { private Integer threadPoolSize; private WorkerThreadPool pool; - private ServletContext ctx; private List excluders; private List modifiers; private Set uriFinders; @@ -134,7 +132,6 @@ public void startup(Application application, ComponentStartupStatus ss) { "startup() called after shutdown()."); } try { - this.ctx = application.getServletContext(); this.wadf = getFilteredWebappDaoFactory(); loadConfiguration(); @@ -150,14 +147,14 @@ public void startup(Application application, ComponentStartupStatus ss) { /** With a filtered factory, only public data goes into the search index. */ private WebappDaoFactory getFilteredWebappDaoFactory() { - WebappDaoFactory rawWadf = ModelAccess.on(ctx).getWebappDaoFactory(); - VitroFilters vf = VitroFilterUtils.getPublicFilter(ctx); + WebappDaoFactory rawWadf = ModelAccess.getInstance().getWebappDaoFactory(); + VitroFilters vf = VitroFilterUtils.getPublicFilter(); return new WebappDaoFactoryFiltering(rawWadf, vf); } private void loadConfiguration() throws ConfigurationBeanLoaderException { ConfigurationBeanLoader beanLoader = new ConfigurationBeanLoader( - ModelAccess.on(ctx).getOntModel(DISPLAY), ctx); + ModelAccess.getInstance().getOntModel(DISPLAY)); uriFinders = beanLoader.loadAll(IndexingUriFinder.class); excluders = new ArrayList<>(); diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/ConfigurationBeanLoader.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/ConfigurationBeanLoader.java index a21ffd2fdc..b1e1638bd0 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/ConfigurationBeanLoader.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/ConfigurationBeanLoader.java @@ -12,7 +12,6 @@ import java.util.Set; import java.util.TreeSet; -import javax.servlet.ServletContext; import javax.servlet.http.HttpServletRequest; import org.apache.commons.logging.Log; @@ -90,12 +89,6 @@ public static boolean isMatchingJavaUri(String uri1, String uri2) { /** Must not be null. */ private final LockableModel locking; - /** - * May be null, but the loader will be unable to satisfy instances of - * ContextModelUser. - */ - private final ServletContext ctx; - /** * May be null, but the loader will be unable to satisfy instances of * RequestModelUser. @@ -103,39 +96,21 @@ public static boolean isMatchingJavaUri(String uri1, String uri2) { private final HttpServletRequest req; public ConfigurationBeanLoader(Model model) { - this(new LockableModel(model), null, null); + this(new LockableModel(model), null); } public ConfigurationBeanLoader(LockableModel locking) { - this(locking, null, null); - } - - public ConfigurationBeanLoader(Model model, ServletContext ctx) { - this(new LockableModel(model), ctx, null); - } - - public ConfigurationBeanLoader(LockableModel locking, ServletContext ctx) { - this(locking, ctx, null); + this(locking, null); } public ConfigurationBeanLoader(Model model, HttpServletRequest req) { this(new LockableModel(model), req); } - public ConfigurationBeanLoader(LockableModel locking, - HttpServletRequest req) { - this(locking, - (req == null) ? null : req.getSession().getServletContext(), - req); - } - - private ConfigurationBeanLoader(LockableModel locking, ServletContext ctx, - HttpServletRequest req) { - this.locking = Objects.requireNonNull(locking, - "locking may not be null."); - this.req = req; - this.ctx = ctx; - } + public ConfigurationBeanLoader(LockableModel locking, HttpServletRequest req) { + this.locking = Objects.requireNonNull(locking, "locking may not be null."); + this.req = req; + } /** * Load the instance with this URI, if it is assignable to this class. @@ -168,7 +143,7 @@ protected T loadSubordinateInstance(String uri, Class resultClass) .parse(locking, uri, resultClass); WrappedInstance wrapper = InstanceWrapper .wrap(parsedRdf.getConcreteClass()); - wrapper.satisfyInterfaces(ctx, req); + wrapper.satisfyInterfaces(req); wrapper.checkCardinality(parsedRdf.getPropertyStatements()); instancesMap.put(uri, wrapper.getInstance()); wrapper.setProperties(this, parsedRdf.getPropertyStatements()); diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/WrappedInstance.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/WrappedInstance.java index fc1b9998af..3bd0b89e95 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/WrappedInstance.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/WrappedInstance.java @@ -9,7 +9,6 @@ import java.util.Map; import java.util.Set; -import javax.servlet.ServletContext; import javax.servlet.http.HttpServletRequest; import edu.cornell.mannlib.vitro.webapp.config.ConfigurationProperties; @@ -43,16 +42,11 @@ public WrappedInstance(T instance, * null. If the instance expects request models, an exception will be * thrown. */ - public void satisfyInterfaces(ServletContext ctx, HttpServletRequest req) + public void satisfyInterfaces(HttpServletRequest req) throws ResourceUnavailableException { if (instance instanceof ContextModelsUser) { - if (ctx == null) { - throw new ResourceUnavailableException("Cannot satisfy " - + "ContextModelsUser interface: context not available."); - } else { - ContextModelsUser cmu = (ContextModelsUser) instance; - cmu.setContextModels(ModelAccess.on(ctx)); - } + ContextModelsUser cmu = (ContextModelsUser) instance; + cmu.setContextModels(ModelAccess.getInstance()); } if (instance instanceof RequestModelsUser) { if (req == null) { @@ -64,13 +58,8 @@ public void satisfyInterfaces(ServletContext ctx, HttpServletRequest req) } } if (instance instanceof ConfigurationReader) { - if (ctx == null) { - throw new ResourceUnavailableException("Cannot satisfy " - + "ConfigurationReader interface: context not available."); - } else { - ConfigurationReader cr = (ConfigurationReader) instance; - cr.setConfigurationProperties(ConfigurationProperties.getBean(ctx)); - } + ConfigurationReader cr = (ConfigurationReader) instance; + cr.setConfigurationProperties(ConfigurationProperties.getInstance()); } } diff --git a/api/src/test/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/ConfigurationBeanLoaderTest.java b/api/src/test/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/ConfigurationBeanLoaderTest.java index 9652a6603e..42c258c6d0 100644 --- a/api/src/test/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/ConfigurationBeanLoaderTest.java +++ b/api/src/test/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/ConfigurationBeanLoaderTest.java @@ -253,31 +253,6 @@ public ConstructorFails() { // -------------------------------------------- - @Test - public void loaderCantSatisfyContextModelsUser_throwsException() - throws ConfigurationBeanLoaderException { - model.add(typeStatement(GENERIC_INSTANCE_URI, - toJavaUri(NeedsContextModels.class))); - - loader = noContextLoader; - - expectSimpleFailure( - NeedsContextModels.class, - throwable(ConfigurationBeanLoaderException.class, - "Failed to load"), - throwable(ResourceUnavailableException.class, - "Cannot satisfy ContextModelsUser")); - } - - public static class NeedsContextModels implements ContextModelsUser { - @Override - public void setContextModels(ContextModelAccess models) { - // Nothing to do - } - } - - // -------------------------------------------- - @Test public void loaderCantSatisfyRequestModelsUser_throwsException() throws ConfigurationBeanLoaderException { diff --git a/api/src/test/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/ConfigurationBeanLoaderTestBase.java b/api/src/test/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/ConfigurationBeanLoaderTestBase.java index 8ca5bbaa34..c46780127d 100644 --- a/api/src/test/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/ConfigurationBeanLoaderTestBase.java +++ b/api/src/test/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/ConfigurationBeanLoaderTestBase.java @@ -55,7 +55,7 @@ public void setup() { model = model(); loader = new ConfigurationBeanLoader(model, req); - noRequestLoader = new ConfigurationBeanLoader(model, ctx); + noRequestLoader = new ConfigurationBeanLoader(model); noContextLoader = new ConfigurationBeanLoader(model); } From 83068d2939b6f3aeb9a74f2f2b9601355ec11e18 Mon Sep 17 00:00:00 2001 From: Georgy Litvinov Date: Thu, 21 Mar 2024 18:23:06 +0100 Subject: [PATCH 14/14] Configuration bean loader modification to load dynamic api instances --- .../ConfigurationBeanLoader.java | 52 ++++++-- .../configuration/ConfigurationRdfParser.java | 121 +++++++++--------- .../ConfigurationBeanLoaderTest.java | 3 +- 3 files changed, 98 insertions(+), 78 deletions(-) diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/ConfigurationBeanLoader.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/ConfigurationBeanLoader.java index b1e1638bd0..f84c509695 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/ConfigurationBeanLoader.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/ConfigurationBeanLoader.java @@ -2,11 +2,9 @@ package edu.cornell.mannlib.vitro.webapp.utils.configuration; -import static org.apache.jena.rdf.model.ResourceFactory.createResource; import java.util.HashMap; import java.util.HashSet; -import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; @@ -16,10 +14,14 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.apache.jena.query.ParameterizedSparqlString; +import org.apache.jena.query.Query; +import org.apache.jena.query.QueryExecution; +import org.apache.jena.query.QueryExecutionFactory; +import org.apache.jena.query.QueryFactory; +import org.apache.jena.query.QuerySolution; +import org.apache.jena.query.ResultSet; import org.apache.jena.rdf.model.Model; -import org.apache.jena.rdf.model.Resource; -import org.apache.jena.vocabulary.RDF; - import edu.cornell.mannlib.vitro.webapp.utils.jena.criticalsection.LockableModel; import edu.cornell.mannlib.vitro.webapp.utils.jena.criticalsection.LockedModel; @@ -28,7 +30,23 @@ */ public class ConfigurationBeanLoader { - private static final Log log = LogFactory.getLog(ConfigurationBeanLoader.class); + private static final String FIND_URI_QUERY = "" + + "PREFIX rdfs: \n" + + "PREFIX dynapi: \n" + + "SELECT ?uri\n" + + "WHERE {\n" + + " {\n" + + " ?uri a ?type .\n" + + " ?type dynapi:implementedBy ?java .\n" + + " }\n" + + " UNION\n" + + " {\n" + + " ?uri a ?java .\n" + + " }\n" + + "} "; + + + private static final Log log = LogFactory.getLog(ConfigurationBeanLoader.class); private static final String JAVA_URI_PREFIX = "java:"; @@ -188,14 +206,20 @@ public Map loadEach(Class resultClass){ private void findUris(Class resultClass, Set uris) { try (LockedModel m = locking.read()) { - for (String typeUri : toPossibleJavaUris(resultClass)) { - List resources = m.listResourcesWithProperty(RDF.type, - createResource(typeUri)).toList(); - for (Resource r : resources) { - if (r.isURIResource()) { - uris.add(r.getURI()); - } - } + for (String typeUri : toPossibleJavaUris(resultClass)) { + ParameterizedSparqlString pss = new ParameterizedSparqlString(FIND_URI_QUERY); + pss.setIri("java", typeUri); + Query query = QueryFactory.create(pss.toString()); + QueryExecution qexec = QueryExecutionFactory.create(query, m); + try { + ResultSet results = qexec.execSelect(); + while (results.hasNext()) { + QuerySolution solution = results.nextSolution(); + uris.add(solution.getResource("uri").getURI()); + } + } finally { + qexec.close(); + } } } } diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/ConfigurationRdfParser.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/ConfigurationRdfParser.java index 9de5f9c58f..17e8b876ed 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/ConfigurationRdfParser.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/ConfigurationRdfParser.java @@ -4,18 +4,21 @@ import static edu.cornell.mannlib.vitro.webapp.utils.configuration.ConfigurationBeanLoader.classnameFromJavaUri; import static edu.cornell.mannlib.vitro.webapp.utils.configuration.ConfigurationBeanLoader.isJavaUri; -import static edu.cornell.mannlib.vitro.webapp.utils.configuration.ConfigurationBeanLoader.isMatchingJavaUri; -import static edu.cornell.mannlib.vitro.webapp.utils.configuration.ConfigurationBeanLoader.toJavaUri; import static org.apache.jena.rdf.model.ResourceFactory.createResource; -import static org.apache.jena.rdf.model.ResourceFactory.createStatement; import java.lang.reflect.Modifier; import java.util.HashSet; import java.util.List; import java.util.Objects; -import java.util.Optional; import java.util.Set; +import org.apache.jena.query.ParameterizedSparqlString; +import org.apache.jena.query.Query; +import org.apache.jena.query.QueryExecution; +import org.apache.jena.query.QueryExecutionFactory; +import org.apache.jena.query.QueryFactory; +import org.apache.jena.query.QuerySolution; +import org.apache.jena.query.ResultSet; import org.apache.jena.rdf.model.Property; import org.apache.jena.rdf.model.RDFNode; import org.apache.jena.rdf.model.Selector; @@ -33,6 +36,28 @@ * ConfigurationRdf object. */ public class ConfigurationRdfParser { + + private static final String implementationClassesQuery = "" + + "PREFIX rdfs: \n" + + "PREFIX dynapi: \n" + + "SELECT DISTINCT \n" + + "?class \n" + + "?java \n" + + "(COUNT(DISTINCT(?intermediateClass)) as ?distance)\n" + + "WHERE {\n" + + " {\n" + + " ?uri a ?java .\n" + + " FILTER(STRSTARTS(STR(?java), 'java:'))\n" + + " }\n" + + " UNION\n" + + " {\n" + + " ?uri a ?type .\n" + + " ?type rdfs:subClassOf* ?intermediateClass .\n" + + " ?intermediateClass rdfs:subClassOf* ?class .\n" + + " ?class dynapi:implementedBy ?java .\n" + + " }\n" + + "} GROUP BY ?class ?java ORDER BY ?distance\n"; + public static ConfigurationRdf parse(LockableModel locking, String uri, Class resultClass) throws InvalidConfigurationRdfException { @@ -42,8 +67,6 @@ public static ConfigurationRdf parse(LockableModel locking, confirmExistenceInModel(locking, uri); - confirmEligibilityForResultClass(locking, uri, resultClass); - Set properties = loadProperties(locking, uri); Class concreteClass = determineConcreteClass(locking, uri, @@ -64,26 +87,6 @@ private static void confirmExistenceInModel(LockableModel locking, } } - private static void confirmEligibilityForResultClass(LockableModel locking, - String uri, Class resultClass) - throws InvalidConfigurationRdfException { - String resultClassUri = toJavaUri(resultClass); - try (LockedModel m = locking.read()) { - Set types = // - m.listObjectsOfProperty(createResource(uri), RDF.type) - .toSet(); - for (RDFNode typeNode : types) { - if (typeNode.isURIResource()) { - String typeUri = typeNode.asResource().getURI(); - if (isMatchingJavaUri(resultClassUri, typeUri)) { - return; - } - } - } - throw noTypeStatementForResultClass(uri, resultClassUri); - } - } - private static Set loadProperties(LockableModel locking, String uri) throws InvalidConfigurationRdfException { Set set = new HashSet<>(); @@ -112,31 +115,39 @@ private static Set loadProperties(LockableModel locking, } } - private static Class determineConcreteClass( - LockableModel locking, String uri, Class resultClass) - throws InvalidConfigurationRdfException { + private static Class determineConcreteClass(LockableModel locking, String uri, + Class resultClass) throws InvalidConfigurationRdfException { Set> concreteClasses = new HashSet<>(); try (LockedModel m = locking.read()) { - final Set objectsWithProperty = m - .listObjectsOfProperty(createResource(uri), RDF.type) - .toSet(); - - for (RDFNode node : objectsWithProperty) { - if (node.isAnon()) { - continue; - } - if (!node.isURIResource()) { - throw typeMustBeUriResource(node); - } - - String typeUri = node.asResource().getURI(); - if (!isConcreteClass(typeUri)) { - continue; - } - - concreteClasses.add(processTypeUri(typeUri, resultClass)); - } + ParameterizedSparqlString pss = new ParameterizedSparqlString(implementationClassesQuery); + pss.setIri("uri", uri); + Query query = QueryFactory.create(pss.toString()); + QueryExecution qexec = QueryExecutionFactory.create(query, m); + try { + ResultSet results = qexec.execSelect(); + int distance = -1; + while (results.hasNext()) { + QuerySolution solution = results.nextSolution(); + String typeUri = solution.getResource("java").toString(); + int solDistance = solution.getLiteral("distance").getInt(); + //set to distance of first solution + if(distance < 0) { + distance = solDistance; + } + //process only solutions with the same distance + if (distance != solDistance) { + break; + } + + if (!isConcreteClass(typeUri)) { + continue; + } + concreteClasses.add(processTypeUri(typeUri, resultClass)); + } + } finally { + qexec.close(); + } } if (concreteClasses.isEmpty()) { @@ -233,20 +244,6 @@ private static InvalidConfigurationRdfException failedToLoadClass( "Can't load this type: '" + typeUri + "'", e); } - private static InvalidConfigurationRdfException typeMustBeUriResource( - RDFNode node) { - return new InvalidConfigurationRdfException( - "Type must be a URI Resource: " + node); - } - - private static InvalidConfigurationRdfException noTypeStatementForResultClass( - String uri, String resultClassUri) { - return new InvalidConfigurationRdfException( - "A type statement is required: '" - + createStatement(createResource(uri), RDF.type, - createResource(resultClassUri))); - } - private static InvalidConfigurationRdfException noRdfStatements( String uri) { return new InvalidConfigurationRdfException( diff --git a/api/src/test/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/ConfigurationBeanLoaderTest.java b/api/src/test/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/ConfigurationBeanLoaderTest.java index 42c258c6d0..3bef516981 100644 --- a/api/src/test/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/ConfigurationBeanLoaderTest.java +++ b/api/src/test/java/edu/cornell/mannlib/vitro/webapp/utils/configuration/ConfigurationBeanLoaderTest.java @@ -83,6 +83,7 @@ public void noStatementsAboutUri_throwsException() "The model contains no statements about")); } + @Ignore @Test public void uriDoesNotDeclareResultClassAsType_throwsException() throws ConfigurationBeanLoaderException { @@ -339,10 +340,8 @@ public void setHelper(SimpleDateFormat sdf) { public void simpleSuccess() throws ConfigurationBeanLoaderException { model.add(typeStatement(SIMPLE_SUCCESS_INSTANCE_URI, toJavaUri(SimpleSuccess.class))); - SimpleSuccess instance = loader.loadInstance( SIMPLE_SUCCESS_INSTANCE_URI, SimpleSuccess.class); - assertNotNull(instance); }