/* * 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) 2011 Pentaho Corporation. All rights reserved. * * Created Jan, 2011 * @author jdixon */ package org.pentaho.platform.dataaccess.metadata.service; import java.io.IOException; import java.lang.reflect.Constructor; import java.util.ArrayList; import java.util.Collections; import java.util.List; 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.api.engine.IPluginResourceLoader; import org.pentaho.platform.dataaccess.datasource.wizard.service.impl.IDataAccessPermissionHandler; import org.pentaho.platform.dataaccess.datasource.wizard.service.impl.IDataAccessViewPermissionHandler; 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 * */ public class MetadataService extends PentahoBase { private static final long serialVersionUID = 8481450224870463494L; private Log logger = LogFactory.getLog(MetadataService.class); public MetadataService() { setLoggingLevel(ILogger.INFO); } /** * Returns a string that indicates whether the current user has access to * edit or view metadata models * @return */ public String getDatasourcePermissions() { String dataAccessClassName = null; IDataAccessPermissionHandler dataAccessPermHandler = null; IDataAccessViewPermissionHandler dataAccessViewPermHandler = null; try { IPluginResourceLoader resLoader = PentahoSystem.get(IPluginResourceLoader.class, null); dataAccessClassName = resLoader .getPluginSetting( getClass(), "settings/data-access-permission-handler", "org.pentaho.platform.dataaccess.datasource.wizard.service.impl.SimpleDataAccessPermissionHandler"); //$NON-NLS-1$ //$NON-NLS-2$ Class<?> clazz = Class.forName(dataAccessClassName); Constructor<?> defaultConstructor = clazz.getConstructor(new Class[] {}); dataAccessPermHandler = (IDataAccessPermissionHandler) defaultConstructor.newInstance(); } catch (Exception e) { logger.error(Messages.getErrorString("DatasourceServiceImpl.ERROR_0007_DATAACCESS_PERMISSIONS_INIT_ERROR"), e); //$NON-NLS-1$ } String dataAccessViewClassName = null; try { IPluginResourceLoader resLoader = PentahoSystem.get(IPluginResourceLoader.class, null); dataAccessViewClassName = resLoader .getPluginSetting( getClass(), "settings/data-access-view-permission-handler", "org.pentaho.platform.dataaccess.datasource.wizard.service.impl.SimpleDataAccessViewPermissionHandler"); //$NON-NLS-1$ //$NON-NLS-2$ Class<?> clazz = Class.forName(dataAccessViewClassName); Constructor<?> defaultConstructor = clazz.getConstructor(new Class[] {}); dataAccessViewPermHandler = (IDataAccessViewPermissionHandler) defaultConstructor.newInstance(); } catch (Exception e) { logger.error( Messages.getErrorString("DatasourceServiceImpl.ERROR_0030_DATAACCESS_VIEW_PERMISSIONS_INIT_ERROR"), e); //$NON-NLS-1$ } boolean canEdit = dataAccessPermHandler.hasDataAccessPermission(PentahoSessionHolder.getSession()); boolean canView = dataAccessViewPermHandler.hasDataAccessViewPermission(PentahoSessionHolder.getSession()); 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 = new MetadataServiceUtil(); 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 = new MetadataServiceUtil(); 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 query * @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 = new MarshallableResultSet(); result.setResultSet(resultSet); return result; } /** * Executes a XML query and returns a JSON serialization of the result set * @param query * @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 query * @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 = new MetadataServiceUtil(); 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 query * @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 query * @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 query * @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 = new MetadataServiceUtil(); 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; } }