001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.wicket.resource; 018 019import java.time.Duration; 020import java.time.Instant; 021import java.util.Locale; 022import java.util.Map; 023import org.apache.wicket.Application; 024import org.apache.wicket.model.IModel; 025import org.apache.wicket.request.resource.IResource; 026import org.apache.wicket.request.resource.ResourceReference; 027import org.apache.wicket.request.resource.ResourceReferenceRegistry; 028import org.apache.wicket.request.resource.ResourceStreamResource; 029import org.apache.wicket.util.io.IClusterable; 030import org.apache.wicket.util.resource.IResourceStream; 031import org.apache.wicket.util.resource.StringResourceStream; 032import org.apache.wicket.util.template.PackageTextTemplate; 033import org.apache.wicket.util.template.TextTemplate; 034 035/** 036 * A class which adapts a {@link PackageTextTemplate} to a {@link ResourceReference}. 037 * 038 * See <a href="https://cwiki.apache.org/confluence/display/WICKET/Dynamically+Generate+a+CSS+Stylesheet">Dynamically generate a CSS stylesheet</a> 039 * 040 * @author James Carman 041 */ 042public class TextTemplateResourceReference extends ResourceReference implements IClusterable 043{ 044 045 private static final long serialVersionUID = 1L; 046 047 private final TextTemplate textTemplate; 048 private final IModel<Map<String, Object>> variablesModel; 049 private final ResourceStreamResource resource; 050 051 /** 052 * Creates a resource reference to a {@link PackageTextTemplate}. 053 * 054 * @param scope 055 * the <code>Class</code> to be used for retrieving the classloader for loading the 056 * <code>PackagedTextTemplate</code> 057 * @param fileName 058 * the file name 059 * @param variablesModel 060 * the template variables as a model 061 */ 062 public TextTemplateResourceReference(final Class<?> scope, final String fileName, 063 IModel<Map<String, Object>> variablesModel) 064 { 065 this(scope, fileName, PackageTextTemplate.DEFAULT_CONTENT_TYPE, 066 PackageTextTemplate.DEFAULT_ENCODING, variablesModel); 067 } 068 069 /** 070 * Creates a resource reference to a {@link PackageTextTemplate}. 071 * 072 * @param scope 073 * the <code>Class</code> to be used for retrieving the classloader for loading the 074 * <code>PackagedTextTemplate</code> 075 * @param fileName 076 * the file name 077 * @param contentType 078 * the mime type of this resource, such as "<code>image/jpeg</code>" or " 079 * <code>text/html</code>" 080 * @param variablesModel 081 * the template variables as a model 082 */ 083 public TextTemplateResourceReference(final Class<?> scope, final String fileName, 084 final String contentType, IModel<Map<String, Object>> variablesModel) 085 { 086 this(scope, fileName, contentType, PackageTextTemplate.DEFAULT_ENCODING, variablesModel); 087 } 088 089 /** 090 * Creates a resource reference to a {@link PackageTextTemplate}. 091 * 092 * @param scope 093 * the <code>Class</code> to be used for retrieving the classloader for loading the 094 * <code>PackagedTextTemplate</code> 095 * @param fileName 096 * the file name 097 * @param contentType 098 * the mime type of this resource, such as "<code>image/jpeg</code>" or " 099 * <code>text/html</code>" 100 * @param encoding 101 * the file's encoding, for example, "<code>UTF-8</code>" 102 * @param variablesModel 103 * the template variables as a model 104 */ 105 public TextTemplateResourceReference(final Class<?> scope, final String fileName, 106 final String contentType, final String encoding, IModel<Map<String, Object>> variablesModel) 107 { 108 this(scope, fileName, contentType, encoding, variablesModel, null, null, null); 109 } 110 111 /** 112 * Construct. 113 * 114 * @param scope 115 * the <code>Class</code> to be used for retrieving the classloader for loading the 116 * <code>PackagedTextTemplate</code> 117 * @param fileName 118 * the file name 119 * @param contentType 120 * the mime type of this resource, such as "<code>image/jpeg</code>" or " 121 * <code>text/html</code>" 122 * @param encoding 123 * the file's encoding, for example, "<code>UTF-8</code>" 124 * @param variablesModel 125 * the template variables as a model 126 * @param locale 127 * Preferred locale for the resource 128 * @param style 129 * Preferred style for the resource 130 * @param variation 131 * Preferred variation for the resource 132 */ 133 public TextTemplateResourceReference(final Class<?> scope, final String fileName, 134 final String contentType, final String encoding, 135 IModel<Map<String, Object>> variablesModel, Locale locale, String style, String variation) 136 { 137 super(scope, fileName, locale, style, variation); 138 139 textTemplate = new PackageTextTemplate(scope, fileName, contentType, encoding); 140 this.variablesModel = variablesModel; 141 142 resource = new ResourceStreamResource(null) 143 { 144 @Override 145 protected IResourceStream getResourceStream(Attributes attributes) 146 { 147 IModel<Map<String, Object>> variables = TextTemplateResourceReference.this.variablesModel; 148 String stringValue = textTemplate.asString(variables.getObject()); 149 variables.detach(); // We're done with the model so detach it! 150 151 StringResourceStream resourceStream = new StringResourceStream(stringValue, 152 textTemplate.getContentType()); 153 resourceStream.setLastModified(Instant.now()); 154 155 return resourceStream; 156 } 157 }; 158 resource.setCacheDuration(Duration.ZERO); 159 160 if (Application.exists()) 161 { 162 // TextTemplateResourceReference should not be cached due to its dynamic nature 163 // Old entry in the registry would keep wrong 'variablesModel' 164 ResourceReferenceRegistry resourceReferenceRegistry = Application.get().getResourceReferenceRegistry(); 165 resourceReferenceRegistry.unregisterResourceReference(getKey()); 166 resourceReferenceRegistry.registerResourceReference(this); 167 } 168 } 169 170 /** 171 * Creates a new resource which returns the interpolated value of the text template. 172 * 173 * @return a new resource which returns the interpolated value of the text template 174 */ 175 @Override 176 public IResource getResource() 177 { 178 return resource; 179 } 180}