/*
* 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
* 2005-2008 Pentaho Corporation. All rights reserved.
* @created Jun 21, 2005
* @author James Dixon
*/
package org.pentaho.platform.repository.solution.dbbased;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.pentaho.metadata.repository.IMetadataDomainRepository;
import org.pentaho.platform.api.engine.IAclHolder;
import org.pentaho.platform.api.engine.IAclPublisher;
import org.pentaho.platform.api.engine.IAclSolutionFile;
import org.pentaho.platform.api.engine.IAclVoter;
import org.pentaho.platform.api.engine.IActionSequence;
import org.pentaho.platform.api.engine.IFileInfo;
import org.pentaho.platform.api.engine.IPentahoAclEntry;
import org.pentaho.platform.api.engine.IPentahoInitializer;
import org.pentaho.platform.api.engine.IPentahoSession;
import org.pentaho.platform.api.engine.IPermissionMask;
import org.pentaho.platform.api.engine.IPermissionRecipient;
import org.pentaho.platform.api.engine.IPluginManager;
import org.pentaho.platform.api.engine.ISolutionAttributeContributor;
import org.pentaho.platform.api.engine.ISolutionFile;
import org.pentaho.platform.api.engine.ISolutionFilter;
import org.pentaho.platform.api.engine.PentahoAccessControlException;
import org.pentaho.platform.api.repository.ISolutionRepository;
import org.pentaho.platform.api.repository.ISubscriptionRepository;
import org.pentaho.platform.api.repository.RepositoryException;
import org.pentaho.platform.engine.core.system.PentahoSystem;
import org.pentaho.platform.engine.security.SecurityHelper;
import org.pentaho.platform.engine.security.SimplePermissionMask;
import org.pentaho.platform.engine.security.SimpleRole;
import org.pentaho.platform.engine.security.SimpleSession;
import org.pentaho.platform.engine.security.SpringSecurityPermissionMgr;
import org.pentaho.platform.engine.services.actionsequence.SequenceDefinition;
import org.pentaho.platform.engine.services.solution.SolutionReposHelper;
import org.pentaho.platform.repository.hibernate.HibernateUtil;
import org.pentaho.platform.repository.messages.Messages;
import org.pentaho.platform.repository.solution.SolutionRepositoryBase;
import org.pentaho.platform.repository.solution.filebased.FileInfo;
import org.pentaho.platform.util.FileHelper;
import org.pentaho.platform.util.StringUtil;
import org.pentaho.platform.util.xml.XmlHelper;
import org.pentaho.platform.util.xml.dom4j.XmlDom4JHelper;
/**
* @author William Seyler
*/
public class DbBasedSolutionRepository extends SolutionRepositoryBase implements IPentahoInitializer {
private static final long serialVersionUID = -8270135463210017284L;
private static final String BREAD_CRUMBS_TAG = "breadcrumbs/"; //$NON-NLS-1$
private ISolutionAttributeContributor defaultSolutionAtributeContributor = new DefaultSolutionAttributeContributor();
private ISolutionFilter defaultSolutionFilter = new DefaultSolutionFilter();
// private String rootFile;
private String repositoryName;
private RepositoryFile rootDirectory;
private static final byte[] lock = new byte[0];
private boolean repositoryInit = false;
public DbBasedSolutionRepository() {
super();
init();
}
@Override
public void init() {
if (!repositoryInit) {
super.init();
String reposName = PentahoSystem.getSystemSetting("solution-repository/db-repository-name", null); //$NON-NLS-1$
if (reposName != null) {
setRepositoryName(reposName);
SolutionRepositoryBase.logger.info(Messages.getInstance().getString(
"SolutionRepository.WARN_0002_USING_SOLUTION_NAME", getRepositoryName())); //$NON-NLS-1$
} else {
SolutionRepositoryBase.logger.info(Messages.getInstance().getString("SolutionRepository.WARN_0001_UNDEFINED_SOLUTION_NAME")); //$NON-NLS-1$
}
RepositoryFile root = (RepositoryFile) internalGetRootFolder();
if (root == null) {
String path = PentahoSystem.getApplicationContext().getSolutionPath(""); //$NON-NLS-1$
loadSolutionFromFileSystem(getSession(), path, true);
root = (RepositoryFile) internalGetRootFolder();
}
repositoryInit = true;
}
}
public IActionSequence getActionSequence(final String solutionName, final String actionPath,
final String sequenceName, final int localLoggingLevel, final int actionOperation) {
String action = buildDirectoryPath(solutionName, actionPath, sequenceName);
if ((action == null) || (action.length() == 0)) {
error(Messages.getInstance().getErrorString("SolutionRepository.ERROR_0008_ACTION_SEQUENCE_NAME_INVALID")); //$NON-NLS-1$
return null;
}
Document actionSequenceDocument = getSolutionDocument(action, actionOperation);
if (actionSequenceDocument == null) {
return null;
}
IActionSequence actionSequence = SequenceDefinition.ActionSequenceFactory(actionSequenceDocument, sequenceName,
actionPath, solutionName, this, PentahoSystem.getApplicationContext(), localLoggingLevel);
if (actionSequence == null) {
return null;
}
return actionSequence;
}
public boolean hasAccess(final ISolutionFile aFile, final int actionOperation) {
if (aFile instanceof IAclSolutionFile) {
return SecurityHelper.hasAccess((IAclSolutionFile) aFile, actionOperation, getSession());
} else {
return true;
}
}
protected boolean isPentahoAdministrator() {
return SecurityHelper.isPentahoAdministrator(getSession());
}
public Document getSolutionDocument(final String documentPath, final int actionOperation) {
ISolutionFile file = (ISolutionFile) getRepositoryObjectFromCache(documentPath);
if (file == null) { // Not in cache... go to the solution repository
file = (ISolutionFile) getFileByPath(documentPath, actionOperation);
if (file != null) {
putRepositoryObjectInCache(documentPath, file);
} else {
SolutionRepositoryBase.logger.info(Messages.getInstance().getString("SolutionRepository.INFO_0010_DOCUMENT_NOT_FOUND", //$NON-NLS-1$
documentPath));
return null; // If it's still null then it doesn't exist
}
}
Document document = null;
if (file.getData() != null) {
try {
document = XmlDom4JHelper.getDocFromStream(new ByteArrayInputStream(file.getData()), this.getResourceLoader());
} catch (Throwable t) {
error(Messages.getInstance().getErrorString("SolutionRepository.ERROR_0017_INVALID_XML_DOCUMENT", documentPath), t); //$NON-NLS-1$
return null;
}
} else {
error(Messages.getInstance().getErrorString("SolutionRepository.ERROR_0019_NO_DATA_IN_FILE", file.getFileName())); //$NON-NLS-1$
return null;
}
if ((document == null) && (file != null) && (file.getData() != null)) {
// the document exists but cannot be parsed
error(Messages.getInstance().getErrorString("SolutionRepository.ERROR_0009_INVALID_DOCUMENT", documentPath)); //$NON-NLS-1$
return null;
}
localizeDoc(document, file);
return document;
}
public void reloadSolutionRepository(final IPentahoSession localSession, final int localLoggingLevel) {
this.loggingLevel = localLoggingLevel;
String path = PentahoSystem.getApplicationContext().getSolutionPath(""); //$NON-NLS-1$
DbRepositoryClassLoader.clearResourceCache();
loadSolutionFromFileSystem(localSession, path, true);
}
private void loadSolutionPath(final String solutionName, final String path, final int localLoggingLevel) {
this.loggingLevel = localLoggingLevel;
if (isCachingAvailable()) {
String localDirStr = buildDirectoryPath(solutionName, path, null);
Document repository = DocumentHelper.createDocument();
Element rootNode = null;
RepositoryFile directory = (RepositoryFile) getFileByPath(localDirStr, ISolutionRepository.ACTION_EXECUTE);
if (directory == null) {
return;
}
if (directory.isRoot()) {
rootNode = repository.addElement("repository"); //$NON-NLS-1$
Document indexDoc = getSolutionDocument(directory.getFullPath() + ISolutionRepository.SEPARATOR
+ ISolutionRepository.INDEX_FILENAME, ISolutionRepository.ACTION_EXECUTE);
if (indexDoc != null) {
addIndexToRepository(indexDoc, directory, rootNode, path, solutionName);
}
processDir(rootNode, directory, solutionName, ISolutionRepository.ACTION_EXECUTE,
SolutionRepositoryBase.BROWSE_DEPTH);
} else {
Element filesNode = repository.addElement("files"); //$NON-NLS-1$
rootNode = filesNode.addElement("file"); //$NON-NLS-1$
rootNode.addAttribute("type", FileInfo.FILE_TYPE_FOLDER); //$NON-NLS-1$
rootNode.addElement("path").setText(path != null ? path : ""); //$NON-NLS-1$//$NON-NLS-2$
Document indexDoc = getSolutionDocument(directory.getFullPath() + ISolutionRepository.SEPARATOR
+ ISolutionRepository.INDEX_FILENAME, ISolutionRepository.ACTION_EXECUTE);
if (indexDoc != null) {
addIndexToRepository(indexDoc, directory, rootNode, path, solutionName);
}
processDir(rootNode, directory, solutionName, ISolutionRepository.ACTION_EXECUTE,
SolutionRepositoryBase.BROWSE_DEPTH);
filesNode.addElement(SolutionRepositoryBase.LOCATION_ATTR_NAME).setText(
getPathNames(solutionName, path) + ISolutionRepository.SEPARATOR);
}
putRepositoryObjectInCache(localDirStr + getLocale().toString(), repository);
}
}
@Override
protected String buildDirectoryPath(final String solution, final String path, final String action) {
String localDirStr = repositoryName;
if ((solution != null) && (solution.length() > 0)) {
localDirStr += solution;
if ((path != null) && (path.length() > 0)) {
localDirStr += ISolutionRepository.SEPARATOR;
localDirStr += path;
}
}
if ((action != null) && (action.length() > 0)) {
String seperator = new String() + ISolutionRepository.SEPARATOR;
if (!localDirStr.endsWith(seperator)) {
localDirStr += ISolutionRepository.SEPARATOR;
}
localDirStr += action;
}
return localDirStr;
}
private Document getCachedSolutionDocument(final String solutionName, final String pathName, final int actionOperation) {
if (actionOperation == ISolutionRepository.ACTION_EXECUTE) {
String localDirStr = buildDirectoryPath(solutionName, pathName, null);
Object cachedRepo = this.getRepositoryObjectFromCache(localDirStr + getLocale().toString());
if (cachedRepo == null) {
loadSolutionPath(solutionName, pathName, this.loggingLevel);
cachedRepo = this.getRepositoryObjectFromCache(localDirStr + getLocale().toString());
}
return (Document) cachedRepo;
} else {
return null;
}
}
/*
* private void processDir(Element parentNode, RepositoryFile parentDir, String solutionId, int actionOperation) {
* processDir(parentNode, parentDir, solutionId, actionOperation, true); }
*/
protected void processDir(final Element parentNode, final RepositoryFile parentDir, final String solutionId,
final int actionOperation, final int recurseLevels) {
if (recurseLevels <= 0) {
return;
}
RepositoryFile[] files = parentDir.listRepositoryFiles();
for (RepositoryFile element : files) {
if (!element.isDirectory()) {
String fileName = element.getFileName();
processFile(fileName, element, parentNode, solutionId, actionOperation);
}
}
for (RepositoryFile element : files) {
if (element.isDirectory()
&& (!element.getFileName().equalsIgnoreCase("system")) && (!element.getFileName().equalsIgnoreCase("CVS")) && (!element.getFileName().equalsIgnoreCase(".svn"))) { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
Element dirNode = parentNode.addElement("file"); //$NON-NLS-1$
dirNode.addAttribute("type", FileInfo.FILE_TYPE_FOLDER); //$NON-NLS-1$
defaultSolutionAtributeContributor.contributeAttributes(element, dirNode);
// TODO read this from the directory index file
String thisSolution;
String path = getSolutionPath(element);
if (solutionId == null) {
thisSolution = getSolutionId(element);
} else {
thisSolution = solutionId;
dirNode.addElement("path").setText(path); //$NON-NLS-1$
}
Document indexDoc = getSolutionDocument(element.getFullPath() + ISolutionRepository.SEPARATOR
+ ISolutionRepository.INDEX_FILENAME, actionOperation);
if (indexDoc != null) {
addIndexToRepository(indexDoc, element, dirNode, path, thisSolution);
} else {
dirNode.addAttribute("visible", "false"); //$NON-NLS-1$ //$NON-NLS-2$
String dirName = element.getFileName();
dirNode.addAttribute("name", XmlHelper.encode(dirName)); //$NON-NLS-1$
dirNode.addElement("title").setText(dirName); //$NON-NLS-1$
}
processDir(dirNode, element, thisSolution, actionOperation, recurseLevels - 1);
}
}
}
protected void processFile(String fileName, RepositoryFile element, final Element parentNode,
final String solutionId, final int actionOperation) {
if (fileName.equals("Entries") || fileName.equals("Repository") || fileName.equals("Root")) { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
// ignore any CVS files
return;
}
int lastPoint = fileName.lastIndexOf('.');
if (lastPoint == -1) {
// ignore anything with no extension
return;
}
String extension = fileName.substring(lastPoint + 1).toLowerCase();
if (fileName.toLowerCase().endsWith(".url")) { //$NON-NLS-1$
if (hasAccess(element, actionOperation)) {
addUrlToRepository(element, parentNode);
return;
}
}
boolean addFile = "xaction".equals(extension); //$NON-NLS-1$
IPluginManager pluginManager = PentahoSystem.get(IPluginManager.class, getSession());
if (pluginManager != null) {
Set<String> types = pluginManager.getContentTypes();
addFile |= types != null && types.contains(extension);
}
if (!addFile) {
return;
}
String path = getSolutionPath(element);
if (fileName.toLowerCase().endsWith(".xaction")) { //$NON-NLS-1$
// create an action sequence document from this
info(Messages.getInstance().getString("SolutionRepository.DEBUG_ADDING_ACTION", fileName)); //$NON-NLS-1$
IActionSequence actionSequence = getActionSequence(solutionId, path, fileName, loggingLevel, actionOperation);
if (actionSequence == null) {
if (((solutionId == null) || (solutionId.length() == 0)) && ((path == null) || (path.length() == 0))) {
info(Messages.getInstance().getString("SolutionRepository.INFO_0008_NOT_ADDED", fileName)); //$NON-NLS-1$
} else {
error(Messages.getInstance().getErrorString("SolutionRepository.ERROR_0006_INVALID_SEQUENCE_DOCUMENT", fileName)); //$NON-NLS-1$
}
} else {
addToRepository(actionSequence, parentNode, element);
}
} else if (pluginManager != null) {
String fullPath = solutionId + ISolutionRepository.SEPARATOR
+ ((StringUtil.isEmpty(path)) ? "" : path + ISolutionRepository.SEPARATOR) + fileName; //$NON-NLS-1$
try {
IFileInfo fileInfo = getFileInfo(solutionId, path, fileName, extension, pluginManager, actionOperation);
addToRepository(fileInfo, solutionId, path, fileName, parentNode, element);
} catch (Exception e) {
error(Messages.getInstance().getErrorString("SolutionRepository.ERROR_0021_FILE_NOT_ADDED", fullPath), e); //$NON-NLS-1$
}
}
}
private void addToRepository(final IFileInfo info, final String solution, final String path, final String fileName,
final Element parentNode, final RepositoryFile file) {
Element dirNode = parentNode.addElement("file"); //$NON-NLS-1$
dirNode.addAttribute("type", FileInfo.FILE_TYPE_ACTIVITY); //$NON-NLS-1$
dirNode.addElement("filename").setText(fileName); //$NON-NLS-1$
dirNode.addElement("path").setText(path); //$NON-NLS-1$
dirNode.addElement("solution").setText(solution); //$NON-NLS-1$
dirNode.addElement("title").setText(info.getTitle()); //$NON-NLS-1$
String description = info.getDescription();
if (description == null) {
dirNode.addElement("description"); //$NON-NLS-1$
} else {
dirNode.addElement("description").setText(description); //$NON-NLS-1$
}
String author = info.getAuthor();
if (author == null) {
dirNode.addElement("author"); //$NON-NLS-1$
} else {
dirNode.addElement("author").setText(author); //$NON-NLS-1$
}
String iconPath = info.getIcon();
if ((iconPath != null) && !iconPath.equals("")) { //$NON-NLS-1$
String rolloverIconPath = null;
int rolloverIndex = iconPath.indexOf("|"); //$NON-NLS-1$
if (rolloverIndex > -1) {
rolloverIconPath = iconPath.substring(rolloverIndex + 1);
iconPath = iconPath.substring(0, rolloverIndex);
}
if (publishIcon(file.retrieveParent().getFullPath(), iconPath)) {
dirNode.addElement("icon").setText("getImage?image=icons/" + iconPath); //$NON-NLS-1$ //$NON-NLS-2$
} else {
dirNode.addElement("icon").setText(info.getIcon()); //$NON-NLS-1$
}
if (rolloverIconPath != null) {
if (publishIcon(PentahoSystem.getApplicationContext().getSolutionPath(solution + File.separator + path),
rolloverIconPath)) {
dirNode.addElement("rollovericon").setText("getImage?image=icons/" + rolloverIconPath); //$NON-NLS-1$ //$NON-NLS-2$
} else {
dirNode.addElement("rollovericon").setText(rolloverIconPath); //$NON-NLS-1$
}
}
}
String displayType = info.getDisplayType();
if ((displayType == null) || ("none".equalsIgnoreCase(displayType))) { //$NON-NLS-1$
// this should be hidden from users
dirNode.addAttribute("visible", "false"); //$NON-NLS-1$ //$NON-NLS-2$
} else {
dirNode.addAttribute("visible", "true"); //$NON-NLS-1$ //$NON-NLS-2$
dirNode.addAttribute("displaytype", displayType); //$NON-NLS-1$
}
ISubscriptionRepository subscriptionRepository = PentahoSystem.get(ISubscriptionRepository.class, getSession());
boolean subscribable = false;
if (subscriptionRepository != null) {
subscribable = subscriptionRepository.getContentByActionReference(solution + ISolutionRepository.SEPARATOR + path
+ ISolutionRepository.SEPARATOR + fileName) != null;
}
dirNode.addElement("properties").setText("subscribable=" + Boolean.toString(subscribable)); //$NON-NLS-1$ //$NON-NLS-2$
}
protected String getSolutionId(final RepositoryFile file) {
String path = file.getFullPath();
if (path.length() < repositoryName.length()) {
return ""; //$NON-NLS-1$
}
path = path.substring(repositoryName.length()); // Strip off the root
// directory
// Strip off the fileName if any
if (path.indexOf(ISolutionRepository.SEPARATOR) != -1) {
path = path.substring(0, path.indexOf(ISolutionRepository.SEPARATOR));
}
return path;
}
protected String getSolutionPath(final RepositoryFile file) {
String path = file.getFullPath();
try {
path = path.substring(repositoryName.length()); // Strip off the
// root
// directory
// Strip off the filename if there is one
if (!file.isDirectory()) {
path = path.substring(0, path.lastIndexOf(ISolutionRepository.SEPARATOR));
}
// Strip off the solution folder and the following / if any else we
// know we have a solution folder and return empty String
if (path.indexOf(ISolutionRepository.SEPARATOR) != -1) {
path = path.substring(path.indexOf(ISolutionRepository.SEPARATOR) + 1);
} else {
return SolutionRepositoryBase.EMPTY_STR;
}
} catch (StringIndexOutOfBoundsException ex) {
return SolutionRepositoryBase.EMPTY_STR;
}
return path;
}
protected void addIndexToRepository(final Document indexDoc, final RepositoryFile directoryFile,
final Element directoryNode, String path, final String solution) {
if (!directoryFile.isDirectory()) {
return;
}
// TODO see if there is a localized attribute file for the current
// locale
String dirName = getValue(indexDoc, "/index/name", directoryFile.getFileName().replace('_', ' ')); //$NON-NLS-1$
String description = getValue(indexDoc, "/index/description", SolutionRepositoryBase.EMPTY_STR); //$NON-NLS-1$
String iconPath = getValue(indexDoc, "/index/icon", SolutionRepositoryBase.EMPTY_STR); //$NON-NLS-1$
String displayType = getValue(indexDoc, "/index/display-type", "icons"); //$NON-NLS-1$ //$NON-NLS-2$
boolean visible = getValue(indexDoc, "/index/visible", "false").equalsIgnoreCase("true"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
path = XmlHelper.encode(path);
String name = XmlHelper.encode(directoryFile.getFileName());
// We want to cache the localized document name if we don't already have it
if (directoryFile.isDirectory() && (getPathNames(solution, path) == null)) {
cacheLocalizedDirectoryName(dirName, directoryFile, solution, path);
}
directoryNode.addAttribute("name", name); //$NON-NLS-1$
directoryNode.addElement("title").setText(dirName); //$NON-NLS-1$
directoryNode.addAttribute("path", path); //$NON-NLS-1$
directoryNode.addElement("description").setText(description); //$NON-NLS-1$
if ((iconPath != null) && !iconPath.equals("")) { //$NON-NLS-1$
String rolloverIconPath = null;
int rolloverIndex = iconPath.indexOf("|"); //$NON-NLS-1$
if (rolloverIndex > -1) {
rolloverIconPath = iconPath.substring(rolloverIndex + 1);
iconPath = iconPath.substring(0, rolloverIndex);
}
if (publishIcon(directoryFile.isDirectory() ? directoryFile.getFullPath() : directoryFile.retrieveParent()
.getFullPath(), iconPath)) {
directoryNode.addElement("icon").setText("getImage?image=icons/" + iconPath); //$NON-NLS-1$ //$NON-NLS-2$
} else {
directoryNode.addElement("icon").setText(iconPath); //$NON-NLS-1$
}
if (rolloverIconPath != null) {
if (publishIcon(directoryFile.isDirectory() ? directoryFile.getFullPath() : directoryFile.retrieveParent()
.getFullPath(), rolloverIconPath)) {
directoryNode.addElement("rollovericon").setText("getImage?image=icons/" + rolloverIconPath); //$NON-NLS-1$ //$NON-NLS-2$
} else {
directoryNode.addElement("rollovericon").setText(rolloverIconPath); //$NON-NLS-1$
}
}
}
Boolean visability = null;
boolean hasAccess = SecurityHelper.hasAccess(directoryFile, ISolutionRepository.ACTION_EXECUTE, getSession());
if (!hasAccess) {
visability = new Boolean(false);
} else {
visability = new Boolean(visible);
}
directoryNode.addAttribute("visible", visability.toString()); //$NON-NLS-1$
directoryNode.addAttribute("displaytype", displayType); //$NON-NLS-1$
if ((solution != null) && (solution.length() > 0)) {
directoryNode.addElement("solution").setText(solution); //$NON-NLS-1$
} else {
directoryNode.addElement("solution").setText(getSolutionId(directoryFile)); //$NON-NLS-1$
}
}
private void cacheLocalizedDirectoryName(String localizedPath, final RepositoryFile directoryFile,
final String solution, final String path) {
if (directoryFile.isRoot()) { // We're done and have the final localized string in dirName
putRepositoryObjectInCache(DbBasedSolutionRepository.BREAD_CRUMBS_TAG + buildDirectoryPath(solution, path, null),
localizedPath);
} else { // We've got to process the name of the parent
String localizedAncestors = (String) getRepositoryObjectFromCache(DbBasedSolutionRepository.BREAD_CRUMBS_TAG
+ directoryFile.retrieveParent().getFullPath());
if (localizedAncestors != null) { // The parents stuff was already cached
localizedPath = localizedAncestors + ISolutionRepository.SEPARATOR + localizedPath;
putRepositoryObjectInCache(DbBasedSolutionRepository.BREAD_CRUMBS_TAG
+ buildDirectoryPath(solution, path, null), localizedPath);
} else { // Now we've got to check to see if the parent has an index.xml
Document indexDoc = getSolutionDocument(directoryFile.retrieveParent().getFullPath()
+ ISolutionRepository.SEPARATOR + ISolutionRepository.INDEX_FILENAME, ISolutionRepository.ACTION_EXECUTE);
if (indexDoc == null) {
localizedPath = directoryFile.retrieveParent().getFileName() + ISolutionRepository.SEPARATOR + localizedPath;
} else {
localizedPath = getValue(indexDoc,
"/index/name", directoryFile.retrieveParent().getFileName().replace('_', ' ')) + ISolutionRepository.SEPARATOR + localizedPath; //$NON-NLS-1$
}
cacheLocalizedDirectoryName(localizedPath, (RepositoryFile) directoryFile.retrieveParent(), solution, path);
}
}
}
protected void addUrlToRepository(final RepositoryFile file, final Element parentNode) {
String urlContent = new String(file.getData());
StringTokenizer tokenizer = new StringTokenizer(urlContent, "\n"); //$NON-NLS-1$
String url = null;
String title = file.getFileName();
String description = null;
String iconPath = null;
String target = null;
while (tokenizer.hasMoreTokens()) {
String line = tokenizer.nextToken();
int pos = line.indexOf('=');
if (pos > 0) {
String name = line.substring(0, pos);
String value = line.substring(pos + 1);
if ((value != null) && (value.length() > 0) && (value.charAt(value.length() - 1) == '\r')) {
value = value.substring(0, value.length() - 1);
}
if ("URL".equalsIgnoreCase(name)) { //$NON-NLS-1$
url = value;
}
if ("name".equalsIgnoreCase(name)) { //$NON-NLS-1$
title = value;
}
if ("description".equalsIgnoreCase(name)) { //$NON-NLS-1$
description = value;
}
if ("icon".equalsIgnoreCase(name)) { //$NON-NLS-1$
iconPath = value;
}
if ("target".equalsIgnoreCase(name)) { //$NON-NLS-1$
target = value;
}
}
}
if (url != null) {
// now create an entry for the database
Element dirNode = parentNode.addElement("file"); //$NON-NLS-1$
dirNode.addAttribute("type", FileInfo.FILE_TYPE_URL); //$NON-NLS-1$
dirNode.addElement("filename").setText(file.getFileName()); //$NON-NLS-1$
dirNode.addElement("title").setText(title); //$NON-NLS-1$
if (target != null) {
dirNode.addElement("target").setText(target); //$NON-NLS-1$
}
if (description != null) {
dirNode.addElement("description").setText(description); //$NON-NLS-1$
}
if ((iconPath != null) && !iconPath.equals("")) { //$NON-NLS-1$
String rolloverIconPath = null;
int rolloverIndex = iconPath.indexOf("|"); //$NON-NLS-1$
if (rolloverIndex > -1) {
rolloverIconPath = iconPath.substring(rolloverIndex + 1);
iconPath = iconPath.substring(0, rolloverIndex);
}
if (publishIcon(file.retrieveParent().getFullPath(), iconPath)) {
dirNode.addElement("icon").setText("getImage?image=icons/" + iconPath); //$NON-NLS-1$ //$NON-NLS-2$
} else {
dirNode.addElement("icon").setText(iconPath); //$NON-NLS-1$
}
if (rolloverIconPath != null) {
if (publishIcon(file.retrieveParent().getFullPath(), rolloverIconPath)) {
dirNode.addElement("rollovericon").setText("getImage?image=icons/" + rolloverIconPath); //$NON-NLS-1$ //$NON-NLS-2$
} else {
dirNode.addElement("rollovericon").setText(rolloverIconPath); //$NON-NLS-1$
}
}
}
dirNode.addElement("url").setText(url); //$NON-NLS-1$
dirNode.addAttribute("visible", "true"); //$NON-NLS-1$ //$NON-NLS-2$
dirNode.addAttribute("displaytype", FileInfo.FILE_DISPLAY_TYPE_URL); //$NON-NLS-1$
localizeDoc(dirNode, file);
}
}
protected void addToRepository(final IActionSequence actionSequence, final Element parentNode,
final RepositoryFile file) {
Element dirNode = parentNode.addElement("file"); //$NON-NLS-1$
dirNode.addAttribute("type", FileInfo.FILE_TYPE_ACTIVITY); //$NON-NLS-1$
defaultSolutionAtributeContributor.contributeAttributes(file, dirNode);
if ((actionSequence.getSequenceName() == null) || (actionSequence.getSolutionPath() == null)
|| (actionSequence.getSolutionName() == null)) {
error(Messages.getInstance().getString("SolutionRepository.ERROR_0008_ACTION_SEQUENCE_NAME_INVALID")); //$NON-NLS-1$
return;
}
dirNode.addElement("filename").setText(actionSequence.getSequenceName()); //$NON-NLS-1$
dirNode.addElement("path").setText(actionSequence.getSolutionPath()); //$NON-NLS-1$
dirNode.addElement("solution").setText(actionSequence.getSolutionName()); //$NON-NLS-1$
String title = actionSequence.getTitle();
if (title == null) {
dirNode.addElement("title").setText(actionSequence.getSequenceName()); //$NON-NLS-1$
} else {
dirNode.addElement("title").setText(title); //$NON-NLS-1$
}
String description = actionSequence.getDescription();
if (description == null) {
dirNode.addElement("description"); //$NON-NLS-1$
} else {
dirNode.addElement("description").setText(description); //$NON-NLS-1$
}
String author = actionSequence.getAuthor();
if (author == null) {
dirNode.addElement("author"); //$NON-NLS-1$
} else {
dirNode.addElement("author").setText(author); //$NON-NLS-1$
}
String iconPath = actionSequence.getIcon();
if ((iconPath != null) && !iconPath.equals("")) { //$NON-NLS-1$
String rolloverIconPath = null;
int rolloverIndex = iconPath.indexOf("|"); //$NON-NLS-1$
if (rolloverIndex > -1) {
rolloverIconPath = iconPath.substring(rolloverIndex + 1);
iconPath = iconPath.substring(0, rolloverIndex);
}
if (publishIcon(file.retrieveParent().getFullPath(), iconPath)) {
dirNode.addElement("icon").setText("getImage?image=icons/" + iconPath); //$NON-NLS-1$ //$NON-NLS-2$
} else {
dirNode.addElement("icon").setText(actionSequence.getIcon()); //$NON-NLS-1$
}
if (rolloverIconPath != null) {
if (publishIcon(file.retrieveParent().getFullPath(), rolloverIconPath)) {
dirNode.addElement("rollovericon").setText("getImage?image=icons/" + rolloverIconPath); //$NON-NLS-1$ //$NON-NLS-2$
} else {
dirNode.addElement("rollovericon").setText(rolloverIconPath); //$NON-NLS-1$
}
}
}
String displayType = actionSequence.getResultType();
if ((displayType == null) || ("none".equalsIgnoreCase(displayType))) { //$NON-NLS-1$
// this should be hidden from users
dirNode.addAttribute("visible", "false"); //$NON-NLS-1$ //$NON-NLS-2$
} else {
dirNode.addAttribute("visible", "true"); //$NON-NLS-1$ //$NON-NLS-2$
dirNode.addAttribute("displaytype", displayType); //$NON-NLS-1$
}
ISubscriptionRepository subscriptionRepository = PentahoSystem.get(ISubscriptionRepository.class, getSession());
boolean subscribable = subscriptionRepository.getContentByActionReference(actionSequence.getSolutionName() + '/'
+ actionSequence.getSolutionPath() + '/' + actionSequence.getSequenceName()) != null;
dirNode.addElement("properties").setText("subscribable=" + Boolean.toString(subscribable)); //$NON-NLS-1$ //$NON-NLS-2$
}
protected boolean publishIcon(final String dirPath, String iconPath) {
String pathSeperator = new StringBuffer().append(ISolutionRepository.SEPARATOR).toString();
if (!iconPath.startsWith(pathSeperator)) {
iconPath = pathSeperator + iconPath;
}
String iconRepositoryPath = dirPath + iconPath;
RepositoryFile iconSource = (RepositoryFile) getFileByPath(iconRepositoryPath, ISolutionRepository.ACTION_EXECUTE);
if (iconSource != null) {
File tmpDir = getFile("system/tmp/icons", true); //$NON-NLS-1$
tmpDir.mkdirs();
File iconDestintation = new File(tmpDir.getAbsoluteFile() + File.separator + iconPath);
if (iconDestintation.exists()) {
iconDestintation.delete();
}
try {
FileOutputStream outputStream = new FileOutputStream(iconDestintation);
outputStream.write(iconSource.getData());
outputStream.flush();
outputStream.close();
} catch (FileNotFoundException e) {
// this one is not very likey
} catch (IOException e) {
}
return true;
} else {
return false;
}
}
public Document getSolutions(final String solutionName, final String pathName, final int actionOperation,
final boolean visibleOnly) {
return getCachedSolutionDocument(solutionName, pathName, actionOperation);
}
private String getPathNames(final String solution, final String path) {
String key = DbBasedSolutionRepository.BREAD_CRUMBS_TAG + buildDirectoryPath(solution, path, null);
return (String) getRepositoryObjectFromCache(key);
}
public Document getSolutionTree(final int actionOperation) {
return getSolutionTree(actionOperation, defaultSolutionFilter);
}
public Document getSolutionTree(final int actionOperation, final ISolutionFilter filter) {
Document document = DocumentHelper.createDocument();
getRootFolder(actionOperation);
if (!hasAccess(rootDirectory, actionOperation)) {
if (SolutionRepositoryBase.logger.isDebugEnabled()) {
SolutionRepositoryBase.logger.debug(Messages.getInstance().getString(
"SolutionRepository.ACCESS_DENIED", rootDirectory.getFullPath(), Integer.toString(actionOperation))); //$NON-NLS-1$
}
return document; // Empty Document
}
Element root = document.addElement("tree"); //$NON-NLS-1$
SolutionReposHelper.processSolutionTree(root, rootDirectory, filter, actionOperation);
return document;
}
public Document getSolutionStructure(final int actionOperation) {
Document document = DocumentHelper.createDocument();
getRootFolder(actionOperation);
if (rootDirectory == null) {
return null;
}
Element root = document.addElement(SolutionRepositoryBase.ROOT_NODE_NAME).addAttribute(
SolutionRepositoryBase.LOCATION_ATTR_NAME, rootDirectory.getFullPath());
SolutionReposHelper.processSolutionStructure(root, rootDirectory, defaultSolutionFilter, actionOperation);
return document;
}
public boolean synchronizeSolutionWithSolutionSource(final IPentahoSession pSession) {
synchronized (lock) {
try {
RepositoryFile solution = (RepositoryFile) getRootFolder(ISolutionRepository.ACTION_EXECUTE);
if (solution != null) {
HibernateUtil.beginTransaction();
HibernateUtil.makeTransient(solution);
HibernateUtil.commitTransaction();
HibernateUtil.flushSession();
}
this.loadSolutionFromFileSystem(pSession, PentahoSystem.getApplicationContext().getSolutionPath(""), true); //$NON-NLS-1$
return true;
} catch (Exception ex) {
SolutionRepositoryBase.logger.error(ex);
}
return false;
}
}
public boolean solutionSynchronizationSupported() {
return true;
}
public String resetSolutionFromFileSystem(final IPentahoSession pSession) {
if (synchronizeSolutionWithSolutionSource(pSession)) {
return Messages.getInstance().getString("SolutionRepository.INFO_0009_RESET_SUCCESS"); //$NON-NLS-1$
} else {
return Messages.getInstance().getString("SolutionRepository.ERROR_0013_RESET_FAILED", "Error"); //$NON-NLS-1$ //$NON-NLS-2$
}
}
/** *************************** New Update DB Repository Classes and Methods ******************************* */
/**
* This method loads solution files and folders from the file system into the RDBMS repository.
*
* @param pSession Users' Session
* @param solutionRoot The file system root folder
* @param deleteOrphans Whether to delete stranded references from RDBMS repository
* @return List of orphans that were deleted - returns list of deleted solution files.
* @throws RepositoryException
* @author mbatchel
*/
public List loadSolutionFromFileSystem(final IPentahoSession pSession, final String solutionRoot,
final boolean deleteOrphans) throws RepositoryException {
synchronized (lock) {
SolutionRepositoryBase.logger.info(Messages.getInstance().getString("SolutionRepository.INFO_0001_BEGIN_LOAD_DB_REPOSITORY")); //$NON-NLS-1$
HibernateUtil.beginTransaction();
File solutionFile = new File(solutionRoot);
RepositoryFile solution = null;
try {
if (solutionFile.isDirectory()) {
Map reposFileStructure = this.getAllRepositoryModDates();
/*
* The fromBase and toBase are, for example: From Base: D:\japps\pentaho\my-solutions\solutions To Base:
* /solutions
*/
String fromBase = solutionFile.getAbsolutePath();
String toBase = (solutionFile.getName().charAt(0) == '/') ? solutionFile.getName()
: "/" + solutionFile.getName(); //$NON-NLS-1$
RepositoryUpdateHelper updateHelper = new RepositoryUpdateHelper(fromBase, toBase, reposFileStructure, this);
//
// Check to see if we're just doing a refresh...
//
InfoHolder checkBase = (InfoHolder) reposFileStructure.get(toBase);
if (checkBase != null) {
// It's there - we're refreshing
checkBase.touched = true;
// Get the solution object from Hibernate
solution = (RepositoryFile) internalGetFileByPath(null); // Hibernate Query
updateHelper.createdOrRetrievedFolders.put(toBase, solution); // Store for later reference
} else {
solution = new RepositoryFile(solutionFile.getName(), null, null, solutionFile.lastModified()); // Create
// entry
// Put the created folder into the created map for later use
updateHelper.createdOrRetrievedFolders.put(toBase, solution); // Store for later reference
SolutionRepositoryBase.logger.info(Messages.getInstance().getString(
"SolutionRepository.INFO_0002_UPDATED_FOLDER", solution.getFullPath())); //$NON-NLS-1$
}
repositoryName = solution.getFullPath() + ISolutionRepository.SEPARATOR;
// Find and record changes and updates
recurseCheckUpdatedFiles(updateHelper, solutionFile);
//
// The following lines are order dependent
//
// Handle updated Files and Folders
updateHelper.processUpdates();
// Handle added folders and files
updateHelper.processAdditions();
// Save solution state
HibernateUtil.makePersistent(solution);
// Process deletions
List deletions = updateHelper.processDeletions(deleteOrphans);
// Publish ACLs
IAclPublisher aclPublisher = PentahoSystem.get(IAclPublisher.class, pSession);
if (aclPublisher != null) {
aclPublisher.publishDefaultAcls(solution);
}
// Tell Hibernate we're ready for a commit - we're done now
HibernateUtil.commitTransaction();
HibernateUtil.flushSession();
// The next two lines were from the old code
resetRepository(); // Clear the cache of old stuff
SolutionRepositoryBase.logger.info(Messages.getInstance().getString("SolutionRepository.INFO_0003_END_LOAD_DB_REPOSITORY")); //$NON-NLS-1$
return deletions;
} else {
throw new RepositoryException(Messages.getInstance().getString(
"SolutionRepository.ERROR_0012_INVALID_SOLUTION_ROOT", solutionRoot)); //$NON-NLS-1$
}
} catch (HibernateException hibernateException) {
// re-throw exception so that it abandons the process
try {
HibernateUtil.rollbackTransaction();
} catch (HibernateException ignored) {
SolutionRepositoryBase.logger.error("SolutionRepository.ERROR_0011_TRANSACTION_FAILED", ignored); //$NON-NLS-1$
}
throw new RepositoryException(hibernateException);
} catch (IOException ex) {
// re-throw exception so that it abandons the process
throw new RepositoryException(ex);
}
}
}
/**
* This method builds up lists of the files modified (based on date/time), new folders, and new files.
*
* @param reposFileStructure Map of what's currently in the DB repository
* @param solutionFile The folder to begin working through
* @param updatedFiles List of files updated
* @param newFolders List of new folders
* @param newFiles List of new files
* @throws IOException
* @author mbatchel
*/
private void recurseCheckUpdatedFiles(final RepositoryUpdateHelper updateHelper, final File solutionFile)
throws IOException {
File[] files = solutionFile.listFiles();
for (File aFile : files) {
if (aFile.isDirectory()) {
String directoryName = aFile.getName();
if (!SolutionReposHelper.ignoreDirectory(directoryName)) {
updateHelper.recordFolder(aFile);
recurseCheckUpdatedFiles(updateHelper, aFile);
}
} else {
if (!SolutionReposHelper.ignoreFile(aFile.getName())) {
updateHelper.recordFile(aFile);
}
}
} // End For
}
/**
* This runs a Hibernate query to get just paths and modified times from the repository.
*
* @return Map [path => InfoHolder(LastModifiedDate)
* @author mbatchel
*/
private Map getAllRepositoryModDates() {
Session hibSession = HibernateUtil.getSession();
String nameQuery = "org.pentaho.platform.repository.solution.dbbased.RepositoryFile.filesWithModDates"; //$NON-NLS-1$
Query qry = hibSession.getNamedQuery(nameQuery).setCacheable(true);
List rtn = qry.list();
HashMap modMap = new HashMap();
for (int i = 0; i < rtn.size(); i++) {
Object[] aResult = (Object[]) rtn.get(i);
modMap.put(aResult[0], new InfoHolder(aResult[1], aResult[2]));
}
return modMap;
}
private RepositoryFile findRootRepositoryByName(final String rootRepositoryName) {
Session hibSession = HibernateUtil.getSession();
String nameQuery = "org.pentaho.platform.repository.solution.dbbased.RepositoryFile.findNamedRootSolutionFolders"; //$NON-NLS-1$
Query qry = hibSession.getNamedQuery(nameQuery).setCacheable(true);
qry.setString("fileName", rootRepositoryName); //$NON-NLS-1$
RepositoryFile rtn = null;
try {
rtn = (RepositoryFile) qry.uniqueResult();
} catch (HibernateException ex) {
logger.error(Messages.getInstance().getString("SolutionRepository.ERROR_0022_HIBERNATE_EXCEPTION", rootRepositoryName), ex); //$NON-NLS-1$
throw ex;
}
return rtn;
}
/**
* Never make this public as it doesn't check access. Used when an IPentahoSession is unavailable or irrelevant.
*/
protected ISolutionFile internalGetRootFolder() {
synchronized (lock) {
if (repositoryName != null) {
rootDirectory = findRootRepositoryByName(getRepositoryName());
if (null == rootDirectory) {
warn(Messages.getInstance().getString("SolutionRepository.WARN_0003_REPOSITORY_NOT_FOUND_BY_NAME", getRepositoryName())); //$NON-NLS-1$
}
} else {
Session hibSession = HibernateUtil.getSession();
String nameQuery = "org.pentaho.platform.repository.solution.dbbased.RepositoryFile.findAllRootSolutionFolders"; //$NON-NLS-1$
Query qry = hibSession.getNamedQuery(nameQuery).setCacheable(true);
RepositoryFile rtn = null;
try {
rtn = (RepositoryFile) qry.uniqueResult();
} catch (HibernateException ex) {
logger.error(Messages.getInstance().getString("SolutionRepository.ERROR_0022_HIBERNATE_EXCEPTION", rootDirectory //$NON-NLS-1$
.getFullPath()), ex);
throw ex;
}
if (rtn == null) {
return null;
}
repositoryName = rtn.getFullPath() + ISolutionRepository.SEPARATOR;
rootDirectory = rtn;
}
}
return rootDirectory;
}
@Override
public ISolutionFile getRootFolder(final int actionOperation) {
ISolutionFile rootFolder = internalGetRootFolder();
if (!hasAccess(rootFolder, actionOperation)) {
rootDirectory = null;
}
return rootDirectory;
}
public Document getFullSolutionTree(final int actionOperation, final ISolutionFilter filter, ISolutionFile startingFile) {
startingFile = startingFile == null ? getRootFolder(actionOperation) : startingFile;
return super.getFullSolutionTree(actionOperation, filter, startingFile);
}
protected RepositoryFile getSolutionById(final String anId) {
Session hibSession = HibernateUtil.getSession();
RepositoryFile rtn = (RepositoryFile) hibSession.load(RepositoryFile.class, anId);
return rtn;
}
protected List getChildrenFilesByParentId(final String parentId) {
Session hibSession = HibernateUtil.getSession();
String parentIdQuery = "org.pentaho.platform.repository.solution.dbbased.RepositoryFile.findChildrenFilesByParentId"; //$NON-NLS-1$
Query qry = hibSession.getNamedQuery(parentIdQuery).setString("parentId", parentId).setCacheable(true); //$NON-NLS-1$
return qry.list();
}
/**
* Check <param>systemPath</param> to see if it has the repository name, followed by "system", followed by anything
* else. If it matches, remove the repository name from the front of the path, and return the path. If not, simply
* return <param>systemPath</param>
*
* @param systemPath String containing a path.
* @return String <param>systemPath</param> with the repository name removed from the path
*/
private String removeRepositoryNameFromSystemPath(String systemPath) {
if (systemPath != null) {
Pattern p = Pattern.compile("[/\\\\]" + getRepositoryName() + "([/\\\\]system[/\\\\].*)"); //$NON-NLS-1$//$NON-NLS-2$
Matcher m = p.matcher(systemPath);
if (m.matches()) {
systemPath = m.group(1);
}
}
return systemPath;
}
/**
* Never make this public as it doesn't check access. Used when an IPentahoSession is unavailable or irrelevant.
*/
protected ISolutionFile internalGetFileByPath(final String path) {
String cleanPath = removeRepositoryNameFromSystemPath(path);
if (cleanPath == null) {
return internalGetRootFolder();
} else if (SolutionRepositoryBase.isSystemPath(cleanPath)) {
return super.getFileByPath(super.buildDirectoryPath(repositoryName, cleanPath), /* ignored */0);
} else {
String fullPath = cleanPath.replace('\\', ISolutionRepository.SEPARATOR); // use our file seperator
if ((repositoryName != null) && !fullPath.startsWith(repositoryName)) {
if (fullPath.startsWith("/") || fullPath.startsWith("\\")) { //$NON-NLS-1$ //$NON-NLS-2$
fullPath = repositoryName + fullPath.substring(1); // Strip off leading slash
} else {
fullPath = repositoryName + fullPath;
}
}
// TODO sbarkdull, this line should probably be removed, we shouldnt be cleaning up the path for the caller, the
// caller should be passing us a correct path string
fullPath = fullPath.replaceAll("//", "/"); //$NON-NLS-1$ //$NON-NLS-2$ // Make sure no double-slashes exist
if (fullPath.endsWith("/")) { //$NON-NLS-1$
fullPath = fullPath.substring(0, fullPath.length() - 1);
}
Session hibernateSession = HibernateUtil.getSession();
String nameQuery = "org.pentaho.platform.repository.solution.dbbased.RepositoryFile.findFileByPath"; //$NON-NLS-1$
Query qry = hibernateSession.getNamedQuery(nameQuery).setCacheable(true).setString("fullPath", fullPath); //$NON-NLS-1$
if (qry.list().size() > 0) {
RepositoryFile rtn = null;
try {
rtn = (RepositoryFile) qry.uniqueResult();
} catch (HibernateException ex) {
logger.error(Messages.getInstance().getString("SolutionRepository.ERROR_0022_HIBERNATE_EXCEPTION", cleanPath), ex); //$NON-NLS-1$
throw ex;
}
return rtn;
} else {
return null;
}
}
}
@Override
protected ISolutionFile getFileByPath(final String path, final int actionOperation) {
ISolutionFile file = internalGetFileByPath(path);
if (!hasAccess(file, actionOperation)) {
return null;
}
return file;
}
public ClassLoader getClassLoader(final String path) {
return new DbRepositoryClassLoader(path, this);
}
// --------------------------------------------------------------------
public boolean resourceExists(final String solutionPath, final int actionOperation) {
return getFileByPath(solutionPath, actionOperation) != null;
}
private boolean internalResourceExists(final String solutionPath) {
return internalGetFileByPath(solutionPath) != null;
}
public long resourceSize(final String solutionPath, final int actionOperation) {
RepositoryFile file = (RepositoryFile) getFileByPath(solutionPath, actionOperation);
if (file == null) {
return 0;
}
return file.getData().length;
}
@Override
public boolean removeSolutionFile(final String solutionPath) {
synchronized (lock) {
if (isSystemPath(solutionPath)) {
return false;
}
// Build the path
String fullPath = repositoryName;
String sepStr = Character.toString(ISolutionRepository.SEPARATOR);
if ((solutionPath != null) && (solutionPath.length() > 0)) {
if (fullPath.endsWith(sepStr) && solutionPath.startsWith(sepStr)) {
fullPath += solutionPath.substring(1);
} else if (!fullPath.endsWith(sepStr) && !solutionPath.startsWith(sepStr)) {
fullPath += sepStr + solutionPath.substring(1);
} else {
fullPath += solutionPath;
}
} else {
if (fullPath.endsWith(sepStr)) {
fullPath = fullPath.substring(0, fullPath.length() - 1); // take off the path separator character
}
}
RepositoryFile file = (RepositoryFile) getFileByPath(fullPath, ISolutionRepository.ACTION_DELETE);
if (file == null) {
return false;
}
RepositoryFile parent = (RepositoryFile) file.retrieveParent();
super.removeSolutionFile(solutionPath);
if (parent != null) { // this take care of the case of deleting the repository completely
parent.removeChildFile(file);
}
Session hibSession = HibernateUtil.getSession();
Transaction trans = hibSession.beginTransaction();
hibSession.delete(file);
trans.commit();
resetRepository();
return true;
}
}
@Override
public boolean removeSolutionFile(final String solution, final String path, final String fileName) {
String solutionPath = ""; //$NON-NLS-1$
if ((solution != null) && (solution.length() > 0)) {
solutionPath += solution;
}
if ((path != null) && (path.length() > 0)) {
solutionPath += ISolutionRepository.SEPARATOR + path;
}
if ((fileName != null) && (fileName.length() > 0)) {
solutionPath += ISolutionRepository.SEPARATOR + fileName;
}
return removeSolutionFile(solutionPath);
}
public boolean deleteRepository(final String repositoryNameToDelete) {
String oldName = getRepositoryName();
setRepositoryName(repositoryNameToDelete);
boolean result = removeSolutionFile("", "", ""); //$NON-NLS-1$ //$NON-NLS-2$//$NON-NLS-3$
setRepositoryName(oldName);
return result;
}
/**
* @return Returns the repositoryName.
*/
public String getRepositoryName() {
return repositoryName == null ? repositoryName : repositoryName.substring(1, repositoryName.length() - 1);
}
/**
* @param repositoryName The repositoryName to set.
*/
public void setRepositoryName(final String value) {
if (value == null) {
repositoryName = null;
return;
}
String repoName = value;
String pathSep = new StringBuffer().append(ISolutionRepository.SEPARATOR).toString();
if (!repoName.startsWith(pathSep)) {
repoName = pathSep + repoName;
}
if (!repoName.endsWith(pathSep)) {
repoName += pathSep;
}
this.repositoryName = repositoryName != null ? repositoryName : repoName;
}
/**
* Add security related attributes to <param>node</param>. The attributes are: aclAdministration aclExecute
* aclSubscribe aclModifyAcl Their values will be "true" or "false", depending on the corresponding property in the
* <param>entry</param> parameter.
*
* @param entry PentahoAclEntry
* @param node Element
*/
@SuppressWarnings("deprecation")
private void setXMLPermissionAttributes(final IPentahoAclEntry entry, final Element node) {
node.addAttribute("aclAdministration", //$NON-NLS-1$
Boolean.toString(entry.isPermitted(IPentahoAclEntry.PERM_ADMINISTRATION)));
node.addAttribute("aclExecute", //$NON-NLS-1$
Boolean.toString(entry.isPermitted(IPentahoAclEntry.PERM_EXECUTE)));
node.addAttribute("aclSubscribe", //$NON-NLS-1$
Boolean.toString(entry.isPermitted(IPentahoAclEntry.PERM_SUBSCRIBE)));
node.addAttribute("aclModifyAcl", //$NON-NLS-1$
Boolean.toString(entry.isPermitted(IPentahoAclEntry.PERM_UPDATE_PERMS)
|| SecurityHelper.isPentahoAdministrator(getSession())));
}
public void exitPoint() {
try {
HibernateUtil.commitTransaction();
HibernateUtil.flushSession();
} catch (Throwable t) {
t.printStackTrace();
}
try {
HibernateUtil.closeSession();
} catch (Throwable t) {
t.printStackTrace();
}
}
@Override
public int addSolutionFile(final String baseUrl, String path, final String fileName, final byte[] data,
boolean overwrite) {
synchronized (lock) {
// baseUrl is ignored
// We handle publish to the system folder differently since it's not in the DB.
while ((path != null) && (path.endsWith("/") || path.endsWith("\\"))) { //$NON-NLS-1$ //$NON-NLS-2$
path = path.substring(0, path.length() - 1);
}
// do not allow publishing to root path
if (StringUtil.isEmpty(path)) {
logger.error(Messages.getInstance().getErrorString("SolutionRepository.ERROR_0023_INVALID_PUBLISH_LOCATION_ROOT")); //$NON-NLS-1$
return ISolutionRepository.FILE_ADD_FAILED;
}
// allow any user to add to system/tmp (e.g. during new analysis view)
if ((SolutionRepositoryBase.isSystemPath(path) && isPentahoAdministrator() || SolutionRepositoryBase
.isSystemTmpPath(path))) {
// add file using file based technique to send it to disk
return super.addSolutionFile(baseUrl, path, fileName, data, overwrite);
}
RepositoryFile parent = (RepositoryFile) getFileByPath(path, ISolutionRepository.ACTION_EXECUTE);
// no access check here because we must know if the file truly exists
RepositoryFile reposFile = (RepositoryFile) internalGetFileByPath(path + ISolutionRepository.SEPARATOR + fileName);
HibernateUtil.beginTransaction();
if (reposFile == null) {
if ((parent == null) || !parent.isDirectory()
|| (!hasAccess(parent, ISolutionRepository.ACTION_CREATE) && !isPentahoAdministrator())) {
HibernateUtil.commitTransaction();
HibernateUtil.flushSession();
return ISolutionRepository.FILE_ADD_FAILED;
}
try {
reposFile = new RepositoryFile(fileName, parent, data);
HibernateUtil.commitTransaction();
HibernateUtil.flushSession();
resetRepository();
super.addSolutionFile(baseUrl, path, fileName, data, overwrite);
return ISolutionRepository.FILE_ADD_SUCCESSFUL;
} catch (Exception e) {
SolutionRepositoryBase.logger.error(e);
return ISolutionRepository.FILE_ADD_FAILED;
}
}
if (!overwrite) {
HibernateUtil.commitTransaction();
HibernateUtil.flushSession();
return ISolutionRepository.FILE_EXISTS;
}
if (hasAccess(reposFile, ISolutionRepository.ACTION_UPDATE) || isPentahoAdministrator()) {
reposFile.setData(data);
super.addSolutionFile(baseUrl, path, fileName, data, overwrite);
resetRepository();
} else {
HibernateUtil.commitTransaction();
HibernateUtil.flushSession();
return ISolutionRepository.FILE_ADD_FAILED;
}
try {
HibernateUtil.commitTransaction();
HibernateUtil.flushSession();
return ISolutionRepository.FILE_ADD_SUCCESSFUL;
} catch (Exception e) {
SolutionRepositoryBase.logger.error(e);
return ISolutionRepository.FILE_ADD_FAILED;
}
}
}
@Override
public int addSolutionFile(final String baseUrl, final String path, final String fileName, final File f,
final boolean overwrite) {
// TODO mlowery Allow this method for Pentaho administrators only. Use a RunAsManager when calling this method
// from within this class. That way you prevent external callers from directly calling this method.
// baseUrl is ignored
byte[] bytes;
try {
bytes = FileHelper.getBytesFromFile(f);
} catch (IOException e) {
error(Messages.getInstance().getErrorString("SolutionRepository.ERROR_0014_COULD_NOT_SAVE_FILE", fileName), e); //$NON-NLS-1$
return ISolutionRepository.FILE_ADD_FAILED;
}
return addSolutionFile(baseUrl, path, fileName, bytes, overwrite);
}
public String[] getAllActionSequences(final int actionOperation) {
Session hibSession = HibernateUtil.getSession();
String nameQuery = "org.pentaho.platform.repository.solution.dbbased.RepositoryFile.findAllActionSequences"; //$NON-NLS-1$
Query qry = hibSession.getNamedQuery(nameQuery).setCacheable(true);
List rtn = qry.list();
List<String> values = new ArrayList<String>();
Iterator iter = rtn.iterator();
while (iter.hasNext()) {
RepositoryFile file = (RepositoryFile) iter.next();
if (defaultSolutionFilter.keepFile(file, actionOperation)) {
String path = file.getFullPath();
path = path.substring(repositoryName.length());
values.add(path);
}
}
return values.toArray(new String[0]);
}
public long getSolutionFileLastModified(final String path, final int actionOperation) {
RepositoryFile file = (RepositoryFile) getFileByPath(path, actionOperation);
long mod = -1;
if (file != null) {
mod = file.getLastModified();
}
return mod;
}
public boolean supportsAccessControls() {
return true;
}
public int publish(final String baseUrl, final String path, final String fileName, final byte[] data,
final boolean overwrite) throws PentahoAccessControlException {
// TODO mlowery This should be wrapped in a transaction to ensure both steps (add file and set perm on file) happen
// together.
String fullPath = path + ISolutionRepository.SEPARATOR + fileName;
boolean alreadyExists = internalResourceExists(fullPath);
int res = addSolutionFile(baseUrl, path, fileName, data, overwrite);
if ((res == ISolutionRepository.FILE_ADD_SUCCESSFUL) && !alreadyExists) {
// get the file
ISolutionFile justPublishedFile = internalGetFileByPath(fullPath);
// entire ACL is replaced for new files
SpringSecurityPermissionMgr permissionMgr = SpringSecurityPermissionMgr.instance();
HibernateUtil.beginTransaction();
if (SecurityHelper.canHaveACLS(justPublishedFile)) {
permissionMgr.setPermissions(getDefaultPublishAcl(), justPublishedFile);
}
}
if ((res == ISolutionRepository.FILE_ADD_SUCCESSFUL) && (fileName != null)
&& (fileName.toLowerCase().endsWith(".xmi"))) { //$NON-NLS-1$
IMetadataDomainRepository repo = PentahoSystem.get(IMetadataDomainRepository.class, null);
// this call forces a reload of the domains
repo.reloadDomains();
}
return res;
}
/**
* Returns the ACL to set on newly published content.
*
* @return an ACL
*/
protected Map<IPermissionRecipient, IPermissionMask> getDefaultPublishAcl() {
Map<IPermissionRecipient, IPermissionMask> acl = new HashMap<IPermissionRecipient, IPermissionMask>();
// the publisher gets full control
acl.put(new SimpleSession(getSession()), new SimplePermissionMask(IPentahoAclEntry.PERM_FULL_CONTROL));
IPentahoSession sess = getSession();
IAclVoter voter = PentahoSystem.get(IAclVoter.class, sess);
// and the Pentaho administrator gets full control
acl.put(new SimpleRole(voter.getAdminRole().getAuthority()), new SimplePermissionMask(
IPentahoAclEntry.PERM_FULL_CONTROL));
return acl;
}
public int publish(final String baseUrl, final String path, final String fileName, final File f,
final boolean overwrite) throws PentahoAccessControlException {
byte[] bytes;
try {
bytes = FileHelper.getBytesFromFile(f);
} catch (IOException e) {
error(Messages.getInstance().getErrorString("SolutionRepository.ERROR_0014_COULD_NOT_SAVE_FILE", fileName), e); //$NON-NLS-1$
return ISolutionRepository.FILE_ADD_FAILED;
}
return publish(baseUrl, path, fileName, bytes, overwrite);
}
public void share(final ISolutionFile file, final List<IPermissionRecipient> shareRecipients) {
for (IPermissionRecipient shareRecipient : shareRecipients) {
addPermission(file, shareRecipient, new SimplePermissionMask(IPentahoAclEntry.PERM_EXECUTE_SUBSCRIBE));
}
}
/*
* Unfortunately, the contract for IAclHolder.getAccessControls() doesn't allow us to tell whether someone
* deliberately set an empty ACL or is simply inheriting. We have to assume that every time we see an empty ACL,
* it is because it was inheriting.
*/
public void addPermission(final ISolutionFile file, final IPermissionRecipient recipient,
final IPermissionMask permission) {
if (hasAccess(file, ISolutionRepository.ACTION_SHARE)) {
SpringSecurityPermissionMgr permissionMgr = SpringSecurityPermissionMgr.instance();
Map<IPermissionRecipient, IPermissionMask> acl = permissionMgr.getPermissions(file);
if (acl.isEmpty()) {
// no direct permissions; get the effective acls
acl = permissionMgr.getEffectivePermissions(file);
}
acl.put(recipient, permission);
try {
setPermissions(file, acl);
} catch (PentahoAccessControlException ignored) {
// unfortunately, throwing this exception would cause a cascade of methods to have to throw it
}
}
}
public void setPermissions(final ISolutionFile file, final Map<IPermissionRecipient, IPermissionMask> acl)
throws PentahoAccessControlException {
if (!SecurityHelper.canHaveACLS(file)) {
throw new PentahoAccessControlException(Messages.getInstance().getString(
"SolutionRepository.ACCESS_DENIED", file.getFullPath(), Integer.toString(ISolutionRepository.ACTION_SHARE))); //$NON-NLS-1$
}
if (hasAccess(file, ISolutionRepository.ACTION_SHARE)) {
SpringSecurityPermissionMgr permissionMgr = SpringSecurityPermissionMgr.instance();
permissionMgr.setPermissions(acl, file);
} else {
throw new PentahoAccessControlException(Messages.getInstance().getString(
"SolutionRepository.ACCESS_DENIED", file.getFullPath(), Integer.toString(ISolutionRepository.ACTION_SHARE))); //$NON-NLS-1$
}
}
/**
* TODO mlowery If we had a READ_PERMS bit, then it would be enforced here. Instead, we use ACTION_EXECUTE.
*/
public Map<IPermissionRecipient, IPermissionMask> getPermissions(final ISolutionFile file) {
if (!SecurityHelper.canHaveACLS(file)) {
return Collections.emptyMap();
}
if (hasAccess(file, ISolutionRepository.ACTION_EXECUTE)) {
SpringSecurityPermissionMgr permissionMgr = SpringSecurityPermissionMgr.instance();
return permissionMgr.getPermissions(file);
} else {
return null;
}
}
/**
* If we had a READ_PERMS bit, then it would be enforced here. Instead, we use ACTION_EXECUTE.
*/
public Map<IPermissionRecipient, IPermissionMask> getEffectivePermissions(final ISolutionFile file) {
if (!SecurityHelper.canHaveACLS(file)) {
return Collections.emptyMap();
}
if (hasAccess(file, ISolutionRepository.ACTION_EXECUTE)) {
SpringSecurityPermissionMgr permissionMgr = SpringSecurityPermissionMgr.instance();
return permissionMgr.getEffectivePermissions(file);
} else {
return null;
}
}
@Override
public ISolutionFile createFolder(final File newFolder) throws IOException {
synchronized (lock) {
HibernateUtil.beginTransaction();
try {
if (!(isPathedUnderSolutionRoot(newFolder))) {
throw new IOException(Messages.getInstance().getErrorString("SolutionRepository.ERROR_0021_FILE_NOT_ADDED", newFolder //$NON-NLS-1$
.getName()));
}
String newFolderCanonicalPath = newFolder.getCanonicalPath();
String relativePath = newFolderCanonicalPath.substring(rootCanonicalName.length());
if (relativePath.startsWith(File.separator)) {
relativePath = relativePath.substring(1);
}
// get the parent folder
int slashIndex = relativePath.indexOf(File.separator);
String relativePathToParent = null;
if (slashIndex > -1) {
relativePathToParent = relativePath.substring(0, slashIndex);
}
ISolutionFile parentFolder = getFileByPath(relativePathToParent, ISolutionRepository.ACTION_CREATE);
if (parentFolder == null) { // no access
return null;
}
String solutionRoot = PentahoSystem.getApplicationContext().getSolutionPath(""); //$NON-NLS-1$
File solutionFile = new File(solutionRoot);
if (solutionFile.isDirectory()) {
Map reposFileStructure = this.getAllRepositoryModDates();
/*
* The fromBase and toBase are, for example: From Base: D:\japps\pentaho\my-solutions\solutions To Base:
* /solutions
*/
String fromBase = solutionFile.getAbsolutePath();
String toBase = (solutionFile.getName().charAt(0) == '/') ? solutionFile.getName()
: "/" + solutionFile.getName(); //$NON-NLS-1$
RepositoryUpdateHelper updateHelper = new RepositoryUpdateHelper(fromBase, toBase, reposFileStructure, this);
ISolutionFile returnFile = updateHelper.createFolder(newFolder);
super.createFolder(newFolder);
return returnFile;
}
} finally {
HibernateUtil.commitTransaction();
HibernateUtil.flushSession();
}
return null;
}
}
private class DefaultSolutionFilter implements ISolutionFilter {
public boolean keepFile(final ISolutionFile solutionFile, final int actionOperation) {
if (solutionFile instanceof IAclHolder) {
return hasAccess(solutionFile, actionOperation);
} else {
return true;
}
}
}
private class DefaultSolutionAttributeContributor implements ISolutionAttributeContributor {
public void contributeAttributes(final ISolutionFile solutionFile, final Element childNode) {
if (solutionFile instanceof IAclHolder) {
IPentahoSession sess = getSession();
IAclVoter voter = PentahoSystem.get(IAclVoter.class, sess);
IPentahoAclEntry access = voter.getEffectiveAcl(sess, (IAclHolder) solutionFile);
if (access != null) {
setXMLPermissionAttributes(access, childNode);
}
}
}
}
}