From 1043251545ff331d02fce9263a48cf5a65d45d60 Mon Sep 17 00:00:00 2001 From: Daniel Gradecak Date: Wed, 28 Sep 2022 17:05:18 +0200 Subject: [PATCH 01/18] refactoring registrars --- .gitignore | 4 +- .../mvc/annotation/EnableAlfrescoMvcAop.java | 2 + .../mvc/aop/AlfrescoProxyRegistrar.java | 24 ++++++---- .../EnableAlfrescoMvcDispatcherServlet.java | 45 ------------------- .../rest/annotation/EnableWebAlfrescoMvc.java | 7 +-- .../config/AlfrescoRestServletRegistrar.java | 36 --------------- .../mvc/webscript/DispatcherWebscript.java | 22 ++++++++- .../config/AlfrescoMvcRestServletContext.java | 3 ++ .../context/AlfrescoMvcServletContext.java | 7 ++- 9 files changed, 50 insertions(+), 100 deletions(-) delete mode 100644 alfresco-mvc-rest/src/main/java/com/gradecak/alfresco/mvc/rest/annotation/EnableAlfrescoMvcDispatcherServlet.java delete mode 100644 alfresco-mvc-rest/src/main/java/com/gradecak/alfresco/mvc/rest/config/AlfrescoRestServletRegistrar.java diff --git a/.gitignore b/.gitignore index 160f20f..36304cb 100644 --- a/.gitignore +++ b/.gitignore @@ -11,4 +11,6 @@ local .idea *iml *.log -*.log.* \ No newline at end of file +*.log.* + +private-key.gpg \ No newline at end of file diff --git a/alfresco-mvc-aop/src/main/java/com/gradecak/alfresco/mvc/annotation/EnableAlfrescoMvcAop.java b/alfresco-mvc-aop/src/main/java/com/gradecak/alfresco/mvc/annotation/EnableAlfrescoMvcAop.java index 445b826..7a98504 100644 --- a/alfresco-mvc-aop/src/main/java/com/gradecak/alfresco/mvc/annotation/EnableAlfrescoMvcAop.java +++ b/alfresco-mvc-aop/src/main/java/com/gradecak/alfresco/mvc/annotation/EnableAlfrescoMvcAop.java @@ -43,4 +43,6 @@ String[] basePackages() default {}; Class[] basePackageClasses() default {}; + + boolean defaultPropertiesSupport() default true; } diff --git a/alfresco-mvc-aop/src/main/java/com/gradecak/alfresco/mvc/aop/AlfrescoProxyRegistrar.java b/alfresco-mvc-aop/src/main/java/com/gradecak/alfresco/mvc/aop/AlfrescoProxyRegistrar.java index d2dcaa0..3453c14 100644 --- a/alfresco-mvc-aop/src/main/java/com/gradecak/alfresco/mvc/aop/AlfrescoProxyRegistrar.java +++ b/alfresco-mvc-aop/src/main/java/com/gradecak/alfresco/mvc/aop/AlfrescoProxyRegistrar.java @@ -26,6 +26,7 @@ import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.beans.factory.xml.XmlBeanDefinitionReader; import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; +import org.springframework.context.support.PropertySourcesPlaceholderConfigurer; import org.springframework.core.Ordered; import org.springframework.core.annotation.AnnotationAttributes; import org.springframework.core.type.AnnotationMetadata; @@ -48,6 +49,11 @@ public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanD Assert.notNull(annotationMetadata, "AnnotationMetadata must not be null!"); Assert.notNull(registry, "BeanDefinitionRegistry must not be null!"); + // Guard against calls for sub-classes + if (annotationMetadata.getAnnotationAttributes(EnableAlfrescoMvcAop.class.getName()) == null) { + return; + } + boolean proxyBeanRegistered = false; for (String beanName : PackageAutoProxyCreator.DEFAULT_INTERCEPTORS) { if (registry.containsBeanDefinition(beanName)) { @@ -55,20 +61,21 @@ public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanD break; } } - + if (!proxyBeanRegistered) { XmlBeanDefinitionReader xmlReader = new XmlBeanDefinitionReader(registry); xmlReader.loadBeanDefinitions("classpath:com/gradecak/alfresco-mvc/alfresco-mvc-aop.xml"); } - // Guard against calls for sub-classes - if (annotationMetadata.getAnnotationAttributes(EnableAlfrescoMvcAop.class.getName()) == null) { - return; - } - this.attributes = new AnnotationAttributes( annotationMetadata.getAnnotationAttributes(EnableAlfrescoMvcAop.class.getName())); this.metadata = annotationMetadata; + + boolean defaultPropertiesSupport = attributes.getBoolean("defaultPropertiesSupport"); + if(defaultPropertiesSupport) { + RootBeanDefinition beanDefinition = new RootBeanDefinition(PropertySourcesPlaceholderConfigurer.class); + registry.registerBeanDefinition("propertySourcesPlaceholderConfigurer", beanDefinition); + } Iterable basePackages = getBasePackages(); for (String basePackage : basePackages) { @@ -115,13 +122,13 @@ public Iterable getBasePackages() { return packages; } - public static BeanDefinition registerOrEscalateApcAsRequired(Class cls, + public static void registerOrEscalateApcAsRequired(Class cls, BeanDefinitionRegistry registry, Object source, String basePackage) { Assert.notNull(registry, "BeanDefinitionRegistry must not be null"); String proxyPackageBeanName = PACKAGE_PROXY_CREATOR_BEAN_NAME + "." + basePackage; if (registry.containsBeanDefinition(proxyPackageBeanName)) { - return null; + return; } RootBeanDefinition beanDefinition = new RootBeanDefinition(cls); @@ -130,6 +137,5 @@ public static BeanDefinition registerOrEscalateApcAsRequired(Class DispatcherWebscriptServlet.this); + Supplier supplier = ((AbstractBeanDefinition) beanDefinition).getInstanceSupplier(); + if (supplier != null) { + Object object = supplier.get(); + if (!(object instanceof DispatcherWebscriptServlet)) { + throw new RuntimeException( + "Webscript dispatcherServlet has not been configured. Make sure to @Import(com.gradecak.alfresco.mvc.rest.config.AlfrescoRestServletRegistrar.class)"); + } + } else { + ((AbstractBeanDefinition) beanDefinition) + .setInstanceSupplier(() -> DispatcherWebscriptServlet.this); + } + } else { AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder .genericBeanDefinition(DispatcherWebscriptServlet.class).getBeanDefinition(); diff --git a/alfresco-mvc-rest/src/test/java/com/gradecak/alfresco/mvc/config/AlfrescoMvcRestServletContext.java b/alfresco-mvc-rest/src/test/java/com/gradecak/alfresco/mvc/config/AlfrescoMvcRestServletContext.java index ebe30b6..431c3b8 100644 --- a/alfresco-mvc-rest/src/test/java/com/gradecak/alfresco/mvc/config/AlfrescoMvcRestServletContext.java +++ b/alfresco-mvc-rest/src/test/java/com/gradecak/alfresco/mvc/config/AlfrescoMvcRestServletContext.java @@ -2,14 +2,17 @@ import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import com.gradecak.alfresco.mvc.controller.TestController; import com.gradecak.alfresco.mvc.rest.annotation.EnableWebAlfrescoMvc; +import com.gradecak.alfresco.mvc.rest.config.DefaultAlfrescoMvcServletContextConfiguration; @Configuration @EnableWebAlfrescoMvc @ComponentScan(basePackageClasses = { TestController.class }) +@Import(DefaultAlfrescoMvcServletContextConfiguration.class) public class AlfrescoMvcRestServletContext implements WebMvcConfigurer { } diff --git a/alfresco-mvc-rest/src/test/java/com/gradecak/alfresco/mvc/test/context/AlfrescoMvcServletContext.java b/alfresco-mvc-rest/src/test/java/com/gradecak/alfresco/mvc/test/context/AlfrescoMvcServletContext.java index aa37fa5..b54c02e 100644 --- a/alfresco-mvc-rest/src/test/java/com/gradecak/alfresco/mvc/test/context/AlfrescoMvcServletContext.java +++ b/alfresco-mvc-rest/src/test/java/com/gradecak/alfresco/mvc/test/context/AlfrescoMvcServletContext.java @@ -17,12 +17,15 @@ package com.gradecak.alfresco.mvc.test.context; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; -import com.gradecak.alfresco.mvc.rest.annotation.EnableAlfrescoMvcDispatcherServlet; +import com.gradecak.alfresco.mvc.rest.annotation.EnableWebAlfrescoMvc; +import com.gradecak.alfresco.mvc.rest.config.DefaultAlfrescoMvcServletContextConfiguration; @Configuration -@EnableAlfrescoMvcDispatcherServlet +@EnableWebAlfrescoMvc +@Import(DefaultAlfrescoMvcServletContextConfiguration.class) public class AlfrescoMvcServletContext implements WebMvcConfigurer { } From 813ede38095da7f190f93d5c085d086b5e765d2c Mon Sep 17 00:00:00 2001 From: Daniel Gradecak Date: Tue, 25 Oct 2022 08:36:06 +0200 Subject: [PATCH 02/18] release 8 preparation --- alfresco-mvc-aop/pom.xml | 2 +- alfresco-mvc-bom/pom.xml | 153 ++++++++++++++++++ alfresco-mvc-rest/pom.xml | 2 +- .../rest/annotation/EnableWebAlfrescoMvc.java | 44 ----- ...lfrescoMvcServletContextConfiguration.java | 56 ++++++- .../jackson/Jackson2QnameDeserializer.java | 4 +- .../mvc/webscript/DispatcherWebscript.java | 21 +-- .../config/AlfrescoMvcRestServletContext.java | 4 +- .../context/AlfrescoMvcServletContext.java | 4 +- .../AlfrescoMvcServletContext.java | 6 +- .../AlfrescoMvcServletContext.java | 5 +- .../test/webscript/AlfrescoMvcRestTest.java | 10 ++ pom.xml | 36 +++-- 13 files changed, 250 insertions(+), 97 deletions(-) create mode 100644 alfresco-mvc-bom/pom.xml delete mode 100644 alfresco-mvc-rest/src/main/java/com/gradecak/alfresco/mvc/rest/annotation/EnableWebAlfrescoMvc.java diff --git a/alfresco-mvc-aop/pom.xml b/alfresco-mvc-aop/pom.xml index 7fd9dca..2aff49f 100644 --- a/alfresco-mvc-aop/pom.xml +++ b/alfresco-mvc-aop/pom.xml @@ -6,7 +6,7 @@ com.gradecak.alfresco-mvc 8.0.0 - alfresco-mvc-bom + alfresco-mvc-parent ../ diff --git a/alfresco-mvc-bom/pom.xml b/alfresco-mvc-bom/pom.xml new file mode 100644 index 0000000..d8620be --- /dev/null +++ b/alfresco-mvc-bom/pom.xml @@ -0,0 +1,153 @@ + + + + 4.0.0 + + com.gradecak.alfresco-mvc + alfresco-mvc-bom + 8.0.0 + pom + + Alfresco MVC bom + Bom for Alfresco @MVC project + https://github.com/dgradecak/alfresco-mvc + + + + The Apache License, Version 2.0 + http://www.apache.org/licenses/LICENSE-2.0.txt + + + + + + Daniel Gradečak + daniel@pleosoft.com + PleoSoft + http://pleosoft.com/ + + + + + scm:git:git://github.com/dgradecak/alfresco-mvc.git + scm:git:ssh://github.com/dgradecak/alfresco-mvc.git + https://github.com/dgradecak/alfresco-mvc/tree/master + + + + 11 + 11 + 11 + 11 + UTF-8 + + + + alfresco-mvc-rest + alfresco-mvc-aop + + + + + + com.gradecak.alfresco-mvc + alfresco-mvc-rest + ${project.version} + compile + + + + com.gradecak.alfresco-mvc + alfresco-mvc-aop + ${project.version} + compile + + + + + + + + maven-surefire-plugin + 2.22.2 + + + maven-source-plugin + + + attach-sources + package + + jar-no-fork + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + 3.1.1 + + + attach-javadocs + + jar + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.1 + + 11 + + + + org.apache.maven.plugins + maven-gpg-plugin + 1.6 + + + sign-artifacts + verify + + sign + + + + --pinentry-mode + loopback + + + + + + + + + + + snapshot + + + + org.apache.maven.plugins + maven-gpg-plugin + 1.6 + + + sign-artifacts + none + + + + + + + + + diff --git a/alfresco-mvc-rest/pom.xml b/alfresco-mvc-rest/pom.xml index 808fa96..92209cf 100644 --- a/alfresco-mvc-rest/pom.xml +++ b/alfresco-mvc-rest/pom.xml @@ -6,7 +6,7 @@ com.gradecak.alfresco-mvc 8.0.0 - alfresco-mvc-bom + alfresco-mvc-parent ../ diff --git a/alfresco-mvc-rest/src/main/java/com/gradecak/alfresco/mvc/rest/annotation/EnableWebAlfrescoMvc.java b/alfresco-mvc-rest/src/main/java/com/gradecak/alfresco/mvc/rest/annotation/EnableWebAlfrescoMvc.java deleted file mode 100644 index 8862d1e..0000000 --- a/alfresco-mvc-rest/src/main/java/com/gradecak/alfresco/mvc/rest/annotation/EnableWebAlfrescoMvc.java +++ /dev/null @@ -1,44 +0,0 @@ -/** - * Copyright gradecak.com - - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.gradecak.alfresco.mvc.rest.annotation; - -import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; -import java.lang.annotation.Inherited; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -import org.springframework.web.servlet.config.annotation.EnableWebMvc; - -/** - * use this class in order to import - * DefaultAlfrescoMvcServletContextConfig and to - * add @EnableAlfrescoMvcDispatcherServlet. You can omit this annotation and - * directly use @EnableAlfrescoMvcDispatcherServlet with your custom servlet - * context configuration within the @Import - * - * The default configuration reuse the Alfresco jackson configuration - * org.alfresco.rest.framework.jacksonextensions.RestJsonModule - */ -@Target(ElementType.TYPE) -@Retention(RetentionPolicy.RUNTIME) -@Documented -@Inherited -@EnableWebMvc -public @interface EnableWebAlfrescoMvc { -} diff --git a/alfresco-mvc-rest/src/main/java/com/gradecak/alfresco/mvc/rest/config/DefaultAlfrescoMvcServletContextConfiguration.java b/alfresco-mvc-rest/src/main/java/com/gradecak/alfresco/mvc/rest/config/DefaultAlfrescoMvcServletContextConfiguration.java index 5cf922e..4ba4a8b 100644 --- a/alfresco-mvc-rest/src/main/java/com/gradecak/alfresco/mvc/rest/config/DefaultAlfrescoMvcServletContextConfiguration.java +++ b/alfresco-mvc-rest/src/main/java/com/gradecak/alfresco/mvc/rest/config/DefaultAlfrescoMvcServletContextConfiguration.java @@ -18,6 +18,8 @@ import java.text.DateFormat; import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.TimeZone; @@ -34,11 +36,14 @@ import org.springframework.http.converter.StringHttpMessageConverter; import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder; import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; +import org.springframework.lang.Nullable; import org.springframework.web.method.support.HandlerMethodArgumentResolver; import org.springframework.web.multipart.commons.CommonsMultipartResolver; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonSerializer; import com.fasterxml.jackson.databind.ObjectMapper; import com.gradecak.alfresco.mvc.rest.AlfrescoApiResponseInterceptor; import com.gradecak.alfresco.mvc.rest.jackson.Jackson2NodeRefDeserializer; @@ -53,7 +58,7 @@ public class DefaultAlfrescoMvcServletContextConfiguration implements WebMvcConf private final NamespaceService namespaceService; @Autowired - public DefaultAlfrescoMvcServletContextConfiguration(RestJsonModule alfrescoRestJsonModule, + public DefaultAlfrescoMvcServletContextConfiguration(@Nullable RestJsonModule alfrescoRestJsonModule, NamespaceService namespaceService) { this.alfrescoRestJsonModule = alfrescoRestJsonModule; this.namespaceService = namespaceService; @@ -78,21 +83,56 @@ public CommonsMultipartResolver multipartResolver() { return resolver; } - private void configureMultipartResolver(final CommonsMultipartResolver resolver) { + void configureMultipartResolver(final CommonsMultipartResolver resolver) { } @Bean @Primary public ObjectMapper objectMapper() { + return jackson2ObjectMapperBuilder().build(); + } + + @Bean + @Primary + public Jackson2ObjectMapperBuilder jackson2ObjectMapperBuilder() { DateFormat DATE_FORMAT_ISO8601 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ"); DATE_FORMAT_ISO8601.setTimeZone(TimeZone.getTimeZone("UTC")); - return Jackson2ObjectMapperBuilder.json().failOnEmptyBeans(false).failOnUnknownProperties(false) - .dateFormat(DATE_FORMAT_ISO8601).modulesToInstall(alfrescoRestJsonModule) - .serializers(jackson2NodeRefSerializer(), jackson2QnameSerializer()) - .deserializers(jackson2NodeRefDeserializer(), jackson2QnameDeserializer()) - .featuresToEnable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY).findModulesViaServiceLoader(true) - .build(); + List> customJsonDeserializers = new ArrayList<>(customJsonDeserializers()); + customJsonDeserializers.add(jackson2NodeRefDeserializer()); + customJsonDeserializers.add(jackson2QnameDeserializer()); + + List> customJsonSerilizers = new ArrayList<>(customJsonSerilizers()); + customJsonSerilizers.add(jackson2NodeRefSerializer()); + customJsonSerilizers.add(jackson2QnameSerializer()); + + Jackson2ObjectMapperBuilder builder = Jackson2ObjectMapperBuilder.json().failOnEmptyBeans(false) + .failOnUnknownProperties(false).dateFormat(DATE_FORMAT_ISO8601) + .serializers(customJsonSerilizers.toArray(new JsonSerializer[0])) + .deserializers(customJsonDeserializers.toArray(new JsonDeserializer[0])) + .featuresToEnable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY) + .findModulesViaServiceLoader(true); + + if (alfrescoRestJsonModule != null) { + builder.modulesToInstall(alfrescoRestJsonModule); + } + + customizeJackson2ObjectMapperBuilder(builder); + + return builder; + } + + protected void customizeJackson2ObjectMapperBuilder(Jackson2ObjectMapperBuilder builder) { + // TODO Auto-generated method stub + + } + + protected List> customJsonDeserializers() { + return Collections.emptyList(); + } + + protected List> customJsonSerilizers() { + return Collections.emptyList(); } @Override diff --git a/alfresco-mvc-rest/src/main/java/com/gradecak/alfresco/mvc/rest/jackson/Jackson2QnameDeserializer.java b/alfresco-mvc-rest/src/main/java/com/gradecak/alfresco/mvc/rest/jackson/Jackson2QnameDeserializer.java index 39cf1cf..6d99da0 100644 --- a/alfresco-mvc-rest/src/main/java/com/gradecak/alfresco/mvc/rest/jackson/Jackson2QnameDeserializer.java +++ b/alfresco-mvc-rest/src/main/java/com/gradecak/alfresco/mvc/rest/jackson/Jackson2QnameDeserializer.java @@ -42,12 +42,12 @@ public Class handledType() { @Override public QName deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException { String qname = jp.getText(); - return QName.createQName(qname, namespaceService); + return QName.resolveToQName(namespaceService, qname); } @Override public QName convert(String qname) { - return QName.createQName(qname, namespaceService); + return QName.resolveToQName(namespaceService, qname); } } diff --git a/alfresco-mvc-rest/src/main/java/com/gradecak/alfresco/mvc/webscript/DispatcherWebscript.java b/alfresco-mvc-rest/src/main/java/com/gradecak/alfresco/mvc/webscript/DispatcherWebscript.java index 90680e3..9fd80b2 100644 --- a/alfresco-mvc-rest/src/main/java/com/gradecak/alfresco/mvc/webscript/DispatcherWebscript.java +++ b/alfresco-mvc-rest/src/main/java/com/gradecak/alfresco/mvc/webscript/DispatcherWebscript.java @@ -262,7 +262,8 @@ public String getRequestURI() { origUri = origUri.replaceAll("\\$", "%24"); } - Pattern pattern = Pattern.compile("(^" + origReq.getServiceContextPath() + "/)(.*)(/" + origUri + ")"); + Pattern pattern = Pattern + .compile("(^" + origReq.getServiceContextPath() + "/)(.*)(/" + Pattern.quote(origUri) + ")"); Matcher matcher = pattern.matcher(uri); if (matcher.find()) { @@ -278,19 +279,13 @@ public String getRequestURI() { } public String getContextPath() { - return origReq.getContextPath(); + return ""; } public String getServletPath() { return ""; } - @Override - public String getPathInfo() { - return super.getPathInfo(); - // return "/s/mvc/swagger-ui/index.html"; - } - public WebScriptServletRequest getWebScriptServletRequest() { return origReq; } @@ -338,11 +333,11 @@ public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throw new RuntimeException( "Webscript dispatcherServlet has not been configured. Make sure to @Import(com.gradecak.alfresco.mvc.rest.config.AlfrescoRestServletRegistrar.class)"); } - Class beanClass = ((AbstractBeanDefinition) beanDefinition).getBeanClass(); - if (!(beanClass.isAssignableFrom(DispatcherWebscriptServlet.class))) { - throw new RuntimeException( - "Webscript dispatcherServlet has not been configured. Make sure to @Import(com.gradecak.alfresco.mvc.rest.config.AlfrescoRestServletRegistrar.class)"); - } +// Class beanClass = ((AbstractBeanDefinition) beanDefinition).getBeanClass(); +// if (!(beanClass.isAssignableFrom(DispatcherWebscriptServlet.class))) { +// throw new RuntimeException( +// "Webscript dispatcherServlet has not been configured. Make sure to @Import(com.gradecak.alfresco.mvc.rest.config.AlfrescoRestServletRegistrar.class)"); +// } Supplier supplier = ((AbstractBeanDefinition) beanDefinition).getInstanceSupplier(); if (supplier != null) { Object object = supplier.get(); diff --git a/alfresco-mvc-rest/src/test/java/com/gradecak/alfresco/mvc/config/AlfrescoMvcRestServletContext.java b/alfresco-mvc-rest/src/test/java/com/gradecak/alfresco/mvc/config/AlfrescoMvcRestServletContext.java index 431c3b8..44fddb7 100644 --- a/alfresco-mvc-rest/src/test/java/com/gradecak/alfresco/mvc/config/AlfrescoMvcRestServletContext.java +++ b/alfresco-mvc-rest/src/test/java/com/gradecak/alfresco/mvc/config/AlfrescoMvcRestServletContext.java @@ -3,14 +3,14 @@ import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; +import org.springframework.web.servlet.config.annotation.EnableWebMvc; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import com.gradecak.alfresco.mvc.controller.TestController; -import com.gradecak.alfresco.mvc.rest.annotation.EnableWebAlfrescoMvc; import com.gradecak.alfresco.mvc.rest.config.DefaultAlfrescoMvcServletContextConfiguration; @Configuration -@EnableWebAlfrescoMvc +@EnableWebMvc @ComponentScan(basePackageClasses = { TestController.class }) @Import(DefaultAlfrescoMvcServletContextConfiguration.class) public class AlfrescoMvcRestServletContext implements WebMvcConfigurer { diff --git a/alfresco-mvc-rest/src/test/java/com/gradecak/alfresco/mvc/test/context/AlfrescoMvcServletContext.java b/alfresco-mvc-rest/src/test/java/com/gradecak/alfresco/mvc/test/context/AlfrescoMvcServletContext.java index b54c02e..bb598c6 100644 --- a/alfresco-mvc-rest/src/test/java/com/gradecak/alfresco/mvc/test/context/AlfrescoMvcServletContext.java +++ b/alfresco-mvc-rest/src/test/java/com/gradecak/alfresco/mvc/test/context/AlfrescoMvcServletContext.java @@ -18,13 +18,13 @@ import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; +import org.springframework.web.servlet.config.annotation.EnableWebMvc; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; -import com.gradecak.alfresco.mvc.rest.annotation.EnableWebAlfrescoMvc; import com.gradecak.alfresco.mvc.rest.config.DefaultAlfrescoMvcServletContextConfiguration; @Configuration -@EnableWebAlfrescoMvc +@EnableWebMvc @Import(DefaultAlfrescoMvcServletContextConfiguration.class) public class AlfrescoMvcServletContext implements WebMvcConfigurer { diff --git a/alfresco-mvc-rest/src/test/java/com/gradecak/alfresco/mvc/test/inheritglobalproperties/AlfrescoMvcServletContext.java b/alfresco-mvc-rest/src/test/java/com/gradecak/alfresco/mvc/test/inheritglobalproperties/AlfrescoMvcServletContext.java index fa19334..88d4ada 100644 --- a/alfresco-mvc-rest/src/test/java/com/gradecak/alfresco/mvc/test/inheritglobalproperties/AlfrescoMvcServletContext.java +++ b/alfresco-mvc-rest/src/test/java/com/gradecak/alfresco/mvc/test/inheritglobalproperties/AlfrescoMvcServletContext.java @@ -16,14 +16,12 @@ package com.gradecak.alfresco.mvc.test.inheritglobalproperties; -import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.EnableWebMvc; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; -import com.gradecak.alfresco.mvc.rest.annotation.EnableWebAlfrescoMvc; - @Configuration -@EnableWebAlfrescoMvc +@EnableWebMvc //@ComponentScan(basePackageClasses = { SuffixController.class }) public class AlfrescoMvcServletContext implements WebMvcConfigurer { diff --git a/alfresco-mvc-rest/src/test/java/com/gradecak/alfresco/mvc/test/inheritservletconfig/AlfrescoMvcServletContext.java b/alfresco-mvc-rest/src/test/java/com/gradecak/alfresco/mvc/test/inheritservletconfig/AlfrescoMvcServletContext.java index 1fa94f2..8136112 100644 --- a/alfresco-mvc-rest/src/test/java/com/gradecak/alfresco/mvc/test/inheritservletconfig/AlfrescoMvcServletContext.java +++ b/alfresco-mvc-rest/src/test/java/com/gradecak/alfresco/mvc/test/inheritservletconfig/AlfrescoMvcServletContext.java @@ -18,12 +18,11 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.EnableWebMvc; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; -import com.gradecak.alfresco.mvc.rest.annotation.EnableWebAlfrescoMvc; - @Configuration -@EnableWebAlfrescoMvc +@EnableWebMvc public class AlfrescoMvcServletContext implements WebMvcConfigurer { @Bean diff --git a/alfresco-mvc-rest/src/test/java/com/gradecak/alfresco/mvc/test/webscript/AlfrescoMvcRestTest.java b/alfresco-mvc-rest/src/test/java/com/gradecak/alfresco/mvc/test/webscript/AlfrescoMvcRestTest.java index 9fcf63d..de343bc 100644 --- a/alfresco-mvc-rest/src/test/java/com/gradecak/alfresco/mvc/test/webscript/AlfrescoMvcRestTest.java +++ b/alfresco-mvc-rest/src/test/java/com/gradecak/alfresco/mvc/test/webscript/AlfrescoMvcRestTest.java @@ -277,6 +277,16 @@ public void when_alfrescoMvcPathSerializationIsUsed_expect_okAndQNameFullySerial String contentAsString = res.getContentAsString(); Assertions.assertEquals("\"{uri}created\"", contentAsString); } + + @Test + public void when_alfrescoMvcPathQnameSerializationIsUsed_expect_okAndQNameFullySerialized() throws Exception { + MockHttpServletResponse res = mockWebscript.withControllerMapping("test/qname/{uri}created").execute(); + Assertions.assertEquals(HttpStatus.OK.value(), res.getStatus()); + + // the response has been changed in the advice + String contentAsString = res.getContentAsString(); + Assertions.assertEquals("\"{uri}created\"", contentAsString); + } @Test public void when_alfrescoRestSerializationIsUsedButMocked_expect_okAndNodrefNotSerialized() throws Exception { diff --git a/pom.xml b/pom.xml index e5df386..3c72341 100644 --- a/pom.xml +++ b/pom.xml @@ -7,11 +7,11 @@ com.gradecak.alfresco-mvc 8.0.0 - alfresco-mvc-bom + alfresco-mvc-parent pom - Alfresco MVC bom - Bom for Alfresco @MVC project + Alfresco MVC parent + Parent for Alfresco @MVC project https://github.com/dgradecak/alfresco-mvc @@ -50,6 +50,7 @@ alfresco-mvc-rest alfresco-mvc-aop + alfresco-mvc-bom @@ -60,6 +61,21 @@ ${dependency.acs-community-packaging.version} pom import + true + + + + com.gradecak.alfresco-mvc + alfresco-mvc-rest + ${project.version} + compile + + + + com.gradecak.alfresco-mvc + alfresco-mvc-aop + ${project.version} + compile @@ -94,20 +110,6 @@ 5.7.1 test - - - com.gradecak.alfresco-mvc - alfresco-mvc-rest - ${project.version} - compile - - - - com.gradecak.alfresco-mvc - alfresco-mvc-aop - ${project.version} - compile - From f45b877de1c2e4893d1500e640fe660c6262201d Mon Sep 17 00:00:00 2001 From: Daniel Gradecak Date: Tue, 25 Oct 2022 12:06:44 +0200 Subject: [PATCH 03/18] qname serilizer is using namespaceservice --- .../alfresco/mvc/annotation/EnableAlfrescoMvcAop.java | 2 +- .../alfresco/mvc/aop/AlfrescoProxyRegistrar.java | 10 +++++----- .../DefaultAlfrescoMvcServletContextConfiguration.java | 4 +--- .../mvc/rest/jackson/Jackson2NodeRefSerializer.java | 2 ++ .../mvc/rest/jackson/Jackson2QnameDeserializer.java | 2 +- .../mvc/rest/jackson/Jackson2QnameSerializer.java | 8 ++++---- .../InheritGlobalPropertiesTest.java | 4 ++-- .../InheritedServletConfigTest.java | 9 +++++---- .../mvc/test/webscript/AlfrescoMvcRestTest.java | 6 +++--- 9 files changed, 24 insertions(+), 23 deletions(-) diff --git a/alfresco-mvc-aop/src/main/java/com/gradecak/alfresco/mvc/annotation/EnableAlfrescoMvcAop.java b/alfresco-mvc-aop/src/main/java/com/gradecak/alfresco/mvc/annotation/EnableAlfrescoMvcAop.java index 7a98504..e448b73 100644 --- a/alfresco-mvc-aop/src/main/java/com/gradecak/alfresco/mvc/annotation/EnableAlfrescoMvcAop.java +++ b/alfresco-mvc-aop/src/main/java/com/gradecak/alfresco/mvc/annotation/EnableAlfrescoMvcAop.java @@ -43,6 +43,6 @@ String[] basePackages() default {}; Class[] basePackageClasses() default {}; - + boolean defaultPropertiesSupport() default true; } diff --git a/alfresco-mvc-aop/src/main/java/com/gradecak/alfresco/mvc/aop/AlfrescoProxyRegistrar.java b/alfresco-mvc-aop/src/main/java/com/gradecak/alfresco/mvc/aop/AlfrescoProxyRegistrar.java index 3453c14..83a798d 100644 --- a/alfresco-mvc-aop/src/main/java/com/gradecak/alfresco/mvc/aop/AlfrescoProxyRegistrar.java +++ b/alfresco-mvc-aop/src/main/java/com/gradecak/alfresco/mvc/aop/AlfrescoProxyRegistrar.java @@ -53,7 +53,7 @@ public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanD if (annotationMetadata.getAnnotationAttributes(EnableAlfrescoMvcAop.class.getName()) == null) { return; } - + boolean proxyBeanRegistered = false; for (String beanName : PackageAutoProxyCreator.DEFAULT_INTERCEPTORS) { if (registry.containsBeanDefinition(beanName)) { @@ -61,7 +61,7 @@ public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanD break; } } - + if (!proxyBeanRegistered) { XmlBeanDefinitionReader xmlReader = new XmlBeanDefinitionReader(registry); xmlReader.loadBeanDefinitions("classpath:com/gradecak/alfresco-mvc/alfresco-mvc-aop.xml"); @@ -70,9 +70,9 @@ public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanD this.attributes = new AnnotationAttributes( annotationMetadata.getAnnotationAttributes(EnableAlfrescoMvcAop.class.getName())); this.metadata = annotationMetadata; - - boolean defaultPropertiesSupport = attributes.getBoolean("defaultPropertiesSupport"); - if(defaultPropertiesSupport) { + + boolean defaultPropertiesSupport = attributes.getBoolean("defaultPropertiesSupport"); + if (defaultPropertiesSupport) { RootBeanDefinition beanDefinition = new RootBeanDefinition(PropertySourcesPlaceholderConfigurer.class); registry.registerBeanDefinition("propertySourcesPlaceholderConfigurer", beanDefinition); } diff --git a/alfresco-mvc-rest/src/main/java/com/gradecak/alfresco/mvc/rest/config/DefaultAlfrescoMvcServletContextConfiguration.java b/alfresco-mvc-rest/src/main/java/com/gradecak/alfresco/mvc/rest/config/DefaultAlfrescoMvcServletContextConfiguration.java index 4ba4a8b..a358270 100644 --- a/alfresco-mvc-rest/src/main/java/com/gradecak/alfresco/mvc/rest/config/DefaultAlfrescoMvcServletContextConfiguration.java +++ b/alfresco-mvc-rest/src/main/java/com/gradecak/alfresco/mvc/rest/config/DefaultAlfrescoMvcServletContextConfiguration.java @@ -91,7 +91,7 @@ void configureMultipartResolver(final CommonsMultipartResolver resolver) { public ObjectMapper objectMapper() { return jackson2ObjectMapperBuilder().build(); } - + @Bean @Primary public Jackson2ObjectMapperBuilder jackson2ObjectMapperBuilder() { @@ -123,8 +123,6 @@ public Jackson2ObjectMapperBuilder jackson2ObjectMapperBuilder() { } protected void customizeJackson2ObjectMapperBuilder(Jackson2ObjectMapperBuilder builder) { - // TODO Auto-generated method stub - } protected List> customJsonDeserializers() { diff --git a/alfresco-mvc-rest/src/main/java/com/gradecak/alfresco/mvc/rest/jackson/Jackson2NodeRefSerializer.java b/alfresco-mvc-rest/src/main/java/com/gradecak/alfresco/mvc/rest/jackson/Jackson2NodeRefSerializer.java index ecab7d7..d94dadf 100644 --- a/alfresco-mvc-rest/src/main/java/com/gradecak/alfresco/mvc/rest/jackson/Jackson2NodeRefSerializer.java +++ b/alfresco-mvc-rest/src/main/java/com/gradecak/alfresco/mvc/rest/jackson/Jackson2NodeRefSerializer.java @@ -27,6 +27,8 @@ public class Jackson2NodeRefSerializer extends StdSerializer { + private static final long serialVersionUID = 1L; + public Jackson2NodeRefSerializer() { super(NodeRef.class); } diff --git a/alfresco-mvc-rest/src/main/java/com/gradecak/alfresco/mvc/rest/jackson/Jackson2QnameDeserializer.java b/alfresco-mvc-rest/src/main/java/com/gradecak/alfresco/mvc/rest/jackson/Jackson2QnameDeserializer.java index 6d99da0..a31e0ec 100644 --- a/alfresco-mvc-rest/src/main/java/com/gradecak/alfresco/mvc/rest/jackson/Jackson2QnameDeserializer.java +++ b/alfresco-mvc-rest/src/main/java/com/gradecak/alfresco/mvc/rest/jackson/Jackson2QnameDeserializer.java @@ -33,7 +33,7 @@ public class Jackson2QnameDeserializer extends JsonDeserializer implement public Jackson2QnameDeserializer(NamespaceService namespaceService) { this.namespaceService = namespaceService; } - + @Override public Class handledType() { return QName.class; diff --git a/alfresco-mvc-rest/src/main/java/com/gradecak/alfresco/mvc/rest/jackson/Jackson2QnameSerializer.java b/alfresco-mvc-rest/src/main/java/com/gradecak/alfresco/mvc/rest/jackson/Jackson2QnameSerializer.java index eb73e50..5f3c2f3 100644 --- a/alfresco-mvc-rest/src/main/java/com/gradecak/alfresco/mvc/rest/jackson/Jackson2QnameSerializer.java +++ b/alfresco-mvc-rest/src/main/java/com/gradecak/alfresco/mvc/rest/jackson/Jackson2QnameSerializer.java @@ -18,7 +18,6 @@ import java.io.IOException; -import org.alfresco.service.namespace.NamespacePrefixResolver; import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.QName; @@ -28,6 +27,8 @@ public class Jackson2QnameSerializer extends StdSerializer { + private static final long serialVersionUID = 1L; + private NamespaceService namespaceService; public Jackson2QnameSerializer(NamespaceService namespaceService) { @@ -37,9 +38,8 @@ public Jackson2QnameSerializer(NamespaceService namespaceService) { @Override public void serialize(QName value, JsonGenerator jgen, SerializerProvider provider) throws IOException { - // String prefixString = value.toPrefixString(namespaceService); - String string = value.toString(); - jgen.writeString(value.toString()); + String qNameString = QName.resolveToQNameString(namespaceService, value.toString()); + jgen.writeString(qNameString); } @Override diff --git a/alfresco-mvc-rest/src/test/java/com/gradecak/alfresco/mvc/test/inheritglobalproperties/InheritGlobalPropertiesTest.java b/alfresco-mvc-rest/src/test/java/com/gradecak/alfresco/mvc/test/inheritglobalproperties/InheritGlobalPropertiesTest.java index 26e9e02..5deeb28 100644 --- a/alfresco-mvc-rest/src/test/java/com/gradecak/alfresco/mvc/test/inheritglobalproperties/InheritGlobalPropertiesTest.java +++ b/alfresco-mvc-rest/src/test/java/com/gradecak/alfresco/mvc/test/inheritglobalproperties/InheritGlobalPropertiesTest.java @@ -62,7 +62,7 @@ public void when_alfrescoMvcInheritGlobalProperties_expect_propertyExists() thro Environment servletEnvironment = dispatcherWebscript.getDispatcherServlet().getEnvironment(); Assertions.assertEquals("myValue", servletEnvironment.getProperty("myKey")); - + Assertions.assertEquals("true", servletEnvironment.getProperty("test.exists")); } @@ -70,7 +70,7 @@ public void when_alfrescoMvcInheritGlobalProperties_expect_propertyExists() thro public void when_alfrescoMvcInheritGlobalProperties_expect_propertyNotExists() throws Exception { Environment servletEnvironment = dispatcherWebscript.getDispatcherServlet().getEnvironment(); Assertions.assertNull(servletEnvironment.getProperty("myKey1")); - + Assertions.assertEquals("true", servletEnvironment.getProperty("test.exists")); } diff --git a/alfresco-mvc-rest/src/test/java/com/gradecak/alfresco/mvc/test/inheritservletconfig/InheritedServletConfigTest.java b/alfresco-mvc-rest/src/test/java/com/gradecak/alfresco/mvc/test/inheritservletconfig/InheritedServletConfigTest.java index e89ec18..9e71562 100644 --- a/alfresco-mvc-rest/src/test/java/com/gradecak/alfresco/mvc/test/inheritservletconfig/InheritedServletConfigTest.java +++ b/alfresco-mvc-rest/src/test/java/com/gradecak/alfresco/mvc/test/inheritservletconfig/InheritedServletConfigTest.java @@ -61,10 +61,11 @@ public void before() throws Exception { /** * @deprecated as of Spring 5.2.4. See class-level note in - * {@link RequestMappingHandlerMapping} on the deprecation of path extension - * config options. As there is no replacement for this method, in Spring 5.2.x it is - * necessary to set it to {@code false}. In Spring 5.3 the default changes to - * {@code false} and use of this property becomes unnecessary. + * {@link RequestMappingHandlerMapping} on the deprecation of path + * extension config options. As there is no replacement for this + * method, in Spring 5.2.x it is necessary to set it to + * {@code false}. In Spring 5.3 the default changes to {@code false} + * and use of this property becomes unnecessary. */ @Deprecated @Test diff --git a/alfresco-mvc-rest/src/test/java/com/gradecak/alfresco/mvc/test/webscript/AlfrescoMvcRestTest.java b/alfresco-mvc-rest/src/test/java/com/gradecak/alfresco/mvc/test/webscript/AlfrescoMvcRestTest.java index de343bc..66302e5 100644 --- a/alfresco-mvc-rest/src/test/java/com/gradecak/alfresco/mvc/test/webscript/AlfrescoMvcRestTest.java +++ b/alfresco-mvc-rest/src/test/java/com/gradecak/alfresco/mvc/test/webscript/AlfrescoMvcRestTest.java @@ -61,7 +61,7 @@ public class AlfrescoMvcRestTest { @Autowired private DispatcherWebscript webScript; - + @Autowired private NamespaceService namespaceService; @@ -267,7 +267,7 @@ public void when_alfrescoMvcParamSerializationIsUsed_expect_okAndQNameFullySeria String contentAsString = res.getContentAsString(); Assertions.assertEquals("\"{uri}created\"", contentAsString); } - + @Test public void when_alfrescoMvcPathSerializationIsUsed_expect_okAndQNameFullySerialized() throws Exception { MockHttpServletResponse res = mockWebscript.withControllerMapping("test/qname/cm:created").execute(); @@ -277,7 +277,7 @@ public void when_alfrescoMvcPathSerializationIsUsed_expect_okAndQNameFullySerial String contentAsString = res.getContentAsString(); Assertions.assertEquals("\"{uri}created\"", contentAsString); } - + @Test public void when_alfrescoMvcPathQnameSerializationIsUsed_expect_okAndQNameFullySerialized() throws Exception { MockHttpServletResponse res = mockWebscript.withControllerMapping("test/qname/{uri}created").execute(); From 8a928040b7aec4cd4569363bfd5c331fa9fb50de Mon Sep 17 00:00:00 2001 From: Daniel Gradecak Date: Tue, 25 Oct 2022 16:53:30 +0200 Subject: [PATCH 04/18] adding constructor to failitate extending --- .../DefaultAlfrescoMvcServletContextConfiguration.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/alfresco-mvc-rest/src/main/java/com/gradecak/alfresco/mvc/rest/config/DefaultAlfrescoMvcServletContextConfiguration.java b/alfresco-mvc-rest/src/main/java/com/gradecak/alfresco/mvc/rest/config/DefaultAlfrescoMvcServletContextConfiguration.java index a358270..d57eba1 100644 --- a/alfresco-mvc-rest/src/main/java/com/gradecak/alfresco/mvc/rest/config/DefaultAlfrescoMvcServletContextConfiguration.java +++ b/alfresco-mvc-rest/src/main/java/com/gradecak/alfresco/mvc/rest/config/DefaultAlfrescoMvcServletContextConfiguration.java @@ -57,6 +57,11 @@ public class DefaultAlfrescoMvcServletContextConfiguration implements WebMvcConf private final RestJsonModule alfrescoRestJsonModule; private final NamespaceService namespaceService; + @Autowired + public DefaultAlfrescoMvcServletContextConfiguration(NamespaceService namespaceService) { + this(null, namespaceService); + } + @Autowired public DefaultAlfrescoMvcServletContextConfiguration(@Nullable RestJsonModule alfrescoRestJsonModule, NamespaceService namespaceService) { From 2dc64f5bcf9f45e0593ca8ee7fd34c27749aad10 Mon Sep 17 00:00:00 2001 From: Daniel Gradecak Date: Tue, 25 Oct 2022 17:01:28 +0200 Subject: [PATCH 05/18] fixing bom --- alfresco-mvc-bom/pom.xml | 5 ----- .../DefaultAlfrescoMvcServletContextConfiguration.java | 5 ----- 2 files changed, 10 deletions(-) diff --git a/alfresco-mvc-bom/pom.xml b/alfresco-mvc-bom/pom.xml index d8620be..967c5a1 100644 --- a/alfresco-mvc-bom/pom.xml +++ b/alfresco-mvc-bom/pom.xml @@ -44,11 +44,6 @@ UTF-8 - - alfresco-mvc-rest - alfresco-mvc-aop - - diff --git a/alfresco-mvc-rest/src/main/java/com/gradecak/alfresco/mvc/rest/config/DefaultAlfrescoMvcServletContextConfiguration.java b/alfresco-mvc-rest/src/main/java/com/gradecak/alfresco/mvc/rest/config/DefaultAlfrescoMvcServletContextConfiguration.java index d57eba1..a358270 100644 --- a/alfresco-mvc-rest/src/main/java/com/gradecak/alfresco/mvc/rest/config/DefaultAlfrescoMvcServletContextConfiguration.java +++ b/alfresco-mvc-rest/src/main/java/com/gradecak/alfresco/mvc/rest/config/DefaultAlfrescoMvcServletContextConfiguration.java @@ -57,11 +57,6 @@ public class DefaultAlfrescoMvcServletContextConfiguration implements WebMvcConf private final RestJsonModule alfrescoRestJsonModule; private final NamespaceService namespaceService; - @Autowired - public DefaultAlfrescoMvcServletContextConfiguration(NamespaceService namespaceService) { - this(null, namespaceService); - } - @Autowired public DefaultAlfrescoMvcServletContextConfiguration(@Nullable RestJsonModule alfrescoRestJsonModule, NamespaceService namespaceService) { From 3dfba3e20f176ad3938d3679151b060ceb220617 Mon Sep 17 00:00:00 2001 From: Zlatin Todorinski <6491638+todorinskiz@users.noreply.github.com> Date: Thu, 27 Oct 2022 19:34:40 +0300 Subject: [PATCH 06/18] Removed unnecessary project references from BOM; (#23) Aligned URI encoding; Added support for WrappingWebScriptRequest for transaction required configuration in webscript descriptors; Added MultipartRespolver for "unorthodox" multipart processing (e.g. with PUT methods); Added escaping for special characters in the URI when comparing with regex; --- ...lfrescoMvcServletContextConfiguration.java | 16 ++++++--- .../mvc/webscript/DispatcherWebscript.java | 35 +++++++++++++++---- 2 files changed, 41 insertions(+), 10 deletions(-) diff --git a/alfresco-mvc-rest/src/main/java/com/gradecak/alfresco/mvc/rest/config/DefaultAlfrescoMvcServletContextConfiguration.java b/alfresco-mvc-rest/src/main/java/com/gradecak/alfresco/mvc/rest/config/DefaultAlfrescoMvcServletContextConfiguration.java index a358270..97b0603 100644 --- a/alfresco-mvc-rest/src/main/java/com/gradecak/alfresco/mvc/rest/config/DefaultAlfrescoMvcServletContextConfiguration.java +++ b/alfresco-mvc-rest/src/main/java/com/gradecak/alfresco/mvc/rest/config/DefaultAlfrescoMvcServletContextConfiguration.java @@ -38,6 +38,7 @@ import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; import org.springframework.lang.Nullable; import org.springframework.web.method.support.HandlerMethodArgumentResolver; +import org.springframework.web.multipart.MultipartResolver; import org.springframework.web.multipart.commons.CommonsMultipartResolver; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @@ -51,6 +52,8 @@ import com.gradecak.alfresco.mvc.rest.jackson.Jackson2QnameDeserializer; import com.gradecak.alfresco.mvc.rest.jackson.Jackson2QnameSerializer; +import javax.servlet.http.HttpServletRequest; + @Configuration public class DefaultAlfrescoMvcServletContextConfiguration implements WebMvcConfigurer { @@ -75,15 +78,20 @@ public AlfrescoApiResponseInterceptor alfrescoResponseInterceptor(ResourceWebScr } @Bean - public CommonsMultipartResolver multipartResolver() { - final CommonsMultipartResolver resolver = new CommonsMultipartResolver(); + public MultipartResolver multipartResolver() { + MultipartResolver resolver = createMultipartResolver(); + configureMultipartResolver(resolver); + return resolver; + } + + protected MultipartResolver createMultipartResolver() { + CommonsMultipartResolver resolver = new CommonsMultipartResolver(); resolver.setMaxUploadSize(-1); resolver.setDefaultEncoding("utf-8"); - configureMultipartResolver(resolver); return resolver; } - void configureMultipartResolver(final CommonsMultipartResolver resolver) { + protected void configureMultipartResolver(final MultipartResolver resolver) { } @Bean diff --git a/alfresco-mvc-rest/src/main/java/com/gradecak/alfresco/mvc/webscript/DispatcherWebscript.java b/alfresco-mvc-rest/src/main/java/com/gradecak/alfresco/mvc/webscript/DispatcherWebscript.java index 9fd80b2..16f56e0 100644 --- a/alfresco-mvc-rest/src/main/java/com/gradecak/alfresco/mvc/webscript/DispatcherWebscript.java +++ b/alfresco-mvc-rest/src/main/java/com/gradecak/alfresco/mvc/webscript/DispatcherWebscript.java @@ -17,6 +17,9 @@ package com.gradecak.alfresco.mvc.webscript; import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; +import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.Collections; import java.util.EnumSet; @@ -52,6 +55,7 @@ import org.springframework.extensions.webscripts.AbstractWebScript; import org.springframework.extensions.webscripts.WebScriptRequest; import org.springframework.extensions.webscripts.WebScriptResponse; +import org.springframework.extensions.webscripts.WrappingWebScriptRequest; import org.springframework.extensions.webscripts.WrappingWebScriptResponse; import org.springframework.extensions.webscripts.servlet.WebScriptServletRequest; import org.springframework.extensions.webscripts.servlet.WebScriptServletResponse; @@ -94,10 +98,14 @@ public DispatcherWebscript(final String servletName, boolean inheritGlobalProper } public void execute(WebScriptRequest req, WebScriptResponse res) throws IOException { + WebScriptServletRequest origReq; + if(req instanceof WrappingWebScriptRequest) { + origReq = (WebScriptServletRequest)((WrappingWebScriptRequest) req).getNext(); + } else { + origReq = (WebScriptServletRequest) req; + } - final WebScriptServletRequest origReq = (WebScriptServletRequest) req; - - WebScriptServletResponse wsr = null; + WebScriptServletResponse wsr; if (res instanceof WrappingWebScriptResponse) { wsr = (WebScriptServletResponse) ((WrappingWebScriptResponse) res).getNext(); } else { @@ -113,6 +121,7 @@ public void execute(WebScriptRequest req, WebScriptResponse res) throws IOExcept s.service(wrapper, sr); } catch (Throwable e) { + LOGGER.error("Failed to call {}", origReq.getURL()); throw new IOException(e); } } @@ -253,6 +262,15 @@ public WebscriptRequestWrapper(WebScriptServletRequest request) { @Override public String getRequestURI() { String uri = super.getRequestURI(); + try { + uri = URLDecoder.decode(uri, StandardCharsets.UTF_8.toString()); + } catch (UnsupportedEncodingException e) { + LOGGER.error(e.getMessage(), e); + return ""; + } + + LOGGER.debug("Processing [{}] {}", super.getMethod(), uri); + if (uri.contains("$")) { uri = uri.replaceAll("\\$", "%24"); } @@ -263,12 +281,17 @@ public String getRequestURI() { } Pattern pattern = Pattern - .compile("(^" + origReq.getServiceContextPath() + "/)(.*)(/" + Pattern.quote(origUri) + ")"); + .compile( + "(^" + origReq.getServiceContextPath() + "/)" + + "(.*)" + + "(/" + Pattern.quote(origUri) + ")" + ); Matcher matcher = pattern.matcher(uri); - if (matcher.find()) { try { - return matcher.group(EXTENSION_PATH_REGEXP_GROUP_INDEX); + String result = matcher.group(EXTENSION_PATH_REGEXP_GROUP_INDEX); + LOGGER.debug("Found [{}] {} ---> {}", super.getMethod(), uri, result); + return result; } catch (Exception e) { // let an empty string be returned LOGGER.warn("no such group (3) in regexp while URI evaluation", e); From a9d83799ee30b41ce2ccfe95fed69bd9d1df6cf8 Mon Sep 17 00:00:00 2001 From: Daniel Gradecak Date: Tue, 1 Nov 2022 15:15:07 +0100 Subject: [PATCH 07/18] preparing release 8.0.0 --- README.md | 2 ++ .../pom.xml | 2 +- ...lfrescoMvcServletContextConfiguration.java | 26 +++++++++---------- .../mvc/webscript/DispatcherWebscript.java | 19 ++++---------- bitbucket-pipelines.yml | 22 ---------------- pom.xml | 2 +- 6 files changed, 21 insertions(+), 52 deletions(-) rename {alfresco-mvc-bom => alfresco-mvc-dependencies}/pom.xml (98%) delete mode 100644 bitbucket-pipelines.yml diff --git a/README.md b/README.md index d7cb0b0..d79d0d8 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,8 @@ For the correct version supported on your Alfresco version [please check the wik [The docs are on the wiki page](https://github.com/dgradecak/alfresco-mvc/wiki) +[The Samples are provided on ](https://github.com/dgradecak/alfresco-mvc-sample) + You should use it when - - You need custom APIs diff --git a/alfresco-mvc-bom/pom.xml b/alfresco-mvc-dependencies/pom.xml similarity index 98% rename from alfresco-mvc-bom/pom.xml rename to alfresco-mvc-dependencies/pom.xml index 967c5a1..43db052 100644 --- a/alfresco-mvc-bom/pom.xml +++ b/alfresco-mvc-dependencies/pom.xml @@ -6,7 +6,7 @@ 4.0.0 com.gradecak.alfresco-mvc - alfresco-mvc-bom + alfresco-mvc-dependencies 8.0.0 pom diff --git a/alfresco-mvc-rest/src/main/java/com/gradecak/alfresco/mvc/rest/config/DefaultAlfrescoMvcServletContextConfiguration.java b/alfresco-mvc-rest/src/main/java/com/gradecak/alfresco/mvc/rest/config/DefaultAlfrescoMvcServletContextConfiguration.java index 97b0603..6a95bdd 100644 --- a/alfresco-mvc-rest/src/main/java/com/gradecak/alfresco/mvc/rest/config/DefaultAlfrescoMvcServletContextConfiguration.java +++ b/alfresco-mvc-rest/src/main/java/com/gradecak/alfresco/mvc/rest/config/DefaultAlfrescoMvcServletContextConfiguration.java @@ -52,8 +52,6 @@ import com.gradecak.alfresco.mvc.rest.jackson.Jackson2QnameDeserializer; import com.gradecak.alfresco.mvc.rest.jackson.Jackson2QnameSerializer; -import javax.servlet.http.HttpServletRequest; - @Configuration public class DefaultAlfrescoMvcServletContextConfiguration implements WebMvcConfigurer { @@ -71,7 +69,7 @@ public DefaultAlfrescoMvcServletContextConfiguration(@Nullable RestJsonModule al public void addArgumentResolvers(List resolvers) { resolvers.add(new ParamsHandlerMethodArgumentResolver()); } - + @Bean public AlfrescoApiResponseInterceptor alfrescoResponseInterceptor(ResourceWebScriptHelper webscriptHelper) { return new AlfrescoApiResponseInterceptor(webscriptHelper); @@ -103,8 +101,6 @@ public ObjectMapper objectMapper() { @Bean @Primary public Jackson2ObjectMapperBuilder jackson2ObjectMapperBuilder() { - DateFormat DATE_FORMAT_ISO8601 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ"); - DATE_FORMAT_ISO8601.setTimeZone(TimeZone.getTimeZone("UTC")); List> customJsonDeserializers = new ArrayList<>(customJsonDeserializers()); customJsonDeserializers.add(jackson2NodeRefDeserializer()); @@ -115,7 +111,7 @@ public Jackson2ObjectMapperBuilder jackson2ObjectMapperBuilder() { customJsonSerilizers.add(jackson2QnameSerializer()); Jackson2ObjectMapperBuilder builder = Jackson2ObjectMapperBuilder.json().failOnEmptyBeans(false) - .failOnUnknownProperties(false).dateFormat(DATE_FORMAT_ISO8601) + .failOnUnknownProperties(false).dateFormat(dateFormat()) .serializers(customJsonSerilizers.toArray(new JsonSerializer[0])) .deserializers(customJsonDeserializers.toArray(new JsonDeserializer[0])) .featuresToEnable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY) @@ -130,6 +126,12 @@ public Jackson2ObjectMapperBuilder jackson2ObjectMapperBuilder() { return builder; } + protected DateFormat dateFormat() { + DateFormat dateFormatIso8601 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ"); + dateFormatIso8601.setTimeZone(TimeZone.getTimeZone("UTC")); + return dateFormatIso8601; + } + protected void customizeJackson2ObjectMapperBuilder(Jackson2ObjectMapperBuilder builder) { } @@ -154,23 +156,19 @@ public void configureMessageConverters(List> converters) converters.add(new MappingJackson2HttpMessageConverter(objectMapper())); } - @Bean - Jackson2NodeRefDeserializer jackson2NodeRefDeserializer() { + protected Jackson2NodeRefDeserializer jackson2NodeRefDeserializer() { return new Jackson2NodeRefDeserializer(); } - @Bean - Jackson2QnameDeserializer jackson2QnameDeserializer() { + protected Jackson2QnameDeserializer jackson2QnameDeserializer() { return new Jackson2QnameDeserializer(namespaceService); } - @Bean - Jackson2NodeRefSerializer jackson2NodeRefSerializer() { + protected Jackson2NodeRefSerializer jackson2NodeRefSerializer() { return new Jackson2NodeRefSerializer(); } - @Bean - Jackson2QnameSerializer jackson2QnameSerializer() { + protected Jackson2QnameSerializer jackson2QnameSerializer() { return new Jackson2QnameSerializer(namespaceService); } diff --git a/alfresco-mvc-rest/src/main/java/com/gradecak/alfresco/mvc/webscript/DispatcherWebscript.java b/alfresco-mvc-rest/src/main/java/com/gradecak/alfresco/mvc/webscript/DispatcherWebscript.java index 16f56e0..f21d33a 100644 --- a/alfresco-mvc-rest/src/main/java/com/gradecak/alfresco/mvc/webscript/DispatcherWebscript.java +++ b/alfresco-mvc-rest/src/main/java/com/gradecak/alfresco/mvc/webscript/DispatcherWebscript.java @@ -99,8 +99,8 @@ public DispatcherWebscript(final String servletName, boolean inheritGlobalProper public void execute(WebScriptRequest req, WebScriptResponse res) throws IOException { WebScriptServletRequest origReq; - if(req instanceof WrappingWebScriptRequest) { - origReq = (WebScriptServletRequest)((WrappingWebScriptRequest) req).getNext(); + if (req instanceof WrappingWebScriptRequest) { + origReq = (WebScriptServletRequest) ((WrappingWebScriptRequest) req).getNext(); } else { origReq = (WebScriptServletRequest) req; } @@ -280,12 +280,8 @@ public String getRequestURI() { origUri = origUri.replaceAll("\\$", "%24"); } - Pattern pattern = Pattern - .compile( - "(^" + origReq.getServiceContextPath() + "/)" + - "(.*)" + - "(/" + Pattern.quote(origUri) + ")" - ); + Pattern pattern = Pattern.compile( + "(^" + origReq.getServiceContextPath() + "/)" + "(.*)" + "(/" + Pattern.quote(origUri) + ")"); Matcher matcher = pattern.matcher(uri); if (matcher.find()) { try { @@ -356,11 +352,7 @@ public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throw new RuntimeException( "Webscript dispatcherServlet has not been configured. Make sure to @Import(com.gradecak.alfresco.mvc.rest.config.AlfrescoRestServletRegistrar.class)"); } -// Class beanClass = ((AbstractBeanDefinition) beanDefinition).getBeanClass(); -// if (!(beanClass.isAssignableFrom(DispatcherWebscriptServlet.class))) { -// throw new RuntimeException( -// "Webscript dispatcherServlet has not been configured. Make sure to @Import(com.gradecak.alfresco.mvc.rest.config.AlfrescoRestServletRegistrar.class)"); -// } + Supplier supplier = ((AbstractBeanDefinition) beanDefinition).getInstanceSupplier(); if (supplier != null) { Object object = supplier.get(); @@ -372,7 +364,6 @@ public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) ((AbstractBeanDefinition) beanDefinition) .setInstanceSupplier(() -> DispatcherWebscriptServlet.this); } - } else { AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder .genericBeanDefinition(DispatcherWebscriptServlet.class).getBeanDefinition(); diff --git a/bitbucket-pipelines.yml b/bitbucket-pipelines.yml deleted file mode 100644 index d4030d7..0000000 --- a/bitbucket-pipelines.yml +++ /dev/null @@ -1,22 +0,0 @@ -image: maven:3-jdk-11 - -pipelines: - custom: - build: - - step: - caches: - - maven - script: - - mvn -B clean install - deploy: - - step: - caches: - - maven - script: - - (echo $RELEASE_GPG_ENC | base64 --decode > ~/private-key.gpg.enc) - - openssl aes-256-cbc -pass pass:$OPENSSL_PWD -in ~/private-key.gpg.enc -md md5 -out ./private-key.gpg -d -salt -pbkdf2 - - export GPG_TTY=$(tty) - - gpg --version - - gpg --batch --import ./private-key.gpg - - mvn -V -B -s settings.xml deploy -DskipTests -DaltDeploymentRepository=ossrh::default::https://oss.sonatype.org/service/local/staging/deploy/maven2 - \ No newline at end of file diff --git a/pom.xml b/pom.xml index 3c72341..bbd093e 100644 --- a/pom.xml +++ b/pom.xml @@ -50,7 +50,7 @@ alfresco-mvc-rest alfresco-mvc-aop - alfresco-mvc-bom + alfresco-mvc-dependencies From 6cc248512111ac9764e6bb04800149c5180f87a8 Mon Sep 17 00:00:00 2001 From: Daniel Gradecak Date: Tue, 1 Nov 2022 15:31:20 +0100 Subject: [PATCH 08/18] adding maven wrapper --- .mvn/wrapper/maven-wrapper.jar | Bin 0 -> 58727 bytes .mvn/wrapper/maven-wrapper.properties | 2 + mvnw | 316 ++++++++++++++++++++++++++ mvnw.cmd | 188 +++++++++++++++ 4 files changed, 506 insertions(+) create mode 100644 .mvn/wrapper/maven-wrapper.jar create mode 100644 .mvn/wrapper/maven-wrapper.properties create mode 100644 mvnw create mode 100644 mvnw.cmd diff --git a/.mvn/wrapper/maven-wrapper.jar b/.mvn/wrapper/maven-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..c1dd12f17644411d6e840bd5a10c6ecda0175f18 GIT binary patch literal 58727 zcmb5W18`>1vNjyPv28mO+cqb*Z6_1kwr$(?#I}=(ZGUs`Jr}3`|DLbDUA3!L?dtC8 zUiH*ktDo+@6r@4HP=SCTA%WmZqm^Ro`Ls)bfPkcdfq?#g1(Fq27W^S8Cq^$TC?_c< zs-#ROD;6C)1wFuk7<3)nGuR^#!H;n&3*IjzXg+s8Z_S!!E0jUq(`}Itt=YdYa5Z_s z&e>2={87knpF*PKNzU;lsbk#P(l^WBvb$yEz)z+nYH43pKodrDkMp@h?;n{;K}hl>Fb^ zqx}C0|D7kg|Cj~3f7hn_zkAE}|6t|cZT|S5Hvb#3nc~C14u5UI{6#F<|FkJ0svs&S zA}S{=DXLT*BM1$`2rK%`D@vEw9l9%*=92X_2g?Fwfi=6Zfpr7+<~sgP#Bav+Df2ts zwtu~70zhqV?mrzM)}r7mMS`Hk_)NrI5K%CTtQtDxqw5iv5F0!ksIon{qqpPVnU?ds zN$|Vm{MHKEReUy>1kVfT-$3))Js0p2W_LFy3cjjZ7za0R zPdBH>y&pb0vr1|ckDpt2p$IQhwnPs5G*^b-y}sg4W!ALn}a`pY0JIa$H0$eV2T8WjWD= zWaENacQhlTyK4O!+aOXBurVR2k$eb8HVTCxy-bcHlZ4Xr!`juLAL#?t6|Ba!g9G4I zSwIt2Lla>C?C4wAZ8cKsZl9-Yd3kqE`%!5HlGdJJaFw0mu#--&**L-i|BcIdc3B$;0FC;FbE-dunVZ; zdIQ=tPKH4iJQQ=$5BeEMLov_Hn>gXib|9nOr}>eZt@B4W^m~>Zp#xhn1dax+?hS!AchWJ4makWZs@dQUeXQ zsI2+425_{X@t2KN zIbqec#)Jg5==VY3^YBeJ2B+%~^Y8|;F!mE8d(`UgNl2B9o>Ir5)qbBr)a?f%nrP zQyW(>FYPZjCVKDOU;Bw#PqPF1CCvp)dGdA&57a5hD&*vIc)jA)Z-!y5pS{5W6%#prH16zgD8s zexvpF#a|=*acp>L^lZ(PT)GiA8BJL-9!r8S$ZvXRKMVtiGe`+!@O%j<1!@msc177U zTDy>WOZu)W5anPrweQyjIu3IJC|ngdjZofGbdW&oj^DJlC7$;|xafB45evT|WBgGf-b|9y0J`fe0W-vw6xh}` z=(Tnq(-K0O{;VUcKe2y63{HXc+`R_#HLwnZ0rzWO*b#VeSuC4NG!H_ApCypbt1qx( z6y7Q$5(JOpQ&pTkc^0f}A0Kq*?;g9lEfzeE?5e2MBNZB)^8W1)YgdjsVyN+I9EZlh z3l}*}*)cFl=dOq|DvF=!ui$V%XhGQ%bDn3PK9 zV%{Y|VkAdt^d9~y4laGDqSwLd@pOnS&^@sI7}YTIb@El1&^_sq+{yAGf0|rq5TMp# z6d~;uAZ(fY3(eH=+rcbItl2=u6mf|P{lD4kiRCv;>GtFaHR3gim?WU9RjHmFZLm+m z+j<}_exaOQ1a}=K#voc~En+Mk_<(L!?1e#Uay~|H5q)LjD*yE6xFYQ-Wx{^iH1@pP zC0De#D6I26&W{;J40sZB!=%{c?XdO?YQvnTMA3TwfhAm@bvkX*(x?JTs*dFDv^=2X z284}AK)1nRn+8(Q2P?f)e>0~;NUI9%p%fnv1wBVpoXL+9OE`Vv1Y7=+nub$o7AN>y zB?R(^G8PYcMk4bxe7XItq@48QqWKb8fa*i9-N)=wdU-Q^=}!nFgTr_uT=Z=9pq z`{7!$U|+fnXFcsJ4GNm3JQQCN+G85k$)ZLhF{NbIy{REj84}Zt;0fe#>MARW)AoSb zrBpwF37ZVBMd>wZn_hAadI*xu8)Y#`aMbwRIA2n^-OS~M58_@j?#P1|PXJ1XBC9{4 zT^8*|xu<@(JlSOT*ILrVGr+7$nZN`Z3GxJJO@nY&mHsv^^duAh*lCu5q+S6zWA+`- z%^*y#)O7ko_RwGJl;bcEpP03FOrhlLWs`V_OUCrR-g>NJz*pN|itmN6O@Hw05Zq;Xtif%+sp4Py0{<7<^c zeoHHhRq>2EtYy9~2dZywm&OSk`u2ECWh6dJY?;fT-3-$U`!c(o$&hhPC%$~fT&bw3 zyj+8aXD;G!p*>BC6rpvx#6!|Qaic;KEv5>`Y+R(6F^1eIeYG6d1q3D3OL{7%7iw3R zwO)W7gMh27ASSB>-=OfP(YrKqBTNFv4hL@Im~~ombbSu44p~VoH$H-6+L_JW>Amkl zhDU~|r77?raaxD!-c$Ta?WAAi{w3T}YV=+S?1HQGC0+{Bny_^b+4Jum}oW4c=$ z#?D<}Ds{#d5v`L`${Pee;W84X*osNQ96xsKp^EAzuUh9#&zDX=eqdAp$UY)EGrkU% z(6m35n=46B$TNnejNSlih_!<)Iu@K!PW5S@Ya^0OK+EMWM=1w=GUKW^(r59U%i?d zzbo?|V4tDWGHHsrAQ}}ma#<`9r=M8%XF#%a=@Hn(p3wFBlkZ2L@8=*@J-^zuyF0aN zzJ7f!Jf8I+^6Tt$e+IIh zb80@?7y#Iz3w-0VEjgbHurqI>$qj<@n916)&O340!_5W9DtwR)P5mk6v2ljyK*DG5 zYjzE~m`>tq8HYXl%1JJ%e-%BqV4kRdPUZB1Cm$BQZr(fzp_@rn_W+;GwI$?L2Y4;b z)}c5D$#LT}2W8Si<`EHKIa_X+>+2PF(C*u~F=8E!jL(=IdQxY40%|( zoNg2Z&Aob@LEui-lJ#@)Ts)tE0_!*3{Uk)r{;-IZpX`N4mZX`#E|A;viQWImB6flI z?M_|xHCXV$5LOY-!U1_O1k;OWa=EchwlDCK4xHwBW2jE-6&%}og+9NILu${v10Z^Z#* zap|)B9a-AMU~>$r)3&|dQuP#MA$jnw54w*Ax~*_$iikp+j^OR8I5Fo<_UR#B-c>$? zeg)=;w^sGeAMi<3RGDRj$jA30Qq$e|zf2z;JyQ}tkU)ZI_k6tY%(`#AvL)p)iYXUy z5W9Su3NJ8mVyy)WqzFSk&vZM!;kUh8dVeA-myqcV%;xUne`PbHCPpvH?br`U2Y&dM zV!nJ!^n%`!H&!QSlpzLWnZpgi;#P0OAleH+<CfLa?&o|kyw1}W%6Pij zp$Vv5=;Z0LFN|j9i&9>zqX>*VnV3h#>n!2L?5gO6HJS3~kpy5G zYAVPMaB-FJOk3@OrxL(*-O~OB9^d{!G0K>wlzXuBm*$&%p1O#6SQ*?Q0CETLQ->XpfkW7< zj&Nep(}eAH1u$wWFvLV*lA{JOltP_%xKXC*a8DB&;{fD&2bATy>rC^kFY+$hFS7us;Y) zy_H?cv9XTHYz<4C<0b`WKC#{nJ15{F=oaq3x5}sYApT?Po+(Cmmo#dHZFO^{M#d~d znRT=TFATGVO%z_FNG-@G;9az|udZ>t@5l+A-K)BUWFn_|T#K3=d3EXRNqHyi#>;hX z*JQ`pT3#&tH>25laFlL6Rllu(seA*OboEd%rxMtz3@5v-+{qDP9&BcoS$2fgjgvp$ zc8!3=p0p@Ee1$u{Gg}Kkxg@M*qgZfYLlnD88{uwG1T?zxCbBR+x(RK$JB(eWJH#~; zZoY6L+esVRV?-*QmRCG}h`rB*Lv=uE%URF@+#l-g!Artx>Y9D;&G=jY2n2`J z{6-J%WX~Glx*QBmOOJ(RDRIzhfk&ibsm1t&&7aU{1P3U0uM%F2zJb4~50uby_ng+# zN)O9lK=dkJpxsUo7u8|e`Y~mmbxOTDn0i!i;d;ml#orN(Lc=j+n422NoSnlH6?0<0?th-qB7u}`5My%#?ES}>@RldOQz}WILz<$+cN~&ET zwUI01HCB((TyU$Ej8bxsE8oLmT-c7gA1Js?Iq`QMzIHV|)v)n2 zT_L(9x5%8*wU(C`VapaHoicWcm|0X@9TiNtbc|<4N6_H1F6&qgEEj=vjegFt;hC7- zLG7_=vedRFZ6Chbw!{#EpAlM?-sc#pc<~j#537n)M%RT)|L}y(ggi_-SLpsE3qi3V z=EEASxc>a{Su)jXcRS41Z@Mxk&0B7B<(?Izt5wpyyIBO|-M}ex8BhbIgi*X4 zDZ+Yk1<6&=PoZ=U-!9`!?sBVpYF#Y!JK<`fx}bXN651o0VVaW;t6ASVF@gq-mIDV_)?F^>rq1XX0NYy~(G=I6x%Fi5C2rMtvs z%P`g2>0{xLUy~#ye)%QAz^NkD5GUyPYl}K#;e-~UQ96`I$U0D!sMdQ>;%+c0h>k*Y z)sD1mi_@|rZnQ+zbWq~QxFlBQXj8WEY7NKaOYjUxAkGB8S#;l@b^C?;twRKl=mt0< zazifrBs`(q7_r14u1ZS`66VmsLpV>b5U!ktX>g4Nq~VPq6`%`3iCdr(>nS~uxxylU z>h(2p$XPJVh9BDpRLLzTDlNdp+oq8sOUlJ#{6boG`k)bwnsw5iy@#d{f_De-I|}vx6evw;ch97=;kLvM)-DBGwl6%fA%JItoMeyqjCR*_5Q70yd!KN zh=>ek8>f#~^6CJR0DXp0;7ifZjjSGBn}Cl{HeX!$iXMbtAU$F+;`%A<3TqbN#PCM& z&ueq$cB%pu2oMm_-@*aYzgn9`OiT@2ter*d+-$Aw42(@2Ng4mKG%M-IqX?q%3R|_( zN|&n$e1L#Ev=YMX5F53!O%))qDG3D(0rsOHblk;9ghWyqEOpg)mC$OduqpHAuIxr_>*|zy+|=EmOFn zFM+Ni%@CymLS-3vRWn=rVk?oZEz0V#y356IE6HR5#>7EigxZ05=cA|4<_tC8jyBJ| zgg!^kNwP7S^ooIj6riI9x`jFeQfRr4JCPumr<82M zto$j^Qb~MPmJ-|*2u{o7?yI8BI``zDaOCg2tG_5X;w<|uj5%oDthnLx-l4l)fmUGx z6N^jR|DC);yLi4q-ztTkf>*U$@2^w5(lhxu=OC|=WuTTp^!?2Nn27R`2FY_ zLHY-zFS}r+4|XyZw9b0D3)DmS!Gr+-LSdI}m{@-gL%^8CFSIYL?UZaCVd)2VI3|ay zwue39zshVrB+s2lp*};!gm<79@0HkjhgF^>`UhoR9Mi`aI#V#fI@x&1K3f&^8kaq% zkHVg$CTBoaGqEjrL)k*Y!rtiD2iQLYZ%|B}oBl8GHvR%n>HiIQN*+$mCN>I=c7H2N z&K4$4e@E^ff-cVHCbrHNMh4Dy|2Q;M{{xu|DYjeaRh2FK5QK!bG_K`kbBk$l$S4UF zq?F-%7UrX_Q?9M)a#WvcZ^R-fzJB5IFP>3uEoeCAAhN5W-ELRB&zsCnWY6#E?!)E56Pe+bxHjGF6;R9Hps)+t092-bf4 z_Wieg+0u5JL++k)#i0r?l`9*k)3ZlHOeMJ1DTdx9E1J2@BtdD3qX;&S_wMExOGv$T zl^T%oxb+)vq6vJvR`8{+YOsc@8}wSXpoK%v0k@8X*04Se3<8f)rE|fRXAoT!$6MdrKSuzeK@L*yug?MQs8oTbofqW)Df# zC2J3irHAaX_e~SGlBoRhEW`W6Z}&YX|5IMfzskAt{B*m z*w=3i!;x5Gfgc~>y9fPXFAPMhO@Si}SQESjh`P|dlV5HPRo7j(hV=$o8UMIT7~7+k z*@Sd>f%#{ARweJYhQs~ECpHie!~YXL|FJA;KS4m|CKFnT{fN`Ws>N?CcV@(>7WMPYN} z1}Wg+XU2(Yjpq7PJ|aSn;THEZ{4s8*@N!dz&bjys_Zk7%HiD+56;cF26`-a zEIo!B(T|L*uMXUvqJs&54`^@sUMtH-i~rOM9%$xGXTpmow$DxI>E5!csP zAHe|);0w%`I<==_Zw9t$e}?R+lIu%|`coRum(1p~*+20mBc?Z=$+z<0n&qS0-}|L4 zrgq|(U*eB%l3nfC=U1Y?(Tf@0x8bhdtsU2w&Y-WvyzkiyJ>GZqUP6c+<_p0`ZOnIK z#a~ynuzRWxO6c;S@*}B1pTjLJQHi(+EuE2;gG*p^Fq%6UoE1x95(^BY$H$$soSf=vpJ)_3E zp&$l=SiNaeoNLAK8x%XaHp3-So@F7 z3NMRRa@%k+Z$a%yb25ud&>Cdcb<+}n>=jZ`91)a z{wcA(j$%z#RoyB|&Z+B4%7Pe*No`pAX0Y;Ju4$wvJE{VF*Qej8C}uVF=xFpG^rY6Y+9mcz$T9^x(VP3uY>G3Zt&eU{pF*Bu<4j9MPbi4NMC=Z$kS6DMW9yN#vhM&1gd1t}8m(*YY9 zh2@s)$1p4yYT`~lYmU>>wKu+DhlnI1#Xn4(Rnv_qidPQHW=w3ZU!w3(@jO*f;4;h? zMH0!08(4=lT}#QA=eR(ZtW1=~llQij7)L6n#?5iY_p>|_mLalXYRH!x#Y?KHyzPB^ z6P3YRD}{ou%9T%|nOpP_??P;Rmra7$Q*Jz-f?42PF_y>d)+0Q^)o5h8@7S=je}xG# z2_?AdFP^t{IZHWK)9+EE_aPtTBahhUcWIQ7Awz?NK)ck2n-a$gplnd4OKbJ;;tvIu zH4vAexlK2f22gTALq5PZ&vfFqqERVT{G_d`X)eGI%+?5k6lRiHoo*Vc?ie6dx75_t z6hmd#0?OB9*OKD7A~P$e-TTv3^aCdZys6@`vq%Vi_D8>=`t&q9`Jn1=M#ktSC>SO3 z1V?vuIlQs6+{aHDHL?BB&3baSv;y#07}(xll9vs9K_vs2f9gC9Biy+9DxS77=)c z6dMbuokO-L*Te5JUSO$MmhIuFJRGR&9cDf)@y5OQu&Q$h@SW-yU&XQd9;_x;l z<`{S&Hnl!5U@%I~5p)BZspK894y7kVQE7&?t7Z|OOlnrCkvEf7$J5dR?0;Jt6oANc zMnb_Xjky|2ID#fhIB2hs-48Er>*M?56YFnjC)ixiCes%fgT?C|1tQupZ0Jon>yr|j z6M66rC(=;vw^orAMk!I1z|k}1Ox9qOILGJFxU*ZrMSfCe?)wByP=U73z+@Pfbcndc=VzYvSUnUy z+-B+_n`=f>kS8QBPwk+aD()=#IqkdxHPQMJ93{JGhP=48oRkmJyQ@i$pk(L&(p6<0 zC9ZEdO*i+t`;%(Ctae(SjV<@i%r5aune9)T4{hdzv33Uo9*K=V18S$6VVm^wgEteF za0zCLO(9~!U9_z@Qrh&rS|L0xG}RWoE1jXiEsrTgIF4qf#{0rl zE}|NGrvYLMtoORV&FWaFadDNCjMt|U8ba8|z&3tvd)s7KQ!Od*Kqe(48&C7=V;?`SQV)Qc?6L^k_vNUPbJ>>!5J?sDYm5kR&h_RZk)MfZ1 znOpQ|T;Me(%mdBJR$sbEmp3!HKDDSmMDnVpeo{S13l#9e6OImR$UPzjd-eCwmMwyT zm5~g6DIbY<_!8;xEUHdT(r_OQ<6QCE9Jy|QLoS>d(B zW6GRzX)~&Mx}})ITysFzl5_6JM*~ciBfVP(WF_r zY>z4gw&AxB%UV3Y{Y6z*t*o!p@~#u3X_t{Q9Us8ar8_9?N% zN&M~6y%2R(mAZ~@Tg1Oapt?vDr&fHuJ=V$wXstq|)eIG_4lB#@eU>fniJh zwJY<8yH5(+SSQ=$Y=-$2f$@^Ak#~kaR^NYFsi{XGlFCvK(eu{S$J(owIv17|p-%0O zL-@NyUg!rx0$Uh~JIeMX6JJE>*t<7vS9ev#^{AGyc;uio_-Je1?u#mA8+JVczhA2( zhD!koe;9$`Qgaxlcly4rdQ1VlmEHUhHe9TwduB+hm3wH2o27edh?|vrY{=;1Doy4& zIhP)IDd91@{`QQqVya(ASth4}6OY z-9BQj2d-%+-N7jO8!$QPq%o$9Fy8ja{4WT$gRP+b=Q1I48g-g|iLNjbhYtoNiR*d- z{sB}~8j*6*C3eM8JQj5Jn?mD#Gd*CrVEIDicLJ-4gBqUwLA-bp58UXko;M|ql+i5` zym-&U5BIS9@iPg#fFbuXCHrprSQKRU0#@yd%qrX1hhs*85R}~hahfFDq=e@bX))mf zWH%mXxMx|h5YhrTy;P_Xi_IDH*m6TYv>|hPX*_-XTW0G9iu!PqonQneKKaCVvvF^% zgBMDpN7!N?|G5t`v{neLaCFB{OyIl>qJQ_^0MJXQ zY2%-si~ej?F^%ytIIHU(pqT+3d+|IQ{ss#!c91R{2l*00e3ry!ha|XIsR%!q=E^Fal`6Oxu`K0fmPM?P6ZgzH7|TVQhl;l2 z)2w0L9CsN-(adU5YsuUw19OY_X69-!=7MIJ^(rUNr@#9l6aB8isAL^M{n2oD0FAHk97;X* z-INjZ5li`a|NYNt9gL2WbKT!`?%?lB^)J)9|025nBcBtEmWBRXQwi21EGg8>!tU>6Wf}S3p!>7vHNFSQR zgC>pb^&OHhRQD~7Q|gh5lV)F6i++k4Hp_F2L2WrcxH&@wK}QgVDg+y~o0gZ=$j&^W zz1aP8*cvnEJ#ffCK!Kz{K>yYW`@fc8ByF9X4XmyIv+h!?4&$YKl*~`ToalM{=Z_#^ zUs<1Do+PA*XaH;&0GW^tDjrctWKPmCF-qo7jGL)MK=XP*vt@O4wN1Y!8o`{DN|Rh) znK?nvyU&`ATc@U*l}=@+D*@l^gYOj&6SE|$n{UvyPwaiRQ_ua2?{Vfa|E~uqV$BhH z^QNqA*9F@*1dA`FLbnq;=+9KC@9Mel*>6i_@oVab95LHpTE)*t@BS>}tZ#9A^X7nP z3mIo+6TpvS$peMe@&=g5EQF9Mi9*W@Q`sYs=% z`J{3llzn$q;2G1{N!-#oTfQDY`8>C|n=Fu=iTk443Ld>>^fIr4-!R3U5_^ftd>VU> zij_ix{`V$I#k6!Oy2-z#QFSZkEPrXWsYyFURAo`Kl$LkN>@A?_);LE0rZIkmjb6T$ zvhc#L-Cv^4Ex*AIo=KQn!)A4;7K`pu-E+atrm@Cpmpl3e>)t(yo4gGOX18pL#xceU zbVB`#5_@(k{4LAygT1m#@(7*7f5zqB)HWH#TCrVLd9}j6Q>?p7HX{avFSb?Msb>Jg z9Q9DChze~0Psl!h0E6mcWh?ky! z$p#@LxUe(TR5sW2tMb#pS1ng@>w3o|r~-o4m&00p$wiWQ5Sh-vx2cv5nemM~Fl1Pn z@3ALEM#_3h4-XQ&z$#6X&r~U-&ge+HK6$)-`hqPj0tb|+kaKy*LS5@a9aSk!=WAEB z7cI`gaUSauMkEbg?nl0$44TYIwTngwzvUu0v0_OhpV;%$5Qgg&)WZm^FN=PNstTzW z5<}$*L;zrw>a$bG5r`q?DRc%V$RwwnGIe?m&(9mClc}9i#aHUKPLdt96(pMxt5u`F zsVoku+IC|TC;_C5rEU!}Gu*`2zKnDQ`WtOc3i#v}_9p>fW{L4(`pY;?uq z$`&LvOMMbLsPDYP*x|AVrmCRaI$UB?QoO(7mlBcHC};gA=!meK)IsI~PL0y1&{Dfm6! zxIajDc1$a0s>QG%WID%>A#`iA+J8HaAGsH z+1JH=+eX5F(AjmZGk|`7}Gpl#jvD6_Z!&{*kn@WkECV-~Ja@tmSR|e_L@9?N9 z3hyyry*D0!XyQh_V=8-SnJco#P{XBd1+7<5S3FA)2dFlkJY!1OO&M7z9uO?$#hp8K z><}uQS-^-B;u7Z^QD!7#V;QFmx0m%{^xtl3ZvPyZdi;^O&c;sNC4CHxzvvOB8&uHl zBN;-lu+P=jNn`2k$=vE0JzL{v67psMe_cb$LsmVfxA?yG z^q7lR00E@Ud3)mBPnT0KM~pwzZiBREupva^PE3~e zBgQ9oh@kcTk2)px3Hv^VzTtMzCG?*X(TDZ1MJ6zx{v- z;$oo46L#QNjk*1przHSQn~Ba#>3BG8`L)xla=P{Ql8aZ!A^Z6rPv%&@SnTI7FhdzT z-x7FR0{9HZg8Bd(puRlmXB(tB?&pxM&<=cA-;RT5}8rI%~CSUsR^{Dr%I2WAQghoqE5 zeQ874(T`vBC+r2Mi(w`h|d zA4x%EfH35I?h933@ic#u`b+%b+T?h=<}m@x_~!>o35p|cvIkkw07W=Ny7YcgssA_^ z|KJQrnu||Nu9@b|xC#C5?8Pin=q|UB?`CTw&AW0b)lKxZVYrBw+whPwZJCl}G&w9r zr7qsqm>f2u_6F@FhZU0%1Ioc3X7bMP%by_Z?hds`Q+&3P9-_AX+3CZ=@n!y7udAV2 zp{GT6;VL4-#t0l_h~?J^;trk1kxNAn8jdoaqgM2+mL&?tVy{I)e`HT9#Tr}HKnAfO zAJZ82j0+49)E0+=x%#1_D;sKu#W>~5HZV6AnZfC`v#unnm=hLTtGWz+21|p)uV+0= zDOyrLYI2^g8m3wtm-=pf^6N4ebLJbV%x`J8yd1!3Avqgg6|ar z=EM0KdG6a2L4YK~_kgr6w5OA;dvw0WPFhMF7`I5vD}#giMbMzRotEs&-q z^ji&t1A?l%UJezWv?>ijh|$1^UCJYXJwLX#IH}_1K@sAR!*q@j(({4#DfT|nj}p7M zFBU=FwOSI=xng>2lYo5*J9K3yZPwv(=7kbl8Xv0biOba>vik>6!sfwnH(pglq1mD-GrQi8H*AmfY*J7&;hny2F zupR}4@kzq+K*BE%5$iX5nQzayWTCLJ^xTam-EEIH-L2;huPSy;32KLb>>4 z#l$W^Sx7Q5j+Sy*E;1eSQQuHHWOT;1#LjoYpL!-{7W3SP4*MXf z<~>V7^&sY|9XSw`B<^9fTGQLPEtj=;<#x^=;O9f2{oR+{Ef^oZ z@N>P$>mypv%_#=lBSIr_5sn zBF-F_WgYS81vyW6$M;D_PoE&%OkNV1&-q+qgg~`A7s}>S`}cn#E$2m z%aeUXwNA(^3tP=;y5%pk#5Yz&H#AD`Jph-xjvZm_3KZ|J>_NR@croB^RUT~K;Exu5%wC}1D4nov3+@b8 zKyU5jYuQ*ZpTK23xXzpN51kB+r*ktnQJ7kee-gP+Ij0J_#rFTS4Gux;pkVB;n(c=6 zMks#)ZuXUcnN>UKDJ-IP-u2de1-AKdHxRZDUGkp)0Q#U$EPKlSLQSlnq)OsCour)+ zIXh@3d!ImInH7VrmR>p8p4%n;Tf6l2jx1qjJu>e3kf5aTzU)&910nXa-g0xn$tFa& z2qZ7UAl*@5o=PAh`6L${6S-0?pe3thPB4pahffb$#nL8ncN(Nyos`}r{%{g64Ji^= zK8BIywT0-g4VrhTt}n~Y;3?FGL74h?EG*QfQy0A8u>BtXuI{C-BYu*$o^}U1)z;8d zVN(ssw?oCbebREPD~I$-t7}`_5{{<0d10So7Pc2%EREdpMWIJI&$|rq<0!LL+BQM4 zn7)cq=qy|8YzdO(?NOsVRk{rW)@e7g^S~r^SCawzq3kj#u(5@C!PKCK0cCy zT@Tey2IeDYafA2~1{gyvaIT^a-Yo9kx!W#P-k6DfasKEgFji`hkzrmJ#JU^Yb%Nc~ zc)+cIfTBA#N0moyxZ~K!`^<>*Nzv-cjOKR(kUa4AkAG#vtWpaD=!Ku&;(D#(>$&~B zI?V}e8@p%s(G|8L+B)&xE<({g^M`#TwqdB=+oP|5pF3Z8u>VA!=w6k)zc6w2=?Q2` zYCjX|)fRKI1gNj{-8ymwDOI5Mx8oNp2JJHG3dGJGg!vK>$ji?n>5qG)`6lEfc&0uV z)te%G&Q1rN;+7EPr-n8LpNz6C6N0*v{_iIbta7OTukSY zt5r@sO!)rjh0aAmShx zd3=DJ3c(pJXGXzIh?#RR_*krI1q)H$FJ#dwIvz);mn;w6Rlw+>LEq4CN6pP4AI;!Y zk-sQ?O=i1Mp5lZX3yka>p+XCraM+a!1)`F`h^cG>0)f0OApGe(^cz-WoOno-Y(EeB zVBy3=Yj}ak7OBj~V259{&B`~tbJCxeVy@OEE|ke4O2=TwIvf-=;Xt_l)y`wuQ-9#D z(xD-!k+2KQzr`l$7dLvWf*$c8=#(`40h6d$m6%!SB1JzK+tYQihGQEwR*-!cM>#LD>x_J*w(LZbcvHW@LTjM?RSN z0@Z*4$Bw~Ki3W|JRI-r3aMSepJNv;mo|5yDfqNLHQ55&A>H5>_V9<_R!Ip`7^ylX=D<5 zr40z>BKiC@4{wSUswebDlvprK4SK2!)w4KkfX~jY9!W|xUKGTVn}g@0fG94sSJGV- z9@a~d2gf5s>8XT@`If?Oway5SNZS!L5=jpB8mceuf2Nd%aK2Zt|2FVcg8~7O{VPgI z#?H*_Kl!9!B}MrK1=O!Aw&faUBluA0v#gWVlAmZt;QN7KC<$;;%p`lmn@d(yu9scs zVjomrund9+p!|LWCOoZ`ur5QXPFJtfr_b5%&Ajig2dI6}s&Fy~t^j}()~4WEpAPL= zTj^d;OoZTUf?weuf2m?|R-7 z*C4M6ZhWF(F@2}nsp85rOqt+!+uZz3$ReX#{MP5-r6b`ztXDWl$_mcjFn*{sEx7f*O(ck+ou8_?~a_2Ztsq6qB|SPw26k!tLk{Q~Rz z$(8F1B;zK-#>AmmDC7;;_!;g&CU7a?qiIT=6Ts0cbUNMT6yPRH9~g zS%x{(kxYd=D&GKCkx;N21sU;OI8@4vLg2}L>Lb{Qv`B*O0*j>yJd#`R5ypf^lp<7V zCc|+>fYgvG`ROo>HK+FAqlDm81MS>&?n2E-(;N7}oF>3T9}4^PhY=Gm`9i(DPpuS- zq)>2qz!TmZ6q8;&M?@B;p1uG6RM_Y8zyId{-~XQD_}bXL{Jp7w`)~IR{l5a2?7!Vg zp!OfP4E$Ty_-K3VY!wdGj%2RL%QPHTL)uKfO5Am5<$`5 zHCBtvI~7q-ochU`=NJF*pPx@^IhAk&ZEA>w$%oPGc-}6~ywV~3-0{>*sb=|ruD{y$ ze%@-m`u28vKDaf*_rmN`tzQT>&2ltg-lofR8~c;p;E@`zK!1lkgi?JR0 z+<61+rEupp7F=mB=Ch?HwEjuQm}1KOh=o@ zMbI}0J>5}!koi&v9?!B?4FJR88jvyXR_v{YDm}C)lp@2G2{a{~6V5CwSrp6vHQsfb-U<{SSrQ zhjRbS;qlDTA&TQ2#?M(4xsRXFZ^;3A+_yLw>o-9GJ5sgsauB`LnB-hGo9sJ~tJ`Q>=X7sVmg<=Fcv=JDe*DjP-SK-0mJ7)>I zaLDLOU*I}4@cro&?@C`hH3tiXmN`!(&>@S2bFyAvI&axlSgd=!4IOi#+W;sS>lQ28 zd}q&dew9=x;5l0kK@1y9JgKWMv9!I`*C;((P>8C@JJRGwP5EL;JAPHi5fI|4MqlLU z^4D!~w+OIklt7dx3^!m6Be{Lp55j{5gSGgJz=hlNd@tt_I>UG(GP5s^O{jFU;m~l0 zfd`QdE~0Ym=6+XN*P`i0ogbgAJVjD9#%eBYJGIbDZ4s(f-KRE_>8D1Dv*kgO1~NSn zigx8f+VcA_xS)V-O^qrs&N9(}L!_3HAcegFfzVAntKxmhgOtsb4k6qHOpGWq6Q0RS zZO=EomYL%;nKgmFqxD<68tSGFOEM^u0M(;;2m1#4GvSsz2$jawEJDNWrrCrbO<}g~ zkM6516erswSi_yWuyR}}+h!VY?-F!&Y5Z!Z`tkJz&`8AyQ=-mEXxkQ%abc`V1s>DE zLXd7!Q6C)`7#dmZ4Lm?>CTlyTOslb(wZbi|6|Pl5fFq3y^VIzE4DALm=q$pK>-WM> z@ETsJj5=7=*4 z#Q8(b#+V=~6Gxl?$xq|?@_yQJ2+hAYmuTj0F76c(B8K%;DPhGGWr)cY>SQS>s7%O- zr6Ml8h`}klA=1&wvbFMqk}6fml`4A%G=o@K@8LHifs$)}wD?ix~Id@9-`;?+I7 zOhQN(D)j=^%EHN16(Z3@mMRM5=V)_z(6y^1b?@Bn6m>LUW7}?nupv*6MUVPSjf!Ym zMPo5YoD~t(`-c9w)tV%RX*mYjAn;5MIsD?0L&NQ#IY`9k5}Fr#5{CeTr)O|C2fRhY z4zq(ltHY2X)P*f?yM#RY75m8c<%{Y?5feq6xvdMWrNuqnR%(o(uo8i|36NaN<#FnT ze-_O*q0DXqR>^*1sAnsz$Ueqe5*AD@Htx?pWR*RP=0#!NjnaE-Gq3oUM~Kc9MO+o6 z7qc6wsBxp7GXx+hwEunnebz!|CX&`z{>loyCFSF-zg za}zec;B1H7rhGMDfn+t9n*wt|C_0-MM~XO*wx7-`@9~-%t?IegrHM(6oVSG^u?q`T zO<+YuVbO2fonR-MCa6@aND4dBy^~awRZcp!&=v+#kH@4jYvxt=)zsHV0;47XjlvDC8M1hSV zm!GB(KGLwSd{F-?dmMAe%W0oxkgDv8ivbs__S{*1U}yQ=tsqHJYI9)jduSKr<63$> zp;a-B^6Hg3OLUPi1UwHnptVSH=_Km$SXrCM2w8P z%F#Boi&CcZ5vAGjR1axw&YNh~Q%)VDYUDZ6f^0;>W7_sZr&QvRWc2v~p^PqkA%m=S zCwFUg2bNM(DaY>=TLmOLaDW&uH;Za?8BAwQo4+Xy4KXX;Z}@D5+}m)U#o?3UF}+(@jr$M4ja*`Y9gy~Y`0 z6Aex1*3ng@2er)@{%E9a3A;cts9cAor=RWt7ege)z=$O3$d5CX&hORZ3htL>jj5qT zW#KGQ;AZ|YbS0fvG~Y)CvVwXnBLJkSps7d~v;cj$D3w=rB9Tx>a&4>(x00yz!o*SOd*M!yIwx;NgqW?(ysFv8XLxs6Lrh8-F`3FO$}V{Avztc4qmZ zoz&YQR`*wWy_^&k-ifJ&N8Qh=E-fH6e}-}0C{h~hYS6L^lP>=pLOmjN-z4eQL27!6 zIe2E}knE;dxIJ_!>Mt|vXj%uGY=I^8(q<4zJy~Q@_^p@JUNiGPr!oUHfL~dw9t7C4I9$7RnG5p9wBpdw^)PtGwLmaQM=KYe z;Dfw@%nquH^nOI6gjP+K@B~0g1+WROmv1sk1tV@SUr>YvK7mxV3$HR4WeQ2&Y-{q~ z4PAR&mPOEsTbo~mRwg&EJE2Dj?TOZPO_@Z|HZX9-6NA!%Pb3h;G3F5J+30BoT8-PU z_kbx`I>&nWEMtfv(-m>LzC}s6q%VdBUVI_GUv3@^6SMkEBeVjWplD5y58LyJhikp4VLHhyf?n%gk0PBr(PZ3 z+V`qF971_d@rCO8p#7*#L0^v$DH>-qB!gy@ut`3 zy3cQ8*t@@{V7F*ti(u{G4i55*xY9Erw3{JZ8T4QPjo5b{n=&z4P^}wxA;x85^fwmD z6mEq9o;kx<5VneT_c-VUqa|zLe+BFgskp_;A)b>&EDmmP7Gx#nU-T@;O+(&&n7ljK zqK7&yV!`FIJAI+SaA6y=-H=tT`zWvBlaed!3X^_Lucc%Q=kuiG%65@@6IeG}e@`ieesOL} zKHBJBso6u&7gzlrpB%_yy<>TFwDI>}Ec|Gieb4=0fGwY|3YGW2Dq46=a1 zVo`Vi%yz+L9)9hbb%FLTC@-G(lODgJ(f&WmSCK9zV3-IV7XI<{2j}ms_Vmb!os)06 zhVIZPZF)hW--kWTCyDVRd2T&t|P&aDrtO5kzXy<*A+5$k7$>4+y%;% znYN-t#1^#}Z6d+ahj*Gzor+@kBD7@f|IGNR$4U=Y0J2#D2)YSxUCtiC1weJg zLp0Q&JFrt|In8!~1?fY0?=fPyaqPy$iQXJDhHP>N%B42Yck`Qz-OM_~GMuWow)>=Q z0pCCC7d0Z^Ipx29`}P3;?b{dO?7z0e{L|O*Z}nxi>X|RL8XAw$1eOLKd5j@f{RQ~Y zG?7$`hy@s7IoRF2@KA%2ZM6{ru9T5Gj)iDCz};VvlG$WuT+>_wCTS~J6`I9D{nsrU z2;X#OyopBgo778Q>D%_E>rMN~Po~d5H<`8|Zcv}F`xL5~NCVLX4Wkg007HhMgj9Pa z94$km3A+F&LzOJlpeFR*j+Y%M!Qm42ziH~cKM&3b;15s)ycD@3_tL-dk{+xP@J7#o z-)bYa-gd2esfy<&-nrj>1{1^_L>j&(MA1#WNPg3UD?reL*}V{ag{b!uT755x>mfbZ z0PzwF+kx91`qqOn`1>xw@801XAJlH>{`~|pyi6J;3s=cTOfelA&K5HX#gBp6s<|r5 zjSSj+CU*-TulqlnlP`}?)JkJ_7fg){;bRlXf+&^e8CWwFqGY@SZ=%NmLCXpYb+}7* z$4k}%iFUi^kBdeJg^kHt)f~<;Ovlz!9frq20cIj>2eIcG(dh57ry;^E^2T)E_8#;_9iJT>4sdCB_db|zO?Z^*lBN zNCs~f+Jkx%EUgkN2-xFF?B%TMr4#)%wq?-~+Nh;g9=n3tM>i5ZcH&nkVcPXgYRjG@ zf(Y7WN@hGV7o0bjx_2@bthJ`hjXXpfaes_(lWIw!(QK_nkyqj?{j#uFKpNVpV@h?7_WC3~&%)xHR1kKo`Cypj15#%0m z-o0GXem63g^|IltM?eZV=b+Z2e8&Z1%{0;*zmFc62mNqLTy$Y_c|9HiH0l>K z+mAx7DVYoHhXfdCE8Bs@j=t0f*uM++Idd25BgIm`Ad;I_{$mO?W%=JF82blr8rl>yMk6?pM z^tMluJ-ckG_}OkxP91t2o>CQ_O8^VZn$s$M_APWIXBGBq0Lt^YrTD5(Vwe2ta4y#DEYa(W~=eLOy7rD^%Vd$kL27M)MSpwgoP3P{ z!yS$zc|uP{yzaIqCwE!AfYNS;KW|OdP1Q%!LZviA0e^WDsIS5#= z!B{TW)VB)VHg{LoS#W7i6W>*sFz!qr^YS0t2kh90y=Je5{p>8)~D@dLS@QM(F# zIp{6M*#(@?tsu1Rq-Mdq+eV}ibRSpv#976C_5xlI`$#1tN`sK1?)5M+sj=OXG6dNu zV1K{y>!i0&9w8O{a>`IA#mo(3a zf*+Q=&HW7&(nX8~C1tiHZj%>;asBEp$p_Q!@Y0T8R~OuPEy3Lq@^t$8=~(FhPVmJJ z#VF8`(fNzK-b%Iin7|cxWP0xr*M&zoz|fCx@=Y!-0j_~cuxsDHHpmSo)qOalZ$bRl z2F$j0k3llJ$>28HH3l_W(KjF^!@LwtLej_b9;i;{ku2x+&WA@jKTO0ad71@_Yta!{ z2oqhO4zaU433LK371>E{bZ?+3kLZ9WQ2+3PTZAP90%P13Yy3lr3mhmy|>eN6(SHs1C%Q39p)YsUr7(kuaoIJGJhXV-PyG zjnxhcAC;fqY@6;MWWBnRK6ocG`%T&0&*k95#yK7DFtZV?;cy;!RD_*YJjsb6Q`$;K zy)&X{P`*5xEgjTQ9r=oh0|>Z_yeFm?ev!p z7q;JA4mtu@qa39v%6i)Z4%qwdxcHuOMO;a1wFMP_290FqH1OsmCG{ zq^afYrz2BQyQ0*JGE}1h!W9fKgk$b!)|!%q(1x?5=}PpmZQ$e;2EB*k4%+&+u;(E* z2n@=9HsqMv;4>Nn^2v&@4T-YTkd`TdWU^U*;sA5|r7TjZGnLY*xC=_K-GmDfkWEGC z;oN&!c1xB-<4J7=9 zJ(BedZwZhG4|64<=wvCn4)}w%Zx_TEs6ehmjVG&p5pi46r zg=3-3Q~;v55KR&8CfG;`Lv6NsXB}RqPVyNeKAfj9=Ol>fQlEUl2cH7=mPV!68+;jgtKvo5F#8&9m? z``w+#S5UR=QHFGM~noocC zVFa#v2%oo{%;wi~_~R2ci}`=B|0@ zinDfNxV3%iHIS(7{h_WEXqu!v~`CMH+7^SkvLe_3i}=pyDRah zN#L)F-`JLj6BiG}sj*WBmrdZuVVEo86Z<6VB}s)T$ZcWvG?i0cqI}WhUq2Y#{f~x# zi1LjxSZCwiKX}*ETGVzZ157=jydo*xC^}mJ<+)!DDCd4sx?VM%Y;&CTpw5;M*ihZ| zJ!FBJj0&j&-oJs?9a_I$;jzd%7|pdsQ3m`bPBe$nLoV1!YV8?Pw~0D zmSD-5Ue60>L$Rw;yk{_2d~v@CnvZa%!7{{7lb$kxWx!pzyh;6G~RbN5+|mFTbxcxf!XyfbLI^zMQSb6P~xzESXmV{9 zCMp)baZSz%)j&JWkc|Gq;_*$K@zQ%tH^91X2|Byv>=SmWR$7-shf|_^>Ll;*9+c(e z{N%43;&e8}_QGW+zE0m0myb-@QU%=Qo>``5UzB(lH0sK=E``{ZBl2Ni^-QtDp0ME1 zK88E-db_XBZQaU}cuvkCgH7crju~9eE-Y`os~0P-J=s;aS#wil$HGdK;Ut?dSO71ssyrdm{QRpMAV2nXslvlIE#+Oh>l7y_~?;}F!;ENCR zO+IG#NWIRI`FLntsz^FldCkky2f!d-%Pij9iLKr>IfCK);=}}?(NL%#4PfE(4kPQN zSC%BpZJ*P+PO5mHw0Wd%!zJsn&4g<$n#_?(=)JnoR2DK(mCPHp6e6VdV>?E5KCUF@ zf7W9wm%G#Wfm*NxTWIcJX-qtR=~NFxz4PSmDVAU8(B2wIm#IdHae-F{3jKQFiX?8NlKEhXR2Z|JCUd@HMnNVwqF~V9YJtD+T zQlOroDX-mg2% zBKV^Q5m5ECK{nWjJ7FHOSUi*a-C_?S_yo~G5HuRZH6R``^dS3Bh6u!nD`kFbxYThD zw~2%zL4tHA26rcdln4^=A(C+f9hLlcuMCv{8`u;?uoEVbU=YVNkBP#s3KnM@Oi)fQ zt_F3VjY)zASub%Q{Y?XgzlD3M5#gUBUuhW;$>uBSJH9UBfBtug*S|-;h?|L#^Z&uE zB&)spqM89dWg9ZrXi#F{KtL@r9g^xeR8J+$EhL~2u@cf`dS{8GUC76JP0hHtCKRg0 zt*rVyl&jaJAez;!fb!yX^+So4-8XMNpP@d3H*eF%t_?I|zN^1Iu5aGBXSm+}eCqn3 z^+vzcM*J>wV-FJRrx@^5;l>h0{OYT)lg{dr8!{s7(i{5T|3bivDoTonV1yo1@nVPR zXxEgGg^x5KHgp?=$xBwm_cKHeDurCgO>$B$GSO`Cd<~J8@>ni>Z-Ef!3+ck(MHVy@ z@#<*kCOb5S$V+Fvc@{Qv$oLfnOAG&YO5z_E2j6E z7a+c(>-`H)>g+6DeY1Y*ag-B6>Cl@@VhkZY@Uihe!{LlRpuTsmIsN4;+UDsHd954n9WZV6qq*{qZ5j<W)`UorOmXtVnLo3T{t#h3q^fooqQ~A+EY<$TDG4RKP*cK0liX95STt= zToC<2M2*(H1tZ)0s|v~iSAa^F-9jMwCy4cK0HM*3$@1Q`Pz}FFYm`PGP0wuamWrt*ehz3(|Fn%;0;K4}!Q~cx{0U0L=cs6lcrY^Y%Vf_rXpQIw~DfxB-72tZU6gdK8C~ea6(2P@kGH}!2N?>r(Ca{ zsI!6B!alPl%j1CHq97PTVRng$!~?s2{+6ffC#;X2z(Xb#9GsSYYe@9zY~7Dc7Hfgh z5Tq!})o30pA3ywg<9W3NpvUs;E%Cehz=s?EfLzcV0H?b{=q?vJCih2y%dhls6w3j$ zk9LB0L&(15mtul3T^QSK7KIZVTod#Sc)?1gzY~M=?ay87V}6G?F>~AIv()-N zD3rHX`;r;L{9N|Z8REN}OZB&SZ|5a80B%dQd-CNESP7HnuNn43T~Agcl1YOF@#W03 z1b*t!>t5G@XwVygHYczDIC|RdMB+ z$s5_5_W-EXN-u_5Pb{((!+8xa+?@_#dwtYHeJ_49Dql%3Fv0yXeV?!cC&Iqx@s~P%$X6%1 zYzS9pqaUv&aBQqO zBQs7d63FZIL1B&<8^oni%CZOdf6&;^oNqQ-9j-NBuQ^|9baQuZ^Jtyt&?cHq$Q9JE z5D>QY1?MU7%VVbvjysl~-a&ImiE(uFwHo{!kp;Jd`OLE!^4k8ID{`e-&>2uB7XB~= z+nIQGZ8-Sbfa}OrVPL}!mdieCrs3Nq8Ic_lpTKMIJ{h>XS$C3`h~ z?p2AbK~%t$t(NcOq5ZB3V|`a0io8A))v_PMt)Hg3x+07RL>i zGUq@t&+VV`kj55_snp?)Y@0rKZr`riC`9Q(B1P^nxffV9AvBLPrE<8D>ZP{HCDY@JIvYcYNRz8 z0Rf+Q0riSU@KaVpK)0M{2}Wuh!o~t*6>)EZSCQD{=}N4Oxjo1KO-MNpPYuPABh}E|rM!=TSl^F%NV^dg+>WNGi@Q5C z%JGsP#em`4LxDdIzA@VF&`2bLDv%J)(7vedDiXDqx{y6$Y0o~j*nVY73pINPCY?9y z$Rd&^64MN)Pkxr-CuZ+WqAJx6vuIAwmjkN{aPkrJ0I4F5-Bl}$hRzhRhZ^xN&Oe5$ za4Wrh6PyFfDG+Nzd8NTp2})j>pGtyejb&;NkU3C5-_H;{?>xK1QQ9S`xaHoMgee=2 zEbEh+*I!ggW@{T{qENlruZT)ODp~ZXHBc_Ngqu{jyC#qjyYGAQsO8VT^lts$z0HP+ z2xs^QjUwWuiEh863(PqO4BAosmhaK`pEI{-geBD9UuIn8ugOt-|6S(xkBLeGhW~)< z8aWBs0)bzOnY4wC$yW{M@&(iTe{8zhDnKP<1yr9J8akUK)1svAuxC)}x-<>S!9(?F zcA?{_C?@ZV2Aei`n#l(9zu`WS-hJsAXWt(SGp4(xg7~3*c5@odW;kXXbGuLOFMj{d z{gx81mQREmRAUHhfp#zoWh>z}GuS|raw1R#en%9R3hSR`qGglQhaq>#K!M%tooG;? zzjo}>sL7a3M5jW*s8R;#Y8b(l;%*I$@YH9)YzWR!T6WLI{$8ScBvw+5&()>NhPzd! z{>P(yk8{(G&2ovV^|#1HbcVMvXU&;0pk&6CxBTvBAB>#tK~qALsH`Ad1P0tAKWHv+BR8Fv4!`+>Obu1UX^Ov zmOpuS@Ui|NK4k-)TbG?+9T$)rkvq+?=0RDa=xdmY#JHLastjqPXdDbShqW>7NrHZ7 z7(9(HjM1-Ef(^`%3TlhySDJ27vQ?H`xr9VOM%0ANsA|A3-jj|r`KAo%oTajX3>^E` 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 YM{}PByk10KD_nuQ4E7Du?}+~TKh4V)`~Uy| literal 0 HcmV?d00001 diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 0000000..b74bf7f --- /dev/null +++ b/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1,2 @@ +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.6/apache-maven-3.8.6-bin.zip +wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar diff --git a/mvnw b/mvnw new file mode 100644 index 0000000..8a8fb22 --- /dev/null +++ b/mvnw @@ -0,0 +1,316 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Maven Start Up Batch script +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# M2_HOME - location of maven2's installed home dir +# MAVEN_OPTS - parameters passed to the Java VM when running Maven +# e.g. to debug Maven itself, use +# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ] ; then + + if [ -f /usr/local/etc/mavenrc ] ; then + . /usr/local/etc/mavenrc + fi + + if [ -f /etc/mavenrc ] ; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ] ; then + . "$HOME/.mavenrc" + fi + +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false; +darwin=false; +mingw=false +case "`uname`" in + CYGWIN*) cygwin=true ;; + MINGW*) mingw=true;; + Darwin*) darwin=true + # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home + # See https://developer.apple.com/library/mac/qa/qa1170/_index.html + if [ -z "$JAVA_HOME" ]; then + if [ -x "/usr/libexec/java_home" ]; then + export JAVA_HOME="`/usr/libexec/java_home`" + else + export JAVA_HOME="/Library/Java/Home" + fi + fi + ;; +esac + +if [ -z "$JAVA_HOME" ] ; then + if [ -r /etc/gentoo-release ] ; then + JAVA_HOME=`java-config --jre-home` + fi +fi + +if [ -z "$M2_HOME" ] ; then + ## resolve links - $0 may be a link to maven's home + PRG="$0" + + # need this for relative symlinks + while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG="`dirname "$PRG"`/$link" + fi + done + + saveddir=`pwd` + + M2_HOME=`dirname "$PRG"`/.. + + # make it fully qualified + M2_HOME=`cd "$M2_HOME" && pwd` + + cd "$saveddir" + # echo Using m2 at $M2_HOME +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin ; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --unix "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --unix "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --unix "$CLASSPATH"` +fi + +# For Mingw, ensure paths are in UNIX format before anything is touched +if $mingw ; then + [ -n "$M2_HOME" ] && + M2_HOME="`(cd "$M2_HOME"; pwd)`" + [ -n "$JAVA_HOME" ] && + JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" +fi + +if [ -z "$JAVA_HOME" ]; then + javaExecutable="`which javac`" + if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then + # readlink(1) is not available as standard on Solaris 10. + readLink=`which readlink` + if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then + if $darwin ; then + javaHome="`dirname \"$javaExecutable\"`" + javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" + else + javaExecutable="`readlink -f \"$javaExecutable\"`" + fi + javaHome="`dirname \"$javaExecutable\"`" + javaHome=`expr "$javaHome" : '\(.*\)/bin'` + JAVA_HOME="$javaHome" + export JAVA_HOME + fi + fi +fi + +if [ -z "$JAVACMD" ] ; then + if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + else + JAVACMD="`\\unset -f command; \\command -v java`" + fi +fi + +if [ ! -x "$JAVACMD" ] ; then + echo "Error: JAVA_HOME is not defined correctly." >&2 + echo " We cannot execute $JAVACMD" >&2 + exit 1 +fi + +if [ -z "$JAVA_HOME" ] ; then + echo "Warning: JAVA_HOME environment variable is not set." +fi + +CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher + +# traverses directory structure from process work directory to filesystem root +# first directory with .mvn subdirectory is considered project base directory +find_maven_basedir() { + + if [ -z "$1" ] + then + echo "Path not specified to find_maven_basedir" + return 1 + fi + + basedir="$1" + wdir="$1" + while [ "$wdir" != '/' ] ; do + if [ -d "$wdir"/.mvn ] ; then + basedir=$wdir + break + fi + # workaround for JBEAP-8937 (on Solaris 10/Sparc) + if [ -d "${wdir}" ]; then + wdir=`cd "$wdir/.."; pwd` + fi + # end of workaround + done + echo "${basedir}" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + echo "$(tr -s '\n' ' ' < "$1")" + fi +} + +BASE_DIR=`find_maven_basedir "$(pwd)"` +if [ -z "$BASE_DIR" ]; then + exit 1; +fi + +########################################################################################## +# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +# This allows using the maven wrapper in projects that prohibit checking in binary data. +########################################################################################## +if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found .mvn/wrapper/maven-wrapper.jar" + fi +else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." + fi + if [ -n "$MVNW_REPOURL" ]; then + jarUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" + else + jarUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" + fi + while IFS="=" read key value; do + case "$key" in (wrapperUrl) jarUrl="$value"; break ;; + esac + done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" + if [ "$MVNW_VERBOSE" = true ]; then + echo "Downloading from: $jarUrl" + fi + wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" + if $cygwin; then + wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` + fi + + if command -v wget > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found wget ... using wget" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + wget "$jarUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" + else + wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" + fi + elif command -v curl > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found curl ... using curl" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + curl -o "$wrapperJarPath" "$jarUrl" -f + else + curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f + fi + + else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Falling back to using Java to download" + fi + javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" + # For Cygwin, switch paths to Windows format before running javac + if $cygwin; then + javaClass=`cygpath --path --windows "$javaClass"` + fi + if [ -e "$javaClass" ]; then + if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Compiling MavenWrapperDownloader.java ..." + fi + # Compiling the Java class + ("$JAVA_HOME/bin/javac" "$javaClass") + fi + if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + # Running the downloader + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Running MavenWrapperDownloader.java ..." + fi + ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") + fi + fi + fi +fi +########################################################################################## +# End of extension +########################################################################################## + +export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} +if [ "$MVNW_VERBOSE" = true ]; then + echo $MAVEN_PROJECTBASEDIR +fi +MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" + +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --path --windows "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --windows "$CLASSPATH"` + [ -n "$MAVEN_PROJECTBASEDIR" ] && + MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` +fi + +# Provide a "standardized" way to retrieve the CLI args that will +# work with both Windows and non-Windows executions. +MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" +export MAVEN_CMD_LINE_ARGS + +WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +exec "$JAVACMD" \ + $MAVEN_OPTS \ + $MAVEN_DEBUG_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.home=${M2_HOME}" \ + "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/mvnw.cmd b/mvnw.cmd new file mode 100644 index 0000000..1d8ab01 --- /dev/null +++ b/mvnw.cmd @@ -0,0 +1,188 @@ +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM https://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Maven Start Up Batch script +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM M2_HOME - location of maven2's installed home dir +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM set title of command window +title %0 +@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %* +if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %* +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" +set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" + +FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B +) + +@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +@REM This allows using the maven wrapper in projects that prohibit checking in binary data. +if exist %WRAPPER_JAR% ( + if "%MVNW_VERBOSE%" == "true" ( + echo Found %WRAPPER_JAR% + ) +) else ( + if not "%MVNW_REPOURL%" == "" ( + SET DOWNLOAD_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" + ) + if "%MVNW_VERBOSE%" == "true" ( + echo Couldn't find %WRAPPER_JAR%, downloading it ... + echo Downloading from: %DOWNLOAD_URL% + ) + + powershell -Command "&{"^ + "$webclient = new-object System.Net.WebClient;"^ + "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ + "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ + "}"^ + "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ + "}" + if "%MVNW_VERBOSE%" == "true" ( + echo Finished downloading %WRAPPER_JAR% + ) +) +@REM End of extension + +@REM Provide a "standardized" way to retrieve the CLI args that will +@REM work with both Windows and non-Windows executions. +set MAVEN_CMD_LINE_ARGS=%* + +%MAVEN_JAVA_EXE% ^ + %JVM_CONFIG_MAVEN_PROPS% ^ + %MAVEN_OPTS% ^ + %MAVEN_DEBUG_OPTS% ^ + -classpath %WRAPPER_JAR% ^ + "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^ + %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat" +if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%"=="on" pause + +if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE% + +cmd /C exit /B %ERROR_CODE% From 2b30698c6c1844f3ef9192905d91fe0de8264c30 Mon Sep 17 00:00:00 2001 From: Daniel Gradecak Date: Tue, 1 Nov 2022 15:32:59 +0100 Subject: [PATCH 09/18] cleansing the usage of parent/platform plugins+dependencies --- alfresco-mvc-aop/pom.xml | 27 -------- alfresco-mvc-dependencies/pom.xml | 111 +++--------------------------- alfresco-mvc-rest/pom.xml | 39 ----------- pom.xml | 15 ++-- 4 files changed, 17 insertions(+), 175 deletions(-) diff --git a/alfresco-mvc-aop/pom.xml b/alfresco-mvc-aop/pom.xml index 2aff49f..a25a39c 100644 --- a/alfresco-mvc-aop/pom.xml +++ b/alfresco-mvc-aop/pom.xml @@ -15,10 +15,6 @@ Alfresco MVC aop Glue between Spring AOP and Alfresco. This library enables the usage of Spring AOP within Alfresco. - - UTF-8 - - org.alfresco @@ -57,27 +53,4 @@ test - - - - - maven-source-plugin - - - attach-sources - package - - jar-no-fork - - - - - - - org.apache.maven.plugins - maven-compiler-plugin - - - - diff --git a/alfresco-mvc-dependencies/pom.xml b/alfresco-mvc-dependencies/pom.xml index 43db052..f10d047 100644 --- a/alfresco-mvc-dependencies/pom.xml +++ b/alfresco-mvc-dependencies/pom.xml @@ -4,14 +4,19 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 + + + com.gradecak.alfresco-mvc + 8.0.0 + alfresco-mvc-parent + ../ + - com.gradecak.alfresco-mvc alfresco-mvc-dependencies - 8.0.0 pom - Alfresco MVC bom - Bom for Alfresco @MVC project + Alfresco MVC dependencies + Dependencies for Alfresco @MVC project https://github.com/dgradecak/alfresco-mvc @@ -30,20 +35,6 @@ - - scm:git:git://github.com/dgradecak/alfresco-mvc.git - scm:git:ssh://github.com/dgradecak/alfresco-mvc.git - https://github.com/dgradecak/alfresco-mvc/tree/master - - - - 11 - 11 - 11 - 11 - UTF-8 - - @@ -61,88 +52,4 @@ - - - - - maven-surefire-plugin - 2.22.2 - - - maven-source-plugin - - - attach-sources - package - - jar-no-fork - - - - - - org.apache.maven.plugins - maven-javadoc-plugin - 3.1.1 - - - attach-javadocs - - jar - - - - - - org.apache.maven.plugins - maven-compiler-plugin - 3.8.1 - - 11 - - - - org.apache.maven.plugins - maven-gpg-plugin - 1.6 - - - sign-artifacts - verify - - sign - - - - --pinentry-mode - loopback - - - - - - - - - - - snapshot - - - - org.apache.maven.plugins - maven-gpg-plugin - 1.6 - - - sign-artifacts - none - - - - - - - - diff --git a/alfresco-mvc-rest/pom.xml b/alfresco-mvc-rest/pom.xml index 92209cf..d825a28 100644 --- a/alfresco-mvc-rest/pom.xml +++ b/alfresco-mvc-rest/pom.xml @@ -15,10 +15,6 @@ Alfresco MVC rest Glue between SpringMVC and Alfresco. This library enables the usage of Spring @MVC within Alfresco. - - UTF-8 - - javax.servlet @@ -68,39 +64,4 @@ test - - - - - maven-source-plugin - - - attach-sources - package - - jar-no-fork - - - - - - - org.apache.maven.plugins - maven-jar-plugin - - - - test-jar - - - - - - - org.apache.maven.plugins - maven-compiler-plugin - - - - diff --git a/pom.xml b/pom.xml index bbd093e..17d1d91 100644 --- a/pom.xml +++ b/pom.xml @@ -1,7 +1,5 @@ - + 4.0.0 @@ -131,11 +129,14 @@ + org.apache.maven.plugins maven-surefire-plugin 2.22.2 + org.apache.maven.plugins maven-source-plugin + 3.2.1 attach-sources @@ -149,7 +150,7 @@ org.apache.maven.plugins maven-javadoc-plugin - 3.1.1 + 3.4.1 attach-javadocs @@ -162,7 +163,7 @@ org.apache.maven.plugins maven-compiler-plugin - 3.8.1 + 3.10.1 11 @@ -170,7 +171,7 @@ org.apache.maven.plugins maven-gpg-plugin - 1.6 + 3.0.1 sign-artifacts @@ -198,7 +199,7 @@ org.apache.maven.plugins maven-gpg-plugin - 1.6 + 3.0.1 sign-artifacts From 612b763ae0865338de19b759d890d7ba6ba3203b Mon Sep 17 00:00:00 2001 From: Daniel Gradecak Date: Tue, 1 Nov 2022 15:44:05 +0100 Subject: [PATCH 10/18] test dependencies versions updated --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 17d1d91..131aa66 100644 --- a/pom.xml +++ b/pom.xml @@ -86,7 +86,7 @@ org.mockito mockito-core - 3.8.0 + 4.8.1 provided @@ -105,7 +105,7 @@ org.junit.jupiter junit-jupiter-engine - 5.7.1 + 5.9.1 test From f96701908441db0536b5ae7b316ae5b64cc18eb3 Mon Sep 17 00:00:00 2001 From: Daniel Gradecak Date: Tue, 1 Nov 2022 15:59:37 +0100 Subject: [PATCH 11/18] readme updated/checked --- README.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index d79d0d8..2bd72d1 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,9 @@ The missing glue between Alfresco and Spring MVC === -Personally I do not like webscripts because of the boilerplate code that comes with them (XML, FTL, Java/Javascript). Also I am not a big fan of javascript on the server side esither, as in a medium sized application this becomes unmaintainable. That is why I wrote Alfresco @MVC. +Personally, I do not like Alfresco Webscripts because of the boilerplate code that comes with them (XML, FTL, Java/Javascript). Also, I am not a big fan of Javascript on the server side either, as in a medium sized application this becomes unmaintainable. That is why I wrote Alfresco @MVC. Alfresco @MVC consists of several libraries for REST, AOP -- Alfresco MVC REST enables the usage of the full Spring MVC stack within the context of a webscript, benefiting of Alfresco standard authentication and security +- Alfresco MVC REST enables the usage of the full Spring MVC stack within the context of an Alfresco Webscript, benefiting of Alfresco standard authentication and security - Alfresco MVC AOP enables simple handling of Alfresco transactions and Alfresco runAs mechanism with simple annotations [Distributed on Maven Central](https://search.maven.org/search?q=g:com.gradecak.alfresco-mvc) @@ -23,24 +23,24 @@ Alfresco @MVC consists of several libraries for REST, AOP ``` Works on Enterprise as well as on Community and it reuses a widely accepted REST framework. -For the correct version supported on your Alfresco version [please check the wiki](https://github.com/dgradecak/alfresco-mvc/wiki) or -[the release page](https://github.com/dgradecak/alfresco-mvc/releases). +For the correct version supported by your Alfresco version [please check the wiki](https://github.com/dgradecak/alfresco-mvc/wiki) or +[The release page](https://github.com/dgradecak/alfresco-mvc/releases). [The docs are on the wiki page](https://github.com/dgradecak/alfresco-mvc/wiki) -[The Samples are provided on ](https://github.com/dgradecak/alfresco-mvc-sample) +[The Samples are provided on](https://github.com/dgradecak/alfresco-mvc-sample) You should use it when - - You need custom APIs - You want to be more productive -- You write custom webscripts +- You write custom Alfresco Webscripts -You would benefit of +You would benefit from - - Faster and cleaner development -- Java developers know how to use Spring MVC while new comers tend to avoid webscripts +- Java developers know how to use Spring MVC while newcomers tend to avoid Alfresco Webscripts -For supported Alfresco versions check the [release notes](https://github.com/dgradecak/alfresco-mvc/releases) +For supported Alfresco versions, check the [release notes](https://github.com/dgradecak/alfresco-mvc/releases) From b66b8971d4007f3fb3efecc0e6349de5b8246abe Mon Sep 17 00:00:00 2001 From: Daniel Gradecak Date: Sat, 5 Nov 2022 16:06:57 +0100 Subject: [PATCH 12/18] alfresco multi version test support --- README.md | 16 ++++ ...tConfigModuleWithSuffixConfiguration.java} | 2 +- ...onfigModuleWithoutSuffixConfiguration.java | 35 +++++++ .../CustomServletConfigTest.java | 40 -------- ...est.java => WithSuffixControllerTest.java} | 30 +++--- .../WithoutSuffixControllerTest.java | 96 +++++++++++++++++++ notice.txt | 2 +- pom.xml | 60 +++++++----- 8 files changed, 202 insertions(+), 79 deletions(-) rename alfresco-mvc-rest/src/test/java/com/gradecak/alfresco/mvc/test/inheritservletconfig/{AlfrescoMvcServletConfigModuleConfiguration.java => AlfrescoMvcServletConfigModuleWithSuffixConfiguration.java} (93%) create mode 100644 alfresco-mvc-rest/src/test/java/com/gradecak/alfresco/mvc/test/inheritservletconfig/AlfrescoMvcServletConfigModuleWithoutSuffixConfiguration.java rename alfresco-mvc-rest/src/test/java/com/gradecak/alfresco/mvc/test/inheritservletconfig/{InheritedServletConfigTest.java => WithSuffixControllerTest.java} (82%) create mode 100644 alfresco-mvc-rest/src/test/java/com/gradecak/alfresco/mvc/test/inheritservletconfig/WithoutSuffixControllerTest.java diff --git a/README.md b/README.md index 2bd72d1..7fdebf8 100644 --- a/README.md +++ b/README.md @@ -44,3 +44,19 @@ You would benefit from For supported Alfresco versions, check the [release notes](https://github.com/dgradecak/alfresco-mvc/releases) + +Maven local installation +- +mvn clean install -Dgpg.skip + + +Profiles +- +We are using profiles to test against different Alfresco versions. If no configured Maven profiles are provided the default will be used and is specified by true in the pom.xml + +example: mvn package -Pcommunity-7.2.0 + +Testing +- +From v8.0.0 we have decided to only run our unit tests against the Alfresco Community versions. Despite not being tested on Alfresco Enterprise it has to be compatible with the respectively tested Alfresco Community distribution + diff --git a/alfresco-mvc-rest/src/test/java/com/gradecak/alfresco/mvc/test/inheritservletconfig/AlfrescoMvcServletConfigModuleConfiguration.java b/alfresco-mvc-rest/src/test/java/com/gradecak/alfresco/mvc/test/inheritservletconfig/AlfrescoMvcServletConfigModuleWithSuffixConfiguration.java similarity index 93% rename from alfresco-mvc-rest/src/test/java/com/gradecak/alfresco/mvc/test/inheritservletconfig/AlfrescoMvcServletConfigModuleConfiguration.java rename to alfresco-mvc-rest/src/test/java/com/gradecak/alfresco/mvc/test/inheritservletconfig/AlfrescoMvcServletConfigModuleWithSuffixConfiguration.java index 13da9fa..1c4cf05 100644 --- a/alfresco-mvc-rest/src/test/java/com/gradecak/alfresco/mvc/test/inheritservletconfig/AlfrescoMvcServletConfigModuleConfiguration.java +++ b/alfresco-mvc-rest/src/test/java/com/gradecak/alfresco/mvc/test/inheritservletconfig/AlfrescoMvcServletConfigModuleWithSuffixConfiguration.java @@ -26,7 +26,7 @@ @Configuration @EnableAlfrescoMvcRest(@AlfrescoDispatcherWebscript(servletContext = AlfrescoMvcServletContext.class)) -public class AlfrescoMvcServletConfigModuleConfiguration implements WebMvcConfigurer { +public class AlfrescoMvcServletConfigModuleWithSuffixConfiguration implements WebMvcConfigurer { @Override public void configurePathMatch(PathMatchConfigurer configurer) { diff --git a/alfresco-mvc-rest/src/test/java/com/gradecak/alfresco/mvc/test/inheritservletconfig/AlfrescoMvcServletConfigModuleWithoutSuffixConfiguration.java b/alfresco-mvc-rest/src/test/java/com/gradecak/alfresco/mvc/test/inheritservletconfig/AlfrescoMvcServletConfigModuleWithoutSuffixConfiguration.java new file mode 100644 index 0000000..840529d --- /dev/null +++ b/alfresco-mvc-rest/src/test/java/com/gradecak/alfresco/mvc/test/inheritservletconfig/AlfrescoMvcServletConfigModuleWithoutSuffixConfiguration.java @@ -0,0 +1,35 @@ +/** + * Copyright gradecak.com + + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gradecak.alfresco.mvc.test.inheritservletconfig; + +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.ImportResource; +import org.springframework.web.servlet.config.annotation.PathMatchConfigurer; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +import com.gradecak.alfresco.mvc.rest.annotation.AlfrescoDispatcherWebscript; +import com.gradecak.alfresco.mvc.rest.annotation.EnableAlfrescoMvcRest; + +@Configuration +@EnableAlfrescoMvcRest(@AlfrescoDispatcherWebscript(servletContext = AlfrescoMvcServletContext.class)) +public class AlfrescoMvcServletConfigModuleWithoutSuffixConfiguration implements WebMvcConfigurer { + + @Override + public void configurePathMatch(PathMatchConfigurer configurer) { + configurer.setUseSuffixPatternMatch(false); + } +} diff --git a/alfresco-mvc-rest/src/test/java/com/gradecak/alfresco/mvc/test/inheritservletconfig/CustomServletConfigTest.java b/alfresco-mvc-rest/src/test/java/com/gradecak/alfresco/mvc/test/inheritservletconfig/CustomServletConfigTest.java index 50220e4..6627613 100644 --- a/alfresco-mvc-rest/src/test/java/com/gradecak/alfresco/mvc/test/inheritservletconfig/CustomServletConfigTest.java +++ b/alfresco-mvc-rest/src/test/java/com/gradecak/alfresco/mvc/test/inheritservletconfig/CustomServletConfigTest.java @@ -16,22 +16,16 @@ package com.gradecak.alfresco.mvc.test.inheritservletconfig; -import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInstance; import org.junit.jupiter.api.TestInstance.Lifecycle; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpStatus; -import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.ContextHierarchy; import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.test.context.web.WebAppConfiguration; -import org.springframework.web.servlet.DispatcherServlet; -import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; import com.gradecak.alfresco.mvc.webscript.DispatcherWebscript; import com.gradecak.alfresco.mvc.webscript.mock.MockWebscript; @@ -59,38 +53,4 @@ public void before() throws Exception { mockWebscript.newRequest(); } - /** - * @deprecated as of Spring 5.2.4. See class-level note in - * {@link RequestMappingHandlerMapping} on the deprecation of path - * extension config options. As there is no replacement for this - * method, in Spring 5.2.x it is necessary to set it to - * {@code false}. In Spring 5.3 the default changes to {@code false} - * and use of this property becomes unnecessary. - */ - @Deprecated - @Test - public void when_alfrescoMvcDispatcherServletConfigOptionsWithSuffix_expect_ok() throws Exception { - DispatcherServlet dispatcherServlet = dispatcherWebscript.getDispatcherServlet().getWebApplicationContext() - .getBean(DispatcherServlet.class); - Assertions.assertNotNull(dispatcherServlet); - - MockHttpServletResponse res = mockWebscript.withControllerMapping("/test/withsufix.test").execute(); - Assertions.assertEquals(HttpStatus.OK.value(), res.getStatus()); - - String contentAsString = res.getContentAsString(); - Assertions.assertEquals("withsufix.test", contentAsString); - } - - @Test - public void when_alfrescoMvcDispatcherServletConfigOptionsWithoutSuffix_expect_ok() throws Exception { - DispatcherServlet dispatcherServlet = dispatcherWebscript.getDispatcherServlet().getWebApplicationContext() - .getBean(DispatcherServlet.class); - Assertions.assertNotNull(dispatcherServlet); - - MockHttpServletResponse res = mockWebscript.withControllerMapping("/test/withoutsufix").execute(); - Assertions.assertEquals(HttpStatus.OK.value(), res.getStatus()); - - String contentAsString = res.getContentAsString(); - Assertions.assertEquals("withoutsufix", contentAsString); - } } diff --git a/alfresco-mvc-rest/src/test/java/com/gradecak/alfresco/mvc/test/inheritservletconfig/InheritedServletConfigTest.java b/alfresco-mvc-rest/src/test/java/com/gradecak/alfresco/mvc/test/inheritservletconfig/WithSuffixControllerTest.java similarity index 82% rename from alfresco-mvc-rest/src/test/java/com/gradecak/alfresco/mvc/test/inheritservletconfig/InheritedServletConfigTest.java rename to alfresco-mvc-rest/src/test/java/com/gradecak/alfresco/mvc/test/inheritservletconfig/WithSuffixControllerTest.java index 9e71562..0aadd1d 100644 --- a/alfresco-mvc-rest/src/test/java/com/gradecak/alfresco/mvc/test/inheritservletconfig/InheritedServletConfigTest.java +++ b/alfresco-mvc-rest/src/test/java/com/gradecak/alfresco/mvc/test/inheritservletconfig/WithSuffixControllerTest.java @@ -39,10 +39,10 @@ @ExtendWith(SpringExtension.class) @ContextHierarchy({ @ContextConfiguration(locations = { "/mock-alfresco-context.xml", "/test-restjsonmodule.xml" }), - @ContextConfiguration(classes = AlfrescoMvcServletConfigModuleConfiguration.class) }) + @ContextConfiguration(classes = AlfrescoMvcServletConfigModuleWithSuffixConfiguration.class) }) @WebAppConfiguration @TestInstance(Lifecycle.PER_CLASS) -public class InheritedServletConfigTest { +public class WithSuffixControllerTest { @Autowired private DispatcherWebscript dispatcherWebscript; @@ -80,17 +80,17 @@ public void when_alfrescoMvcDispatcherServletConfigOptionsWithSuffix_expect_suff String contentAsString = res.getContentAsString(); Assertions.assertEquals("withsufix", contentAsString); } - - @Test - public void when_alfrescoMvcDispatcherServletConfigOptionsWithoutSuffix_expect_ok() throws Exception { - DispatcherServlet dispatcherServlet = dispatcherWebscript.getDispatcherServlet().getWebApplicationContext() - .getBean(DispatcherServlet.class); - Assertions.assertNotNull(dispatcherServlet); - - MockHttpServletResponse res = mockWebscript.withControllerMapping("/test/withoutsufix").execute(); - Assertions.assertEquals(HttpStatus.OK.value(), res.getStatus()); - - String contentAsString = res.getContentAsString(); - Assertions.assertEquals("withoutsufix", contentAsString); - } +// +// @Test +// public void when_alfrescoMvcDispatcherServletConfigOptionsWithoutSuffix_expect_ok() throws Exception { +// DispatcherServlet dispatcherServlet = dispatcherWebscript.getDispatcherServlet().getWebApplicationContext() +// .getBean(DispatcherServlet.class); +// Assertions.assertNotNull(dispatcherServlet); +// +// MockHttpServletResponse res = mockWebscript.withControllerMapping("/test/withoutsufix").execute(); +// Assertions.assertEquals(HttpStatus.OK.value(), res.getStatus()); +// +// String contentAsString = res.getContentAsString(); +// Assertions.assertEquals("withoutsufix", contentAsString); +// } } diff --git a/alfresco-mvc-rest/src/test/java/com/gradecak/alfresco/mvc/test/inheritservletconfig/WithoutSuffixControllerTest.java b/alfresco-mvc-rest/src/test/java/com/gradecak/alfresco/mvc/test/inheritservletconfig/WithoutSuffixControllerTest.java new file mode 100644 index 0000000..dfc9442 --- /dev/null +++ b/alfresco-mvc-rest/src/test/java/com/gradecak/alfresco/mvc/test/inheritservletconfig/WithoutSuffixControllerTest.java @@ -0,0 +1,96 @@ +/** + * Copyright gradecak.com + + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.gradecak.alfresco.mvc.test.inheritservletconfig; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; +import org.junit.jupiter.api.TestInstance.Lifecycle; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.mock.web.MockHttpServletResponse; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.ContextHierarchy; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.springframework.test.context.web.WebAppConfiguration; +import org.springframework.web.servlet.DispatcherServlet; +import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; + +import com.gradecak.alfresco.mvc.webscript.DispatcherWebscript; +import com.gradecak.alfresco.mvc.webscript.mock.MockWebscript; +import com.gradecak.alfresco.mvc.webscript.mock.MockWebscriptBuilder; + +@ExtendWith(SpringExtension.class) +@ContextHierarchy({ @ContextConfiguration(locations = { "/mock-alfresco-context.xml", "/test-restjsonmodule.xml" }), + @ContextConfiguration(classes = AlfrescoMvcServletConfigModuleWithoutSuffixConfiguration.class) }) +@WebAppConfiguration +@TestInstance(Lifecycle.PER_CLASS) +public class WithoutSuffixControllerTest { + + @Autowired + private DispatcherWebscript dispatcherWebscript; + + MockWebscript mockWebscript; + + @BeforeAll + public void beforeAll() throws Exception { + mockWebscript = MockWebscriptBuilder.singleWebscript(dispatcherWebscript); + } + + @BeforeEach + public void before() throws Exception { + mockWebscript.newRequest(); + } + + /** + * @deprecated as of Spring 5.2.4. See class-level note in + * {@link RequestMappingHandlerMapping} on the deprecation of path + * extension config options. As there is no replacement for this + * method, in Spring 5.2.x it is necessary to set it to + * {@code false}. In Spring 5.3 the default changes to {@code false} + * and use of this property becomes unnecessary. + */ +// @Deprecated +// @Test +// public void when_alfrescoMvcDispatcherServletConfigOptionsWithSuffix_expect_suffixHandledAndOk() throws Exception { +// DispatcherServlet dispatcherServlet = dispatcherWebscript.getDispatcherServlet().getWebApplicationContext() +// .getBean(DispatcherServlet.class); +// Assertions.assertNotNull(dispatcherServlet); +// +// MockHttpServletResponse res = mockWebscript.withControllerMapping("/test/withsufix.test").execute(); +// Assertions.assertEquals(HttpStatus.OK.value(), res.getStatus()); +// +// String contentAsString = res.getContentAsString(); +// Assertions.assertEquals("withsufix", contentAsString); +// } + + @Test + public void when_alfrescoMvcDispatcherServletConfigOptionsWithoutSuffix_expect_ok() throws Exception { + DispatcherServlet dispatcherServlet = dispatcherWebscript.getDispatcherServlet().getWebApplicationContext() + .getBean(DispatcherServlet.class); + Assertions.assertNotNull(dispatcherServlet); + + MockHttpServletResponse res = mockWebscript.withControllerMapping("/test/withoutsufix").execute(); + Assertions.assertEquals(HttpStatus.OK.value(), res.getStatus()); + + String contentAsString = res.getContentAsString(); + Assertions.assertEquals("withoutsufix", contentAsString); + } +} diff --git a/notice.txt b/notice.txt index 03dcd31..506780c 100644 --- a/notice.txt +++ b/notice.txt @@ -1,6 +1,6 @@ Alfresco @MVC -Copyright (c) [2010-2015] Pleo Soft d.o.o +Copyright (c) [2010-2022] Pleo Soft d.o.o http://www.pleosoft.com diff --git a/pom.xml b/pom.xml index 131aa66..36c5123 100644 --- a/pom.xml +++ b/pom.xml @@ -40,9 +40,9 @@ 11 11 UTF-8 - 5.3.9 4.0.1 - 7.1.0.1 + 4.8.1 + 5.9.1 @@ -86,14 +86,14 @@ org.mockito mockito-core - 4.8.1 + ${dependency.mockito.version} provided org.springframework spring-test - ${dependency.spring.version} + ${dependency.spring-test.version} test @@ -105,7 +105,7 @@ org.junit.jupiter junit-jupiter-engine - 5.9.1 + ${dependency.junit-jupiter.version} test @@ -183,7 +183,7 @@ --pinentry-mode loopback - + @@ -193,22 +193,38 @@ - snapshot - - - - org.apache.maven.plugins - maven-gpg-plugin - 3.0.1 - - - sign-artifacts - none - - - - - + community-6.2.0-ga + + 6.2.0-ga + 5.1.8.RELEASE + + + + + community-7.1.0.1 + + 7.1.0.1 + 5.3.9 + + + + + community-7.2.0 + + true + + + 7.2.0 + 5.3.15 + + + + + community-7.3.0 + + 7.3.0 + 5.3.23 + From df71dec896e976496ae7f117061d9189eaaa940a Mon Sep 17 00:00:00 2001 From: Daniel Gradecak Date: Sat, 5 Nov 2022 16:45:56 +0100 Subject: [PATCH 13/18] reverting dependencies to bom --- {alfresco-mvc-dependencies => alfresco-mvc-bom}/pom.xml | 2 +- pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename {alfresco-mvc-dependencies => alfresco-mvc-bom}/pom.xml (96%) diff --git a/alfresco-mvc-dependencies/pom.xml b/alfresco-mvc-bom/pom.xml similarity index 96% rename from alfresco-mvc-dependencies/pom.xml rename to alfresco-mvc-bom/pom.xml index f10d047..6f11d8c 100644 --- a/alfresco-mvc-dependencies/pom.xml +++ b/alfresco-mvc-bom/pom.xml @@ -12,7 +12,7 @@ ../ - alfresco-mvc-dependencies + alfresco-mvc-bom pom Alfresco MVC dependencies diff --git a/pom.xml b/pom.xml index 36c5123..6aea2a1 100644 --- a/pom.xml +++ b/pom.xml @@ -48,7 +48,7 @@ alfresco-mvc-rest alfresco-mvc-aop - alfresco-mvc-dependencies + alfresco-mvc-bom From 12556f181b475b198d58d9b9867217f1245b166d Mon Sep 17 00:00:00 2001 From: Daniel Gradecak Date: Sat, 5 Nov 2022 16:56:04 +0100 Subject: [PATCH 14/18] github actions --- .github/workflows/ci.yml | 43 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..57b640b --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,43 @@ +name: build +on: + push: + pull_request: + +jobs: + compile: + name: "compile" + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + - uses: actions/setup-java@v1 + with: + java-version: 11 + - name: Build with Maven + run: ./mvnw compile + + test: + name: "tests (Alfresco version ${{ matrix.alfresco-version }})" + runs-on: ubuntu-latest + continue-on-error: ${{ matrix.experimental }} + strategy: + matrix: + include: + - alfresco-version: 6.2.0-ga + experimental: false + - alfresco-version: 7.1.0.1 + experimental: false + - alfresco-version: 7.2.0 + experimental: false + - alfresco-version: 7.3.0 + experimental: false + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + - uses: actions/setup-java@v1 + with: + java-version: 11 + - name: Run integration test + run: ./mvnw test -Pcommunity-${{ matrix.alfresco-boot-version }} From 6b43352d59aacec716384e7a9d0346102228000e Mon Sep 17 00:00:00 2001 From: Daniel Gradecak Date: Sat, 5 Nov 2022 16:58:02 +0100 Subject: [PATCH 15/18] chmod on files --- mvnw | 0 mvnw.cmd | 0 2 files changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 mvnw mode change 100644 => 100755 mvnw.cmd diff --git a/mvnw b/mvnw old mode 100644 new mode 100755 diff --git a/mvnw.cmd b/mvnw.cmd old mode 100644 new mode 100755 From 3f8f2de426cca4444545e09cadf159c5b897d7d0 Mon Sep 17 00:00:00 2001 From: Daniel Gradecak Date: Sat, 5 Nov 2022 17:13:59 +0100 Subject: [PATCH 16/18] correcting matrix variable name --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 57b640b..5d734dc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -40,4 +40,4 @@ jobs: with: java-version: 11 - name: Run integration test - run: ./mvnw test -Pcommunity-${{ matrix.alfresco-boot-version }} + run: ./mvnw test -Pcommunity-${{ matrix.alfresco-version }} From b8f09c01e69981842836ea71b8837d36b9c349ab Mon Sep 17 00:00:00 2001 From: Daniel Gradecak Date: Sat, 5 Nov 2022 18:39:37 +0100 Subject: [PATCH 17/18] changing defaults --- .github/workflows/ci.yml | 2 +- pom.xml | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5d734dc..4a5cbe4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,5 +39,5 @@ jobs: - uses: actions/setup-java@v1 with: java-version: 11 - - name: Run integration test + - name: Run tests run: ./mvnw test -Pcommunity-${{ matrix.alfresco-version }} diff --git a/pom.xml b/pom.xml index 6aea2a1..b5299c4 100644 --- a/pom.xml +++ b/pom.xml @@ -165,7 +165,7 @@ maven-compiler-plugin 3.10.1 - 11 + ${maven.compiler.source} @@ -183,7 +183,7 @@ --pinentry-mode loopback - + @@ -210,9 +210,6 @@ community-7.2.0 - - true - 7.2.0 5.3.15 @@ -221,6 +218,9 @@ community-7.3.0 + + true + 7.3.0 5.3.23 From a3464b3e817155e3f85fc3a18ec516c481c157bc Mon Sep 17 00:00:00 2001 From: Daniel Gradecak Date: Fri, 16 Dec 2022 13:12:09 +0100 Subject: [PATCH 18/18] ci test for 6.1.2-ga --- .github/workflows/ci.yml | 2 ++ pom.xml | 8 ++++++++ 2 files changed, 10 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4a5cbe4..e60018b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,6 +24,8 @@ jobs: strategy: matrix: include: + - alfresco-version: 6.1.2-ga + experimental: false - alfresco-version: 6.2.0-ga experimental: false - alfresco-version: 7.1.0.1 diff --git a/pom.xml b/pom.xml index b5299c4..13ef0a4 100644 --- a/pom.xml +++ b/pom.xml @@ -192,6 +192,14 @@ + + community-6.1.2-ga + + 6.1.2-ga + 5.1.3.RELEASE + + + community-6.2.0-ga