package diskCacheV111.util ; import com.google.common.util.concurrent.ListenableFuture; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.security.auth.Subject; import java.util.Collections; import java.util.EnumSet; import java.util.List; import java.util.Set; import java.util.concurrent.TimeUnit; import diskCacheV111.vehicles.PnfsAddCacheLocationMessage; import diskCacheV111.vehicles.PnfsClearCacheLocationMessage; import diskCacheV111.vehicles.PnfsCreateEntryMessage; import diskCacheV111.vehicles.PnfsDeleteEntryMessage; import diskCacheV111.vehicles.PnfsFlagMessage; import diskCacheV111.vehicles.PnfsGetCacheLocationsMessage; import diskCacheV111.vehicles.PnfsGetParentMessage; import diskCacheV111.vehicles.PnfsMapPathMessage; import diskCacheV111.vehicles.PnfsMessage; import diskCacheV111.vehicles.PnfsRenameMessage; import diskCacheV111.vehicles.PnfsSetChecksumMessage; import diskCacheV111.vehicles.PoolFileFlushedMessage; import dmg.cells.nucleus.CellEndpoint; import dmg.cells.nucleus.CellMessageSender; import dmg.cells.nucleus.CellPath; import dmg.cells.nucleus.NoRouteToCellException; import org.dcache.acl.enums.AccessMask; import org.dcache.auth.attributes.Restriction; import org.dcache.cells.CellStub; import org.dcache.namespace.FileAttribute; import org.dcache.namespace.FileType; import org.dcache.util.Checksum; import org.dcache.util.ChecksumType; import org.dcache.vehicles.FileAttributes; import org.dcache.vehicles.PnfsCreateSymLinkMessage; import org.dcache.vehicles.PnfsGetFileAttributes; import org.dcache.vehicles.PnfsRemoveChecksumMessage; import org.dcache.vehicles.PnfsSetFileAttributes; import static com.google.common.base.Preconditions.checkState; import static org.dcache.namespace.FileAttribute.PNFSID; import static org.dcache.namespace.FileType.DIR; import static org.dcache.namespace.FileType.LINK; public class PnfsHandler implements CellMessageSender { private final String _poolName; private static final long DEFAULT_PNFS_TIMEOUT = TimeUnit.MINUTES.toMillis( 30); private final CellStub _cellStub; private Subject _subject; private Restriction _restriction; private static final Logger _logNameSpace = LoggerFactory.getLogger("logger.org.dcache.namespace." + PnfsHandler.class.getName()); private static CellStub createStub(CellPath path) { CellStub stub = new CellStub(); stub.setDestinationPath(path); stub.setTimeout(DEFAULT_PNFS_TIMEOUT); stub.setTimeoutUnit(TimeUnit.MILLISECONDS); stub.setFlags(CellEndpoint.SendFlag.RETRY_ON_NO_ROUTE_TO_CELL); return stub; } public PnfsHandler(CellEndpoint endpoint, CellPath pnfsManagerPath) { this(pnfsManagerPath); setCellEndpoint(endpoint); } public PnfsHandler(CellPath pnfsManagerPath) { this(createStub(pnfsManagerPath)); } public PnfsHandler(CellPath pnfsManagerPath, String poolName) { this(createStub(pnfsManagerPath), poolName); } public PnfsHandler(CellStub stub) { this(stub, "<client>"); } public PnfsHandler(CellStub stub, String poolName) { _cellStub = stub; _poolName = poolName; } /** * Copy constructor. The primary purpose is to create session * specific PnfsHandlers with a session specific subject. Notice * that the CellStub is shared between the two handlers and thus * the timeout will always be the same. * * @param handler The PnfsHandler to copy * @param subject The Subject to apply to the copy */ public PnfsHandler(PnfsHandler handler, Subject subject, Restriction restriction) { _poolName = handler._poolName; _cellStub = handler._cellStub; _subject = subject; _restriction = restriction; } @Override public void setCellEndpoint(CellEndpoint endpoint) { _cellStub.setCellEndpoint(endpoint); } public void setSubject(Subject subject) { _subject = subject; } public void setRestriction(Restriction restriction) { _restriction = restriction; } /** * Sends a PnfsMessage to PnfsManager. */ public void send(PnfsMessage msg) { if (_cellStub == null) { throw new IllegalStateException("Missing endpoint"); } if (_subject != null) { msg.setSubject(_subject); } if (_restriction != null) { msg.setRestriction(_restriction); } _cellStub.notify(msg); } /** * Sends a PnfsMessage notification to PnfsManager. No reply is * expected for a notification and no failure is reported if the * message could not be delivered. */ public void notify(PnfsMessage msg) { msg.setReplyRequired(false); send(msg); } public void clearCacheLocation(PnfsId id) { clearCacheLocation(id, false); } public void clearCacheLocation(PnfsId id, boolean removeIfLast) { notify(new PnfsClearCacheLocationMessage(id, _poolName, removeIfLast)); } public void addCacheLocation(PnfsId id) throws CacheException { addCacheLocation(id, _poolName); } public void addCacheLocation(PnfsId id, String pool) throws CacheException { request(new PnfsAddCacheLocationMessage(id, pool)); } public List<String> getCacheLocations( PnfsId pnfsId )throws CacheException { PnfsGetCacheLocationsMessage pnfsMessage = new PnfsGetCacheLocationsMessage(pnfsId) ; pnfsMessage = request(pnfsMessage) ; List<String> assumedLocations = pnfsMessage.getCacheLocations() ; if (assumedLocations == null) { return Collections.emptyList(); } else { return assumedLocations; } } public List<String> getCacheLocationsByPath( String fileName )throws CacheException { PnfsGetCacheLocationsMessage pnfsMessage = new PnfsGetCacheLocationsMessage() ; pnfsMessage.setPnfsPath( fileName ) ; pnfsMessage = request(pnfsMessage) ; List<String> assumedLocations = pnfsMessage.getCacheLocations() ; if (assumedLocations == null) { return Collections.emptyList(); } else { return assumedLocations; } } /** * Sends a message to the request manager and blocks until a reply * is received. In case of errors in the reply, those are thrown * as a CacheException. Timeouts and failure to send the message * to the PnfsManager are reported as a timeout CacheException. */ public <T extends PnfsMessage> T request(T msg) throws CacheException { try { return CellStub.getMessage(requestAsync(msg)); } catch (InterruptedException e) { throw new CacheException(CacheException.UNEXPECTED_SYSTEM_EXCEPTION, "Sending message to " + _cellStub.getDestinationPath() + " interrupted"); } catch (NoRouteToCellException e) { throw new TimeoutCacheException(e.getMessage(), e); } } /** * Sends a message to the pnfs manager and returns a promise of a future reply. */ public <T extends PnfsMessage> ListenableFuture<T> requestAsync(T msg) { checkState(_cellStub != null, "Missing endpoint"); return requestAsync(msg, _cellStub.getTimeoutInMillis()); } /** * Sends a message to the pnfs manager and returns a promise of a future reply. */ public <T extends PnfsMessage> ListenableFuture<T> requestAsync(T msg, long timeout) { checkState(_cellStub != null, "Missing endpoint"); msg.setReplyRequired(true); if (_subject != null) { msg.setSubject(_subject); } if (_restriction != null) { msg.setRestriction(_restriction); } return _cellStub.send(msg, timeout); } public PnfsCreateEntryMessage createPnfsDirectory(String path) throws CacheException { return request(new PnfsCreateEntryMessage(path, FileAttributes.ofFileType(DIR))); } public PnfsCreateEntryMessage createPnfsDirectory(String path, Set<FileAttribute> attributes) throws CacheException { return request(new PnfsCreateEntryMessage(path, FileAttributes.ofFileType(DIR), attributes)); } public PnfsCreateEntryMessage createPnfsDirectory(String path, FileAttributes attributes) throws CacheException { return request(new PnfsCreateEntryMessage(path, attributes)); } /** * Creates a directory and all its parent directories. * * REVISIT: Should eventually be moved to PnfsManager with a flag * in the PnfsCreateEntryMessage indicating whether parent * directories should be created. * * @returns the FileAttributes of <code>path</code> */ public FileAttributes createDirectories(FsPath path) throws CacheException { PnfsCreateEntryMessage message; try { message = createPnfsDirectory(path.toString()); } catch (FileNotFoundCacheException e) { createDirectories(path.parent()); message = createPnfsDirectory(path.toString()); } /* In case of incomplete create, delete the directory right * away. FIXME: PnfsManagerV3 has the exact opposite comment, * saying that lack of attributes is a non-error. */ if (message.getFileAttributes() == null) { try { deletePnfsEntry(message.getPnfsId(), path.toString()); } catch (FileNotFoundCacheException e) { // Already gone, so never mind } catch (CacheException e) { _logNameSpace.error(e.toString()); } throw new CacheException("Failed to create directory: " + path); } return message.getFileAttributes(); } public PnfsCreateEntryMessage createSymLink(String path, String dest, FileAttributes assignAttributes) throws CacheException { assignAttributes.setFileType(LINK); return request(new PnfsCreateSymLinkMessage(path, dest, assignAttributes)); } public void renameEntry(PnfsId pnfsId, String path, String newName, boolean overwrite) throws CacheException { request(new PnfsRenameMessage(pnfsId, path, newName, overwrite)); } public void renameEntry(String path, String newName, boolean overwrite) throws CacheException { request(new PnfsRenameMessage(path, newName, overwrite)); } public PnfsCreateEntryMessage createPnfsEntry(String path, FileAttributes attributes) throws CacheException { return request(new PnfsCreateEntryMessage(path, attributes)); } public PnfsId getParentOf(PnfsId pnfsId) throws CacheException { return request(new PnfsGetParentMessage(pnfsId)).getParent(); } public PnfsId deletePnfsEntry(String path) throws CacheException { return deletePnfsEntry(path, EnumSet.allOf(FileType.class)); } public PnfsId deletePnfsEntry(String path, Set<FileType> allowed) throws CacheException { FileAttributes attributes = deletePnfsEntry(null, path, allowed, EnumSet.of(PNFSID)); return attributes.getPnfsId(); } public void deletePnfsEntry(PnfsId pnfsid) throws CacheException { deletePnfsEntry(pnfsid, null, EnumSet.allOf(FileType.class), EnumSet.noneOf(FileAttribute.class)); } public void deletePnfsEntry(PnfsId pnfsid, String path) throws CacheException { deletePnfsEntry(pnfsid, path, EnumSet.allOf(FileType.class), EnumSet.noneOf(FileAttribute.class)); } public FileAttributes deletePnfsEntry(PnfsId pnfsid, String path, Set<FileType> allowed, Set<FileAttribute> attr) throws CacheException { return request(new PnfsDeleteEntryMessage(pnfsid, path, allowed, attr)).getFileAttributes(); } /** * Getter for property __pnfsTimeout. * @return Value of property __pnfsTimeout. */ public long getPnfsTimeout() { return _cellStub.getTimeoutInMillis(); } /** * Setter for property __pnfsTimeout. * @param pnfsTimeout New value of property __pnfsTimeout. */ public void setPnfsTimeout(long pnfsTimeout) { _cellStub.setTimeout(pnfsTimeout); _cellStub.setTimeoutUnit(TimeUnit.MILLISECONDS); } public void putPnfsFlag(PnfsId pnfsId, String flag, String value) { PnfsFlagMessage flagMessage = new PnfsFlagMessage( pnfsId ,flag , PnfsFlagMessage.FlagOperation.SET ) ; flagMessage.setReplyRequired( false ); flagMessage.setValue(value); notify(flagMessage); } public void fileFlushed(PnfsId pnfsId, FileAttributes fileAttributes) throws CacheException { PoolFileFlushedMessage fileFlushedMessage = new PoolFileFlushedMessage(_poolName, pnfsId, fileAttributes); // throws exception if something goes wrong request(fileFlushedMessage); } /** * Get path corresponding to given pnfsid. * * @param pnfsID * @return path * @throws CacheException */ public FsPath getPathByPnfsId(PnfsId pnfsID) throws CacheException { return FsPath.create(request(new PnfsMapPathMessage(pnfsID)).getPnfsPath()); } /** * Get pnfsid corresponding to given path. * * @param path * @return pnfsid * @throws CacheException */ public PnfsId getPnfsIdByPath(String path) throws CacheException { return request(new PnfsMapPathMessage(path)).getPnfsId(); } /** * Get pnfsid corresponding to given path. * * @param path * @param resolve whether sym-links should be followed. If set to false * then the operation will fail if any path element is a symbolic link. * @return pnfsid * @throws CacheException */ public PnfsId getPnfsIdByPath(String path, boolean resolve) throws CacheException { PnfsMapPathMessage message = new PnfsMapPathMessage(path); message.setShouldResolve(resolve); return request(message).getPnfsId(); } /** * Remove the registered checksum (of the specified type) from the file * with the given id. * @param id * @param type */ public void removeChecksum(PnfsId id, ChecksumType type) { notify(new PnfsRemoveChecksumMessage(id, type)); } /** * Get file attributes. The PnfsManager is free to return fewer attributes * than requested. If <code>attr</code> is an empty array, file existence * if checked. * * @param pnfsid * @param attr array of requested attributes. * @return requested attributes */ public FileAttributes getFileAttributes(PnfsId pnfsid, Set<FileAttribute> attr) throws CacheException { return request(new PnfsGetFileAttributes(pnfsid, attr)).getFileAttributes(); } /** * Get file attributes. The PnfsManager is free to return fewer attributes * than requested. If <code>attr</code> is an empty array, file existence * if checked. * * @param pnfsid * @param attr array of requested attributes. * @param mask Additional AccessMask access rights to check * @param updateAtime update file's last access time * @return requested attributes */ public FileAttributes getFileAttributes( PnfsId pnfsid, Set<FileAttribute> attr, Set<AccessMask> mask, boolean updateAtime) throws CacheException { PnfsGetFileAttributes msg = new PnfsGetFileAttributes(pnfsid, attr); msg.setAccessMask(mask); msg.setUpdateAtime(updateAtime); return request(msg).getFileAttributes(); } /** * Get file attributes. The PnfsManager is free to return fewer attributes * than requested. If <code>attr</code> is an empty array, file existence * if checked. * * @param path * @param attr array of requested attributes. * @return requested attributes */ public FileAttributes getFileAttributes(String path, Set<FileAttribute> attr) throws CacheException { return request(new PnfsGetFileAttributes(path, attr)).getFileAttributes(); } public FileAttributes getFileAttributes(FsPath path, Set<FileAttribute> attr) throws CacheException { return getFileAttributes(path.toString(), attr); } /** * Get file attributes. The PnfsManager is free to return less attributes * than requested. If <code>attr</code> is an empty array, file existence * if checked. * * @param path * @param attr array of requested attributes. * @param mask Additional AccessMask access rights to check * @param updateAtime update file's last access time * @return requested attributes */ public FileAttributes getFileAttributes(String path, Set<FileAttribute> attr, Set<AccessMask> mask, boolean updateAtime) throws CacheException { PnfsGetFileAttributes msg = new PnfsGetFileAttributes(path, attr); msg.setAccessMask(mask); msg.setUpdateAtime(updateAtime); return request(msg).getFileAttributes(); } /** * Set file attributes. If <code>attr</code> is an empty array, * file existence if checked. The updated FileAttribute values in * acquire are returned. * * In principal, the NameSpaceProvider can adjust or ignore updated * FileAttribute values. For Chimera, only updates to ACCESS_TIME and * CHANGE_TIME might not be honoured. * * @param pnfsid * @param attr array of requested attributes. * @param acquire set of updated FileAttributes to return. * @return The updated values requested via acquire */ public FileAttributes setFileAttributes(PnfsId pnfsid, FileAttributes attr, Set<FileAttribute> acquire) throws CacheException { return request(new PnfsSetFileAttributes(pnfsid, attr, acquire)).getFileAttributes(); } /** * Set file attributes. If <code>attr</code> is an empty array, * file existence if checked. * * @param pnfsid * @param attr array of requested attributes. */ public void setFileAttributes(PnfsId pnfsid, FileAttributes attr) throws CacheException { request(new PnfsSetFileAttributes(pnfsid, attr)); } /** * Set file attributes by path. If <code>attr</code> is an empty array, * file existence if checked. The updated FileAttribute values requested * by the acquire argument are returned. * * In principal, the NameSpaceProvider can adjust or ignore updated * FileAttribute values. For Chimera, only updates to ACCESS_TIME and * CHANGE_TIME might not be honoured. * * @param path location of file or directory to modify * @param attr array of requested attributes. * @param acquire set of updated FileAttributes to return. * @return The updated values requested via acquire */ public FileAttributes setFileAttributes(FsPath path, FileAttributes attr, Set<FileAttribute> acquire) throws CacheException { return request(new PnfsSetFileAttributes(path.toString(), attr, acquire)).getFileAttributes(); } /** * Set file attributes by path. If <code>attr</code> is an empty array, * file existence if checked. * * @param path location of file or directory to modify * @param attr array of requested attributes. */ public void setFileAttributes(FsPath path, FileAttributes attr) throws CacheException { request(new PnfsSetFileAttributes(path.toString(), attr, EnumSet.noneOf(FileAttribute.class))); } public void setChecksum(PnfsId pnfsId, Checksum checksum) throws CacheException { PnfsSetChecksumMessage message = new PnfsSetChecksumMessage(pnfsId, checksum.getType().getType(), checksum.getValue()); request(message); } }