/*
* Copyright 2000-2013 Enonic AS
* http://www.enonic.com/license
*/
package com.enonic.cms.core.portal.datasource.executor;
import org.jdom.Document;
import org.jdom.Element;
import com.google.common.base.Strings;
import com.enonic.cms.framework.xml.XMLDocument;
import com.enonic.cms.framework.xml.XMLDocumentFactory;
import com.enonic.cms.core.portal.datasource.DataSourceException;
import com.enonic.cms.core.portal.datasource.context.DataSourcesContextXmlCreator;
import com.enonic.cms.core.portal.datasource.el.ExpressionContext;
import com.enonic.cms.core.portal.datasource.el.ExpressionFunctionsExecutor;
import com.enonic.cms.core.portal.datasource.handler.DataSourceRequest;
import com.enonic.cms.core.portal.datasource.xml.DataSourceElement;
import com.enonic.cms.core.portal.datasource.xml.DataSourcesElement;
import com.enonic.cms.core.portal.livetrace.DatasourceExecutionTrace;
import com.enonic.cms.core.portal.livetrace.DatasourceExecutionTracer;
import com.enonic.cms.core.portal.livetrace.LivePortalTraceService;
import com.enonic.cms.core.portal.rendering.tracing.DataTraceInfo;
import com.enonic.cms.core.portal.rendering.tracing.RenderTrace;
final class DataSourceExecutorImpl
implements DataSourceExecutor
{
private final DataSourceExecutorContext context;
private DataSourcesContextXmlCreator datasourcesContextXmlCreator;
private LivePortalTraceService livePortalTraceService;
private String defaultResultRootElementName;
private final ExpressionFunctionsExecutor expressionFunctionsExecutor;
private DataSourceInvoker invoker;
public DataSourceExecutorImpl( final DataSourceExecutorContext context )
{
this.context = context;
final ExpressionContext expressionFunctionsContext = new ExpressionContext();
expressionFunctionsContext.setSite( context.getSite() );
expressionFunctionsContext.setMenuItem( context.getMenuItem() );
expressionFunctionsContext.setContentFromRequest( context.getContentFromRequest() );
expressionFunctionsContext.setUser( context.getUser() );
expressionFunctionsContext.setPortalInstanceKey( context.getPortalInstanceKey() );
expressionFunctionsContext.setLocale( context.getLocale() );
expressionFunctionsContext.setDeviceClass( context.getDeviceClass() );
expressionFunctionsContext.setPortletWindowRenderedInline( context.isPortletWindowRenderedInline() );
this.expressionFunctionsExecutor = new ExpressionFunctionsExecutor();
this.expressionFunctionsExecutor.setExpressionContext( expressionFunctionsContext );
this.expressionFunctionsExecutor.setHttpRequest( context.getHttpRequest() );
this.expressionFunctionsExecutor.setRequestParameters( context.getRequestParameters() );
this.expressionFunctionsExecutor.setSiteProperties( context.getSiteProperties() );
this.expressionFunctionsExecutor.setRootProperties( context.getRootProperties() );
this.expressionFunctionsExecutor.setVerticalSession( context.getVerticalSession() );
}
public XMLDocument execute( final DataSourcesElement element )
{
final String rootName = resolveResultRootElementName( element );
final DataSourceResultBuilder result = new DataSourceResultBuilder( rootName );
executeContext( result );
for ( final DataSourceElement ds : element.getList() )
{
executeDataSource( result, ds );
}
final Document resultDoc = new Document( result.getRootElement() );
setTraceDataSourceResult( resultDoc );
return XMLDocumentFactory.create( resultDoc );
}
private void executeContext( final DataSourceResultBuilder result )
{
final Element contextElem = this.datasourcesContextXmlCreator.createContextElement( this.context );
result.addElement( contextElem );
}
private void executeDataSource( final DataSourceResultBuilder result, final DataSourceElement ds )
{
final DatasourceExecutionTrace trace =
DatasourceExecutionTracer.startTracing( this.context.getDataSourceType(), ds.getName(), this.livePortalTraceService );
try
{
DatasourceExecutionTracer.traceRunnableCondition( trace, ds.getCondition() );
boolean runnableByCondition = isRunnableByCondition( ds );
DatasourceExecutionTracer.traceIsExecuted( trace, runnableByCondition );
if ( runnableByCondition )
{
doExecuteDataSource( result, ds, trace );
}
}
finally
{
DatasourceExecutionTracer.stopTracing( trace, livePortalTraceService );
}
}
protected boolean isRunnableByCondition( final DataSourceElement dataSource )
{
final String condition = dataSource.getCondition();
if ( Strings.isNullOrEmpty( condition ) )
{
return true;
}
try
{
final String result = this.expressionFunctionsExecutor.evaluate( condition );
return "true".equals( result );
}
catch ( final Exception e )
{
throw new DataSourceException( "Failed to evaluate expression for [{0}]", dataSource.getName() ).withCause( e );
}
}
private String resolveResultRootElementName( final DataSourcesElement dataSources )
{
final String name = dataSources.getResultElement();
if ( !Strings.isNullOrEmpty( name ) )
{
return name;
}
return this.defaultResultRootElementName;
}
private void setTraceDataSourceResult( final Document doc )
{
final DataTraceInfo info = RenderTrace.getCurrentDataTraceInfo();
if ( info != null )
{
info.setDataSourceResult( XMLDocumentFactory.create( (Document) doc.clone() ) );
}
}
public void setDataSourcesContextXmlCreator( final DataSourcesContextXmlCreator datasourcesContextXmlCreator )
{
this.datasourcesContextXmlCreator = datasourcesContextXmlCreator;
}
public void setLivePortalTraceService( final LivePortalTraceService livePortalTraceService )
{
this.livePortalTraceService = livePortalTraceService;
}
public void setDefaultResultRootElementName( final String value )
{
this.defaultResultRootElementName = value;
}
public void setInvoker( final DataSourceInvoker invoker )
{
this.invoker = invoker;
}
private void doExecuteDataSource( final DataSourceResultBuilder result, final DataSourceElement element,
final DatasourceExecutionTrace trace )
{
final DataSourceRequestFactory factory = new DataSourceRequestFactory( this.expressionFunctionsExecutor, this.context );
final DataSourceRequest request = factory.createRequest( element );
final Document doc = doExecuteDataSource( request, trace );
final String groupName = Strings.emptyToNull( element.getResultElement() );
final Element resultElement = (Element) doc.getRootElement().clone();
result.addElementToGroup( groupName, resultElement );
}
private Document doExecuteDataSource( final DataSourceRequest request, final DatasourceExecutionTrace trace )
{
DatasourceExecutionTracer.traceMethodCall( request, trace );
RenderTrace.enterFunction( request.getName() );
try
{
return doExecuteDataSource( request, this.context.getInvocationCache() );
}
finally
{
RenderTrace.exitFunction();
}
}
private Document doExecuteDataSource( final DataSourceRequest request, final DataSourceInvocationCache cache )
{
Document result = null;
if ( request.isCache() )
{
result = cache.get( request );
}
if ( result != null )
{
DatasourceExecutionTracer.traceIsCacheUsed( true, this.livePortalTraceService );
return result;
}
result = this.invoker.execute( request );
if ( request.isCache() )
{
cache.put( request, result );
}
return result;
}
}