DocumentContentService.java
/*
* Copyright (c) 2002-2023, City of Paris
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright notice
* and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice
* and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* 3. Neither the name of 'Mairie de Paris' nor 'Lutece' nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* License 1.0
*/
package fr.paris.lutece.plugins.document.service;
import fr.paris.lutece.plugins.document.business.Document;
import fr.paris.lutece.plugins.document.business.DocumentHome;
import fr.paris.lutece.plugins.document.business.DocumentType;
import fr.paris.lutece.plugins.document.business.DocumentTypeHome;
import fr.paris.lutece.plugins.document.business.portlet.DocumentListPortletHome;
import fr.paris.lutece.plugins.document.business.publication.DocumentPublication;
import fr.paris.lutece.plugins.document.service.publishing.PublishingService;
import fr.paris.lutece.plugins.document.utils.IntegerUtils;
import fr.paris.lutece.portal.business.page.Page;
import fr.paris.lutece.portal.business.page.PageHome;
import fr.paris.lutece.portal.business.portlet.AliasPortlet;
import fr.paris.lutece.portal.business.portlet.AliasPortletHome;
import fr.paris.lutece.portal.business.portlet.Portlet;
import fr.paris.lutece.portal.business.portlet.PortletHome;
import fr.paris.lutece.portal.business.resourceenhancer.ResourceEnhancer;
import fr.paris.lutece.portal.business.style.ModeHome;
import fr.paris.lutece.portal.service.cache.ICacheKeyService;
import fr.paris.lutece.portal.service.content.ContentService;
import fr.paris.lutece.portal.service.content.PageData;
import fr.paris.lutece.portal.service.html.XmlTransformerService;
import fr.paris.lutece.portal.service.message.SiteMessageException;
import fr.paris.lutece.portal.service.page.PortletCacheService;
import fr.paris.lutece.portal.service.portal.PortalService;
import fr.paris.lutece.portal.service.security.LuteceUser;
import fr.paris.lutece.portal.service.security.SecurityService;
import fr.paris.lutece.portal.service.security.UserNotSignedException;
import fr.paris.lutece.portal.service.spring.SpringContextService;
import fr.paris.lutece.portal.service.template.AppTemplateService;
import fr.paris.lutece.portal.service.util.AppLogService;
import fr.paris.lutece.portal.service.util.AppPathService;
import fr.paris.lutece.portal.service.util.AppPropertiesService;
import fr.paris.lutece.portal.web.PortalJspBean;
import fr.paris.lutece.portal.web.constants.Parameters;
import fr.paris.lutece.util.ReferenceList;
import fr.paris.lutece.util.date.DateUtil;
import fr.paris.lutece.util.html.HtmlTemplate;
import fr.paris.lutece.util.xml.XmlUtil;
import net.sf.ehcache.CacheException;
import net.sf.ehcache.Ehcache;
import net.sf.ehcache.Element;
import net.sf.ehcache.event.CacheEventListener;
import org.apache.commons.lang3.StringUtils;
import java.io.FileInputStream;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import javax.servlet.http.HttpServletRequest;
import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;
/**
*
*/
public final class DocumentContentService extends ContentService implements CacheEventListener
{
///////////////////////////////////////////////////////////////////////////////////////////////////
// Constants
private static final String CONTENT_SERVICE_NAME = "Document Content Service";
private static final String SLASH = "/";
private static final int MODE_ADMIN = 1;
private static final String CONSTANT_DEFAULT_PORTLET_DOCUMENT_LIST_XSL = "WEB-INF/xsl/normal/portlet_document_list.xsl";
private static final String DOCUMENT_STYLE_PREFIX_ID = "document-";
private static final String LOCALE_EN = "en";
private static final String LOCALE_FR = "fr";
// XML tags
private static final String XML_TAG_CONTENT = "content";
private static final String XML_TAG_SITE_LOCALE = "site_locale";
// Parameters
private static final String PARAMETER_DOCUMENT_ID = "document_id";
private static final String PARAMETER_SITE_PATH = "site-path";
private static final String PARAMETER_PUBLICATION_DATE = "publication-date";
private static final String PARAMETER_SITE_LOCALE = "site_locale";
// Markers
private static final String MARK_PUBLICATION = "publication";
private static final String MARK_DOCUMENT = "document";
private static final String MARK_PORTLET = "portlet";
private static final String MARK_CATEGORY = "categories";
private static final String MARK_DOCUMENT_ID = "document_id";
private static final String MARK_PORTLET_ID = "portlet_id";
private static final String MARK_PORTLET_ID_LIST = "portlet_id_list";
private static final String MARK_DOCUMENT_CATEGORIES_LIST = "document_categories_list";
private static final String MARK_URL_LOGIN = "url_login";
private static final String MARKER_TARGET = "target";
private static final String MARK_IS_EXTEND_INSTALLED = "isExtendInstalled";
// Templates
private static final String TEMPLATE_DOCUMENT_PAGE_DEFAULT = "/skin/plugins/document/document_content_service.html";
private static final String TEMPLATE_DOCUMENT_CATEGORIES = "/skin/plugins/document/document_categories.html";
//Properties
private static final String PROPERTY_DEFAULT_PORTLET_DOCUMENT_LIST_XSL = "document.contentService.defaultPortletDocumentListXSL";
private static final String PROPERTY_CACHE_ENABLED = "document.cache.enabled";
private static final String TARGET_TOP = "target=_top";
private static final String PROPERTY_RESOURCE_TYPE = "document";
// Performance patch
private static ConcurrentMap<String, String> _keyMemory = new ConcurrentHashMap<String, String>( );
//Portlet cache
//Should be equal to PortletCacheService.CACHE_PORTLET_PREFIX without the final semicolon
private static final String PARAMETER_PORTLET = "portlet";
private static final String PORTLET_CACHE_KEY_SUFFIX = "[documentContentService]";
private boolean _bInit;
private PortletCacheService _cachePortlets;
private ICacheKeyService _cksPortlet;
/**
* Returns the document page for a given document and a given portlet. The
* page is built from XML data or retrieved
* from the cache if it's enable and the document in it.
*
* @param request The HTTP request.
* @param nMode The current mode.
* @return The HTML code of the page as a String.
* @throws UserNotSignedException If the user is not signed
* @throws SiteMessageException occurs when a site message need to be
* displayed
*/
public String getPage( HttpServletRequest request, int nMode )
throws UserNotSignedException, SiteMessageException
{
if ( !_bInit )
{
init( );
}
String strDocumentId = request.getParameter( PARAMETER_DOCUMENT_ID );
String strPortletId = request.getParameter( Parameters.PORTLET_ID );
String strSiteLocale = request.getParameter( PARAMETER_SITE_LOCALE );
if ( ( strSiteLocale == null ) || !strSiteLocale.equalsIgnoreCase( LOCALE_EN ) )
{
strSiteLocale = LOCALE_FR;
}
String strKey = getKey( strDocumentId, strPortletId, strSiteLocale, nMode );
String strPage = (String) getFromCache( strKey );
if ( strPage == null )
{
// only one thread can evaluate the page
synchronized ( strKey )
{
// can be useful if an other thread had evaluate the page
strPage = (String) getFromCache( strKey );
// ignore CheckStyle, this double verification is useful when page cache has been created when thread is
// blocked on synchronized
if ( strPage == null )
{
AppLogService.debug( " -- Page generation " + strKey + " : doc=" + strDocumentId + " portletid=" +
strPortletId + "site_locale=" + strSiteLocale + "nMode=" + nMode );
strPage = buildPage( request, strDocumentId, strPortletId, strSiteLocale, nMode );
if ( IntegerUtils.isNumeric( strDocumentId ) )
{
int nDocumentId = IntegerUtils.convert( strDocumentId );
Document document = DocumentHome.findByPrimaryKeyWithoutBinaries( nDocumentId );
if ( ( document != null ) )
{
putInCache( strKey, strPage );
}
}
}
else
{
AppLogService.debug( "Page read from cache after synchronisation " + strKey );
}
}
}
else
{
AppLogService.debug( "Page read from cache " + strKey );
}
return strPage;
}
/**
* Initializes the service
*/
private void init( )
{
// Initialize the cache according property value.
// If the property isn't found the default is true
String strCache = AppPropertiesService.getProperty( PROPERTY_CACHE_ENABLED, "true" );
if ( strCache.equalsIgnoreCase( "true" ) )
{
initCache( getName( ) );
}
//initCache( ) by the core in PageService
_cachePortlets = SpringContextService.getBean( "portletCacheService" );
_cksPortlet = SpringContextService.getBean( "portletCacheKeyService" );
_bInit = true;
}
/**
* Build the document page
* @param request The HTTP Request
* @param strDocumentId The document ID
* @param strPortletId The portlet ID
* @param strSiteLocale the site locale code
* @param nMode The current mode
* @return
* @throws fr.paris.lutece.portal.service.security.UserNotSignedException
* @throws fr.paris.lutece.portal.service.message.SiteMessageException
*/
private String buildPage( HttpServletRequest request, String strDocumentId, String strPortletId,
String strSiteLocale, int nMode ) throws UserNotSignedException, SiteMessageException
{
int nPortletId;
int nDocumentId;
boolean bPortletExist = false;
Map<String, String> mapXslParams = new HashMap<String, String>( );
try
{
nPortletId = Integer.parseInt( strPortletId );
nDocumentId = Integer.parseInt( strDocumentId );
}
catch ( NumberFormatException nfe )
{
return PortalService.getDefaultPage( request, nMode );
}
Document document = DocumentHome.findByPrimaryKeyWithoutBinaries( nDocumentId );
if ( ( document == null ) || ( !document.isValid( ) ) )
{
return PortalService.getDefaultPage( request, nMode );
}
DocumentType type = DocumentTypeHome.findByPrimaryKey( document.getCodeDocumentType( ) );
DocumentPublication documentPublication = PublishingService.getInstance( )
.getDocumentPublication( nPortletId, nDocumentId );
Map<String, Object> model = new HashMap<String, Object>( );
if ( documentPublication != null )
{
// Check if portlet is an alias portlet
boolean bIsAlias = DocumentListPortletHome.checkIsAliasPortlet( documentPublication.getPortletId( ) );
if ( bIsAlias && ( documentPublication.getPortletId( ) != nPortletId ) )
{
AliasPortlet alias = (AliasPortlet) AliasPortletHome.findByPrimaryKey( nPortletId );
nPortletId = alias.getAliasId( );
strPortletId = Integer.toString( nPortletId );
}
if ( ( documentPublication.getPortletId( ) == nPortletId ) &&
( documentPublication.getStatus( ) == DocumentPublication.STATUS_PUBLISHED ) )
{
bPortletExist = true;
}
// The publication informations are available in Xsl (only publication date) and in template (full DocumentPublication object)
mapXslParams.put( PARAMETER_PUBLICATION_DATE,
DateUtil.getDateString( documentPublication.getDatePublishing( ), request.getLocale( ) ) );
model.put( MARK_PUBLICATION, documentPublication );
}
if ( bPortletExist )
{
// Fill a PageData structure for those elements
PageData data = new PageData( );
data.setName( document.getTitle( ) );
data.setPagePath( PortalService.getXPagePathContent( document.getTitle( ), 0, request ) );
Portlet portlet = PortletHome.findByPrimaryKey( nPortletId );
Page page = PageHome.getPage( portlet.getPageId( ) );
String strRole = page.getRole( );
if ( !strRole.equals( Page.ROLE_NONE ) && SecurityService.isAuthenticationEnable( ) )
{
LuteceUser user = SecurityService.getInstance( ).getRegisteredUser( request );
if ( ( user == null ) && ( !SecurityService.getInstance( ).isExternalAuthentication( ) ) )
{
// The user is not registered and identify itself with the Portal authentication
String strAccessControledTemplate = SecurityService.getInstance( ).getAccessControledTemplate( );
HashMap<String, Object> modelAccessControledTemplate = new HashMap<String, Object>( );
String strLoginUrl = SecurityService.getInstance( ).getLoginPageUrl( );
modelAccessControledTemplate.put( MARK_URL_LOGIN, strLoginUrl );
HtmlTemplate tAccessControled = AppTemplateService.getTemplate( strAccessControledTemplate,
request.getLocale( ), modelAccessControledTemplate );
data.setContent( tAccessControled.getHtml( ) );
return PortalService.buildPageContent( data, nMode, request );
}
if ( !SecurityService.getInstance( ).isUserInRole( request, strRole ) )
{
// The user doesn't have the correct role
String strAccessDeniedTemplate = SecurityService.getInstance( ).getAccessDeniedTemplate( );
HtmlTemplate tAccessDenied = AppTemplateService.getTemplate( strAccessDeniedTemplate,
request.getLocale( ) );
data.setContent( tAccessDenied.getHtml( ) );
return PortalService.buildPageContent( data, nMode, request );
}
}
// Get request paramaters and store them in a hashtable
Enumeration<?> enumParam = request.getParameterNames( );
Hashtable<String, String> htParamRequest = new Hashtable<String, String>( );
String paramName = "";
while ( enumParam.hasMoreElements( ) )
{
paramName = (String) enumParam.nextElement( );
htParamRequest.put( paramName, request.getParameter( paramName ) );
}
XmlTransformerService xmlTransformerService = new XmlTransformerService( );
StringBuffer strXml = new StringBuffer( );
XmlUtil.beginElement( strXml, XML_TAG_CONTENT );
XmlUtil.addElement( strXml, XML_TAG_SITE_LOCALE, strSiteLocale );
strXml.append( document.getXmlValidatedContent( ) );
XmlUtil.endElement( strXml, XML_TAG_CONTENT );
String strDocument = xmlTransformerService.transformBySourceWithXslCache( strXml.toString( ),
type.getContentServiceXslSource( ), DOCUMENT_STYLE_PREFIX_ID + type.getStyleSheetId( nMode ),
htParamRequest, null );
model.put( MARK_DOCUMENT, strDocument );
if ( !document.isSkipPortlet() )
{
model.put( MARK_PORTLET, getPortlet( request, strPortletId, nMode ) );
}
if ( !document.isSkipCategories() )
{
model.put( MARK_CATEGORY, getRelatedDocumentsPortlet( request, document, nPortletId, nMode ) );
}
model.put( MARK_DOCUMENT_ID, strDocumentId );
model.put( MARK_PORTLET_ID, strPortletId );
model.put( MARK_IS_EXTEND_INSTALLED, PortalService.isExtendActivated( ) );
// Additional page info
ResourceEnhancer.buildPageAddOn( model, PROPERTY_RESOURCE_TYPE, nDocumentId, strPortletId, request );
HtmlTemplate template = AppTemplateService.getTemplate( getTemplatePage( document ), request.getLocale( ),
model );
data.setContent( template.getHtml( ) );
return PortalService.buildPageContent( data, nMode, request );
}
//portlet does not exists
return PortalService.getDefaultPage( request, nMode );
}
/**
* Analyzes request parameters to see if the request should be handled by
* the current Content Service
*
* @param request The HTTP request
* @return true if this ContentService should handle this request
*/
public boolean isInvoked( HttpServletRequest request )
{
String strDocumentId = request.getParameter( PARAMETER_DOCUMENT_ID );
String strIdPortlet = request.getParameter( Parameters.PORTLET_ID );
if ( ( strDocumentId != null ) && ( strDocumentId.length( ) > 0 ) && ( strIdPortlet != null ) &&
( strIdPortlet.length( ) > 0 ) )
{
return true;
}
return false;
}
/**
* Returns the Content Service name
*
* @return The name as a String
*/
public String getName( )
{
return CONTENT_SERVICE_NAME;
}
/**
* Return the template page
* @param document
* @return the template
*/
private String getTemplatePage( Document document )
{
if ( document.getPageTemplateDocumentId( ) != 0 )
{
String strPageTemplateDocument = DocumentHome.getPageTemplateDocumentPath( document.getPageTemplateDocumentId( ) );
return strPageTemplateDocument;
}
else
{
return TEMPLATE_DOCUMENT_PAGE_DEFAULT;
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////
// Comments implementation
/**
* Gets the documents list portlet containing the document
*
* @param strPortletId The ID of the documents list portlet where the
* document has been published.
* @param nMode The current mode.
* @param request The Http request
* @return The HTML code of the documents list portlet as a String
* @throws SiteMessageException IF a message need to be displayed
*/
private String getPortlet( HttpServletRequest request, String strPortletId, int nMode )
throws SiteMessageException
{
try
{
int nPortletId = Integer.parseInt( strPortletId );
Portlet portlet = PortletHome.findByPrimaryKey( nPortletId );
// Selection of the XSL stylesheet
// byte[] baXslSource = portlet.getXslSource( nMode );
//FIXME Temporary solution (see LUTECE-824)
String strFilePath = AppPropertiesService.getProperty( PROPERTY_DEFAULT_PORTLET_DOCUMENT_LIST_XSL,
CONSTANT_DEFAULT_PORTLET_DOCUMENT_LIST_XSL );
if ( strFilePath == null )
{
return StringUtils.EMPTY;
}
if ( !strFilePath.startsWith( SLASH ) )
{
strFilePath = SLASH + strFilePath;
}
String strFileName = strFilePath.substring( strFilePath.lastIndexOf( SLASH ) + 1 );
strFilePath = strFilePath.substring( 0, strFilePath.lastIndexOf( SLASH ) + 1 );
FileInputStream fis = AppPathService.getResourceAsStream( strFilePath, strFileName );
Source xslSource = new StreamSource( fis );
// Get request paramaters and store them in a hashtable
Enumeration<?> enumParam = request.getParameterNames( );
Hashtable<String, String> htParamRequest = new Hashtable<String, String>( );
String paramName = "";
while ( enumParam.hasMoreElements( ) )
{
paramName = (String) enumParam.nextElement( );
htParamRequest.put( paramName, request.getParameter( paramName ) );
}
Properties outputProperties = ModeHome.getOuputXslProperties( nMode );
// Add a path param for choose url to use in admin or normal mode
if ( nMode != MODE_ADMIN )
{
htParamRequest.put( PARAMETER_SITE_PATH, AppPathService.getPortalUrl( ) );
}
else
{
htParamRequest.put( PARAMETER_SITE_PATH, AppPathService.getAdminPortalUrl( ) );
htParamRequest.put( MARKER_TARGET, TARGET_TOP );
}
if ( _cachePortlets.isCacheEnable( ) )
{
LuteceUser user = null;
if ( SecurityService.isAuthenticationEnable( ) )
{
user = SecurityService.getInstance( ).getRegisteredUser( request );
}
boolean bCanBeCached = ( user != null ) ? ( portlet.canBeCachedForConnectedUsers( ) )
: ( portlet.canBeCachedForAnonymousUsers( ) );
if ( bCanBeCached )
{
//To delete keys when portlets are modified through _cachePortlets implementing PortletEventListener
htParamRequest.put( PARAMETER_PORTLET, String.valueOf( portlet.getId( ) ) );
//Add [documentContentService] to not clash with PageService keys because we don't synchronize
String strKey = getKey( _cksPortlet.getKey( htParamRequest, nMode, user ) +
PORTLET_CACHE_KEY_SUFFIX );
// get portlet from cache
String strPortlet = (String) _cachePortlets.getFromCache( strKey );
if ( strPortlet == null )
{
// only one thread can evaluate the page
synchronized ( strKey )
{
// can be useful if an other thread had evaluate the
// porlet
strPortlet = (String) _cachePortlets.getFromCache( strKey );
// ignore checkstyle, this double verification is useful
// when page cache has been created when thread is
// blocked on synchronized
if ( strPortlet == null )
{
String strXml = portlet.getXmlDocument( request );
XmlTransformerService xmlTransformerService = new XmlTransformerService( );
String strXslUniquePrefix = DOCUMENT_STYLE_PREFIX_ID + strFilePath + strFileName;
strPortlet = xmlTransformerService.transformBySourceWithXslCache( strXml, xslSource,
strXslUniquePrefix, htParamRequest, outputProperties );
_cachePortlets.putInCache( strKey, strPortlet );
}
}
}
return strPortlet;
}
}
String strXml = portlet.getXmlDocument( request );
XmlTransformerService xmlTransformerService = new XmlTransformerService( );
String strXslUniquePrefix = DOCUMENT_STYLE_PREFIX_ID + strFilePath + strFileName;
return xmlTransformerService.transformBySourceWithXslCache( strXml, xslSource, strXslUniquePrefix,
htParamRequest, outputProperties );
}
catch ( NumberFormatException e )
{
return null;
}
}
/**
* Gets the category list portlet linked with the document
*
* @param request The Http request
* @param document The document
* @param nPortletId The ID of the documents list portlet where the document
* has been published.
* @param nMode The current mode.
* @return The HTML code of the categories list portlet as a String
*/
private String getRelatedDocumentsPortlet( HttpServletRequest request, Document document, int nPortletId, int nMode )
{
if ( ( nMode != MODE_ADMIN ) && ( document.getCategories( ) != null ) &&
( document.getCategories( ).size( ) > 0 ) )
{
HashMap<String, Object> model = new HashMap<String, Object>( );
List<Document> listRelatedDocument = DocumentHome.findByRelatedCategories( document, request.getLocale( ) );
List<Document> listDocument = new ArrayList<Document>( );
ReferenceList listDocumentPortlet = new ReferenceList( );
// Create list of related documents from the specified categories of input document
for ( Document relatedDocument : listRelatedDocument )
{
// Get list of portlets for each document
for ( Portlet portlet : PublishingService.getInstance( )
.getPortletsByDocumentId( Integer.toString(
relatedDocument.getId( ) ) ) )
{
// Check if document and portlet are published and document is not the input document
if ( ( PublishingService.getInstance( ).isPublished( relatedDocument.getId( ), portlet.getId( ) ) ) &&
( portlet.getStatus( ) == Portlet.STATUS_PUBLISHED ) && ( relatedDocument.isValid( ) ) &&
( relatedDocument.getId( ) != document.getId( ) ) )
{
listDocumentPortlet.addItem( Integer.toString( relatedDocument.getId( ) ),
Integer.toString( portlet.getId( ) ) );
listDocument.add( relatedDocument );
break;
}
}
}
model.put( MARK_DOCUMENT_CATEGORIES_LIST, listDocument );
model.put( MARK_PORTLET_ID_LIST, listDocumentPortlet );
HtmlTemplate template = AppTemplateService.getTemplate( TEMPLATE_DOCUMENT_CATEGORIES,
request.getLocale( ), model );
return template.getHtml( );
}
else
{
return StringUtils.EMPTY;
}
}
/**
* @see net.sf.ehcache.event.CacheEventListener#dispose()
*/
public void dispose( )
{
}
/**
* @see net.sf.ehcache.event.CacheEventListener#notifyElementEvicted(net.sf.ehcache.Ehcache,
* net.sf.ehcache.Element)
*/
public void notifyElementEvicted( Ehcache cache, Element element )
{
_keyMemory.remove( element.getKey( ) );
}
/**
* @see net.sf.ehcache.event.CacheEventListener#notifyElementExpired(net.sf.ehcache.Ehcache,
* net.sf.ehcache.Element)
*/
public void notifyElementExpired( Ehcache cache, Element element )
{
_keyMemory.remove( element.getKey( ) );
}
/**
* @see net.sf.ehcache.event.CacheEventListener#notifyElementPut(net.sf.ehcache.Ehcache,
* net.sf.ehcache.Element)
*/
public void notifyElementPut( Ehcache cache, Element element )
throws CacheException
{
}
/**
* @see net.sf.ehcache.event.CacheEventListener#notifyElementRemoved(net.sf.ehcache.Ehcache,
* net.sf.ehcache.Element)
*/
public void notifyElementRemoved( Ehcache cache, Element element )
throws CacheException
{
_keyMemory.remove( element.getKey( ) );
}
/**
* @see net.sf.ehcache.event.CacheEventListener#notifyElementUpdated(net.sf.ehcache.Ehcache,
* net.sf.ehcache.Element)
*/
public void notifyElementUpdated( Ehcache cache, Element element )
throws CacheException
{
}
/**
* @see net.sf.ehcache.event.CacheEventListener#notifyRemoveAll(net.sf.ehcache.Ehcache)
*/
public void notifyRemoveAll( Ehcache cache )
{
_keyMemory.clear( );
}
/**
* Build the Cache HashMap key for pages Goal is to be able to have a
* synchronized on the key but a synchronize only work with strict
* reference. So we manage an hashmap of string reference for cache keys to
* be able to get them back.
*
* @param strDocumentId The id of the document
* @param strPortletId The id of the portlet of the document
* @param strSiteLocale The site locale
* @param nMode The mode
* @return The HashMap key for articles pages as a String.
*/
private String getKey( String strDocumentId, String strPortletId, String strSiteLocale, int nMode )
{
String key = "D" + strDocumentId + "P" + strPortletId + "L" + strSiteLocale + "M" + nMode;
String keyInMemory = _keyMemory.putIfAbsent( key, key );
if ( keyInMemory != null )
{
return keyInMemory;
}
return key;
}
/**
* Same as above, for portlet keys. Use the same hashmap because they don't collide
* @param strDocumentId The id of the document
* @return The HashMap key for portlet as a String.
*/
private String getKey( String strPortletKey )
{
String keyInMemory = _keyMemory.putIfAbsent( strPortletKey, strPortletKey );
if ( keyInMemory != null )
{
return keyInMemory;
}
return strPortletKey;
}
/**
* @see java.lang.Object#clone()
*/
@Override
public Object clone( )
{
return new DocumentContentService( );
}
/**
* Remove a document from the cache
* @param strDocumentId the document id
* @param strPortletId the portlet id
*/
public void removeFromCache( String strDocumentId, String strPortletId )
{
if ( getCache( ) != null )
{
String strKey = getKey( strDocumentId, strPortletId, LOCALE_FR, PortalJspBean.MODE_HTML );
getCache( ).remove( strKey );
strKey = getKey( strDocumentId, strPortletId, LOCALE_EN, PortalJspBean.MODE_HTML );
getCache( ).remove( strKey );
}
}
}