/*
* Copyright (C) 2013 Glencoe Software, Inc. 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.nio.ByteBuffer;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import Ice.Current;
import ome.io.nio.FileBuffer;
import ome.services.blitz.fire.Registry;
import ome.services.blitz.util.ChecksumAlgorithmMapper;
import omero.cmd.ERR;
import omero.cmd.HandleI.Cancel;
import omero.cmd.Helper;
import omero.cmd.IRequest;
import omero.cmd.Response;
import omero.cmd.Unknown;
import omero.grid.InternalRepositoryPrx;
import omero.grid.RawAccessRequest;
import omero.grid.RepositoryException;
import omero.model.ChecksumAlgorithm;
/**
* Command request for accessing a repository directly.
*
* @author Josh Moore, josh at glencoesoftware.com
* @since 4.5.0
*/
public class RawAccessRequestI extends RawAccessRequest implements IRequest {
private static final long serialVersionUID = -303948503984L;
private static Logger log = LoggerFactory.getLogger(RawAccessRequestI.class);
private final Registry reg;
protected Helper helper;
protected InternalRepositoryPrx repo;
public RawAccessRequestI(Registry reg) {
this.reg = reg;
}
//
// IRequest methods
//
public Map<String, String> getCallContext() {
return null;
}
public void init(Helper helper) {
this.helper = helper;
if (!helper.getEventContext().isCurrentUserAdmin()) {
throw helper.cancel(new ERR(), new omero.SecurityViolation(),
"not-admin");
}
log.debug("Looking repo " + repoUuid);
try {
String proposedName = "InternalRepository-" + repoUuid;
InternalRepositoryPrx[] proxies = reg.lookupRepositories();
for (InternalRepositoryPrx prx : proxies) {
Ice.Identity id = prx.ice_getIdentity();
if (proposedName.equals(id.name)) {
repo = prx;
log.debug("Found repo " + repoUuid);
break;
}
}
}
catch (Exception e) {
throw helper.cancel(new ERR(), e, "registry-lookup", "repoUuid", repoUuid);
}
if (repo == null) {
throw helper.cancel(new Unknown(), null, "unknown-repo", "repoUuid",
repoUuid);
}
this.helper.setSteps(1);
}
public Object step(int step) throws Cancel {
helper.assertStep(step);
return rawAccess();
}
@Override
public void finish() throws Cancel {
// no-op
}
public void buildResponse(int step, Object object) {
helper.assertResponse(step);
helper.setResponseIfNull(((Response) object));
}
public Response getResponse() {
return helper.getResponse();
}
public Response rawAccess() {
try {
log.debug("Calling raw access for command " + command);
return repo.rawAccess(this);
}
catch (Throwable t) {
throw helper.cancel(new ERR(), t, "raw-access",
"command", command);
} finally {
log.debug("Done calling raw access for command " + command);
}
}
/**
* Method called locally to the repository during {@link #rawAccess()}. Only
* the remoteable fields should be accessed during this method call since this
* will be a copy of the object originally called.
*
* @param abstractRepositoryI
* @param servant
* @param __current
*/
public void local(AbstractRepositoryI abstractRepositoryI,
PublicRepositoryI servant, Current __current) throws Exception {
if ("touch".equals(command)) {
for (String arg : args) {
final CheckedPath checked = servant.checkPath(parse(arg), null, __current);
if (!checked.exists()) {
final CheckedPath parent = checked.parent();
if (!(parent.isDirectory() || checked.parent().mkdirs())) {
throw new RepositoryException(null, null, "cannot create directory: " + parent);
}
final FileBuffer buffer = checked.getFileBuffer("rw");
buffer.write(ByteBuffer.allocate(0));
buffer.close();
} else if (!checked.markModified()) {
throw new RepositoryException(null, null, "cannot touch file: " + checked);
}
}
} else if ("exists".equals(command)) {
final String arg = args.get(0);
final CheckedPath checked = servant.checkPath(parse(arg), null, __current);
if (!checked.exists()) {
throw new RepositoryException(null, null, "file does not exist: " + checked);
}
} else if ("mkdir".equals(command)) {
boolean parents = false;
for (String arg: args) {
if ("-p".equals(arg)) {
parents = true;
continue;
}
final CheckedPath checked = servant.checkPath(parse(arg), null, __current);
if (parents) {
checked.mkdirs();
} else {
checked.mkdir();
}
}
} else if ("rm".equals(command)) {
if (args.size() == 1) {
final CheckedPath checked = servant.checkPath(parse(args.get(0)), null, __current);
if (!checked.delete()) {
throw new omero.grid.FileDeleteException(null, null,
"Delete file failed: " + args.get(0));
}
} else {
throw new omero.ApiUsageException(null, null,
"Command: " + command + " takes just one argument");
}
} else if ("mv".equals(command)) {
if (args.size() == 2) {
final CheckedPath source = servant.checkPath(parse(args.get(0)), null, __current);
final CheckedPath target = servant.checkPath(parse(args.get(1)), null, __current);
boolean success = false;
if (target.exists() && target.isDirectory()) {
try {
source.moveToDir(target, false);
success = true;
} catch (java.io.IOException ex) {
success = false;
log.warn("IOException on moveToDir: {}->{}",
source, target, ex);
}
} else {
success = source.renameTo(target);
}
if (!success) {
throw new omero.ResourceError(null, null,
String.format("'mv %s %s' failed", source, target));
}
} else {
throw new omero.ApiUsageException(null, null,
"Command: " + command + " takes two arguments");
}
} else if ("checksum".equals(command)) {
if (args.size() == 3) {
final String checksumType = args.get(0);
final ChecksumAlgorithm algo = ChecksumAlgorithmMapper.getChecksumAlgorithm(checksumType);
final String expectedHash = args.get(1);
final CheckedPath checked = servant.checkPath(parse(args.get(2)), algo, __current);
final String currentHash = checked.hash();
if (!currentHash.equals(expectedHash)) {
// TODO: ADD ANNOTATION TO DATABASE HERE!
throw new omero.ResourceError(null, null, String.format(
"Checksum mismatch (%s): expected=%s found=%s",
checksumType, expectedHash, currentHash));
}
} else {
throw new omero.ApiUsageException(null, null,
"'checksum' requires HASHER HASH FILEPATH, not: " + args.toString());
}
} else {
throw new omero.ApiUsageException(null, null,
"Unknown command: " + command);
}
}
/**
* Prepend a "./" if necessary to make the path relative.
*/
//TODO: this could likely be removed once the prefix is always stripped.
private String parse(String arg) {
if (arg.startsWith("/")) {
arg = "./" + arg;
}
return arg;
}
}