/* * This program is free software; you can redistribute it and/or modify it under the * terms of the GNU General Public License, version 2 as published by the Free Software * Foundation. * * You should have received a copy of the GNU General Public License along with this * program; if not, you can obtain a copy at http://www.gnu.org/licenses/gpl-2.0.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 General Public License for more details. * * Copyright 2007 - 2008 Pentaho Corporation. All rights reserved. * */ package org.pentaho.platform.plugin.services.metadata; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.util.List; import java.util.Set; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.dom4j.Document; import org.dom4j.Element; import org.pentaho.metadata.model.Domain; import org.pentaho.metadata.repository.DomainAlreadyExistsException; import org.pentaho.metadata.repository.DomainIdNullException; import org.pentaho.metadata.repository.DomainStorageException; import org.pentaho.metadata.repository.FileBasedMetadataDomainRepository; import org.pentaho.metadata.util.XmiParser; import org.pentaho.platform.api.engine.IAclHolder; import org.pentaho.platform.api.engine.IPentahoSession; import org.pentaho.platform.api.engine.ISolutionFile; import org.pentaho.platform.api.engine.ISolutionFilter; import org.pentaho.platform.api.repository.ISolutionRepository; import org.pentaho.platform.engine.core.system.PentahoSessionHolder; import org.pentaho.platform.engine.core.system.PentahoSystem; import org.pentaho.platform.engine.services.messages.Messages; import org.pentaho.platform.engine.services.metadata.MetadataPublisher; import org.pentaho.platform.util.messages.LocaleHelper; /** * This is the platform implementation of the IMetadataDomainRepository. * * TODO: Update this class to use CacheControl, getting created per session * per Marc's input * * @author Will Gorman (wgorman@pentaho.com) */ public class MetadataDomainRepository extends FileBasedMetadataDomainRepository { private static final String LEGACY_LOCATION = "__LEGACY_LOCATION_"; //$NON-NLS-1$ protected final Log logger = LogFactory.getLog(MetadataDomainRepository.class); public static String LEGACY_XMI_FILENAME = "metadata.xmi"; //$NON-NLS-1$ public static final int[] ACCESS_TYPE_MAP = new int[] { IAclHolder.ACCESS_TYPE_READ, IAclHolder.ACCESS_TYPE_WRITE, IAclHolder.ACCESS_TYPE_UPDATE, IAclHolder.ACCESS_TYPE_DELETE, IAclHolder.ACCESS_TYPE_ADMIN, IAclHolder.ACCESS_TYPE_ADMIN }; private static final String DOMAIN_FOLDER = "system/metadata/domains"; //$NON-NLS-1$ // // Use this approach for accessing the session object when // updated to use the CacheManager // // private IPentahoSession session; // // public void setSession(IPentahoSession session) { // this.session = session; // } // // public IPentahoSession getSession() { // return session; // } public IPentahoSession getSession() { return PentahoSessionHolder.getSession(); } protected File getDomainsFolder() { String domainsFolder = PentahoSystem.getApplicationContext().getSolutionPath(DOMAIN_FOLDER); File folder = new File(domainsFolder); return folder; } public Set<String> getDomainIds() { // double check that all the legacy domains are loaded // this is necessary because the reloading of domains might have been done by // a user who might not have permissions to all solutions // the super call is made to avoid having it called twice, the logic in getDomainIds // calls reload if the map is empty. synchronized(domains) { super.reloadDomains(); reloadLegacyDomains(false); } return super.getDomainIds(); } public void reloadDomains() { synchronized(domains) { // first reload new metadata domains super.reloadDomains(); // then populate the legacy domains reloadLegacyDomains(true); } } public void removeDomain(String domainId) { synchronized(domains) { // determine if domain is legacy or not Domain domain = domains.get(domainId); if (domain.getProperty(LEGACY_LOCATION) != null) { // this is an xmi based domain, remove it removeLegacyDomain(domainId); } else { super.removeDomain(domainId); } } } public Domain getDomain(String id) { Domain domain = super.getDomain(id); if (domain == null) { // reload legacy domains if not found reloadLegacyDomains(false); return super.getDomain(id); } else { return domain; } } private void removeLegacyDomain(String domainId) { ISolutionRepository repo = PentahoSystem.get(ISolutionRepository.class, getSession()); repo.removeSolutionFile(domainId); domains.remove(domainId); } @SuppressWarnings("unchecked") private void reloadLegacyDomains(boolean overwrite) { // also load the XMI domains ISolutionRepository repo = PentahoSystem.get(ISolutionRepository.class, getSession()); // filter the repository looking for XMI files Document doc = repo.getSolutionTree(ISolutionRepository.ACTION_EXECUTE, new ISolutionFilter() { public boolean keepFile(ISolutionFile solutionFile, int actionOperation) { return solutionFile.isDirectory() || solutionFile.getExtension().equalsIgnoreCase("xmi") || solutionFile.getExtension().equalsIgnoreCase(".xmi"); //$NON-NLS-1$ //$NON-NLS-2$ } }); int allSuccess = MetadataPublisher.NO_ERROR; try { // first look for metadata.xmi leaves in solution roots // e.g. /tree/pentaho-solutions/somedir/metadata.xmi List nodes = doc.selectNodes("/tree/branch/branch/leaf[@isDir='false']"); //$NON-NLS-1$ for (Object obj : nodes) { Element node = (Element) obj; Element pathNode = (Element)(node).selectSingleNode("path"); //$NON-NLS-1$ String path = pathNode.getText(); // remove the first node int idx = path.indexOf(ISolutionRepository.SEPARATOR, 1); path = path.substring( idx+1 ); // make sure the filename is 'metadata.xmi' if( path.endsWith( LEGACY_XMI_FILENAME )) { if (overwrite || !domains.containsKey(path)) { allSuccess |= loadMetadata(path); } } } // now look for */resources/metadata/*.xmi // e.g. /tree/pentaho-solutions/some/resources/metadata/*.xmi nodes = doc.selectNodes("/tree/branch/branch/branch/branch/leaf[@isDir='false']"); //$NON-NLS-1$ for (Object obj : nodes) { // build up the directory path Element node = (Element) obj; String dir = node.getParent().attributeValue( "id" ); //$NON-NLS-1$ // make sure the parent branch ends with /resources/metadata if( dir.endsWith( "/resources/metadata" ) ) { //$NON-NLS-1$ Element pathNode = (Element)(node).selectSingleNode("path"); //$NON-NLS-1$ String path = pathNode.getText(); // remove the first node int idx = path.indexOf(ISolutionRepository.SEPARATOR, 1); path = path.substring( idx+1 ); if (overwrite || !domains.containsKey(path)) { allSuccess |= loadMetadata(path); } } } } catch (Exception e) { e.printStackTrace(); } } private int loadMetadata(final String resourceName) { int result = MetadataPublisher.NO_ERROR; InputStream xmiInputStream; xmiInputStream = null; ISolutionRepository repo = PentahoSystem.get(ISolutionRepository.class, getSession()); if (repo.resourceExists(resourceName, ISolutionRepository.ACTION_EXECUTE)) { try { xmiInputStream = repo.getResourceInputStream(resourceName, true, ISolutionRepository.ACTION_EXECUTE); Domain domain = new XmiParser().parseXmi(xmiInputStream); domain.setProperty(LEGACY_LOCATION, resourceName); // The domain key is the full path and name of the XMI file within the solution repository // e.g. mysolution/resources/metadata/mymodel.xmi or // mysolution/metadata.xmi domain.setId(resourceName); domains.put(resourceName, domain); } catch (Throwable t) { logger.error(Messages.getInstance().getString("MetadataPublisher.ERROR_0001_COULD_NOT_LOAD", resourceName), t); //$NON-NLS-1$ throw new RuntimeException(Messages.getInstance().getString("MetadataPublisher.ERROR_0001_COULD_NOT_LOAD"), t); //$NON-NLS-1$ } finally { if (xmiInputStream != null) { try { xmiInputStream.close(); } catch (IOException ex) { logger.error(Messages.getInstance().getString("MetadataPublisher.ERROR_0001_COULD_NOT_LOAD", resourceName), ex); //$NON-NLS-1$ throw new RuntimeException(Messages.getInstance().getString("MetadataPublisher.ERROR_0001_COULD_NOT_LOAD"), ex); //$NON-NLS-1$ } } } } else { return result; } return result; } public void storeDomain(Domain domain, boolean overwrite) throws DomainIdNullException, DomainAlreadyExistsException, DomainStorageException { synchronized(domains) { if (domain.getId() == null) { throw new DomainIdNullException(org.pentaho.metadata.messages.Messages.getErrorString("IMetadataDomainRepository.ERROR_0001_DOMAIN_ID_NULL")); //$NON-NLS-1$ } if (!overwrite && domains.get(domain.getId()) != null) { throw new DomainAlreadyExistsException(org.pentaho.metadata.messages.Messages.getErrorString("IMetadataDomainRepository.ERROR_0002_DOMAIN_OBJECT_EXISTS", domain.getId())); //$NON-NLS-1$ } if (domain.getProperty(LEGACY_LOCATION) != null) { storeLegacyDomain(domain, overwrite); } else { super.storeDomain(domain, overwrite); } } } private void storeLegacyDomain(Domain domain, boolean overwrite) throws DomainIdNullException, DomainAlreadyExistsException, DomainStorageException { if (domain.getId() == null) { throw new DomainIdNullException(org.pentaho.metadata.messages.Messages.getErrorString("IMetadataDomainRepository.ERROR_0001_DOMAIN_ID_NULL")); //$NON-NLS-1$ } // only allow editing vs. creation if (!overwrite) { throw new DomainAlreadyExistsException(org.pentaho.metadata.messages.Messages.getErrorString("IMetadataDomainRepository.ERROR_0002_DOMAIN_OBJECT_EXISTS", domain.getId())); //$NON-NLS-1$ } XmiParser parser = new XmiParser(); String xmi = parser.generateXmi(domain); try { String domainId = domain.getId(); // parse the domain id into a path and filename // e.g. domainId = mysolution/resources/metadata/mymodel.xmi // path = mysolution/resources/metadata // fileName = mymodel.xmi int idx = domainId.lastIndexOf(ISolutionRepository.SEPARATOR); String path = domainId.substring(0, idx); String fileName = domainId.substring( idx+1); ISolutionRepository repo = PentahoSystem.get(ISolutionRepository.class, getSession()); String solutionPath = PentahoSystem.getApplicationContext().getSolutionPath(""); //$NON-NLS-1$ repo.addSolutionFile(solutionPath, path, fileName, xmi.getBytes(LocaleHelper.getSystemEncoding()), true); // adds the domain to the domains list domains.put(domain.getId(), domain); } catch (Exception e) { throw new DomainStorageException(Messages.getInstance().getErrorString("MetadataDomainRepository.ERROR_0006_FAILED_TO_STORE_LEGACY_DOMAIN", domain.getId()), e); //$NON-NLS-1$ } } }