This is an automated email from the ASF dual-hosted git repository. rombert pushed a commit to annotated tag org.apache.sling.scripting.thymeleaf-1.0.0 in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-scripting-thymeleaf.git
commit 9606b65e59e0559643caf888ae08ab374827c073 Author: Oliver Lietz <[email protected]> AuthorDate: Mon Jan 4 20:35:53 2016 +0000 SLING-5075 Upgrade Thymeleaf to 3.0 * switch to OSGi R6 annotation-based configuration * add AbsentMessageRepresentationProvider * add TemplateModeProvider * add PatternSpecTemplateModeProvider * fix SlingIncludeAttributeTagProcessor * fix javadoc in Thymeleaf sources * update README git-svn-id: https://svn.apache.org/repos/asf/sling/trunk/contrib/scripting/thymeleaf@1722960 13f79535-47bb-0310-9956-ffa450edef68 --- README.md | 4 +- pom.xml | 49 ++--- ...va => AbsentMessageRepresentationProvider.java} | 11 +- ...TagProcessor.java => TemplateModeProvider.java} | 13 +- .../internal/NonCachingTemplateResolver.java | 186 ++++------------ .../NonCachingTemplateResolverConfiguration.java} | 20 +- .../internal/PatternSpecTemplateModeProvider.java | 137 ++++++++++++ ...tternSpecTemplateModeProviderConfiguration.java | 76 +++++++ .../internal/ResourceBundleMessageResolver.java | 116 +++++++--- ...ResourceBundleMessageResolverConfiguration.java | 48 ++++ .../thymeleaf/internal/SlingTemplateResource.java | 2 +- .../internal/ThymeleafScriptEngineFactory.java | 244 +++++++++++---------- .../ThymeleafScriptEngineFactoryConfiguration.java | 60 +++++ .../thymeleaf/internal/dialect/SlingDialect.java | 24 +- .../SlingIncludeAttributeTagProcessor.java | 21 +- .../sling/scripting/thymeleaf/package-info.java | 4 +- .../templateresolver/AbstractTemplateResolver.java | 4 +- .../templateresolver/ITemplateResolver.java | 1 + .../OSGI-INF/metatype/metatype.properties | 60 ----- 19 files changed, 668 insertions(+), 412 deletions(-) diff --git a/README.md b/README.md index 43afc3b..79023af 100644 --- a/README.md +++ b/README.md @@ -16,8 +16,8 @@ Installation For running Sling Scripting Thymeleaf with Sling's Launchpad some dependencies need to be resolved. This can be achieved by installing the following bundles: - mvn:org.apache.sling/org.apache.sling.i18n/2.2.10 - mvn:org.javassist/javassist/3.18.2-GA + mvn:org.apache.sling/org.apache.sling.i18n/2.4.4 + mvn:org.javassist/javassist/3.20.0-GA mvn:commons-io/commons-io/2.4 There is a feature for Karaf: diff --git a/pom.xml b/pom.xml index 986a691..cee7944 100644 --- a/pom.xml +++ b/pom.xml @@ -60,12 +60,26 @@ <!-- OSGi --> <dependency> <groupId>org.osgi</groupId> - <artifactId>org.osgi.core</artifactId> + <artifactId>osgi.core</artifactId> + <version>6.0.0</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.osgi</groupId> - <artifactId>org.osgi.compendium</artifactId> + <artifactId>osgi.annotation</artifactId> + <version>6.0.1</version> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>org.osgi</groupId> + <artifactId>org.osgi.service.component.annotations</artifactId> + <version>1.3.0</version> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>org.osgi</groupId> + <artifactId>org.osgi.service.metatype.annotations</artifactId> + <version>1.3.0</version> <scope>provided</scope> </dependency> <!-- Apache Commons --> @@ -96,29 +110,10 @@ </dependency> <dependency> <groupId>org.apache.sling</groupId> - <artifactId>org.apache.sling.commons.osgi</artifactId> - <version>2.3.0</version> - <scope>provided</scope> - </dependency> - <dependency> - <groupId>org.apache.sling</groupId> <artifactId>org.apache.sling.i18n</artifactId> <version>2.4.4</version> <scope>provided</scope> </dependency> - <!-- Apache Felix --> - <dependency> - <groupId>org.apache.felix</groupId> - <artifactId>org.apache.felix.scr.annotations</artifactId> - <scope>provided</scope> - </dependency> - <!-- bndlib --> - <dependency> - <groupId>biz.aQute.bnd</groupId> - <artifactId>bndlib</artifactId> - <version>2.3.0</version> - <scope>provided</scope> - </dependency> <!-- Thymeleaf --> <dependency> <groupId>org.thymeleaf</groupId> @@ -158,10 +153,10 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> - <version>3.1</version> + <version>3.3</version> <configuration> - <source>1.6</source> - <target>1.6</target> + <source>1.8</source> + <target>1.8</target> </configuration> </plugin> <plugin> @@ -182,6 +177,8 @@ </DynamicImport-Package> <ScriptEngine-Name>${project.name}</ScriptEngine-Name> <ScriptEngine-Version>${project.version}</ScriptEngine-Version> + <_dsannotations>*</_dsannotations> + <_metatypeannotations>*</_metatypeannotations> <_removeheaders> Embed-Dependency, Private-Package, @@ -193,10 +190,6 @@ </filters> </configuration> </plugin> - <plugin> - <groupId>org.apache.felix</groupId> - <artifactId>maven-scr-plugin</artifactId> - </plugin> </plugins> </build> diff --git a/src/main/java/org/apache/sling/scripting/thymeleaf/package-info.java b/src/main/java/org/apache/sling/scripting/thymeleaf/AbsentMessageRepresentationProvider.java similarity index 71% copy from src/main/java/org/apache/sling/scripting/thymeleaf/package-info.java copy to src/main/java/org/apache/sling/scripting/thymeleaf/AbsentMessageRepresentationProvider.java index 1b17768..28c3232 100644 --- a/src/main/java/org/apache/sling/scripting/thymeleaf/package-info.java +++ b/src/main/java/org/apache/sling/scripting/thymeleaf/AbsentMessageRepresentationProvider.java @@ -16,5 +16,14 @@ * specific language governing permissions and limitations * under the License. */ [email protected]("0.0.2") package org.apache.sling.scripting.thymeleaf; + +import org.osgi.annotation.versioning.ProviderType; +import org.thymeleaf.context.ITemplateContext; + +@ProviderType +public interface AbsentMessageRepresentationProvider { + + String provideAbsentMessageRepresentation(final ITemplateContext templateContext, final Class<?> origin, final String key, final Object[] messageParameters); + +} diff --git a/src/main/java/org/apache/sling/scripting/thymeleaf/internal/processor/SlingHtmlAttributeTagProcessor.java b/src/main/java/org/apache/sling/scripting/thymeleaf/TemplateModeProvider.java similarity index 58% rename from src/main/java/org/apache/sling/scripting/thymeleaf/internal/processor/SlingHtmlAttributeTagProcessor.java rename to src/main/java/org/apache/sling/scripting/thymeleaf/TemplateModeProvider.java index 3491477..f32b95a 100644 --- a/src/main/java/org/apache/sling/scripting/thymeleaf/internal/processor/SlingHtmlAttributeTagProcessor.java +++ b/src/main/java/org/apache/sling/scripting/thymeleaf/TemplateModeProvider.java @@ -16,16 +16,15 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.sling.scripting.thymeleaf.internal.processor; +package org.apache.sling.scripting.thymeleaf; -import org.thymeleaf.dialect.IProcessorDialect; -import org.thymeleaf.processor.element.AbstractAttributeTagProcessor; +import org.apache.sling.api.resource.Resource; +import org.osgi.annotation.versioning.ProviderType; import org.thymeleaf.templatemode.TemplateMode; -public abstract class SlingHtmlAttributeTagProcessor extends AbstractAttributeTagProcessor { +@ProviderType +public interface TemplateModeProvider { - public SlingHtmlAttributeTagProcessor(final IProcessorDialect processorDialect, final String dialectPrefix, final String attributeName, final int precedence, final boolean removeAttribute) { - super(processorDialect, TemplateMode.HTML, dialectPrefix, null, false, attributeName, false, precedence, removeAttribute); - } + TemplateMode provideTemplateMode(final Resource resource); } diff --git a/src/main/java/org/apache/sling/scripting/thymeleaf/internal/NonCachingTemplateResolver.java b/src/main/java/org/apache/sling/scripting/thymeleaf/internal/NonCachingTemplateResolver.java index 8e71908..c763259 100644 --- a/src/main/java/org/apache/sling/scripting/thymeleaf/internal/NonCachingTemplateResolver.java +++ b/src/main/java/org/apache/sling/scripting/thymeleaf/internal/NonCachingTemplateResolver.java @@ -18,149 +18,87 @@ */ package org.apache.sling.scripting.thymeleaf.internal; -import java.util.Collections; -import java.util.Dictionary; -import java.util.HashSet; import java.util.Map; -import java.util.Set; - -import org.apache.felix.scr.annotations.Activate; -import org.apache.felix.scr.annotations.Component; -import org.apache.felix.scr.annotations.Deactivate; -import org.apache.felix.scr.annotations.Modified; -import org.apache.felix.scr.annotations.Properties; -import org.apache.felix.scr.annotations.Property; -import org.apache.felix.scr.annotations.PropertyUnbounded; -import org.apache.felix.scr.annotations.Service; + import org.apache.sling.api.resource.Resource; import org.apache.sling.api.resource.ResourceResolver; import org.apache.sling.api.scripting.SlingScriptConstants; -import org.apache.sling.commons.osgi.PropertiesUtil; +import org.apache.sling.scripting.thymeleaf.TemplateModeProvider; import org.osgi.framework.Constants; -import org.osgi.service.component.ComponentContext; +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Deactivate; +import org.osgi.service.component.annotations.Modified; +import org.osgi.service.component.annotations.Reference; +import org.osgi.service.component.annotations.ReferenceCardinality; +import org.osgi.service.component.annotations.ReferencePolicy; +import org.osgi.service.component.annotations.ReferencePolicyOption; +import org.osgi.service.metatype.annotations.Designate; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.thymeleaf.IEngineConfiguration; -import org.thymeleaf.cache.ICacheEntryValidity; import org.thymeleaf.cache.NonCacheableCacheEntryValidity; import org.thymeleaf.context.IContext; import org.thymeleaf.templatemode.TemplateMode; import org.thymeleaf.templateresolver.ITemplateResolver; import org.thymeleaf.templateresolver.TemplateResolution; import org.thymeleaf.templateresource.ITemplateResource; -import org.thymeleaf.util.PatternSpec; @Component( - label = "Apache Sling Scripting Thymeleaf “Non-Caching Template Resolver”", - description = "non-caching template resolver for Sling Scripting Thymeleaf", immediate = true, - metatype = true + property = { + Constants.SERVICE_DESCRIPTION + "=Non-caching template resolver for Sling Scripting Thymeleaf", + Constants.SERVICE_VENDOR + "=The Apache Software Foundation" + } +) +@Designate( + ocd = NonCachingTemplateResolverConfiguration.class ) -@Service -@Properties({ - @Property( - name = Constants.SERVICE_VENDOR, - value = "The Apache Software Foundation" - ), - @Property( - name = Constants.SERVICE_DESCRIPTION, - value = "non-caching template resolver for Sling Scripting Thymeleaf" - ) -}) public class NonCachingTemplateResolver implements ITemplateResolver { - private Integer order; - - public static final int DEFAULT_ORDER = 0; - - @Property(intValue = DEFAULT_ORDER) - public static final String ORDER_PARAMETER = "org.apache.sling.scripting.thymeleaf.internal.NonCachingTemplateResolver.order"; - - private final PatternSpec htmlPatternSpec = new PatternSpec(); - - @Property(unbounded = PropertyUnbounded.ARRAY) - public static final String HTML_PATTERNS_PARAMETER = "org.apache.sling.scripting.thymeleaf.internal.NonCachingTemplateResolver.htmlPatterns"; - - private final PatternSpec xmlPatternSpec = new PatternSpec(); - - @Property(unbounded = PropertyUnbounded.ARRAY) - public static final String XML_PATTERNS_PARAMETER = "org.apache.sling.scripting.thymeleaf.internal.NonCachingTemplateResolver.xmlPatterns"; - - private final PatternSpec textPatternSpec = new PatternSpec(); - - @Property(unbounded = PropertyUnbounded.ARRAY) - public static final String TEXT_PATTERNS_PARAMETER = "org.apache.sling.scripting.thymeleaf.internal.NonCachingTemplateResolver.textPatterns"; - - private final PatternSpec javascriptPatternSpec = new PatternSpec(); - - @Property(unbounded = PropertyUnbounded.ARRAY) - public static final String JAVASCRIPT_PATTERNS_PARAMETER = "org.apache.sling.scripting.thymeleaf.internal.NonCachingTemplateResolver.javascriptPatterns"; - - private final PatternSpec cssPatternSpec = new PatternSpec(); - - @Property(unbounded = PropertyUnbounded.ARRAY) - public static final String CSS_PATTERNS_PARAMETER = "org.apache.sling.scripting.thymeleaf.internal.NonCachingTemplateResolver.cssPatterns"; - - private final PatternSpec rawPatternSpec = new PatternSpec(); + @Reference( + cardinality = ReferenceCardinality.OPTIONAL, + policy = ReferencePolicy.DYNAMIC, + policyOption = ReferencePolicyOption.GREEDY, + bind = "setTemplateModeProvider", + unbind = "unsetTemplateModeProvider" + ) + private volatile TemplateModeProvider templateModeProvider; - @Property(unbounded = PropertyUnbounded.ARRAY) - public static final String RAW_PATTERNS_PARAMETER = "org.apache.sling.scripting.thymeleaf.internal.NonCachingTemplateResolver.rawPatterns"; + private Integer order; private final Logger logger = LoggerFactory.getLogger(NonCachingTemplateResolver.class); public NonCachingTemplateResolver() { } + public void setTemplateModeProvider(final TemplateModeProvider templateModeProvider) { + logger.debug("setting template mode provider: {}", templateModeProvider); + } + + public void unsetTemplateModeProvider(final TemplateModeProvider templateModeProvider) { + logger.debug("unsetting template mode provider: {}", templateModeProvider); + } + @Activate - private void activate(final ComponentContext componentContext) { + private void activate(final NonCachingTemplateResolverConfiguration configuration) { logger.debug("activate"); - configure(componentContext); + configure(configuration); } @Modified - private void modified(final ComponentContext componentContext) { + private void modified(final NonCachingTemplateResolverConfiguration configuration) { logger.debug("modified"); - configure(componentContext); + configure(configuration); } @Deactivate - private void deactivate(final ComponentContext componentContext) { + private void deactivate() { logger.debug("deactivate"); } - private void configure(final ComponentContext componentContext) { - final Dictionary properties = componentContext.getProperties(); - order = PropertiesUtil.toInteger(properties.get(ORDER_PARAMETER), DEFAULT_ORDER); - // HTML - final String[] htmlPatterns = PropertiesUtil.toStringArray(properties.get(HTML_PATTERNS_PARAMETER), new String[]{}); - setPatterns(htmlPatterns, htmlPatternSpec); - logger.debug("HTML Pattern Spec: {}", htmlPatternSpec.getPatterns()); - // XML - final String[] xmlPatterns = PropertiesUtil.toStringArray(properties.get(XML_PATTERNS_PARAMETER), new String[]{}); - setPatterns(xmlPatterns, xmlPatternSpec); - logger.debug("XML Pattern Spec: {}", xmlPatternSpec.getPatterns()); - // TEXT - final String[] textPatterns = PropertiesUtil.toStringArray(properties.get(TEXT_PATTERNS_PARAMETER), new String[]{}); - setPatterns(textPatterns, textPatternSpec); - logger.debug("TEXT Pattern Spec: {}", textPatternSpec.getPatterns()); - // JAVASCRIPT - final String[] javascriptPatterns = PropertiesUtil.toStringArray(properties.get(JAVASCRIPT_PATTERNS_PARAMETER), new String[]{}); - setPatterns(javascriptPatterns, javascriptPatternSpec); - logger.debug("JAVASCRIPT Pattern Spec: {}", javascriptPatternSpec.getPatterns()); - // CSS - final String[] cssPatterns = PropertiesUtil.toStringArray(properties.get(CSS_PATTERNS_PARAMETER), new String[]{}); - setPatterns(cssPatterns, cssPatternSpec); - logger.debug("CSS Pattern Spec: {}", cssPatternSpec.getPatterns()); - // RAW - final String[] rawPatterns = PropertiesUtil.toStringArray(properties.get(RAW_PATTERNS_PARAMETER), new String[]{}); - setPatterns(rawPatterns, rawPatternSpec); - logger.debug("RAW Pattern Spec: {}", rawPatternSpec.getPatterns()); - } - - private void setPatterns(final String[] strings, final PatternSpec patternSpec) { - final Set<String> set = new HashSet<String>(); - Collections.addAll(set, strings); - patternSpec.setPatterns(set); + private void configure(final NonCachingTemplateResolverConfiguration configuration) { + order = configuration.order(); } @Override @@ -176,40 +114,12 @@ public class NonCachingTemplateResolver implements ITemplateResolver { @Override public TemplateResolution resolveTemplate(final IEngineConfiguration engineConfiguration, final IContext context, final String ownerTemplate, final String template, final Map<String, Object> templateResolutionAttributes) { logger.debug("resolving template '{}'", template); - final TemplateMode templateMode = computeTemplateMode(template); - if (templateMode != null) { - logger.debug("using template mode '{}' for template '{}'", templateMode, template); - final ResourceResolver resourceResolver = (ResourceResolver) context.getVariable(SlingScriptConstants.ATTR_SCRIPT_RESOURCE_RESOLVER); - final Resource resource = resourceResolver.getResource(template); - final ITemplateResource templateResource = new SlingTemplateResource(resource); - final ICacheEntryValidity cacheEntryValidity = new NonCacheableCacheEntryValidity(); - return new TemplateResolution(templateResource, templateMode, cacheEntryValidity); - } else { - logger.warn("no template mode for template '{}'", template); - return null; // will fail at caller - } - } - - private TemplateMode computeTemplateMode(final String templateName) { - if (htmlPatternSpec.matches(templateName)) { - return TemplateMode.HTML; - } - if (xmlPatternSpec.matches(templateName)) { - return TemplateMode.XML; - } - if (textPatternSpec.matches(templateName)) { - return TemplateMode.TEXT; - } - if (javascriptPatternSpec.matches(templateName)) { - return TemplateMode.JAVASCRIPT; - } - if (cssPatternSpec.matches(templateName)) { - return TemplateMode.CSS; - } - if (rawPatternSpec.matches(templateName)) { - return TemplateMode.RAW; - } - return null; + final ResourceResolver resourceResolver = (ResourceResolver) context.getVariable(SlingScriptConstants.ATTR_SCRIPT_RESOURCE_RESOLVER); // TODO SlingBindings.RESOLVER + final Resource resource = resourceResolver.getResource(template); + final ITemplateResource templateResource = new SlingTemplateResource(resource); + final TemplateMode templateMode = templateModeProvider.provideTemplateMode(resource); + logger.debug("using template mode {} for template '{}'", templateMode, template); + return new TemplateResolution(templateResource, templateMode, NonCacheableCacheEntryValidity.INSTANCE); } } diff --git a/src/main/java/org/apache/sling/scripting/thymeleaf/package-info.java b/src/main/java/org/apache/sling/scripting/thymeleaf/internal/NonCachingTemplateResolverConfiguration.java similarity index 56% copy from src/main/java/org/apache/sling/scripting/thymeleaf/package-info.java copy to src/main/java/org/apache/sling/scripting/thymeleaf/internal/NonCachingTemplateResolverConfiguration.java index 1b17768..54edd53 100644 --- a/src/main/java/org/apache/sling/scripting/thymeleaf/package-info.java +++ b/src/main/java/org/apache/sling/scripting/thymeleaf/internal/NonCachingTemplateResolverConfiguration.java @@ -16,5 +16,21 @@ * specific language governing permissions and limitations * under the License. */ [email protected]("0.0.2") -package org.apache.sling.scripting.thymeleaf; +package org.apache.sling.scripting.thymeleaf.internal; + +import org.osgi.service.metatype.annotations.AttributeDefinition; +import org.osgi.service.metatype.annotations.ObjectClassDefinition; + +@ObjectClassDefinition( + name = "Apache Sling Scripting Thymeleaf “Non-Caching Template Resolver”", + description = "non-caching template resolver for Sling Scripting Thymeleaf" +) +@interface NonCachingTemplateResolverConfiguration { + + @AttributeDefinition( + name = "order", + description = "property for ordering template resolvers inside the Thymeleaf template engine" + ) + int order() default 0; + +} diff --git a/src/main/java/org/apache/sling/scripting/thymeleaf/internal/PatternSpecTemplateModeProvider.java b/src/main/java/org/apache/sling/scripting/thymeleaf/internal/PatternSpecTemplateModeProvider.java new file mode 100644 index 0000000..568bad5 --- /dev/null +++ b/src/main/java/org/apache/sling/scripting/thymeleaf/internal/PatternSpecTemplateModeProvider.java @@ -0,0 +1,137 @@ +/* + * 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 + * + * 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 org.apache.sling.scripting.thymeleaf.internal; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +import org.apache.sling.api.resource.Resource; +import org.apache.sling.scripting.thymeleaf.TemplateModeProvider; +import org.osgi.framework.Constants; +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Deactivate; +import org.osgi.service.component.annotations.Modified; +import org.osgi.service.metatype.annotations.Designate; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.thymeleaf.templatemode.TemplateMode; +import org.thymeleaf.util.PatternSpec; + +@Component( + immediate = true, + property = { + Constants.SERVICE_DESCRIPTION + "=PatternSpec TemplateMode Provider for Sling Scripting Thymeleaf", + Constants.SERVICE_VENDOR + "=The Apache Software Foundation" + } +) +@Designate( + ocd = PatternSpecTemplateModeProviderConfiguration.class +) +public class PatternSpecTemplateModeProvider implements TemplateModeProvider { + + private final PatternSpec htmlPatternSpec = new PatternSpec(); + + private final PatternSpec xmlPatternSpec = new PatternSpec(); + + private final PatternSpec textPatternSpec = new PatternSpec(); + + private final PatternSpec javascriptPatternSpec = new PatternSpec(); + + private final PatternSpec cssPatternSpec = new PatternSpec(); + + private final PatternSpec rawPatternSpec = new PatternSpec(); + + private final Logger logger = LoggerFactory.getLogger(PatternSpecTemplateModeProvider.class); + + public PatternSpecTemplateModeProvider() { + } + + @Activate + private void activate(final PatternSpecTemplateModeProviderConfiguration configuration) { + logger.debug("activate"); + configure(configuration); + } + + @Modified + private void modified(final PatternSpecTemplateModeProviderConfiguration configuration) { + logger.debug("modified"); + configure(configuration); + } + + @Deactivate + private void deactivate() { + logger.debug("deactivate"); + } + + private void configure(final PatternSpecTemplateModeProviderConfiguration configuration) { + // HTML + setPatterns(configuration.htmlPatterns(), htmlPatternSpec); + logger.debug("configured HTML patterns: {}", htmlPatternSpec.getPatterns()); + // XML + setPatterns(configuration.xmlPatterns(), xmlPatternSpec); + logger.debug("configured XML patterns: {}", xmlPatternSpec.getPatterns()); + // TEXT + setPatterns(configuration.textPatterns(), textPatternSpec); + logger.debug("configured TEXT patterns: {}", textPatternSpec.getPatterns()); + // JAVASCRIPT + setPatterns(configuration.javascriptPatterns(), javascriptPatternSpec); + logger.debug("configured JAVASCRIPT patterns: {}", javascriptPatternSpec.getPatterns()); + // CSS + setPatterns(configuration.cssPatterns(), cssPatternSpec); + logger.debug("configured CSS patterns: {}", cssPatternSpec.getPatterns()); + // RAW + setPatterns(configuration.rawPatterns(), rawPatternSpec); + logger.debug("configured RAW patterns: {}", rawPatternSpec.getPatterns()); + } + + private void setPatterns(final String[] strings, final PatternSpec patternSpec) { + final Set<String> set = new HashSet<String>(); + if (strings != null) { + Collections.addAll(set, strings); + } + patternSpec.setPatterns(set); + } + + @Override + public TemplateMode provideTemplateMode(final Resource resource) { + final String path = resource.getPath(); + if (htmlPatternSpec.matches(path)) { + return TemplateMode.HTML; + } + if (xmlPatternSpec.matches(path)) { + return TemplateMode.XML; + } + if (textPatternSpec.matches(path)) { + return TemplateMode.TEXT; + } + if (javascriptPatternSpec.matches(path)) { + return TemplateMode.JAVASCRIPT; + } + if (cssPatternSpec.matches(path)) { + return TemplateMode.CSS; + } + if (rawPatternSpec.matches(path)) { + return TemplateMode.RAW; + } + return null; + } + +} diff --git a/src/main/java/org/apache/sling/scripting/thymeleaf/internal/PatternSpecTemplateModeProviderConfiguration.java b/src/main/java/org/apache/sling/scripting/thymeleaf/internal/PatternSpecTemplateModeProviderConfiguration.java new file mode 100644 index 0000000..e79c18f --- /dev/null +++ b/src/main/java/org/apache/sling/scripting/thymeleaf/internal/PatternSpecTemplateModeProviderConfiguration.java @@ -0,0 +1,76 @@ +/* + * 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 + * + * 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 org.apache.sling.scripting.thymeleaf.internal; + +import org.osgi.service.metatype.annotations.AttributeDefinition; +import org.osgi.service.metatype.annotations.ObjectClassDefinition; + +@ObjectClassDefinition( + name = "Apache Sling Scripting Thymeleaf “PatternSpec TemplateMode Provider”", + description = "PatternSpec TemplateMode Provider for Sling Scripting Thymeleaf" +) +@interface PatternSpecTemplateModeProviderConfiguration { + + @AttributeDefinition( + name = "patterns for Template Mode HTML", + description = "the template patterns (regular expressions) which should be handled by Template Mode HTML (e.g. *.html - NOTE: extension needs to be enabled for this script engine)" + ) + String[] htmlPatterns() default { + "*.html" + }; + + @AttributeDefinition( + name = "patterns for Template Mode XML", + description = "the template patterns (regular expressions) which should be handled by Template Mode XML (e.g. *.xml - NOTE: extension needs to be enabled for this script engine)" + ) + String[] xmlPatterns() default { + "*.xml" + }; + + @AttributeDefinition( + name = "patterns for Template Mode TEXT", + description = "the template patterns (regular expressions) which should be handled by Template Mode TEXT (e.g. *.txt - NOTE: extension needs to be enabled for this script engine)" + ) + String[] textPatterns() default { + "*.txt" + }; + + @AttributeDefinition( + name = "patterns for Template Mode JAVASCRIPT", + description = "the template patterns (regular expressions) which should be handled by Template Mode JAVASCRIPT (e.g. *.js - NOTE: extension needs to be enabled for this script engine)" + ) + String[] javascriptPatterns() default { + "*.js" + }; + + @AttributeDefinition( + name = "patterns for Template Mode CSS", + description = "the template patterns (regular expressions) which should be handled by Template Mode CSS (e.g. *.css - NOTE: extension needs to be enabled for this script engine)" + ) + String[] cssPatterns() default { + "*.css" + }; + + @AttributeDefinition( + name = "patterns for Template Mode RAW", + description = "the template patterns (regular expressions) which should be handled by Template Mode RAW (e.g. *.raw - NOTE: extension needs to be enabled for this script engine)" + ) + String[] rawPatterns(); + +} diff --git a/src/main/java/org/apache/sling/scripting/thymeleaf/internal/ResourceBundleMessageResolver.java b/src/main/java/org/apache/sling/scripting/thymeleaf/internal/ResourceBundleMessageResolver.java index 03ee718..8385b3d 100644 --- a/src/main/java/org/apache/sling/scripting/thymeleaf/internal/ResourceBundleMessageResolver.java +++ b/src/main/java/org/apache/sling/scripting/thymeleaf/internal/ResourceBundleMessageResolver.java @@ -19,49 +19,60 @@ package org.apache.sling.scripting.thymeleaf.internal; import java.text.MessageFormat; -import java.util.Dictionary; import java.util.Locale; import java.util.ResourceBundle; -import org.apache.felix.scr.annotations.Activate; -import org.apache.felix.scr.annotations.Component; -import org.apache.felix.scr.annotations.Deactivate; -import org.apache.felix.scr.annotations.Modified; -import org.apache.felix.scr.annotations.Properties; -import org.apache.felix.scr.annotations.Property; -import org.apache.felix.scr.annotations.Reference; -import org.apache.felix.scr.annotations.Service; -import org.apache.sling.commons.osgi.PropertiesUtil; import org.apache.sling.i18n.ResourceBundleProvider; +import org.apache.sling.scripting.thymeleaf.AbsentMessageRepresentationProvider; +import org.apache.sling.scripting.thymeleaf.internal.ResourceBundleMessageResolverConfiguration.AbsentMessageRepresentationType; import org.osgi.framework.Constants; -import org.osgi.service.component.ComponentContext; +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Deactivate; +import org.osgi.service.component.annotations.Modified; +import org.osgi.service.component.annotations.Reference; +import org.osgi.service.component.annotations.ReferenceCardinality; +import org.osgi.service.component.annotations.ReferencePolicy; +import org.osgi.service.component.annotations.ReferencePolicyOption; +import org.osgi.service.metatype.annotations.Designate; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.thymeleaf.context.ITemplateContext; import org.thymeleaf.messageresolver.IMessageResolver; @Component( - label = "Apache Sling Scripting Thymeleaf “Resource Bundle Message Resolver”", - description = "resource bundle message resolver for Sling Scripting Thymeleaf", immediate = true, - metatype = true + property = { + Constants.SERVICE_DESCRIPTION + "=ResourceBundle MessageResolver for Sling Scripting Thymeleaf", + Constants.SERVICE_VENDOR + "=The Apache Software Foundation" + } +) +@Designate( + ocd = ResourceBundleMessageResolverConfiguration.class ) -@Service -@Properties({ - @Property(name = Constants.SERVICE_VENDOR, value = "The Apache Software Foundation"), - @Property(name = Constants.SERVICE_DESCRIPTION, value = "resource bundle message resolver for Sling Scripting Thymeleaf") -}) public class ResourceBundleMessageResolver implements IMessageResolver { - @Reference - private ResourceBundleProvider resourceBundleProvider; + @Reference( + cardinality = ReferenceCardinality.OPTIONAL, + policy = ReferencePolicy.DYNAMIC, + policyOption = ReferencePolicyOption.GREEDY, + bind = "setResourceBundleProvider", + unbind = "unsetResourceBundleProvider" + ) + private volatile ResourceBundleProvider resourceBundleProvider; + + @Reference( + cardinality = ReferenceCardinality.OPTIONAL, + policy = ReferencePolicy.DYNAMIC, + policyOption = ReferencePolicyOption.GREEDY, + bind = "setAbsentMessageRepresentationProvider", + unbind = "unsetAbsentMessageRepresentationProvider" + ) + private volatile AbsentMessageRepresentationProvider absentMessageRepresentationProvider; private Integer order; - public static final int DEFAULT_ORDER = 0; - - @Property(intValue = DEFAULT_ORDER) - public static final String ORDER_PARAMETER = "org.apache.sling.scripting.thymeleaf.internal.ResourceBundleMessageResolver.order"; + private AbsentMessageRepresentationType absentMessageRepresentationType; public static final Object[] EMPTY_MESSAGE_PARAMETERS = new Object[0]; @@ -70,26 +81,42 @@ public class ResourceBundleMessageResolver implements IMessageResolver { public ResourceBundleMessageResolver() { } + public void setResourceBundleProvider(final ResourceBundleProvider resourceBundleProvider) { + logger.info("setting resource bundle provider: {}", resourceBundleProvider); + } + + public void unsetResourceBundleProvider(final ResourceBundleProvider resourceBundleProvider) { + logger.info("unsetting resource bundle provider: {}", resourceBundleProvider); + } + + public void setAbsentMessageRepresentationProvider(final AbsentMessageRepresentationProvider absentMessageRepresentationProvider) { + logger.info("setting absent message representation provider: {}", absentMessageRepresentationProvider); + } + + public void unsetAbsentMessageRepresentationProvider(final AbsentMessageRepresentationProvider absentMessageRepresentationProvider) { + logger.info("unsetting absent message representation provider: {}", absentMessageRepresentationProvider); + } + @Activate - private void activate(final ComponentContext componentContext) { + private void activate(final ResourceBundleMessageResolverConfiguration configuration) { logger.debug("activate"); - configure(componentContext); + configure(configuration); } @Modified - private void modified(final ComponentContext componentContext) { + private void modified(final ResourceBundleMessageResolverConfiguration configuration) { logger.debug("modified"); - configure(componentContext); + configure(configuration); } @Deactivate - private void deactivate(final ComponentContext componentContext) { + private void deactivate() { logger.debug("deactivate"); } - private synchronized void configure(final ComponentContext componentContext) { - final Dictionary properties = componentContext.getProperties(); - order = PropertiesUtil.toInteger(properties.get(ORDER_PARAMETER), DEFAULT_ORDER); + private void configure(final ResourceBundleMessageResolverConfiguration configuration) { + order = configuration.order(); + absentMessageRepresentationType = configuration.absentMessageRepresentationType(); } @Override @@ -104,7 +131,7 @@ public class ResourceBundleMessageResolver implements IMessageResolver { @Override public String resolveMessage(final ITemplateContext templateContext, final Class<?> origin, final String key, final Object[] messageParameters) { - logger.debug("template context: {}, origin: {}, key: {}, message parameters: {}", templateContext, origin, key, messageParameters); + logger.debug("resolving message for '{}' ({}) with message parameters {}", key, origin, messageParameters); // TODO can origin be useful with Sling i18n? final Locale locale = templateContext.getLocale(); final ResourceBundle resourceBundle = resourceBundleProvider.getResourceBundle(locale); @@ -117,7 +144,26 @@ public class ResourceBundleMessageResolver implements IMessageResolver { @Override public String createAbsentMessageRepresentation(final ITemplateContext templateContext, final Class<?> origin, final String key, final Object[] messageParameters) { - return key; // TODO make configurable + logger.debug("creating absent message representation for '{}' ({}) with message parameters {}", key, origin, messageParameters); + String message = null; + final AbsentMessageRepresentationProvider absentMessageRepresentationProvider = this.absentMessageRepresentationProvider; + if (absentMessageRepresentationProvider == null) { + switch (absentMessageRepresentationType) { + case EMPTY: + message = ""; + break; + case BLANK: + message = " "; + break; + case KEY: + message = key; + break; + } + } else { + message = absentMessageRepresentationProvider.provideAbsentMessageRepresentation(templateContext, origin, key, messageParameters); + } + logger.debug("message: '{}'", message); + return message; } } diff --git a/src/main/java/org/apache/sling/scripting/thymeleaf/internal/ResourceBundleMessageResolverConfiguration.java b/src/main/java/org/apache/sling/scripting/thymeleaf/internal/ResourceBundleMessageResolverConfiguration.java new file mode 100644 index 0000000..fac3d1c --- /dev/null +++ b/src/main/java/org/apache/sling/scripting/thymeleaf/internal/ResourceBundleMessageResolverConfiguration.java @@ -0,0 +1,48 @@ +/* + * 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 + * + * 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 org.apache.sling.scripting.thymeleaf.internal; + +import org.osgi.service.metatype.annotations.AttributeDefinition; +import org.osgi.service.metatype.annotations.ObjectClassDefinition; + +@ObjectClassDefinition( + name = "Apache Sling Scripting Thymeleaf “ResourceBundle Message Resolver”", + description = "resource bundle message resolver for Sling Scripting Thymeleaf" +) +@interface ResourceBundleMessageResolverConfiguration { + + enum AbsentMessageRepresentationType { + EMPTY, + BLANK, + KEY + } + + @AttributeDefinition( + name = "order", + description = "property for ordering message resolvers inside the Thymeleaf template engine" + ) + int order() default 0; + + @AttributeDefinition( + name = "absent message representation type", + description = "if no AbsentMessageRepresentationProvider is present, empty (''), blank (' ') or the message key can represent an absent message" + ) + AbsentMessageRepresentationType absentMessageRepresentationType() default AbsentMessageRepresentationType.KEY; + +} diff --git a/src/main/java/org/apache/sling/scripting/thymeleaf/internal/SlingTemplateResource.java b/src/main/java/org/apache/sling/scripting/thymeleaf/internal/SlingTemplateResource.java index 0422d1a..52a18b9 100644 --- a/src/main/java/org/apache/sling/scripting/thymeleaf/internal/SlingTemplateResource.java +++ b/src/main/java/org/apache/sling/scripting/thymeleaf/internal/SlingTemplateResource.java @@ -63,7 +63,7 @@ public class SlingTemplateResource implements ITemplateResource { @Override public ITemplateResource relative(final String relativeLocation) throws IOException { - return null; // TODO + throw new UnsupportedOperationException("not yet implemented"); // TODO } } diff --git a/src/main/java/org/apache/sling/scripting/thymeleaf/internal/ThymeleafScriptEngineFactory.java b/src/main/java/org/apache/sling/scripting/thymeleaf/internal/ThymeleafScriptEngineFactory.java index 615a3a8..ea01cbe 100644 --- a/src/main/java/org/apache/sling/scripting/thymeleaf/internal/ThymeleafScriptEngineFactory.java +++ b/src/main/java/org/apache/sling/scripting/thymeleaf/internal/ThymeleafScriptEngineFactory.java @@ -18,163 +18,167 @@ */ package org.apache.sling.scripting.thymeleaf.internal; -import java.util.Dictionary; -import java.util.LinkedHashSet; +import java.util.HashSet; +import java.util.List; +import java.util.Properties; import java.util.Set; import javax.script.ScriptEngine; +import javax.script.ScriptEngineFactory; -import org.apache.felix.scr.annotations.Activate; -import org.apache.felix.scr.annotations.Component; -import org.apache.felix.scr.annotations.Deactivate; -import org.apache.felix.scr.annotations.Modified; -import org.apache.felix.scr.annotations.Properties; -import org.apache.felix.scr.annotations.Property; -import org.apache.felix.scr.annotations.PropertyUnbounded; -import org.apache.felix.scr.annotations.Reference; -import org.apache.felix.scr.annotations.ReferenceCardinality; -import org.apache.felix.scr.annotations.ReferencePolicy; -import org.apache.felix.scr.annotations.Service; -import org.apache.sling.commons.osgi.PropertiesUtil; import org.apache.sling.scripting.api.AbstractScriptEngineFactory; import org.osgi.framework.Constants; -import org.osgi.service.component.ComponentContext; +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Deactivate; +import org.osgi.service.component.annotations.Modified; +import org.osgi.service.component.annotations.Reference; +import org.osgi.service.component.annotations.ReferenceCardinality; +import org.osgi.service.component.annotations.ReferencePolicy; +import org.osgi.service.component.annotations.ReferencePolicyOption; +import org.osgi.service.metatype.annotations.Designate; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.thymeleaf.TemplateEngine; +import org.thymeleaf.cache.ICacheManager; import org.thymeleaf.dialect.IDialect; import org.thymeleaf.messageresolver.IMessageResolver; import org.thymeleaf.standard.StandardDialect; import org.thymeleaf.templateresolver.ITemplateResolver; @Component( - label = "Apache Sling Scripting Thymeleaf “Script Engine Factory”", - description = "scripting engine for Thymeleaf templates", + service = ScriptEngineFactory.class, immediate = true, - metatype = true + property = { + Constants.SERVICE_DESCRIPTION + "=Scripting engine for Thymeleaf templates", + Constants.SERVICE_VENDOR + "=The Apache Software Foundation" + } +) +@Designate( + ocd = ThymeleafScriptEngineFactoryConfiguration.class ) -@Service -@Properties({ - @Property(name = Constants.SERVICE_VENDOR, value = "The Apache Software Foundation"), - @Property(name = Constants.SERVICE_DESCRIPTION, value = "scripting engine for Thymeleaf templates"), - @Property(name = Constants.SERVICE_RANKING, intValue = 0, propertyPrivate = false) -}) public final class ThymeleafScriptEngineFactory extends AbstractScriptEngineFactory { - @Reference(referenceInterface = ITemplateResolver.class, cardinality = ReferenceCardinality.MANDATORY_MULTIPLE, policy = ReferencePolicy.DYNAMIC) - private final Set<ITemplateResolver> templateResolvers = new LinkedHashSet<ITemplateResolver>(); - - @Reference(referenceInterface = IMessageResolver.class, cardinality = ReferenceCardinality.MANDATORY_MULTIPLE, policy = ReferencePolicy.DYNAMIC) - private final Set<IMessageResolver> messageResolvers = new LinkedHashSet<IMessageResolver>(); - - @Reference(referenceInterface = IDialect.class, cardinality = ReferenceCardinality.MANDATORY_MULTIPLE, policy = ReferencePolicy.DYNAMIC) - private final Set<IDialect> dialects = new LinkedHashSet<IDialect>(); + @Reference( + cardinality = ReferenceCardinality.AT_LEAST_ONE, + policy = ReferencePolicy.DYNAMIC, + bind = "addTemplateResolver", + unbind = "removeTemplateResolver" + ) + private List<ITemplateResolver> templateResolvers; + + @Reference( + cardinality = ReferenceCardinality.AT_LEAST_ONE, + policy = ReferencePolicy.DYNAMIC, + bind = "addMessageResolver", + unbind = "removeMessageResolver" + ) + private List<IMessageResolver> messageResolvers; + + @Reference( + cardinality = ReferenceCardinality.AT_LEAST_ONE, + policy = ReferencePolicy.DYNAMIC, + bind = "addDialect", + unbind = "removeDialect" + ) + private List<IDialect> dialects; + + @Reference( + cardinality = ReferenceCardinality.OPTIONAL, + policy = ReferencePolicy.DYNAMIC, + policyOption = ReferencePolicyOption.GREEDY, + bind = "setCacheManager", + unbind = "unsetCacheManager" + ) + private volatile ICacheManager cacheManager; private TemplateEngine templateEngine; - public static final String DEFAULT_EXTENSION = "html"; - - @Property(value = {DEFAULT_EXTENSION}, unbounded = PropertyUnbounded.ARRAY) - public static final String EXTENSIONS_PARAMETER = "org.apache.sling.scripting.thymeleaf.extensions"; - - public static final String DEFAULT_MIMETYPE = "text/html"; - - @Property(value = {DEFAULT_MIMETYPE}, unbounded = PropertyUnbounded.ARRAY) - public static final String MIMETYPES_PARAMETER = "org.apache.sling.scripting.thymeleaf.mimetypes"; - - public static final String DEFAULT_NAME = "thymeleaf"; - - @Property(value = {DEFAULT_NAME}, unbounded = PropertyUnbounded.ARRAY) - public static final String NAMES_PARAMETER = "org.apache.sling.scripting.thymeleaf.names"; + private final Object lock = new Object(); private final Logger logger = LoggerFactory.getLogger(ThymeleafScriptEngineFactory.class); public ThymeleafScriptEngineFactory() { } - @Activate - private void activate(final ComponentContext componentContext) { - logger.debug("activate"); - configure(componentContext); - configureTemplateEngine(); + protected void addTemplateResolver(final ITemplateResolver templateResolver) { + synchronized (lock) { + logger.debug("adding template resolver '{}'", templateResolver.getName()); + templateEngine = null; + } } - @Modified - private void modified(final ComponentContext componentContext) { - logger.debug("modified"); - configure(componentContext); - configureTemplateEngine(); + protected void removeTemplateResolver(final ITemplateResolver templateResolver) { + synchronized (lock) { + logger.debug("removing template resolver '{}'", templateResolver.getName()); + templateEngine = null; + } } - @Deactivate - private void deactivate(final ComponentContext componentContext) { - logger.debug("deactivate"); - templateEngine = null; + protected void addMessageResolver(final IMessageResolver messageResolver) { + synchronized (lock) { + logger.debug("adding message resolver '{}'", messageResolver.getName()); + templateEngine = null; + } } - protected synchronized void bindTemplateResolvers(final ITemplateResolver templateResolver) { - logger.debug("binding template resolver '{}'", templateResolver.getName()); - templateResolvers.add(templateResolver); - configureTemplateEngine(); + protected void removeMessageResolver(final IMessageResolver messageResolver) { + synchronized (lock) { + logger.debug("removing message resolver '{}'", messageResolver.getName()); + templateEngine = null; + } } - protected synchronized void unbindTemplateResolvers(final ITemplateResolver templateResolver) { - logger.debug("unbinding template resolver '{}'", templateResolver.getName()); - templateResolvers.remove(templateResolver); - configureTemplateEngine(); + protected void addDialect(final IDialect dialect) { + synchronized (lock) { + logger.debug("adding dialect '{}'", dialect.getName()); + templateEngine = null; + } } - protected synchronized void bindMessageResolvers(final IMessageResolver messageResolver) { - logger.debug("binding message resolver '{}'", messageResolver.getName()); - messageResolvers.add(messageResolver); - configureTemplateEngine(); + protected void removeDialect(final IDialect dialect) { + synchronized (lock) { + logger.debug("removing dialect '{}'", dialect.getName()); + templateEngine = null; + } } - protected synchronized void unbindMessageResolvers(final IMessageResolver messageResolver) { - logger.debug("unbinding message resolver '{}'", messageResolver.getName()); - messageResolvers.remove(messageResolver); - configureTemplateEngine(); + protected void setCacheManager(final ICacheManager cacheManager) { + synchronized (lock) { + logger.debug("setting cache manager '{}'", cacheManager.getClass().getName()); + templateEngine = null; + } } - protected synchronized void bindDialects(final IDialect dialect) { - logger.debug("binding dialect '{}'", dialect.getName()); - dialects.add(dialect); - configureTemplateEngine(); + protected void unsetCacheManager(final ICacheManager cacheManager) { + synchronized (lock) { + logger.debug("unsetting cache manager '{}'", cacheManager.getClass().getName()); + templateEngine = null; + } } - protected synchronized void unbindDialects(final IDialect dialect) { - logger.debug("unbinding dialect '{}'", dialect.getName()); - dialects.remove(dialect); - configureTemplateEngine(); + @Activate + private void activate(final ThymeleafScriptEngineFactoryConfiguration configuration) { + logger.debug("activate"); + configure(configuration); } - private synchronized void configure(final ComponentContext componentContext) { - final Dictionary properties = componentContext.getProperties(); - - final String[] extensions = PropertiesUtil.toStringArray(properties.get(EXTENSIONS_PARAMETER), new String[]{DEFAULT_EXTENSION}); - setExtensions(extensions); - - final String[] mimeTypes = PropertiesUtil.toStringArray(properties.get(MIMETYPES_PARAMETER), new String[]{DEFAULT_MIMETYPE}); - setMimeTypes(mimeTypes); + @Modified + private void modified(final ThymeleafScriptEngineFactoryConfiguration configuration) { + logger.debug("modified"); + configure(configuration); + } - final String[] names = PropertiesUtil.toStringArray(properties.get(NAMES_PARAMETER), new String[]{DEFAULT_NAME}); - setNames(names); + @Deactivate + private void deactivate() { + logger.debug("deactivate"); + templateEngine = null; } - // the configuration of the Thymeleaf TemplateEngine is static and we need to recreate on modification - private synchronized void configureTemplateEngine() { - logger.info("configuring template engine"); - if (templateEngine == null || templateEngine.isInitialized()) { - templateEngine = new TemplateEngine(); - } - templateEngine.setTemplateResolvers(templateResolvers); - templateEngine.setMessageResolvers(messageResolvers); - templateEngine.setDialects(dialects); - final IDialect standardDialect = new StandardDialect(); - templateEngine.addDialect(standardDialect); - // TODO - // final ICacheManager cacheManager = null; - // templateEngine.setCacheManager(cacheManager); + private void configure(final ThymeleafScriptEngineFactoryConfiguration configuration) { + setExtensions(configuration.extensions()); + setMimeTypes(configuration.mimeTypes()); + setNames(configuration.names()); } @Override @@ -185,12 +189,13 @@ public final class ThymeleafScriptEngineFactory extends AbstractScriptEngineFact @Override public String getLanguageVersion() { try { - final java.util.Properties properties = new java.util.Properties(); - properties.load(getClass().getResourceAsStream("org/thymeleaf/thymeleaf.properties")); + final Properties properties = new Properties(); + properties.load(getClass().getResourceAsStream("/org/thymeleaf/thymeleaf.properties")); return properties.getProperty("version"); } catch (Exception e) { + logger.error("error reading version from thymeleaf.properties", e); + return ""; // null breaks output of web console } - return ""; } @Override @@ -200,7 +205,22 @@ public final class ThymeleafScriptEngineFactory extends AbstractScriptEngineFact } TemplateEngine getTemplateEngine() { - return templateEngine; + synchronized (lock) { + if (this.templateEngine == null) { + logger.info("setting up new template engine"); + templateEngine = new TemplateEngine(); + final Set<ITemplateResolver> templateResolvers = new HashSet<>(this.templateResolvers); + templateEngine.setTemplateResolvers(templateResolvers); + final Set<IMessageResolver> messageResolvers = new HashSet<>(this.messageResolvers); + templateEngine.setMessageResolvers(messageResolvers); + final Set<IDialect> dialects = new HashSet<>(this.dialects); + templateEngine.setDialects(dialects); + final IDialect standardDialect = new StandardDialect(); + templateEngine.addDialect(standardDialect); + templateEngine.setCacheManager(cacheManager); + } + return templateEngine; + } } } diff --git a/src/main/java/org/apache/sling/scripting/thymeleaf/internal/ThymeleafScriptEngineFactoryConfiguration.java b/src/main/java/org/apache/sling/scripting/thymeleaf/internal/ThymeleafScriptEngineFactoryConfiguration.java new file mode 100644 index 0000000..a97b394 --- /dev/null +++ b/src/main/java/org/apache/sling/scripting/thymeleaf/internal/ThymeleafScriptEngineFactoryConfiguration.java @@ -0,0 +1,60 @@ +/* + * 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 + * + * 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 org.apache.sling.scripting.thymeleaf.internal; + +import org.osgi.service.metatype.annotations.AttributeDefinition; +import org.osgi.service.metatype.annotations.ObjectClassDefinition; + +@ObjectClassDefinition( + name = "Apache Sling Scripting Thymeleaf “ScriptEngine Factory”", + description = "scripting engine for Thymeleaf template" +) +@interface ThymeleafScriptEngineFactoryConfiguration { + + @AttributeDefinition( + name = "service ranking", + description = "service property for identifying the service's ranking number" + ) + int service_ranking() default 0; + + @AttributeDefinition( + name = "extensions", + description = "extensions" + ) + String[] extensions() default { + "html" + }; + + @AttributeDefinition( + name = "mime types", + description = "mime types" + ) + String[] mimeTypes() default { + "text/html" + }; + + @AttributeDefinition( + name = "names", + description = "names" + ) + String[] names() default { + "thymeleaf" + }; + +} diff --git a/src/main/java/org/apache/sling/scripting/thymeleaf/internal/dialect/SlingDialect.java b/src/main/java/org/apache/sling/scripting/thymeleaf/internal/dialect/SlingDialect.java index ccaa3eb..289586c 100644 --- a/src/main/java/org/apache/sling/scripting/thymeleaf/internal/dialect/SlingDialect.java +++ b/src/main/java/org/apache/sling/scripting/thymeleaf/internal/dialect/SlingDialect.java @@ -21,29 +21,21 @@ package org.apache.sling.scripting.thymeleaf.internal.dialect; import java.util.HashSet; import java.util.Set; -import org.apache.felix.scr.annotations.Component; -import org.apache.felix.scr.annotations.Properties; -import org.apache.felix.scr.annotations.Property; -import org.apache.felix.scr.annotations.Service; import org.apache.sling.scripting.thymeleaf.internal.processor.SlingIncludeAttributeTagProcessor; import org.osgi.framework.Constants; +import org.osgi.service.component.annotations.Component; import org.thymeleaf.dialect.AbstractProcessorDialect; +import org.thymeleaf.dialect.IDialect; import org.thymeleaf.processor.IProcessor; @Component( - immediate = true + service = IDialect.class, + immediate = true, + property = { + Constants.SERVICE_DESCRIPTION + "=Sling dialect for Sling Scripting Thymeleaf", + Constants.SERVICE_VENDOR + "=The Apache Software Foundation" + } ) -@Service -@Properties({ - @Property( - name = Constants.SERVICE_VENDOR, - value = "The Apache Software Foundation" - ), - @Property( - name = Constants.SERVICE_DESCRIPTION, - value = "Sling dialect for Sling Scripting Thymeleaf" - ) -}) public final class SlingDialect extends AbstractProcessorDialect { public static final String NAME = "Sling"; diff --git a/src/main/java/org/apache/sling/scripting/thymeleaf/internal/processor/SlingIncludeAttributeTagProcessor.java b/src/main/java/org/apache/sling/scripting/thymeleaf/internal/processor/SlingIncludeAttributeTagProcessor.java index 01fed6b..4fe1d1d 100644 --- a/src/main/java/org/apache/sling/scripting/thymeleaf/internal/processor/SlingIncludeAttributeTagProcessor.java +++ b/src/main/java/org/apache/sling/scripting/thymeleaf/internal/processor/SlingIncludeAttributeTagProcessor.java @@ -37,13 +37,16 @@ import org.thymeleaf.IEngineConfiguration; import org.thymeleaf.context.ITemplateContext; import org.thymeleaf.dialect.IProcessorDialect; import org.thymeleaf.engine.AttributeName; +import org.thymeleaf.model.IElementAttributes; import org.thymeleaf.model.IProcessableElementTag; +import org.thymeleaf.processor.element.AbstractAttributeTagProcessor; import org.thymeleaf.processor.element.IElementTagStructureHandler; import org.thymeleaf.standard.expression.IStandardExpression; import org.thymeleaf.standard.expression.IStandardExpressionParser; import org.thymeleaf.standard.expression.StandardExpressions; +import org.thymeleaf.templatemode.TemplateMode; -public class SlingIncludeAttributeTagProcessor extends SlingHtmlAttributeTagProcessor { +public class SlingIncludeAttributeTagProcessor extends AbstractAttributeTagProcessor { public static final int ATTRIBUTE_PRECEDENCE = 100; @@ -62,7 +65,7 @@ public class SlingIncludeAttributeTagProcessor extends SlingHtmlAttributeTagProc private final Logger logger = LoggerFactory.getLogger(SlingIncludeAttributeTagProcessor.class); public SlingIncludeAttributeTagProcessor(final IProcessorDialect processorDialect, final String dialectPrefix) { - super(processorDialect, dialectPrefix, ATTRIBUTE_NAME, ATTRIBUTE_PRECEDENCE, true); + super(processorDialect, TemplateMode.HTML, dialectPrefix, null, true, ATTRIBUTE_NAME, true, ATTRIBUTE_PRECEDENCE, true); } @Override @@ -88,8 +91,6 @@ public class SlingIncludeAttributeTagProcessor extends SlingHtmlAttributeTagProc final RequestDispatcherOptions requestDispatcherOptions = prepareRequestDispatcherOptions(expressionParser, templateContext, processableElementTag); // dispatch final String content = dispatch(resource, path, slingHttpServletRequest, slingHttpServletResponse, requestDispatcherOptions); - // cleanup TODO: still needed? - // processableElementTag.getAttributes().removeAttribute(attributeName); // add output final Boolean unwrap = (Boolean) parseAttribute(expressionParser, templateContext, processableElementTag, UNWRAP_ATTRIBUTE_NAME); if (unwrap != null && unwrap) { @@ -103,9 +104,15 @@ public class SlingIncludeAttributeTagProcessor extends SlingHtmlAttributeTagProc } protected Object parseAttribute(final IStandardExpressionParser expressionParser, final ITemplateContext templateContext, final IProcessableElementTag processableElementTag, final String name) { - final String attributeValue = processableElementTag.getAttributes().getValue(getDialect().getPrefix(), name); - final IStandardExpression expression = expressionParser.parseExpression(templateContext, attributeValue); - return expression.execute(templateContext); + final IElementAttributes attributes = processableElementTag.getAttributes(); + final String value = attributes.getValue(getDialect().getPrefix(), name); + Object result = null; + if (value != null) { + final IStandardExpression expression = expressionParser.parseExpression(templateContext, value); + result = expression.execute(templateContext); + } + attributes.removeAttribute(getDialect().getPrefix(), name); + return result; } protected RequestDispatcherOptions prepareRequestDispatcherOptions(final IStandardExpressionParser expressionParser, final ITemplateContext templateContext, final IProcessableElementTag processableElementTag) { diff --git a/src/main/java/org/apache/sling/scripting/thymeleaf/package-info.java b/src/main/java/org/apache/sling/scripting/thymeleaf/package-info.java index 1b17768..d08560a 100644 --- a/src/main/java/org/apache/sling/scripting/thymeleaf/package-info.java +++ b/src/main/java/org/apache/sling/scripting/thymeleaf/package-info.java @@ -16,5 +16,7 @@ * specific language governing permissions and limitations * under the License. */ [email protected]("0.0.2") +@Version("0.0.2") package org.apache.sling.scripting.thymeleaf; + +import org.osgi.annotation.versioning.Version; diff --git a/src/main/java/org/thymeleaf/templateresolver/AbstractTemplateResolver.java b/src/main/java/org/thymeleaf/templateresolver/AbstractTemplateResolver.java index f4805e6..bdde094 100755 --- a/src/main/java/org/thymeleaf/templateresolver/AbstractTemplateResolver.java +++ b/src/main/java/org/thymeleaf/templateresolver/AbstractTemplateResolver.java @@ -210,7 +210,7 @@ public abstract class AbstractTemplateResolver implements ITemplateResolver { * </p> * <p> * If this <em>existence check</em> is enabled and a resource is determined to not exist, - * {@link ITemplateResolver#resolveTemplate(IEngineConfiguration, String, String, Map)} will return <tt>null</tt>. + * {@link ITemplateResolver#resolveTemplate(IEngineConfiguration, IContext, String, String, Map)} will return <tt>null</tt>. * </p> * * @return <tt>true</tt> if resource existence will be checked, <tt>false</tt> if not @@ -245,7 +245,7 @@ public abstract class AbstractTemplateResolver implements ITemplateResolver { * </p> * <p> * If this <em>existence check</em> is enabled and a resource is determined to not exist, - * {@link ITemplateResolver#resolveTemplate(IEngineConfiguration, String, String, Map)} will return <tt>null</tt>. + * {@link ITemplateResolver#resolveTemplate(IEngineConfiguration, IContext, String, String, Map)} will return <tt>null</tt>. * </p> * * @param checkExistence <tt>true</tt> if resource existence should be checked, <tt>false</tt> if not diff --git a/src/main/java/org/thymeleaf/templateresolver/ITemplateResolver.java b/src/main/java/org/thymeleaf/templateresolver/ITemplateResolver.java index 935b97f..16319c2 100755 --- a/src/main/java/org/thymeleaf/templateresolver/ITemplateResolver.java +++ b/src/main/java/org/thymeleaf/templateresolver/ITemplateResolver.java @@ -140,6 +140,7 @@ public interface ITemplateResolver { * </p> * * @param configuration the engine configuration. + * @param context the context * @param ownerTemplate the containing template from which we want to resolve a new one as a fragment. Can be null. * @param template the template to be resolved (usually its name). * @param templateResolutionAttributes the template resolution attributes to be used (usually coming from a diff --git a/src/main/resources/OSGI-INF/metatype/metatype.properties b/src/main/resources/OSGI-INF/metatype/metatype.properties deleted file mode 100644 index 93dbaa7..0000000 --- a/src/main/resources/OSGI-INF/metatype/metatype.properties +++ /dev/null @@ -1,60 +0,0 @@ -# -# 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 -# -# 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. -# - -service.ranking.name = service ranking -service.ranking.description = service property for identifying the service's ranking number - -# ThymeleafScriptEngineFactory -org.apache.sling.scripting.thymeleaf.extensions.name = extensions -org.apache.sling.scripting.thymeleaf.extensions.description = extensions - -org.apache.sling.scripting.thymeleaf.mimetypes.name = mime types -org.apache.sling.scripting.thymeleaf.mimetypes.description = mime types - -org.apache.sling.scripting.thymeleaf.names.name = names -org.apache.sling.scripting.thymeleaf.names.description = names - -# NonCachingTemplateResolver -org.apache.sling.scripting.thymeleaf.internal.NonCachingTemplateResolver.order.name = order -org.apache.sling.scripting.thymeleaf.internal.NonCachingTemplateResolver.order.description = property for ordering template resolvers inside the Thymeleaf template engine - -org.apache.sling.scripting.thymeleaf.internal.NonCachingTemplateResolver.encoding.name = encoding -org.apache.sling.scripting.thymeleaf.internal.NonCachingTemplateResolver.encoding.description = encoding for reading templates - -org.apache.sling.scripting.thymeleaf.internal.NonCachingTemplateResolver.htmlPatterns.name = patterns for Template Mode HTML -org.apache.sling.scripting.thymeleaf.internal.NonCachingTemplateResolver.htmlPatterns.description = the template patterns (regular expressions) which should be handled by Template Mode HTML (e.g. /apps/sling/*.html - NOTE: extension needs to be enabled for this script engine) - -org.apache.sling.scripting.thymeleaf.internal.NonCachingTemplateResolver.xmlPatterns.name = patterns for Template Mode XML -org.apache.sling.scripting.thymeleaf.internal.NonCachingTemplateResolver.xmlPatterns.description = the template patterns (regular expressions) which should be handled by Template Mode XML (e.g. /apps/sling/*.xml - NOTE: extension needs to be enabled for this script engine) - -org.apache.sling.scripting.thymeleaf.internal.NonCachingTemplateResolver.textPatterns.name = patterns for Template Mode TEXT -org.apache.sling.scripting.thymeleaf.internal.NonCachingTemplateResolver.textPatterns.description = the template patterns (regular expressions) which should be handled by Template Mode TEXT (e.g. /apps/sling/*.txt - NOTE: extension needs to be enabled for this script engine) - -org.apache.sling.scripting.thymeleaf.internal.NonCachingTemplateResolver.javascriptPatterns.name = patterns for Template Mode JAVASCRIPT -org.apache.sling.scripting.thymeleaf.internal.NonCachingTemplateResolver.javascriptPatterns.description = the template patterns (regular expressions) which should be handled by Template Mode JAVASCRIPT (e.g. /apps/sling/*.js - NOTE: extension needs to be enabled for this script engine) - -org.apache.sling.scripting.thymeleaf.internal.NonCachingTemplateResolver.cssPatterns.name = patterns for Template Mode CSS -org.apache.sling.scripting.thymeleaf.internal.NonCachingTemplateResolver.cssPatterns.description = the template patterns (regular expressions) which should be handled by Template Mode CSS (e.g. /apps/sling/*.css) - NOTE: extension needs to be enabled for this script engine) - -org.apache.sling.scripting.thymeleaf.internal.NonCachingTemplateResolver.rawPatterns.name = patterns for Template Mode RAW -org.apache.sling.scripting.thymeleaf.internal.NonCachingTemplateResolver.rawPatterns.description = the template patterns (regular expressions) which should be handled by Template Mode RAW (e.g. /apps/sling/*.raw) - NOTE: extension needs to be enabled for this script engine) - -# ResourceBundleMessageResolver -org.apache.sling.scripting.thymeleaf.internal.ResourceBundleMessageResolver.order.name = order -org.apache.sling.scripting.thymeleaf.internal.ResourceBundleMessageResolver.order.description = property for ordering message resolvers inside the Thymeleaf template engine -- To stop receiving notification emails like this one, please contact "[email protected]" <[email protected]>.
