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.wrapper; 029 030import org.opencms.file.CmsFile; 031import org.opencms.file.CmsObject; 032import org.opencms.file.CmsProperty; 033import org.opencms.file.CmsRequestContext; 034import org.opencms.file.CmsResource; 035import org.opencms.file.CmsResource.CmsResourceCopyMode; 036import org.opencms.file.CmsResource.CmsResourceDeleteMode; 037import org.opencms.file.CmsResourceFilter; 038import org.opencms.file.CmsUser; 039import org.opencms.file.I_CmsResource; 040import org.opencms.file.types.CmsResourceTypeJsp; 041import org.opencms.file.types.CmsResourceTypePlain; 042import org.opencms.file.types.CmsResourceTypeXmlContent; 043import org.opencms.file.types.CmsResourceTypeXmlPage; 044import org.opencms.file.types.I_CmsResourceType; 045import org.opencms.i18n.CmsEncoder; 046import org.opencms.i18n.CmsLocaleManager; 047import org.opencms.loader.CmsLoaderException; 048import org.opencms.lock.CmsLock; 049import org.opencms.main.CmsException; 050import org.opencms.main.CmsIllegalArgumentException; 051import org.opencms.main.CmsLog; 052import org.opencms.main.OpenCms; 053import org.opencms.util.CmsUUID; 054 055import java.util.ArrayList; 056import java.util.Collections; 057import java.util.Iterator; 058import java.util.List; 059import java.util.Map; 060 061import org.apache.commons.logging.Log; 062 063/** 064 * This class contains a subset of the methods of {@link CmsObject} and uses the 065 * configured resource wrappers ({@link I_CmsResourceWrapper}) to change the view 066 * to the existing resources in the VFS.<p> 067 * 068 * Almost every method in this class iterates through the configured list of 069 * {@link I_CmsResourceWrapper} and calls the same method there. The first resource 070 * wrapper in the list which feels responsible for that action handles it and the 071 * iteration ends. So the resource wrappers should check in every method if it is 072 * responsible or not. Be careful if there are more than one resource wrapper for 073 * the same resource in the VFS, because the first in the list wins. If the iteration is 074 * finished and no resource wrapper felt responsible the default action is to call the 075 * method in the {@link CmsObject}.<p> 076 * 077 * It is possible to create an unchanged access to the resource in the VFS by creating 078 * a new instance of the CmsObjectWrapper with an empty list of resource wrappers.<p> 079 * 080 * @since 6.2.4 081 */ 082public class CmsObjectWrapper { 083 084 /** The name of the attribute in the {@link CmsRequestContext} where the current CmsObjectWrapper can be found. */ 085 public static final String ATTRIBUTE_NAME = "org.opencms.file.wrapper.CmsObjectWrapper"; 086 087 /** The log object for this class. */ 088 private static final Log LOG = CmsLog.getLog(CmsObjectWrapper.class); 089 090 /** Flag to contro whether byte order marks should be added to plaintext files. */ 091 private boolean m_addByteOrderMark = true; 092 093 /** The initialized CmsObject. */ 094 private CmsObject m_cms; 095 096 /** The list with the configured wrappers (entries of type {@link I_CmsResourceWrapper}). */ 097 private List<I_CmsResourceWrapper> m_wrappers; 098 099 /** 100 * Constructor with the CmsObject to wrap and the resource wrappers to use.<p> 101 * 102 * @param cms the initialized CmsObject 103 * @param wrappers the configured wrappers to use (entries of type {@link I_CmsResourceWrapper}) 104 */ 105 public CmsObjectWrapper(CmsObject cms, List<I_CmsResourceWrapper> wrappers) { 106 107 m_cms = cms; 108 m_wrappers = wrappers; 109 } 110 111 /** 112 * Copies a resource.<p> 113 * 114 * Iterates through all configured resource wrappers till the first returns <code>true</code>.<p> 115 * 116 * @see I_CmsResourceWrapper#copyResource(CmsObject, String, String, CmsResource.CmsResourceCopyMode) 117 * @see CmsObject#copyResource(String, String, CmsResource.CmsResourceCopyMode) 118 * 119 * @param source the name of the resource to copy (full path) 120 * @param destination the name of the copy destination (full path) 121 * @param siblingMode indicates how to handle siblings during copy 122 * 123 * @throws CmsException if something goes wrong 124 * @throws CmsIllegalArgumentException if the <code>destination</code> argument is null or of length 0 125 */ 126 public void copyResource(String source, String destination, CmsResourceCopyMode siblingMode) 127 throws CmsException, CmsIllegalArgumentException { 128 129 boolean exec = false; 130 131 // iterate through all wrappers and call "copyResource" till one does not return null 132 List<I_CmsResourceWrapper> wrappers = getWrappers(); 133 Iterator<I_CmsResourceWrapper> iter = wrappers.iterator(); 134 while (iter.hasNext()) { 135 I_CmsResourceWrapper wrapper = iter.next(); 136 exec = wrapper.copyResource(m_cms, source, destination, siblingMode); 137 if (exec) { 138 break; 139 } 140 } 141 142 // delegate the call to the CmsObject 143 if (!exec) { 144 m_cms.copyResource(source, destination, siblingMode); 145 } 146 147 } 148 149 /** 150 * Creates a new resource of the given resource type with empty content and no properties.<p> 151 * 152 * @see #createResource(String, int, byte[], List) 153 * 154 * @param resourcename the name of the resource to create (full path) 155 * @param type the type of the resource to create 156 * 157 * @return the created resource 158 * 159 * @throws CmsException if something goes wrong 160 * @throws CmsIllegalArgumentException if the given <code>resourcename</code> is null or of length 0 161 */ 162 public CmsResource createResource(String resourcename, int type) throws CmsException, CmsIllegalArgumentException { 163 164 return createResource(resourcename, type, new byte[0], new ArrayList<CmsProperty>(0)); 165 } 166 167 /** 168 * Creates a new resource of the given resource type with the provided content and properties.<p> 169 * 170 * Iterates through all configured resource wrappers till the first returns not <code>null</code>.<p> 171 * 172 * @see I_CmsResourceWrapper#createResource(CmsObject, String, int, byte[], List) 173 * @see CmsObject#createResource(String, int, byte[], List) 174 * 175 * @param resourcename the name of the resource to create (full path) 176 * @param type the type of the resource to create 177 * @param content the contents for the new resource 178 * @param properties the properties for the new resource 179 * 180 * @return the created resource 181 * 182 * @throws CmsException if something goes wrong 183 * @throws CmsIllegalArgumentException if the <code>resourcename</code> argument is null or of length 0 184 */ 185 public CmsResource createResource(String resourcename, int type, byte[] content, List<CmsProperty> properties) 186 throws CmsException, CmsIllegalArgumentException { 187 188 CmsResource res = null; 189 190 // iterate through all wrappers and call "createResource" till one does not return null 191 List<I_CmsResourceWrapper> wrappers = getWrappers(); 192 Iterator<I_CmsResourceWrapper> iter = wrappers.iterator(); 193 while (iter.hasNext()) { 194 I_CmsResourceWrapper wrapper = iter.next(); 195 res = wrapper.createResource(m_cms, resourcename, type, content, properties); 196 if (res != null) { 197 break; 198 } 199 } 200 201 // delegate the call to the CmsObject 202 if (res == null) { 203 res = m_cms.createResource(resourcename, type, content, properties); 204 } 205 206 return res; 207 } 208 209 /** 210 * Deletes a resource given its name.<p> 211 * 212 * Iterates through all configured resource wrappers till the first returns <code>true</code>.<p> 213 * 214 * @see I_CmsResourceWrapper#deleteResource(CmsObject, String, CmsResource.CmsResourceDeleteMode) 215 * @see CmsObject#deleteResource(String, CmsResource.CmsResourceDeleteMode) 216 * 217 * @param resourcename the name of the resource to delete (full path) 218 * @param siblingMode indicates how to handle siblings of the deleted resource 219 * 220 * @throws CmsException if something goes wrong 221 */ 222 public void deleteResource(String resourcename, CmsResourceDeleteMode siblingMode) throws CmsException { 223 224 boolean exec = false; 225 226 // iterate through all wrappers and call "deleteResource" till one does not return false 227 List<I_CmsResourceWrapper> wrappers = getWrappers(); 228 Iterator<I_CmsResourceWrapper> iter = wrappers.iterator(); 229 while (iter.hasNext()) { 230 I_CmsResourceWrapper wrapper = iter.next(); 231 exec = wrapper.deleteResource(m_cms, resourcename, siblingMode); 232 if (exec) { 233 break; 234 } 235 } 236 237 // delegate the call to the CmsObject 238 if (!exec) { 239 m_cms.deleteResource(resourcename, siblingMode); 240 } 241 } 242 243 /** 244 * Checks the availability of a resource in the VFS, 245 * using the {@link CmsResourceFilter#DEFAULT} filter.<p> 246 * 247 * Here it will be first checked if the resource exists in the VFS by calling 248 * {@link org.opencms.file.CmsObject#existsResource(String)}. Only if it doesn't exist 249 * in the VFS the method {@link I_CmsResourceWrapper#readResource(CmsObject, String, CmsResourceFilter)} 250 * in the configured resource wrappers are called till the first does not throw an exception or returns 251 * <code>null</code>.<p> 252 * 253 * @param resourcename the name of the resource to check (full path) 254 * 255 * @return <code>true</code> if the resource is available 256 */ 257 public boolean existsResource(String resourcename) { 258 259 // first try to find the resource 260 boolean ret = m_cms.existsResource(resourcename); 261 262 // if not exists, ask the resource type wrappers 263 if (!ret) { 264 265 List<I_CmsResourceWrapper> wrappers = getWrappers(); 266 Iterator<I_CmsResourceWrapper> iter = wrappers.iterator(); 267 while (iter.hasNext()) { 268 I_CmsResourceWrapper wrapper = iter.next(); 269 try { 270 CmsResource res = wrapper.readResource(m_cms, resourcename, CmsResourceFilter.DEFAULT); 271 if (res != null) { 272 ret = true; 273 break; 274 } 275 } catch (CmsException ex) { 276 // noop 277 } 278 } 279 280 } 281 282 return ret; 283 } 284 285 /** 286 * Returns the lock state for a specified resource.<p> 287 * 288 * Iterates through all configured resource wrappers till the first returns not <code>null</code>.<p> 289 * 290 * @see I_CmsResourceWrapper#getLock(CmsObject, CmsResource) 291 * @see CmsObject#getLock(CmsResource) 292 * 293 * @param resource the resource to return the lock state for 294 * 295 * @return the lock state for the specified resource 296 * 297 * @throws CmsException if something goes wrong 298 */ 299 public CmsLock getLock(CmsResource resource) throws CmsException { 300 301 CmsLock lock = null; 302 303 // iterate through all wrappers and call "getLock" till one does not return null 304 List<I_CmsResourceWrapper> wrappers = getWrappers(); 305 Iterator<I_CmsResourceWrapper> iter = wrappers.iterator(); 306 while (iter.hasNext()) { 307 I_CmsResourceWrapper wrapper = iter.next(); 308 lock = wrapper.getLock(m_cms, resource); 309 if (lock != null) { 310 break; 311 } 312 } 313 314 // delegate the call to the CmsObject 315 if (lock == null) { 316 lock = m_cms.getLock(resource); 317 } 318 319 return lock; 320 } 321 322 /** 323 * Delegate method for {@link CmsObject#getRequestContext()}.<p> 324 * 325 * @see CmsObject#getRequestContext() 326 327 * @return the current users request context 328 */ 329 public CmsRequestContext getRequestContext() { 330 331 return m_cms.getRequestContext(); 332 } 333 334 /** 335 * Returns all child resources of a resource, that is the resources 336 * contained in a folder.<p> 337 * 338 * First fetch all child resources from VFS by calling {@link CmsObject#getResourcesInFolder(String, CmsResourceFilter)}. 339 * After that all resource wrapper are called {@link I_CmsResourceWrapper#addResourcesToFolder(CmsObject, String, CmsResourceFilter)} 340 * to have the chance to add additional resources to those already existing. In that list every resource is given to 341 * the appropriate resource wrapper ({@link I_CmsResourceWrapper#wrapResource(CmsObject, CmsResource)}) to have the 342 * possibility to change the existing resources. The matching resource wrapper for a resource is found by a call to 343 * {@link I_CmsResourceWrapper#isWrappedResource(CmsObject, CmsResource)}.<p> 344 * 345 * @see I_CmsResourceWrapper#addResourcesToFolder(CmsObject, String, CmsResourceFilter) 346 * @see CmsObject#getResourcesInFolder(String, CmsResourceFilter) 347 * 348 * @param resourcename the full path of the resource to return the child resources for 349 * @param filter the resource filter to use 350 * 351 * @return a list of all child <code>{@link CmsResource}</code>s 352 * 353 * @throws CmsException if something goes wrong 354 */ 355 public List<CmsResource> getResourcesInFolder(String resourcename, CmsResourceFilter filter) throws CmsException { 356 357 List<CmsResource> list = new ArrayList<CmsResource>(); 358 359 // read children existing in the VFS 360 try { 361 list.addAll(m_cms.getResourcesInFolder(resourcename, filter)); 362 } catch (CmsException ex) { 363 //noop 364 } 365 366 // iterate through all wrappers and call "addResourcesToFolder" and add the results to the list 367 List<I_CmsResourceWrapper> wrappers = getWrappers(); 368 Iterator<I_CmsResourceWrapper> iter1 = wrappers.iterator(); 369 while (iter1.hasNext()) { 370 I_CmsResourceWrapper wrapper = iter1.next(); 371 List<CmsResource> added = wrapper.addResourcesToFolder(m_cms, resourcename, filter); 372 if (added != null) { 373 list.addAll(added); 374 } 375 } 376 377 // create a new list to add all resources 378 ArrayList<CmsResource> wrapped = new ArrayList<CmsResource>(); 379 380 // eventually wrap the found resources 381 Iterator<CmsResource> iter2 = list.iterator(); 382 while (iter2.hasNext()) { 383 CmsResource res = iter2.next(); 384 385 // correct the length of the content if an UTF-8 marker would be added later 386 if (needUtf8Marker(res) && !startsWithUtf8Marker(res)) { 387 CmsWrappedResource wrap = new CmsWrappedResource(res); 388 wrap.setLength(res.getLength() + CmsResourceWrapperUtils.UTF8_MARKER.length); 389 390 res = wrap.getResource(); 391 } 392 393 // get resource type wrapper for the resource 394 I_CmsResourceWrapper resWrapper = getResourceTypeWrapper(res); 395 396 if (resWrapper != null) { 397 398 // adds the wrapped resources 399 wrapped.add(resWrapper.wrapResource(m_cms, res)); 400 } else { 401 402 // add the resource unwrapped 403 wrapped.add(res); 404 } 405 } 406 407 // sort the wrapped list correctly 408 Collections.sort(wrapped, I_CmsResource.COMPARE_ROOT_PATH_IGNORE_CASE_FOLDERS_FIRST); 409 410 return wrapped; 411 } 412 413 /** 414 * Delegate method for {@link CmsObject#getSitePath(CmsResource)}.<p> 415 * 416 * @see CmsObject#getSitePath(org.opencms.file.CmsResource) 417 * 418 * @param resource the resource to get the adjusted site root path for 419 * 420 * @return the absolute resource path adjusted for the current site 421 */ 422 public String getSitePath(CmsResource resource) { 423 424 return m_cms.getSitePath(resource); 425 } 426 427 /** 428 * Returns the configured resource wrappers used by this instance.<p> 429 * 430 * Entries in list are from type {@link I_CmsResourceWrapper}.<p> 431 * 432 * @return the configured resource wrappers for this instance 433 */ 434 public List<I_CmsResourceWrapper> getWrappers() { 435 436 return m_wrappers; 437 } 438 439 /** 440 * Locks a resource.<p> 441 * 442 * Iterates through all configured resource wrappers till the first returns <code>true</code>.<p> 443 * 444 * @see CmsObject#lockResource(String) 445 * 446 * @param resourcename the name of the resource to lock (full path) 447 * 448 * @throws CmsException if something goes wrong 449 */ 450 public void lockResource(String resourcename) throws CmsException { 451 452 boolean exec = false; 453 454 // iterate through all wrappers and call "lockResource" till one does not return false 455 List<I_CmsResourceWrapper> wrappers = getWrappers(); 456 Iterator<I_CmsResourceWrapper> iter = wrappers.iterator(); 457 while (iter.hasNext()) { 458 I_CmsResourceWrapper wrapper = iter.next(); 459 exec = wrapper.lockResource(m_cms, resourcename, false); 460 if (exec) { 461 break; 462 } 463 } 464 465 // delegate the call to the CmsObject 466 if (!exec) { 467 m_cms.lockResource(resourcename); 468 } 469 } 470 471 /** 472 * Locks a resource temporarily.<p> 473 * 474 * @param resourceName the name of the resource to lock 475 * 476 * @throws CmsException if something goes wrong 477 */ 478 public void lockResourceTemporary(String resourceName) throws CmsException { 479 480 boolean exec = false; 481 // iterate through all wrappers and call "lockResource" till one does not return false 482 List<I_CmsResourceWrapper> wrappers = getWrappers(); 483 for (I_CmsResourceWrapper wrapper : wrappers) { 484 exec = wrapper.lockResource(m_cms, resourceName, true); 485 if (exec) { 486 break; 487 } 488 } 489 490 // delegate the call to the CmsObject 491 if (!exec) { 492 m_cms.lockResourceTemporary(resourceName); 493 } 494 } 495 496 /** 497 * Moves a resource to the given destination.<p> 498 * 499 * Iterates through all configured resource wrappers till the first returns <code>true</code>.<p> 500 * 501 * @see I_CmsResourceWrapper#moveResource(CmsObject, String, String) 502 * @see CmsObject#moveResource(String, String) 503 * 504 * @param source the name of the resource to move (full path) 505 * @param destination the destination resource name (full path) 506 * 507 * @throws CmsException if something goes wrong 508 */ 509 public void moveResource(String source, String destination) throws CmsException { 510 511 boolean exec = false; 512 513 // iterate through all wrappers and call "moveResource" till one does not return false 514 List<I_CmsResourceWrapper> wrappers = getWrappers(); 515 Iterator<I_CmsResourceWrapper> iter = wrappers.iterator(); 516 while (iter.hasNext()) { 517 I_CmsResourceWrapper wrapper = iter.next(); 518 exec = wrapper.moveResource(m_cms, source, destination); 519 if (exec) { 520 break; 521 } 522 } 523 524 // delegate the call to the CmsObject 525 if (!exec) { 526 m_cms.moveResource(source, destination); 527 } 528 } 529 530 /** 531 * Reads a file resource (including it's binary content) from the VFS, 532 * using the specified resource filter.<p> 533 * 534 * Iterates through all configured resource wrappers till the first returns not <code>null</code>.<p> 535 * 536 * If the resource contains textual content and the encoding is UTF-8, then the byte order mask 537 * for UTF-8 is added at the start of the content to make sure that a client using this content 538 * displays it correctly.<p> 539 * 540 * @see I_CmsResourceWrapper#readFile(CmsObject, String, CmsResourceFilter) 541 * @see CmsObject#readFile(String, CmsResourceFilter) 542 * 543 * @param resourcename the name of the resource to read (full path) 544 * @param filter the resource filter to use while reading 545 * 546 * @return the file resource that was read 547 * 548 * @throws CmsException if the file resource could not be read for any reason 549 */ 550 public CmsFile readFile(String resourcename, CmsResourceFilter filter) throws CmsException { 551 552 CmsFile res = null; 553 554 // iterate through all wrappers and call "readFile" till one does not return null 555 List<I_CmsResourceWrapper> wrappers = getWrappers(); 556 Iterator<I_CmsResourceWrapper> iter = wrappers.iterator(); 557 while (iter.hasNext()) { 558 I_CmsResourceWrapper wrapper = iter.next(); 559 res = wrapper.readFile(m_cms, resourcename, filter); 560 if (res != null) { 561 break; 562 } 563 } 564 565 // delegate the call to the CmsObject 566 if (res == null) { 567 res = m_cms.readFile(resourcename, filter); 568 } 569 570 // for text based resources which are encoded in UTF-8 add the UTF marker at the start 571 // of the content 572 if (needUtf8Marker(res)) { 573 574 if (LOG.isDebugEnabled()) { 575 LOG.debug(Messages.get().getBundle().key(Messages.LOG_ADD_UTF8_MARKER_1, res.getRootPath())); 576 } 577 578 res.setContents(CmsResourceWrapperUtils.addUtf8Marker(res.getContents())); 579 } 580 581 return res; 582 } 583 584 /** 585 * Reads the properties for a resource with a specific path, if it exists. 586 * 587 * @param path a VFS path 588 * @return the map of properties for the resource 589 * 590 * @throws CmsException if something goes wrong 591 */ 592 public Map<String, CmsProperty> readProperties(String path) throws CmsException { 593 594 if (m_cms.existsResource(path, CmsResourceFilter.IGNORE_EXPIRATION)) { 595 return CmsProperty.toObjectMap(m_cms.readPropertyObjects(path, false)); 596 } else { 597 return Collections.emptyMap(); 598 } 599 600 } 601 602 /** 603 * Delegate method for {@link CmsObject#readPropertyObject(CmsResource, String, boolean)}.<p> 604 * 605 * @see CmsObject#readPropertyObject(CmsResource, String, boolean) 606 * 607 * @param resource the resource where the property is attached to 608 * @param property the property name 609 * @param search if true, the property is searched on all parent folders of the resource, 610 * if it's not found attached directly to the resource 611 * 612 * @return the required property, or <code>{@link CmsProperty#getNullProperty()}</code> if the property was not found 613 * 614 * @throws CmsException if something goes wrong 615 */ 616 public CmsProperty readPropertyObject(CmsResource resource, String property, boolean search) throws CmsException { 617 618 return m_cms.readPropertyObject(resource, property, search); 619 } 620 621 /** 622 * Delegate method for {@link CmsObject#readResource(CmsUUID, CmsResourceFilter)}.<p> 623 * 624 * @see CmsObject#readResource(CmsUUID, CmsResourceFilter) 625 * 626 * @param structureID the ID of the structure to read 627 * @param filter the resource filter to use while reading 628 * 629 * @return the resource that was read 630 * 631 * @throws CmsException if the resource could not be read for any reason 632 */ 633 public CmsResource readResource(CmsUUID structureID, CmsResourceFilter filter) throws CmsException { 634 635 return m_cms.readResource(structureID, filter); 636 } 637 638 /** 639 * Reads a resource from the VFS, 640 * using the <code>{@link CmsResourceFilter#DEFAULT}</code> filter.<p> 641 * 642 * Iterates through all configured resource wrappers till the first returns not <code>null</code>.<p> 643 * 644 * @see I_CmsResourceWrapper#readResource(CmsObject, String, CmsResourceFilter) 645 * @see CmsObject#readResource(String, CmsResourceFilter) 646 * 647 * @param resourcename The name of the resource to read (full path) 648 * @param filter the resource filter to use while reading 649 * 650 * @return the resource that was read 651 * 652 * @throws CmsException if the resource could not be read for any reason 653 */ 654 public CmsResource readResource(String resourcename, CmsResourceFilter filter) throws CmsException { 655 656 CmsResource res = null; 657 658 // iterate through all wrappers and call "readResource" till one does not return null 659 List<I_CmsResourceWrapper> wrappers = getWrappers(); 660 Iterator<I_CmsResourceWrapper> iter = wrappers.iterator(); 661 while (iter.hasNext()) { 662 I_CmsResourceWrapper wrapper = iter.next(); 663 res = wrapper.readResource(m_cms, resourcename, filter); 664 if (res != null) { 665 break; 666 } 667 } 668 669 // delegate the call to the CmsObject 670 if (res == null) { 671 res = m_cms.readResource(resourcename, filter); 672 } 673 674 // correct the length of the content if an UTF-8 marker would be added later 675 if (needUtf8Marker(res) && !startsWithUtf8Marker(res)) { 676 CmsWrappedResource wrap = new CmsWrappedResource(res); 677 wrap.setLength(res.getLength() + CmsResourceWrapperUtils.UTF8_MARKER.length); 678 679 return wrap.getResource(); 680 } 681 682 return res; 683 } 684 685 /** 686 * Delegate method for {@link CmsObject#readUser(CmsUUID)}.<p> 687 * 688 * @see CmsObject#readUser(CmsUUID) 689 * 690 * @param userId the id of the user to be read 691 * 692 * @return the user with the given id 693 * 694 * @throws CmsException if something goes wrong 695 */ 696 public CmsUser readUser(CmsUUID userId) throws CmsException { 697 698 return m_cms.readUser(userId); 699 } 700 701 /** 702 * Returns a link to an existing resource in the VFS.<p> 703 * 704 * Because it is possible through the <code>CmsObjectWrapper</code> to create "virtual" resources, 705 * which can not be found in the VFS, it is necessary to change the links in pages 706 * as well, so that they point to resources which really exists in the VFS.<p> 707 * 708 * Iterates through all configured resource wrappers till the first returns not <code>null</code>.<p> 709 * 710 * @see #rewriteLink(String) 711 * @see I_CmsResourceWrapper#restoreLink(CmsObject, String) 712 * 713 * @param path the path to the resource 714 * 715 * @return the path for the resource which exists in the VFS 716 */ 717 public String restoreLink(String path) { 718 719 if ((path != null) && (path.startsWith("#"))) { 720 return path; 721 } 722 723 String ret = null; 724 725 // iterate through all wrappers and call "restoreLink" till one does not return null 726 List<I_CmsResourceWrapper> wrappers = getWrappers(); 727 Iterator<I_CmsResourceWrapper> iter = wrappers.iterator(); 728 while (iter.hasNext()) { 729 I_CmsResourceWrapper wrapper = iter.next(); 730 ret = wrapper.restoreLink(m_cms, m_cms.getRequestContext().removeSiteRoot(path)); 731 if (ret != null) { 732 return ret; 733 } 734 } 735 736 return path; 737 } 738 739 /** 740 * Returns a link to a resource after it was wrapped by the CmsObjectWrapper.<p> 741 * 742 * Because it is possible to change the names of resources inside the VFS by this 743 * <code>CmsObjectWrapper</code>, it is necessary to change the links used in pages 744 * as well, so that they point to the changed name of the resource.<p> 745 * 746 * For example: <code>/sites/default/index.html</code> becomes to 747 * <code>/sites/default/index.html.jsp</code>, because it is a jsp page, the links 748 * in pages where corrected so that they point to the new name (with extension "jsp").<p> 749 * 750 * Used for the link processing in the class {@link org.opencms.relations.CmsLink}.<p> 751 * 752 * Iterates through all configured resource wrappers till the first returns not <code>null</code>.<p> 753 * 754 * @see #restoreLink(String) 755 * @see I_CmsResourceWrapper#rewriteLink(CmsObject, CmsResource) 756 * 757 * @param path the full path where to find the resource 758 * 759 * @return the rewritten link for the resource 760 */ 761 public String rewriteLink(String path) { 762 763 CmsResource res = null; 764 765 try { 766 res = readResource(m_cms.getRequestContext().removeSiteRoot(path), CmsResourceFilter.ALL); 767 if (res != null) { 768 String ret = null; 769 770 // iterate through all wrappers and call "rewriteLink" till one does not return null 771 List<I_CmsResourceWrapper> wrappers = getWrappers(); 772 Iterator<I_CmsResourceWrapper> iter = wrappers.iterator(); 773 while (iter.hasNext()) { 774 I_CmsResourceWrapper wrapper = iter.next(); 775 ret = wrapper.rewriteLink(m_cms, res); 776 if (ret != null) { 777 return ret; 778 } 779 } 780 } 781 } catch (CmsException ex) { 782 // noop 783 } 784 785 return path; 786 } 787 788 /** 789 * Enables or disables the automatic adding of byte order marks to plaintext files.<p> 790 * 791 * @param addByteOrderMark true if byte order marks should be added to plaintext files automatically 792 */ 793 public void setAddByteOrderMark(boolean addByteOrderMark) { 794 795 m_addByteOrderMark = addByteOrderMark; 796 } 797 798 /** 799 * Unlocks a resource.<p> 800 * 801 * Iterates through all configured resource wrappers till the first returns <code>true</code>.<p> 802 * 803 * @see I_CmsResourceWrapper#unlockResource(CmsObject, String) 804 * @see CmsObject#unlockResource(String) 805 * 806 * @param resourcename the name of the resource to unlock (full path) 807 * 808 * @throws CmsException if something goes wrong 809 */ 810 public void unlockResource(String resourcename) throws CmsException { 811 812 boolean exec = false; 813 814 // iterate through all wrappers and call "lockResource" till one does not return false 815 List<I_CmsResourceWrapper> wrappers = getWrappers(); 816 Iterator<I_CmsResourceWrapper> iter = wrappers.iterator(); 817 while (iter.hasNext()) { 818 I_CmsResourceWrapper wrapper = iter.next(); 819 exec = wrapper.unlockResource(m_cms, resourcename); 820 if (exec) { 821 break; 822 } 823 } 824 825 // delegate the call to the CmsObject 826 if (!exec) { 827 m_cms.unlockResource(resourcename); 828 } 829 } 830 831 /** 832 * Writes a resource to the OpenCms VFS, including it's content.<p> 833 * 834 * Iterates through all configured resource wrappers till the first returns not <code>null</code>.<p> 835 * 836 * @see I_CmsResourceWrapper#writeFile(CmsObject, CmsFile) 837 * @see CmsObject#writeFile(CmsFile) 838 * 839 * @param resource the resource to write 840 * 841 * @return the written resource (may have been modified) 842 * 843 * @throws CmsException if something goes wrong 844 */ 845 public CmsFile writeFile(CmsFile resource) throws CmsException { 846 847 CmsFile res = null; 848 849 // remove the added UTF-8 marker 850 if (needUtf8Marker(resource)) { 851 resource.setContents(CmsResourceWrapperUtils.removeUtf8Marker(resource.getContents())); 852 } 853 854 String resourcename = m_cms.getSitePath(resource); 855 if (!m_cms.existsResource(resourcename)) { 856 857 // iterate through all wrappers and call "writeFile" till one does not return null 858 List<I_CmsResourceWrapper> wrappers = getWrappers(); 859 Iterator<I_CmsResourceWrapper> iter = wrappers.iterator(); 860 while (iter.hasNext()) { 861 I_CmsResourceWrapper wrapper = iter.next(); 862 res = wrapper.writeFile(m_cms, resource); 863 if (res != null) { 864 break; 865 } 866 } 867 868 // delegate the call to the CmsObject 869 if (res == null) { 870 res = m_cms.writeFile(resource); 871 } 872 } else { 873 res = m_cms.writeFile(resource); 874 } 875 876 return res; 877 } 878 879 /** 880 * Writes properties to the resource with the given path, if it exists 881 * 882 * @param path the path 883 * @param props the properties to write 884 * 885 * @throws CmsException if something goes wrong 886 */ 887 public void writeProperties(String path, Map<String, CmsProperty> props) throws CmsException { 888 889 if (m_cms.existsResource(path, CmsResourceFilter.IGNORE_EXPIRATION)) { 890 m_cms.writePropertyObjects(path, new ArrayList<>(props.values())); 891 } 892 893 } 894 895 /** 896 * Try to find a resource type wrapper for the resource.<p> 897 * 898 * Takes all configured resource type wrappers and ask if one of them is responsible 899 * for that resource. The first in the list which feels responsible is returned. 900 * If no wrapper could be found null will be returned.<p> 901 * 902 * @see I_CmsResourceWrapper#isWrappedResource(CmsObject, CmsResource) 903 * 904 * @param res the resource to find a resource type wrapper for 905 * 906 * @return the found resource type wrapper for the resource or null if not found 907 */ 908 private I_CmsResourceWrapper getResourceTypeWrapper(CmsResource res) { 909 910 Iterator<I_CmsResourceWrapper> iter = getWrappers().iterator(); 911 while (iter.hasNext()) { 912 I_CmsResourceWrapper wrapper = iter.next(); 913 914 if (wrapper.isWrappedResource(m_cms, res)) { 915 return wrapper; 916 } 917 } 918 919 return null; 920 } 921 922 /** 923 * Checks if the resource type needs an UTF-8 marker.<p> 924 * 925 * If the encoding of the resource is "UTF-8" and the resource 926 * type is one of the following:<br/> 927 * <ul> 928 * <li>{@link CmsResourceTypeJsp}</li> 929 * <li>{@link CmsResourceTypePlain}</li> 930 * <li>{@link CmsResourceTypeXmlContent}</li> 931 * <li>{@link CmsResourceTypeXmlPage}</li> 932 * </ul> 933 * 934 * it needs an UTF-8 marker.<p> 935 * 936 * @param res the resource to check if the content needs a UTF-8 marker 937 * 938 * @return <code>true</code> if the resource needs an UTF-8 maker otherwise <code>false</code> 939 */ 940 private boolean needUtf8Marker(CmsResource res) { 941 942 if (!m_addByteOrderMark) { 943 return false; 944 } 945 // if the encoding of the resource is not UTF-8 return false 946 String encoding = CmsLocaleManager.getResourceEncoding(m_cms, res); 947 boolean result = false; 948 if (CmsEncoder.ENCODING_UTF_8.equals(encoding)) { 949 try { 950 I_CmsResourceType resType = OpenCms.getResourceManager().getResourceType(res.getTypeId()); 951 if (resType instanceof CmsResourceTypeJsp) { 952 result = true; 953 } else if (resType instanceof CmsResourceTypePlain) { 954 result = true; 955 } else if (resType instanceof CmsResourceTypeXmlContent) { 956 result = true; 957 } else if (resType instanceof CmsResourceTypeXmlPage) { 958 result = true; 959 } 960 } catch (CmsLoaderException e) { 961 LOG.debug(e.getLocalizedMessage(), e); 962 } 963 } 964 return result; 965 } 966 967 /** 968 * Checks if the file content already contains the UTF8 marker.<p> 969 * 970 * @param res the resource to check 971 * 972 * @return <code>true</code> if the file content already contains the UTF8 marker 973 */ 974 private boolean startsWithUtf8Marker(CmsResource res) { 975 976 boolean result = false; 977 try { 978 if (res.isFile()) { 979 CmsFile file = m_cms.readFile(res); 980 if ((file.getContents().length >= 3) 981 && (file.getContents()[0] == CmsResourceWrapperUtils.UTF8_MARKER[0]) 982 && (file.getContents()[1] == CmsResourceWrapperUtils.UTF8_MARKER[1]) 983 && (file.getContents()[2] == CmsResourceWrapperUtils.UTF8_MARKER[2])) { 984 result = true; 985 } 986 } 987 } catch (CmsException e) { 988 LOG.debug(e.getLocalizedMessage(), e); 989 } 990 return result; 991 } 992}