/* * ome.services.blitz.repo.PublicRepositoryI * *------------------------------------------------------------------------------ * Copyright (C) 2006-2015 University of Dundee. All rights reserved. * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * 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. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * *------------------------------------------------------------------------------ * * */ package ome.services.blitz.repo; import java.io.File; import java.io.PrintWriter; import java.io.StringWriter; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList.Builder; import org.apache.commons.io.filefilter.FileFilterUtils; import org.apache.commons.io.filefilter.IOFileFilter; import org.apache.commons.io.filefilter.NameFileFilter; import org.apache.commons.lang.StringUtils; import org.hibernate.Session; import org.postgresql.util.PSQLException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.transaction.annotation.Transactional; import Ice.Current; import ome.api.IQuery; import ome.api.RawFileStore; import ome.formats.importer.ImportConfig; import ome.formats.importer.OMEROWrapper; import ome.services.blitz.impl.AbstractAmdServant; import ome.services.blitz.impl.ServiceFactoryI; import ome.services.blitz.repo.path.FilePathRestrictionInstance; import ome.services.blitz.repo.path.FilePathRestrictions; import ome.services.blitz.repo.path.FsFile; import ome.services.blitz.repo.path.MakePathComponentSafe; import ome.services.blitz.repo.path.ServerFilePathTransformer; import ome.services.blitz.util.BlitzExecutor; import ome.services.blitz.util.ChecksumAlgorithmMapper; import ome.services.blitz.util.FindServiceFactoryMessage; import ome.services.blitz.util.RegisterServantMessage; import ome.services.util.Executor; import ome.services.util.SleepTimer; import ome.system.OmeroContext; import ome.system.Principal; import ome.system.ServiceFactory; import ome.util.SqlAction; import ome.util.checksum.ChecksumProviderFactory; import ome.util.messages.InternalMessage; import omero.InternalException; import omero.RLong; import omero.RMap; import omero.RType; import omero.SecurityViolation; import omero.ServerError; import omero.ValidationException; import omero.api.RawFileStorePrx; import omero.api.RawFileStorePrxHelper; import omero.api.RawPixelsStorePrx; import omero.api.RawPixelsStorePrxHelper; import omero.api._RawFileStoreTie; import omero.api._RawPixelsStoreTie; import omero.cmd.AMD_Session_submit; import omero.cmd.Delete2; import omero.cmd.HandlePrx; import omero.grid._RepositoryOperations; import omero.grid._RepositoryTie; import omero.model.ChecksumAlgorithm; import omero.model.OriginalFile; import omero.model.enums.ChecksumAlgorithmSHA1160; import omero.util.IceMapper; /** * An implementation of the PublicRepository interface. * * @author Colin Blackburn <cblackburn at dundee dot ac dot uk> * @author Josh Moore, josh at glencoesoftware.com * @author m.t.b.carroll@dundee.ac.uk */ public class PublicRepositoryI implements _RepositoryOperations, ApplicationContextAware { public static class AMD_submit implements AMD_Session_submit { HandlePrx ret; Exception ex; public void ice_response(HandlePrx __ret) { this.ret = __ret; } public void ice_exception(Exception ex) { this.ex = ex; } } /** key for finding the real session UUID under sudo */ static final String SUDO_REAL_SESSIONUUID = "omero.internal.sudo.real:" + omero.constants.SESSIONUUID.value; /** key for finding the real group name under sudo */ static final String SUDO_REAL_GROUP_NAME = "omero.internal.sudo.real:" + omero.constants.GROUP.value; private final static Logger log = LoggerFactory.getLogger(PublicRepositoryI.class); private final static IOFileFilter DEFAULT_SKIP = FileFilterUtils.notFileFilter( FileFilterUtils.orFileFilter(new NameFileFilter(".omero"), new NameFileFilter(".git"))); /** * Mimetype used to connote a directory {@link OriginalFile} object. */ public static final String DIRECTORY_MIMETYPE = "Directory"; /** media type for import logs */ public static final String IMPORT_LOG_MIMETYPE = "application/omero-log-file"; private /*final*/ long id; protected /*final*/ ServerFilePathTransformer serverPaths; protected final RepositoryDao repositoryDao; protected final ChecksumProviderFactory checksumProviderFactory; /* in descending order of preference */ protected final ImmutableList<ChecksumAlgorithm> checksumAlgorithms; protected /*final*/ FilePathRestrictions filePathRestrictions; protected OmeroContext context; private String repoUuid; public PublicRepositoryI(RepositoryDao repositoryDao, ChecksumProviderFactory checksumProviderFactory, String checksumAlgorithmSupported, String pathRules) throws ServerError { this.repositoryDao = repositoryDao; this.checksumProviderFactory = checksumProviderFactory; this.repoUuid = null; final Builder<ChecksumAlgorithm> checksumAlgorithmsBuilder = ImmutableList.builder(); for (final String term : checksumAlgorithmSupported.split(",")) { if (StringUtils.isNotBlank(term)) { checksumAlgorithmsBuilder.add(ChecksumAlgorithmMapper.getChecksumAlgorithm(term.trim())); } } this.checksumAlgorithms = checksumAlgorithmsBuilder.build(); if (this.checksumAlgorithms.isEmpty()) { throw new IllegalArgumentException("a checksum algorithm must be supported"); } final Set<String> terms = new HashSet<String>(); for (final String term : pathRules.split(",")) { if (StringUtils.isNotBlank(term)) { terms.add(term.trim()); } } final String[] termArray = terms.toArray(new String[terms.size()]); try { this.filePathRestrictions = FilePathRestrictionInstance.getFilePathRestrictions(termArray); } catch (NullPointerException e) { throw new ServerError(null, null, "unknown rule set named in: " + pathRules); } } /** * Called by the internal repository once initialization has taken place. * @param fileMaker * @param id */ public void initialize(FileMaker fileMaker, Long id, String repoUuid) throws ValidationException { this.id = id; File root = new File(fileMaker.getDir()); if (!root.isDirectory()) { throw new ValidationException(null, null, "Root directory must be a existing, readable directory."); } this.repoUuid = repoUuid; this.serverPaths = new ServerFilePathTransformer(); this.serverPaths.setBaseDirFile(root); this.serverPaths.setPathSanitizer(new MakePathComponentSafe(this.filePathRestrictions)); } /** * Wrap the current instance with an {@link Ice.TieBase} so that it * can be turned into a proxy. This is required due to the subclassing * between public repo instances. */ public Ice.Object tie() { return new _RepositoryTie(this); } public String getRepoUuid() { return repoUuid; } // // OriginalFile-based Interface methods // public OriginalFile root(Current __current) throws ServerError { return this.repositoryDao.getOriginalFile(this.id, __current); } // // Path-based Interface methods // public boolean fileExists(String path, Current __current) throws ServerError { final CheckedPath checked = checkPath(path, null, __current); final OriginalFile ofile = repositoryDao.findRepoFile(repoUuid, checked, null, __current); return (ofile != null); } public List<String> list(String path, Current __current) throws ServerError { List<OriginalFile> ofiles = listFiles(path, __current); List<String> contents = new ArrayList<String>(ofiles.size()); for (OriginalFile ofile : ofiles) { contents.add(ofile.getPath().getValue() + ofile.getName().getValue()); } return contents; } public List<OriginalFile> listFiles(String path, Current __current) throws ServerError { final CheckedPath checked = checkPath(path, null, __current).mustExist(); return repositoryDao.getOriginalFiles(repoUuid, checked, __current); } public RMap treeList(String path, Current __current) throws ServerError { final CheckedPath checked = checkPath(path, null, __current); return repositoryDao.treeList(repoUuid, checked, __current); } /** * Register an OriginalFile using its path * * @param path * Absolute path of the file to be registered. * @param mimetype * Mimetype as an RString * @param __current * ice context. * @return The OriginalFile with id set (unloaded) * */ public OriginalFile register(String path, omero.RString mimetype, Current __current) throws ServerError { final CheckedPath checked = checkPath(path, null, __current); return this.repositoryDao.register(repoUuid, checked, mimetype == null ? null : mimetype.getValue(), __current); } /** * Delete paths recursively as described in Repositories.ice. Internally * uses {@link #treeList(String, Ice.Current)} to build the recursive * list of files. * * @param files non-null, preferably non-empty list of files to check. * @param recursive See Repositories.ice for an explanation * @param force See Repositories.ice for an explanation * @param __current Non-null ice context. */ public HandlePrx deletePaths(String[] files, boolean recursive, boolean force, Current __current) throws ServerError { // TODO: This could be refactored to be the default in shared servants final Ice.Current adjustedCurr = makeAdjustedCurrent(__current); final String delId = Delete2.ice_staticId(); final Delete2 deleteRequest = (Delete2) getFactory(delId, adjustedCurr).create(delId); final List<Long> fileIds = new ArrayList<Long>(); deleteRequest.targetObjects = new HashMap<String, List<Long>>(); deleteRequest.targetObjects.put("OriginalFile", fileIds); for (String path : files) { // treeList() calls checkedPath RMap map = treeList(path, __current); _deletePaths(map, fileIds); } final FindServiceFactoryMessage msg = new FindServiceFactoryMessage(this, adjustedCurr); publishMessage(msg); final ServiceFactoryI sf = msg.getServiceFactory(); AMD_submit submit = submitRequest(sf, deleteRequest, adjustedCurr); return submit.ret; } private void _deletePaths(RMap map, List<Long> fileIds) { if (map != null && map.getValue() != null) { // Each of the entries for (RType value : map.getValue().values()) { // We know that the value for any key at the // "top" level is going to be a RMap RMap val = (RMap) value; if (val != null && val.getValue() != null) { if (val.getValue().containsKey("files")) { // then we need to recurse. files points to the next // "top" level. RMap files = (RMap) val.getValue().get("files"); _deletePaths(files, fileIds); } // Now after we've recursed, note the actual delete. RLong id = (RLong) val.getValue().get("id"); fileIds.add(id.getValue()); } } } } /** * Get the mimetype for a file. * * @param path * A path on a repository. * @param __current * ice context. * @return mimetype * */ public String mimetype(String path, Current __current) throws ServerError { return checkPath(path, null, __current).mustExist().getMimetype(); } public RawPixelsStorePrx pixels(String path, Current __current) throws ServerError { final CheckedPath checked = checkPath(path, null, __current); // See comment below in RawFileStorePrx Ice.Current adjustedCurr = makeAdjustedCurrent(__current); // Check that the file is in the DB and has minimally "r" permissions // Sets the ID value on the checked object. findInDb(checked, "r", adjustedCurr); BfPixelsStoreI rps; try { // FIXME ImportConfig should be injected // Is this ever used? No Memoizer is active here! // Perhaps better to use the PixelsService directly and // omit OMEROWrapper. rps = new BfPixelsStoreI(path, new OMEROWrapper(new ImportConfig()).getImageReader()); } catch (Throwable t) { if (t instanceof ServerError) { throw (ServerError) t; } else { omero.InternalException ie = new omero.InternalException(); IceMapper.fillServerError(ie, t); throw ie; } } // See comment below in RawFileStorePrx _RawPixelsStoreTie tie = new _RawPixelsStoreTie(rps); RegisterServantMessage msg = new RegisterServantMessage(this, tie, adjustedCurr); publishMessage(msg); Ice.ObjectPrx prx = msg.getProxy(); if (prx == null) { throw new omero.InternalException(null, null, "No ServantHolder for proxy."); } return RawPixelsStorePrxHelper.uncheckedCast(prx); } public RawFileStorePrx file(String path, String mode, Current __current) throws ServerError { final CheckedPath check = checkPath(path, null, __current); findOrCreateInDb(check, mode, __current); return createRepoRFS(check, mode, __current); } public RawFileStorePrx fileById(long fileId, Current __current) throws ServerError { CheckedPath checked = checkId(fileId, __current); return createRepoRFS(checked, "r", __current); } /** * Find the given path in the DB or create. * * "requiresWrite" is set to true unless the mode is "r". If requiresWrite * is true, then the caller needs the file to be modifiable (both on disk * and the DB). If this doesn't hold, then a SecurityViolation will be thrown. */ protected OriginalFile findInDb(CheckedPath checked, String mode, Ice.Current current) throws ServerError { final OriginalFile ofile = repositoryDao.findRepoFile(repoUuid, checked, null, current); if (ofile == null) { return null; // EARLY EXIT! } boolean requiresWrite = true; if ("r".equals(mode)) { requiresWrite = false; } checked.setId(ofile.getId().getValue()); boolean canUpdate = repositoryDao.canUpdate(ofile, current); if (requiresWrite && !canUpdate) { throw new omero.SecurityViolation(null, null, "requiresWrite is true but cannot modify"); } return ofile; } /* TODO: The server should not have any hard-coded preference for the SHA-1 algorithm * (which may not be the setting of omero.checksum.supported) in such a generic code path. * Clients wishing to assume SHA-1 for checksumming files created using this method should * somehow specify this to the server via the API. This method can then be removed. */ /** * Set the hasher of the original file of the given ID to SHA-1. Clears any previous hash. * @param id the ID of an original file * @param current the ICE method invocation context * @throws ServerError if there was a problem in executing this internal task */ @Deprecated private void setOriginalFileHasherToSHA1(final long id, Current current) throws ServerError { final Executor executor = this.context.getBean("executor", Executor.class); final Map<String, String> ctx = current.ctx; final String session = ctx.get(omero.constants.SESSIONUUID.value); final String group = ctx.get(omero.constants.GROUP.value); final Principal principal = new Principal(session, group, null); try { executor.execute(ctx, principal, new Executor.SimpleWork(this, "setOriginalFileHasherToSHA1", id) { @Transactional public Object doWork(Session session, ServiceFactory sf) { final IQuery iQuery = sf.getQueryService(); final ome.model.core.OriginalFile originalFile = iQuery.find(ome.model.core.OriginalFile.class, id); final ome.model.enums.ChecksumAlgorithm sha1 = iQuery.findByString(ome.model.enums.ChecksumAlgorithm.class, "value", ChecksumAlgorithmSHA1160.value); originalFile.setHash(null); originalFile.setHasher(sha1); sf.getUpdateService().saveObject(originalFile); return null; } }); } catch (Exception e) { throw (ServerError) new IceMapper().handleException(e, executor.getContext()); } } /** * Set the repository of the given original file to be this one. * @param originalFile the ID of the log file * @param current the Ice method invocation context */ //TODO: Should be refactored elsewhere. @Deprecated protected ome.model.core.OriginalFile persistLogFile(final ome.model.core.OriginalFile originalFile, Ice.Current current) throws ServerError { final Executor executor = this.context.getBean("executor", Executor.class); final Map<String, String> ctx = current.ctx; final String session = ctx.get(omero.constants.SESSIONUUID.value); final String group = ctx.get(omero.constants.GROUP.value); final Principal principal = new Principal(session, group, null); try { return (ome.model.core.OriginalFile) executor.execute(ctx, principal, new Executor.SimpleWork(this, "persistLogFile", id) { @Transactional(readOnly = false) public ome.model.core.OriginalFile doWork(Session session, ServiceFactory sf) { final ome.model.core.OriginalFile persisted = sf.getUpdateService().saveAndReturnObject(originalFile); getSqlAction().setFileRepo(Collections.singleton(persisted.getId()), repoUuid); return persisted; } }); } catch (Exception e) { throw (ServerError) new IceMapper().handleException(e, executor.getContext()); } } protected OriginalFile findOrCreateInDb(CheckedPath checked, String mode, Ice.Current curr) throws ServerError { return findOrCreateInDb(checked, mode, null, curr); } protected OriginalFile findOrCreateInDb(CheckedPath checked, String mode, String mimetype, Ice.Current curr) throws ServerError { OriginalFile ofile = findInDb(checked, mode, curr); if (ofile != null) { return ofile; } if (checked.exists()) { omero.grid.UnregisteredFileException ufe = new omero.grid.UnregisteredFileException(); ofile = (OriginalFile) new IceMapper().map(checked.asOriginalFile(mimetype)); ufe.file = ofile; throw ufe; } ofile = repositoryDao.register(repoUuid, checked, null, curr); final long originalFileId = ofile.getId().getValue(); setOriginalFileHasherToSHA1(originalFileId, curr); checked.setId(originalFileId); return ofile; } protected Ice.Current makeAdjustedCurrent(Ice.Current __current) { // WORKAROUND: See the comment in RawFileStoreI. // The most likely correction of this // is to have PublicRepositories not be global objects, but be created // on demand for each session via SharedResourcesI final String sessionUuid = __current.ctx.get(omero.constants.SESSIONUUID.value); final Ice.Current adjustedCurr = new Ice.Current(); adjustedCurr.ctx = __current.ctx; adjustedCurr.adapter = __current.adapter; adjustedCurr.operation = __current.operation; adjustedCurr.id = new Ice.Identity(__current.id.name, sessionUuid); return adjustedCurr; } /** * Provide a {@link Ice.Current} like the given one, except with the request context session UUID replaced. * @param current an {@link Ice.Current} instance * @param sessionUuid a new session UUID for the instance * @return a new {@link Ice.Current} instance like the given one but with the new session UUID */ protected Current sudo(Current current, String sessionUuid) { final Current sudoCurrent = makeAdjustedCurrent(current); sudoCurrent.ctx = new HashMap<String, String>(current.ctx); sudoCurrent.ctx.put(SUDO_REAL_SESSIONUUID, current.ctx.get(omero.constants.SESSIONUUID.value)); sudoCurrent.ctx.put(SUDO_REAL_GROUP_NAME, current.ctx.get(omero.constants.GROUP.value)); sudoCurrent.ctx.put(omero.constants.SESSIONUUID.value, sessionUuid); return sudoCurrent; } /** * Create, initialize, and register an {@link RepoRawFileStoreI} * with the proper setting (read or write). * * @param checked The file that will be read. Can't be null, * and must have ID set. * @param mode The mode for writing. If null, read-only. * @param __current The current user's session information. * @return A proxy ready to be returned to the user. * @throws ServerError * @throws InternalException */ protected RawFileStorePrx createRepoRFS(CheckedPath checked, String mode, Current __current) throws ServerError, InternalException { final Ice.Current adjustedCurr = makeAdjustedCurrent(__current); final BlitzExecutor be = context.getBean("throttlingStrategy", BlitzExecutor.class); RepoRawFileStoreI rfs; try { final RawFileStore service = repositoryDao.getRawFileStore( checked.getId(), checked, mode, __current); rfs = new RepoRawFileStoreI(be, service, adjustedCurr); rfs.setApplicationContext(this.context); } catch (Throwable t) { if (t instanceof ServerError) { throw (ServerError) t; } else { omero.InternalException ie = new omero.InternalException(); IceMapper.fillServerError(ie, t); throw ie; } } final _RawFileStoreTie tie = new _RawFileStoreTie(rfs); Ice.ObjectPrx prx = registerServant(tie, rfs, adjustedCurr); return RawFileStorePrxHelper.uncheckedCast(prx); } /** * Registers the given tie/servant combo with the service factory connected * to the current connection. If none is found, and exception will be * thrown. Once the tie/servant pair is registered, cleanup by the client * will cause this servant to be closed, etc. * * @param tie * @param servant * @param current * @return See above. * @throws ServerError */ Ice.ObjectPrx registerServant(Ice.Object tie, AbstractAmdServant servant, Ice.Current current) throws ServerError { final RegisterServantMessage msg = new RegisterServantMessage(this, tie, servant.getClass().getSimpleName(), current); publishMessage(msg); Ice.ObjectPrx prx = msg.getProxy(); if (prx == null) { throw new omero.InternalException(null, null, "No ServantHolder for proxy."); } return prx; } protected void publishMessage(final InternalMessage msg) throws ServerError, InternalException { try { this.context.publishMessage(msg); } catch (Throwable t) { if (t instanceof ServerError) { throw (ServerError) t; } else { omero.InternalException ie = new omero.InternalException(); IceMapper.fillServerError(ie, t); throw ie; } } } protected AMD_submit submitRequest(final ServiceFactoryI sf, final omero.cmd.Request req, final Ice.Current current) throws ServerError, InternalException { final AMD_submit submit = new AMD_submit(); sf.submit_async(submit, req, current); if (submit.ex != null) { IceMapper mapper = new IceMapper(); throw mapper.handleServerError(submit.ex, context); } else if (submit.ret == null) { throw new omero.InternalException(null, null, "No handle proxy found for: " + req); } return submit; } protected Ice.ObjectFactory getFactory(String id, Ice.Current current) { final Ice.Communicator ic = current.adapter.getCommunicator(); return ic.findObjectFactory(id); } /** * Create a nested path in the repository. Creates each directory * in the path is it doen't already exist. Silently returns if * the directory already exists. * * @param path * A path on a repository. * @param parents * Boolean switch like the "mkdir -p" flag in unix. * @param current * ice context. */ public void makeDir(String path, boolean parents, Current current) throws ServerError { CheckedPath checked = checkPath(path, null, current); repositoryDao.makeDirs(this, Arrays.asList(checked), parents, current); } public void makeDir(CheckedPath checked, boolean parents, Session s, ServiceFactory sf, SqlAction sql, ome.system.EventContext effectiveEventContext) throws ServerError { final LinkedList<CheckedPath> paths = new LinkedList<CheckedPath>(); while (!checked.isRoot) { paths.addFirst(checked); checked = checked.parent(); if (!parents) { break; // Only include last element } } if (paths.size() == 0) { if (parents) { throw new omero.ResourceError(null, null, "Cannot re-create root!"); } else{ log.debug("Ignoring re-creation of root"); return; } } makeCheckedDirs(paths, parents, s, sf, sql, effectiveEventContext); } /** * Internal method to be used by subclasses to perform any extra checks on * the listed of {@link CheckedPath} instances before allowing the creation * of directories. * * @param paths Not null, not empty. (Will be emptied by this method.) * @param parents "mkdir -p" like flag. * @param s The session * @param sf * @param sql * @param effectiveEventContext */ protected void makeCheckedDirs(final LinkedList<CheckedPath> paths, boolean parents, Session s, ServiceFactory sf, SqlAction sql, ome.system.EventContext effectiveEventContext) throws ServerError { CheckedPath checked; // Since we now have some number of elements, we start at the most // parent element and work our way down through all the parents. // If the file exists, then we check its permissions. If it doesn't // exist, it gets created. while (paths.size() > 1) { // Only possible if `parents` checked = paths.removeFirst(); if (checked.exists()) { if (!checked.isDirectory()) { throw new omero.ResourceError(null, null, "Path is not a directory."); } else if (!checked.canRead()) { throw new omero.ResourceError(null, null, "Directory is not readable"); } assertFindDir(checked, s, sf, sql); } else { // This will fail if the directory already exists try { repositoryDao.register(repoUuid, checked, DIRECTORY_MIMETYPE, sf, sql); } catch (ValidationException ve) { if (ve.getCause() instanceof PSQLException) { // Could have collided with another thread also creating the directory. // See Trac #11096 regarding originalfile table uniqueness of columns repo, path, name. // So, give the other thread time to complete registration. SleepTimer.sleepFor(1000); if (checked.exists()) { // The path now exists! It did not a moment ago. // We are not going to rethrow the validation exception, // so we otherwise note that something unexpected did occur. log.warn("retrying after exception in registering directory " + checked + ": " + ve.getCause()); // Another thread may have succeeded where this one failed, // so try this directory again. paths.add(0, checked); continue; } } // We cannot recover from the validation exception. throw ve; } } } // Now we are ready to work on the actual intended path. checked = paths.removeFirst(); // Size is now empty if (checked.exists()) { if (parents) { assertFindDir(checked, s, sf, sql); } else { throw new omero.ResourceError(null, null, "Path exists on disk: " + checked.fsFile); } } repositoryDao.register(repoUuid, checked, DIRECTORY_MIMETYPE, sf, sql); } // // // Utility methods // // public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.context = (OmeroContext) applicationContext; } /** * Create a new {@link CheckedPath} object based on the given user input. * This method is included to allow subclasses a chance to introduce their * own {@link CheckedPath} implementations. * * @param path * A path on a repository. * */ protected CheckedPath checkPath(final String path, ChecksumAlgorithm checksumAlgorithm, final Ice.Current curr) throws ValidationException { return new CheckedPath(this.serverPaths, path, this.checksumProviderFactory, checksumAlgorithm); } /** * Get an {@link OriginalFile} object based on its id. Returns null if * the file does not exist or does not belong to this repo. * * @param id * long, db id of original file. * @return OriginalFile object. * */ private CheckedPath checkId(final long id, final Ice.Current curr) throws SecurityViolation, ValidationException { // TODO: could getOriginalFile and getFile be reduced to a single call? final FsFile file = this.repositoryDao.getFile(id, curr, this.repoUuid); if (file == null) { throw new SecurityViolation(null, null, "FileNotFound: " + id); } final OriginalFile originalFile = this.repositoryDao.getOriginalFile(id, curr); if (originalFile == null) { /* reachable even if file != null because getFile uses SQL, * evading the filter on the HQL used here by getOriginalFile */ throw new SecurityViolation(null, null, "FileNotAccessible: " + id); } final CheckedPath checked = new CheckedPath(this.serverPaths,file.toString(), checksumProviderFactory, originalFile.getHasher()); checked.setId(id); return checked; } private void assertFindDir(final CheckedPath checked, Session s, ServiceFactory sf, SqlAction sql) throws omero.ServerError { if (null == repositoryDao.findRepoFile(sf, sql, repoUuid, checked, null)) { omero.ResourceError re = new omero.ResourceError(); IceMapper.fillServerError(re, new RuntimeException( "Directory exists but is not registered: " + checked)); throw re; } } // Utility function for passing stack traces back in exceptions. protected String stackTraceAsString(Throwable t) { StringWriter sw = new StringWriter(); t.printStackTrace(new PrintWriter(sw)); return sw.toString(); } }