001/* 002 * This library is part of OpenCms - 003 * the Open Source Content Management System 004 * 005 * Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com) 006 * 007 * This library is free software; you can redistribute it and/or 008 * modify it under the terms of the GNU Lesser General Public 009 * License as published by the Free Software Foundation; either 010 * version 2.1 of the License, or (at your option) any later version. 011 * 012 * This library is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 015 * Lesser General Public License for more details. 016 * 017 * For further information about Alkacon Software GmbH & Co. KG, please see the 018 * company website: http://www.alkacon.com 019 * 020 * For further information about OpenCms, please see the 021 * project website: http://www.opencms.org 022 * 023 * You should have received a copy of the GNU Lesser General Public 024 * License along with this library; if not, write to the Free Software 025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 026 */ 027 028package org.opencms.file.types; 029 030import org.opencms.configuration.CmsConfigurationException; 031import org.opencms.db.CmsSecurityManager; 032import org.opencms.file.CmsFile; 033import org.opencms.file.CmsObject; 034import org.opencms.file.CmsProperty; 035import org.opencms.file.CmsPropertyDefinition; 036import org.opencms.file.CmsResource; 037import org.opencms.file.CmsResource.CmsResourceDeleteMode; 038import org.opencms.file.CmsResource.CmsResourceUndoMode; 039import org.opencms.i18n.CmsEncoder; 040import org.opencms.i18n.CmsLocaleManager; 041import org.opencms.json.JSONObject; 042import org.opencms.jsp.util.CmsJspLinkMacroResolver; 043import org.opencms.loader.CmsJspLoader; 044import org.opencms.main.CmsException; 045import org.opencms.main.CmsIllegalArgumentException; 046import org.opencms.main.CmsLog; 047import org.opencms.main.OpenCms; 048import org.opencms.relations.CmsLink; 049import org.opencms.util.CmsStringUtil; 050import org.opencms.xml.containerpage.CmsFormatterBean; 051import org.opencms.xml.containerpage.CmsFormatterConfiguration; 052import org.opencms.xml.containerpage.I_CmsFormatterBean; 053 054import java.io.UnsupportedEncodingException; 055import java.util.ArrayList; 056import java.util.Collections; 057import java.util.HashSet; 058import java.util.List; 059import java.util.Set; 060 061import org.apache.commons.logging.Log; 062 063/** 064 * Resource type descriptor for the type "jsp".<p> 065 * 066 * Ensures that some required file properties are attached to new JSPs.<p> 067 * 068 * The value for the encoding properties of a new JSP usually is the 069 * system default encoding, but this can be overwritten by 070 * a configuration parameters set in <code>opencms-vfs.xml</code>.<p> 071 * 072 * @since 6.0.0 073 */ 074public class CmsResourceTypeJsp extends A_CmsResourceTypeLinkParseable { 075 076 /** Key for formatter setting container maximum width. */ 077 public static final String FORMATTER_SETTING_MAX_WIDTH = "maxwidth"; 078 079 /** Key for formatter setting container minimum width. */ 080 public static final String FORMATTER_SETTING_MIN_WIDTH = "minwidth"; 081 082 /** Key for formatter setting container type. */ 083 public static final String FORMATTER_SETTING_TYPE = "type"; 084 085 /** The type id of the containerpage_template resource type. */ 086 private static final int CONTAINERPAGE_TEMPLATE_TYPE_ID = 21; 087 088 /** The type name of the containerpage_template resource type. */ 089 private static final String CONTAINERPAGE_TEMPLATE_TYPE_NAME = "containerpage_template"; 090 091 /** The type id of the JSP resource type. */ 092 private static final int JSP_RESOURCE_TYPE_ID = 4; 093 094 /** Static reference to the log. */ 095 private static final Log LOG = CmsLog.getLog(org.opencms.file.types.CmsResourceTypeJsp.class); 096 097 /** The registered JSP resource type id's. */ 098 private static List<Integer> m_jspResourceTypeIds = new ArrayList<Integer>(); 099 100 /** The name of this resource type. */ 101 private static final String RESOURCE_TYPE_NAME = "jsp"; 102 103 /** The serial version id. */ 104 private static final long serialVersionUID = 6852747481533451911L; 105 106 /** JSP Loader instance. */ 107 protected transient CmsJspLoader m_jspLoader; 108 109 /** 110 * Returns the type id of the containerpage_template resource type.<p> 111 * 112 * @return the type id of the containerpage_template resource type 113 */ 114 public static int getContainerPageTemplateTypeId() { 115 116 return CONTAINERPAGE_TEMPLATE_TYPE_ID; 117 } 118 119 /** 120 * Returns the type name of the containerpage_template resource type.<p> 121 * 122 * @return the type name of the containerpage_template resource type 123 */ 124 public static String getContainerPageTemplateTypeName() { 125 126 return CONTAINERPAGE_TEMPLATE_TYPE_NAME; 127 } 128 129 /** 130 * Returns the registered JSP resource type id's.<p> 131 * 132 * @return the resource type id's 133 */ 134 public static List<Integer> getJspResourceTypeIds() { 135 136 return m_jspResourceTypeIds; 137 } 138 139 /** 140 * Returns the type id of the (default)JSP resource type.<p> 141 * 142 * @return the type id of this (default)JSP resource type 143 */ 144 public static int getJSPTypeId() { 145 146 return JSP_RESOURCE_TYPE_ID; 147 } 148 149 /** 150 * Returns the static type name of this (default) resource type.<p> 151 * 152 * @return the static type name of this (default) resource type 153 */ 154 public static String getStaticTypeName() { 155 156 return RESOURCE_TYPE_NAME; 157 } 158 159 /** 160 * Returns <code>true</code> in case the given resource is a JSP.<p> 161 * 162 * Internally this checks if the given resource type has an id that is registered as a JSP resource type.<p> 163 * 164 * @param resource the resource to check 165 * 166 * @return <code>true</code> in case the given resource is a JSP 167 * 168 * @since 8.0.0 169 */ 170 public static boolean isJsp(CmsResource resource) { 171 172 return resource == null ? false : isJspTypeId(resource.getTypeId()); 173 } 174 175 /** 176 * Returns <code>true</code> in case the given resource type id is a JSP type.<p> 177 * 178 * Internally this checks if the given resource type id is registered as a JSP resource type.<p> 179 * 180 * @param typeId the resource type id to check 181 * 182 * @return <code>true</code> in case the given resource type id is a JSP type 183 * 184 * @since 8.0.0 185 */ 186 public static boolean isJspTypeId(int typeId) { 187 188 return m_jspResourceTypeIds.contains(Integer.valueOf(typeId)); 189 } 190 191 /** 192 * @see org.opencms.file.types.A_CmsResourceType#chtype(org.opencms.file.CmsObject, org.opencms.db.CmsSecurityManager, org.opencms.file.CmsResource, int) 193 */ 194 @Override 195 public void chtype(CmsObject cms, CmsSecurityManager securityManager, CmsResource resource, int type) 196 throws CmsException { 197 198 Set<String> references = getReferencingStrongLinks(cms, resource); 199 super.chtype(cms, securityManager, resource, type); 200 removeReferencingFromCache(references); 201 } 202 203 /** 204 * @see org.opencms.file.types.A_CmsResourceType#deleteResource(org.opencms.file.CmsObject, org.opencms.db.CmsSecurityManager, org.opencms.file.CmsResource, org.opencms.file.CmsResource.CmsResourceDeleteMode) 205 */ 206 @Override 207 public void deleteResource( 208 CmsObject cms, 209 CmsSecurityManager securityManager, 210 CmsResource resource, 211 CmsResourceDeleteMode siblingMode) 212 throws CmsException { 213 214 Set<String> references = getReferencingStrongLinks(cms, resource); 215 super.deleteResource(cms, securityManager, resource, siblingMode); 216 removeReferencingFromCache(references); 217 } 218 219 /** 220 * @see org.opencms.file.types.A_CmsResourceType#getFormattersForResource(org.opencms.file.CmsObject, org.opencms.file.CmsResource) 221 */ 222 @Override 223 public CmsFormatterConfiguration getFormattersForResource(CmsObject cms, CmsResource res) { 224 225 // by default JSPs may be rendered within any container with itself as formatter 226 String containerType = CmsFormatterBean.WILDCARD_TYPE; 227 int minWidth = 1; 228 int maxWidth = Integer.MAX_VALUE; 229 try { 230 // check template property to override default settings 231 String formatterSetting = cms.readPropertyObject( 232 res, 233 CmsPropertyDefinition.PROPERTY_TEMPLATE, 234 false).getValue(); 235 if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(formatterSetting)) { 236 JSONObject setting = new JSONObject(formatterSetting); 237 if (setting.has(FORMATTER_SETTING_TYPE)) { 238 containerType = setting.getString(FORMATTER_SETTING_TYPE); 239 } 240 if (setting.has(FORMATTER_SETTING_MIN_WIDTH)) { 241 minWidth = setting.getInt(FORMATTER_SETTING_MIN_WIDTH); 242 } 243 if (setting.has(FORMATTER_SETTING_MAX_WIDTH)) { 244 maxWidth = setting.getInt(FORMATTER_SETTING_MAX_WIDTH); 245 } 246 } 247 } catch (Exception e) { 248 LOG.error( 249 Messages.get().getBundle().key( 250 Messages.ERR_PARSING_FORMATTER_SETTINGS_FROM_PROPERTY_2, 251 res.getName(), 252 CmsPropertyDefinition.PROPERTY_TEMPLATE), 253 e); 254 } 255 CmsFormatterBean selfFormatter = new CmsFormatterBean( 256 containerType, 257 res.getRootPath(), 258 res.getStructureId(), 259 minWidth, 260 maxWidth, 261 true, 262 false, 263 res.getRootPath()); 264 265 return CmsFormatterConfiguration.create(cms, Collections.<I_CmsFormatterBean> singletonList(selfFormatter)); 266 } 267 268 /** 269 * @see org.opencms.file.types.I_CmsResourceType#getLoaderId() 270 */ 271 @Override 272 public int getLoaderId() { 273 274 return CmsJspLoader.RESOURCE_LOADER_ID; 275 } 276 277 /** 278 * @see org.opencms.file.types.A_CmsResourceType#initConfiguration(java.lang.String, java.lang.String, String) 279 */ 280 @Override 281 public void initConfiguration(String name, String id, String className) throws CmsConfigurationException { 282 283 super.initConfiguration(name, id, className); 284 // set static members with values from the configuration 285 addTypeId(m_typeId); 286 } 287 288 /** 289 * @see org.opencms.file.types.A_CmsResourceType#initialize(org.opencms.file.CmsObject) 290 */ 291 @Override 292 public void initialize(CmsObject cms) { 293 294 super.initialize(cms); 295 try { 296 m_jspLoader = (CmsJspLoader)OpenCms.getResourceManager().getLoader(CmsJspLoader.RESOURCE_LOADER_ID); 297 } catch (ArrayIndexOutOfBoundsException e) { 298 // ignore, loader not configured 299 } 300 } 301 302 /** 303 * @see org.opencms.file.types.A_CmsResourceType#moveResource(org.opencms.file.CmsObject, org.opencms.db.CmsSecurityManager, org.opencms.file.CmsResource, java.lang.String) 304 */ 305 @Override 306 public void moveResource( 307 CmsObject cms, 308 CmsSecurityManager securityManager, 309 CmsResource resource, 310 String destination) 311 throws CmsException, CmsIllegalArgumentException { 312 313 Set<String> references = getReferencingStrongLinks(cms, resource); 314 super.moveResource(cms, securityManager, resource, destination); 315 removeReferencingFromCache(references); 316 } 317 318 /** 319 * @see org.opencms.relations.I_CmsLinkParseable#parseLinks(org.opencms.file.CmsObject, org.opencms.file.CmsFile) 320 */ 321 public List<CmsLink> parseLinks(CmsObject cms, CmsFile file) { 322 323 CmsJspLinkMacroResolver macroResolver = new CmsJspLinkMacroResolver(cms, file.getRootPath(), false); 324 String encoding = CmsLocaleManager.getResourceEncoding(cms, file); 325 String content = CmsEncoder.createString(file.getContents(), encoding); 326 macroResolver.resolveMacros(content); // ignore return value 327 return macroResolver.getLinks(); 328 } 329 330 /** 331 * @see org.opencms.file.types.A_CmsResourceType#replaceResource(org.opencms.file.CmsObject, org.opencms.db.CmsSecurityManager, org.opencms.file.CmsResource, int, byte[], java.util.List) 332 */ 333 @Override 334 public void replaceResource( 335 CmsObject cms, 336 CmsSecurityManager securityManager, 337 CmsResource resource, 338 int type, 339 byte[] content, 340 List<CmsProperty> properties) 341 throws CmsException { 342 343 Set<String> references = getReferencingStrongLinks(cms, resource); 344 super.replaceResource(cms, securityManager, resource, type, content, properties); 345 removeReferencingFromCache(references); 346 } 347 348 /** 349 * @see org.opencms.file.types.A_CmsResourceType#restoreResource(org.opencms.file.CmsObject, org.opencms.db.CmsSecurityManager, org.opencms.file.CmsResource, int) 350 */ 351 @Override 352 public void restoreResource(CmsObject cms, CmsSecurityManager securityManager, CmsResource resource, int version) 353 throws CmsException { 354 355 Set<String> references = getReferencingStrongLinks(cms, resource); 356 super.restoreResource(cms, securityManager, resource, version); 357 removeReferencingFromCache(references); 358 } 359 360 /** 361 * @see org.opencms.file.types.A_CmsResourceType#setDateExpired(org.opencms.file.CmsObject, org.opencms.db.CmsSecurityManager, org.opencms.file.CmsResource, long, boolean) 362 */ 363 @Override 364 public void setDateExpired( 365 CmsObject cms, 366 CmsSecurityManager securityManager, 367 CmsResource resource, 368 long dateExpired, 369 boolean recursive) 370 throws CmsException { 371 372 Set<String> references = getReferencingStrongLinks(cms, resource); 373 super.setDateExpired(cms, securityManager, resource, dateExpired, recursive); 374 removeReferencingFromCache(references); 375 } 376 377 /** 378 * @see org.opencms.file.types.A_CmsResourceType#setDateLastModified(org.opencms.file.CmsObject, org.opencms.db.CmsSecurityManager, org.opencms.file.CmsResource, long, boolean) 379 */ 380 @Override 381 public void setDateLastModified( 382 CmsObject cms, 383 CmsSecurityManager securityManager, 384 CmsResource resource, 385 long dateLastModified, 386 boolean recursive) 387 throws CmsException { 388 389 Set<String> references = getReferencingStrongLinks(cms, resource); 390 super.setDateLastModified(cms, securityManager, resource, dateLastModified, recursive); 391 removeReferencingFromCache(references); 392 } 393 394 /** 395 * @see org.opencms.file.types.A_CmsResourceType#setDateReleased(org.opencms.file.CmsObject, org.opencms.db.CmsSecurityManager, org.opencms.file.CmsResource, long, boolean) 396 */ 397 @Override 398 public void setDateReleased( 399 CmsObject cms, 400 CmsSecurityManager securityManager, 401 CmsResource resource, 402 long dateReleased, 403 boolean recursive) 404 throws CmsException { 405 406 Set<String> references = getReferencingStrongLinks(cms, resource); 407 super.setDateReleased(cms, securityManager, resource, dateReleased, recursive); 408 removeReferencingFromCache(references); 409 } 410 411 /** 412 * @see org.opencms.file.types.A_CmsResourceType#undoChanges(org.opencms.file.CmsObject, org.opencms.db.CmsSecurityManager, org.opencms.file.CmsResource, org.opencms.file.CmsResource.CmsResourceUndoMode) 413 */ 414 @Override 415 public void undoChanges( 416 CmsObject cms, 417 CmsSecurityManager securityManager, 418 CmsResource resource, 419 CmsResourceUndoMode mode) 420 throws CmsException { 421 422 Set<String> references = getReferencingStrongLinks(cms, resource); 423 super.undoChanges(cms, securityManager, resource, mode); 424 if (m_jspLoader != null) { 425 // we need to remove the JSP explicitly because undoing the changes also 426 // resets the last modification date, so the automatic mechanism for updating 427 // JSPs based on modification dates doesn't work. 428 m_jspLoader.removeOfflineJspFromRepository(resource); 429 } 430 removeReferencingFromCache(references); 431 } 432 433 /** 434 * @see org.opencms.file.types.A_CmsResourceType#writeFile(org.opencms.file.CmsObject, org.opencms.db.CmsSecurityManager, org.opencms.file.CmsFile) 435 */ 436 @Override 437 public CmsFile writeFile(CmsObject cms, CmsSecurityManager securityManager, CmsFile resource) throws CmsException { 438 439 // actualize the link paths and/or ids 440 CmsJspLinkMacroResolver macroResolver = new CmsJspLinkMacroResolver(cms, resource.getRootPath(), false); 441 String encoding = CmsLocaleManager.getResourceEncoding(cms, resource); 442 String content = CmsEncoder.createString(resource.getContents(), encoding); 443 content = macroResolver.resolveMacros(content); 444 try { 445 resource.setContents(content.getBytes(encoding)); 446 } catch (UnsupportedEncodingException e) { 447 // this should usually never happen since the encoding is already used before 448 resource.setContents(content.getBytes()); 449 } 450 // write the content with the 'right' links 451 Set<String> references = getReferencingStrongLinks(cms, resource); 452 CmsFile file = super.writeFile(cms, securityManager, resource); 453 removeReferencingFromCache(references); 454 return file; 455 } 456 457 /** 458 * Returns a set of root paths of files that are including the given resource using the 'link.strong' macro.<p> 459 * 460 * @param cms the current cms context 461 * @param resource the resource to check 462 * 463 * @return the set of referencing paths 464 * 465 * @throws CmsException if something goes wrong 466 */ 467 protected Set<String> getReferencingStrongLinks(CmsObject cms, CmsResource resource) throws CmsException { 468 469 Set<String> references = new HashSet<String>(); 470 if (m_jspLoader == null) { 471 return references; 472 } 473 m_jspLoader.getReferencingStrongLinks(cms, resource, references); 474 return references; 475 } 476 477 /** 478 * Removes the referencing resources from the cache.<p> 479 * 480 * @param references the references to remove 481 */ 482 protected void removeReferencingFromCache(Set<String> references) { 483 484 if (m_jspLoader != null) { 485 m_jspLoader.removeFromCache(references, false); 486 } 487 } 488 489 /** 490 * Adds another resource type id to the registered JSP resource type id's.<p> 491 * 492 * @param typeId the resource type id to add 493 */ 494 private void addTypeId(int typeId) { 495 496 m_jspResourceTypeIds.add(Integer.valueOf(typeId)); 497 } 498}