/*!
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License, version 2.1 as published by the Free Software
* Foundation.
*
* You should have received a copy of the GNU Lesser General Public License along with this
* program; if not, you can obtain a copy at http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html
* or from the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
*
* Copyright (c) 2002-2016 Pentaho Corporation.. All rights reserved.
*/
package org.pentaho.platform.dataaccess.metadata.service;
import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.json.JSONException;
import org.pentaho.commons.connection.IPentahoResultSet;
import org.pentaho.commons.connection.marshal.MarshallableResultSet;
import org.pentaho.metadata.model.Domain;
import org.pentaho.metadata.model.LogicalModel;
import org.pentaho.metadata.query.model.util.QueryXmlHelper;
import org.pentaho.metadata.repository.IMetadataDomainRepository;
import org.pentaho.platform.api.engine.ILogger;
import org.pentaho.platform.dataaccess.datasource.utils.DataAccessPermissionUtil;
import org.pentaho.platform.dataaccess.metadata.messages.Messages;
import org.pentaho.platform.dataaccess.metadata.model.impl.Model;
import org.pentaho.platform.dataaccess.metadata.model.impl.ModelInfo;
import org.pentaho.platform.dataaccess.metadata.model.impl.ModelInfoComparator;
import org.pentaho.platform.dataaccess.metadata.model.impl.Query;
import org.pentaho.platform.engine.core.system.PentahoBase;
import org.pentaho.platform.engine.core.system.PentahoSessionHolder;
import org.pentaho.platform.engine.core.system.PentahoSystem;
import org.pentaho.platform.plugin.action.pentahometadata.MetadataQueryComponent;
import org.pentaho.platform.util.messages.LocaleHelper;
import org.pentaho.pms.core.exception.PentahoMetadataException;
import flexjson.JSONSerializer;
/**
* An object that makes lightweight, serializable metadata models available to callers, and allow queries to be
* executed. All objects are simple POJOs. This object can be used as a Axis web service.
*
* @author jamesdixon
*/
@Path( "/data-access/api/metadataDA" )
public class MetadataService extends PentahoBase {
private static final long serialVersionUID = 8481450224870463494L;
private Log logger = LogFactory.getLog( MetadataService.class );
public MetadataService() {
setLoggingLevel( ILogger.ERROR );
}
/**
* Returns a string that indicates whether the current user has access to edit or view metadata models
*
* @return
*/
@GET
@Path( "/getDatasourcePermissions" )
@Produces( { APPLICATION_JSON } )
public String getDatasourcePermissions() {
boolean canEdit = hasManageAccess();
boolean canView = hasViewAccess();
if ( canEdit ) {
return "EDIT"; //$NON-NLS-1$
} else if ( canView ) {
return "VIEW"; //$NON-NLS-1$
}
return "NONE"; //$NON-NLS-1$
}
/**
* Returns a list of the available business models
*
* @param domainName optional domain to limit the results
* @return list of ModelInfo objects representing the available models
* @throws IOException
*/
public ModelInfo[] listBusinessModels( String domainName ) throws IOException {
List<ModelInfo> models = new ArrayList<ModelInfo>();
// get hold of the metadata repository
IMetadataDomainRepository repo = getMetadataRepository();
if ( repo == null ) {
error( Messages.getErrorString( "MetadataService.ERROR_0001_BAD_REPO" ) ); //$NON-NLS-1$
return null;
}
try {
if ( domainName == null ) {
// if no domain has been specified, loop over all of them
for ( String domain : getMetadataRepository().getDomainIds() ) {
getModelInfos( domain, models );
}
} else {
// get the models for the specified domain
getModelInfos( domainName, models );
}
} catch ( Throwable t ) {
error( Messages.getErrorString( "MetadataService.ERROR_0002_BAD_MODEL_LIST" ), t ); //$NON-NLS-1$
}
Collections.sort( models, new ModelInfoComparator() );
return models.toArray( new ModelInfo[ models.size() ] );
}
/**
* Returns a JSON list of the available business models
*
* @param domainName optional domain to limit the results
* @return JSON string of list of ModelInfo objects representing the available models
* @throws IOException
*/
public String listBusinessModelsJson( String domainName ) throws IOException {
ModelInfo[] models = listBusinessModels( domainName );
JSONSerializer serializer = new JSONSerializer();
String json = serializer.deepSerialize( models );
return json;
}
/**
* Returns a list of ModelInfo objects for the specified domain. These objects are small and this list is intended to
* allow a client to provide a list of models to a user so the user can pick which one they want to work with.
*
* @param domain
* @param models
*/
private void getModelInfos( final String domain, List<ModelInfo> models ) {
IMetadataDomainRepository repo = getMetadataRepository();
Domain domainObject = repo.getDomain( domain );
// find the best locale
String locale = LocaleHelper.getClosestLocale( LocaleHelper.getLocale().toString(), domainObject.getLocaleCodes() );
// iterate over all of the models in this domain
for ( LogicalModel model : domainObject.getLogicalModels() ) {
// create a new ModelInfo object and give it the envelope information about the model
ModelInfo modelInfo = new ModelInfo();
modelInfo.setDomainId( domain );
modelInfo.setModelId( model.getId() );
modelInfo.setModelName( model.getName( locale ) );
if ( model.getDescription() != null ) {
String modelDescription = model.getDescription( locale );
modelInfo.setModelDescription( modelDescription );
}
models.add( modelInfo );
}
return;
}
/**
* Returns a Model object for the requested model. The model will include the basic metadata - categories and
* columns.
*
* @param domainId
* @param modelId
* @return
*/
public Model loadModel( String domainId, String modelId ) {
if ( domainId == null ) {
// we can't do this without a model
error( Messages.getErrorString( "MetadataService.ERROR_0003_NULL_DOMAIN" ) ); //$NON-NLS-1$
return null;
}
if ( modelId == null ) {
// we can't do this without a model
error( Messages.getErrorString( "MetadataService.ERROR_0004_NULL_Model" ) ); //$NON-NLS-1$
return null;
}
// because it's lighter weight, check the thin model
Domain domain = getMetadataRepository().getDomain( domainId );
if ( domain == null ) {
error( Messages.getErrorString( "MetadataService.ERROR_0005_DOMAIN_NOT_FOUND", domainId ) ); //$NON-NLS-1$
return null;
}
LogicalModel model = domain.findLogicalModel( modelId );
if ( model == null ) {
// the model cannot be found or cannot be loaded
error( Messages.getErrorString( "MetadataService.ERROR_0006_MODEL_NOT_FOUND", modelId ) ); //$NON-NLS-1$
return null;
}
// create the thin metadata model and return it
MetadataServiceUtil util = getMetadataServiceUtil();
util.setDomain( domain );
Model thinModel = util.createThinModel( model, domainId );
return thinModel;
}
/**
* Returns a JSON Model object for the requested model. The model will include the basic metadata - categories and
* columns.
*
* @param domainId
* @param modelId
* @return JSON string of the model
*/
public String loadModelJson( String domainId, String modelId ) {
Model model = loadModel( domainId, modelId );
JSONSerializer serializer = new JSONSerializer();
String json = serializer.deepSerialize( model );
return json;
}
/**
* Executes a query model and returns a serializable result set
*
* @param query
* @param rowLimit An optional row limit, -1 or null means all rows
* @return
*/
public MarshallableResultSet doQuery( Query query, Integer rowLimit ) {
MetadataServiceUtil util = getMetadataServiceUtil();
org.pentaho.metadata.query.model.Query fullQuery = util.convertQuery( query );
QueryXmlHelper helper = new QueryXmlHelper();
String xml = helper.toXML( fullQuery );
return doXmlQuery( xml, rowLimit );
}
/**
* Executes a XML query and returns a serializable result set
*
* @param rowLimit An optional row limit, -1 or null means all rows
* @return
*/
public MarshallableResultSet doXmlQuery( String xml, Integer rowLimit ) {
IPentahoResultSet resultSet = executeQuery( xml, rowLimit );
if ( resultSet == null ) {
return null;
}
MarshallableResultSet result = getMarshallableResultSet();
result.setResultSet( resultSet );
return result;
}
/**
* Executes a XML query and returns a JSON serialization of the result set
*
* @param rowLimit
* @return
*/
public String doXmlQueryToJson( String xml, int rowLimit ) {
MarshallableResultSet resultSet = doXmlQuery( xml, rowLimit );
if ( resultSet == null ) {
return null;
}
JSONSerializer serializer = new JSONSerializer();
String json = serializer.deepSerialize( resultSet );
return json;
}
/**
* Executes a XML query and returns a CDA compatible JSON serialization of the result set
*
* @param rowLimit
* @return
*/
public String doXmlQueryToCdaJson( String xml, int rowLimit ) {
IPentahoResultSet resultSet = executeQuery( xml, rowLimit );
if ( resultSet == null ) {
return null;
}
String json = null;
try {
MetadataServiceUtil util = getMetadataServiceUtil();
Domain domain = util.getDomainObject( xml );
util.setDomain( domain );
String locale = LocaleHelper.getClosestLocale( LocaleHelper.getLocale().toString(), domain.getLocaleCodes() );
json = util.createCdaJson( resultSet, locale );
} catch ( JSONException e ) {
error( Messages.getErrorString( "MetadataService.ERROR_0007_JSON_ERROR" ), e ); //$NON-NLS-1$
} catch ( PentahoMetadataException e ) {
error( Messages.getErrorString( "MetadataService.ERROR_0007_BAD_QUERY_DOMAIN" ), e ); //$NON-NLS-1$
}
return json;
}
/**
* Executes a XML query and returns a serializable result set
*
* @param rowLimit An optional row limit, -1 or null means all rows
* @return
*/
public MarshallableResultSet doJsonQuery( String json, Integer rowLimit ) {
// return the results
return doXmlQuery( getQueryXmlFromJson( json ), rowLimit );
}
/**
* Executes a XML query and returns a JSON serialization of the result set
*
* @param rowLimit
* @return
*/
public String doJsonQueryToJson( String json, int rowLimit ) {
// return the results
return doXmlQueryToJson( getQueryXmlFromJson( json ), rowLimit );
}
/**
* Executes a XML query and returns a CDA compatible JSON serialization of the result set
*
* @param rowLimit
* @return
*/
public String doJsonQueryToCdaJson( String json, int rowLimit ) {
// return the results
return doXmlQueryToCdaJson( getQueryXmlFromJson( json ), rowLimit );
}
/**
* Executes a XML query and returns a native result set
*
* @param query
* @param rowLimit An optional row limit, -1 or null means all rows
* @return
*/
protected IPentahoResultSet executeQuery( String query, Integer rowLimit ) {
// create a component to execute the query
MetadataQueryComponent dataComponent = new MetadataQueryComponent();
dataComponent.setQuery( query );
dataComponent.setLive( false );
dataComponent.setUseForwardOnlyResultSet( true );
if ( rowLimit != null && rowLimit > -1 ) {
// set the row limit
dataComponent.setMaxRows( rowLimit );
}
if ( dataComponent.execute() ) {
return dataComponent.getResultSet();
}
return null;
}
/**
* Converts a JSON query into a full Query object by going via a thin Query object
*
* @param json
* @return
*/
protected String getQueryXmlFromJson( String json ) {
MetadataServiceUtil util = getMetadataServiceUtil();
Query query = util.deserializeJsonQuery( json );
try {
// convert the thin query model into a full one
org.pentaho.metadata.query.model.Query fullQuery = util.convertQuery( query );
// get the XML for the query
QueryXmlHelper helper = new QueryXmlHelper();
String xml = helper.toXML( fullQuery );
return xml;
} catch ( Exception e ) {
error( Messages.getErrorString( "MetadataService.ERROR_0008_BAD_QUERY" ), e ); //$NON-NLS-1$
}
return null;
}
/**
* Returns a instance of the IMetadataDomainRepository for the current session
*
* @return
*/
protected IMetadataDomainRepository getMetadataRepository() {
IMetadataDomainRepository mdr =
PentahoSystem.get( IMetadataDomainRepository.class, PentahoSessionHolder.getSession() );
if ( mdr instanceof ILogger ) {
( (ILogger) mdr ).setLoggingLevel( getLoggingLevel() );
}
return mdr;
}
@Override
public Log getLogger() {
return logger;
}
protected boolean hasManageAccess() {
return DataAccessPermissionUtil.hasManageAccess();
}
protected boolean hasViewAccess() {
return DataAccessPermissionUtil.hasViewAccess();
}
protected MetadataServiceUtil getMetadataServiceUtil() {
return new MetadataServiceUtil();
}
protected MarshallableResultSet getMarshallableResultSet() {
return new MarshallableResultSet();
}
}