/** * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.camel.component.file; import java.io.File; import java.nio.file.Files; import java.nio.file.Path; import java.util.Map; import org.apache.camel.Exchange; import org.apache.camel.WrappedFile; import org.apache.camel.util.FileUtil; import org.apache.camel.util.ObjectHelper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Generic File. Specific implementations of a file based endpoint need to * provide a File for transfer. */ public class GenericFile<T> implements WrappedFile<T> { private static final Logger LOG = LoggerFactory.getLogger(GenericFile.class); private final boolean probeContentType; private String copyFromAbsoluteFilePath; private String endpointPath; private String fileName; private String fileNameOnly; private String relativeFilePath; private String absoluteFilePath; private long fileLength; private long lastModified; private T file; private GenericFileBinding<T> binding; private boolean absolute; private boolean directory; private String charset; private Map<String, Object> extendedAttributes; public GenericFile() { this(false); } public GenericFile(boolean probeContentType) { this.probeContentType = probeContentType; } public char getFileSeparator() { return File.separatorChar; } /** * Creates a copy based on the source * * @param source the source * @return a copy of the source */ @SuppressWarnings("unchecked") public GenericFile<T> copyFrom(GenericFile<T> source) { GenericFile<T> result; try { result = source.getClass().newInstance(); } catch (Exception e) { throw ObjectHelper.wrapRuntimeCamelException(e); } result.setCopyFromAbsoluteFilePath(source.getAbsoluteFilePath()); result.setEndpointPath(source.getEndpointPath()); result.setAbsolute(source.isAbsolute()); result.setDirectory(source.isDirectory()); result.setAbsoluteFilePath(source.getAbsoluteFilePath()); result.setRelativeFilePath(source.getRelativeFilePath()); result.setFileName(source.getFileName()); result.setFileNameOnly(source.getFileNameOnly()); result.setFileLength(source.getFileLength()); result.setLastModified(source.getLastModified()); result.setFile(source.getFile()); result.setBody(source.getBody()); result.setBinding(source.getBinding()); result.setCharset(source.getCharset()); copyFromPopulateAdditional(source, result); return result; } /** * Copies additional information from the source to the result. * <p/> * Inherited classes can override this method and copy their specific data. * * @param source the source * @param result the result */ public void copyFromPopulateAdditional(GenericFile<T> source, GenericFile<T> result) { // noop } /** * Bind this GenericFile to an Exchange */ public void bindToExchange(Exchange exchange) { Map<String, Object> headers; exchange.setProperty(FileComponent.FILE_EXCHANGE_FILE, this); GenericFileMessage<T> msg = new GenericFileMessage<T>(this); if (exchange.hasOut()) { headers = exchange.getOut().hasHeaders() ? exchange.getOut().getHeaders() : null; exchange.setOut(msg); } else { headers = exchange.getIn().hasHeaders() ? exchange.getIn().getHeaders() : null; exchange.setIn(msg); } // preserve any existing (non file) headers, before we re-populate headers if (headers != null) { msg.setHeaders(headers); // remove any file related headers, as we will re-populate file headers msg.removeHeaders("CamelFile*"); } populateHeaders(msg); } /** * Populates the {@link GenericFileMessage} relevant headers * * @param message the message to populate with headers */ public void populateHeaders(GenericFileMessage<T> message) { if (message != null) { message.setHeader(Exchange.FILE_NAME_ONLY, getFileNameOnly()); message.setHeader(Exchange.FILE_NAME, getFileName()); message.setHeader(Exchange.FILE_NAME_CONSUMED, getFileName()); message.setHeader("CamelFileAbsolute", isAbsolute()); message.setHeader("CamelFileAbsolutePath", getAbsoluteFilePath()); if (extendedAttributes != null) { message.setHeader("CamelFileExtendedAttributes", extendedAttributes); } if (probeContentType && file instanceof File) { File f = (File) file; Path path = f.toPath(); try { message.setHeader(Exchange.FILE_CONTENT_TYPE, Files.probeContentType(path)); } catch (Throwable e) { // just ignore the exception } } if (isAbsolute()) { message.setHeader(Exchange.FILE_PATH, getAbsoluteFilePath()); } else { // we must normalize path according to protocol if we build our own paths String path = normalizePathToProtocol(getEndpointPath() + File.separator + getRelativeFilePath()); message.setHeader(Exchange.FILE_PATH, path); } message.setHeader("CamelFileRelativePath", getRelativeFilePath()); message.setHeader(Exchange.FILE_PARENT, getParent()); if (getFileLength() >= 0) { message.setHeader(Exchange.FILE_LENGTH, getFileLength()); } if (getLastModified() > 0) { message.setHeader(Exchange.FILE_LAST_MODIFIED, getLastModified()); } } } protected boolean isAbsolute(String name) { return FileUtil.isAbsolute(new File(name)); } protected String normalizePath(String name) { return FileUtil.normalizePath(name); } /** * Changes the name of this remote file. This method alters the absolute and * relative names as well. * * @param newName the new name */ public void changeFileName(String newName) { LOG.trace("Changing name to: {}", newName); // Make sure the names is normalized. String newFileName = FileUtil.normalizePath(newName); String newEndpointPath = FileUtil.normalizePath(endpointPath.endsWith("" + File.separatorChar) ? endpointPath : endpointPath + File.separatorChar); LOG.trace("Normalized endpointPath: {}", newEndpointPath); LOG.trace("Normalized newFileName: ()", newFileName); File file = new File(newFileName); if (!absolute) { // for relative then we should avoid having the endpoint path duplicated so clip it if (ObjectHelper.isNotEmpty(newEndpointPath) && newFileName.startsWith(newEndpointPath)) { // clip starting endpoint in case it was added // use File.separatorChar as the normalizePath uses this as path separator so we should use the same // in this logic here if (newEndpointPath.endsWith("" + File.separatorChar)) { newFileName = ObjectHelper.after(newFileName, newEndpointPath); } else { newFileName = ObjectHelper.after(newFileName, newEndpointPath + File.separatorChar); } // reconstruct file with clipped name file = new File(newFileName); } } // store the file name only setFileNameOnly(file.getName()); setFileName(file.getName()); // relative path if (file.getParent() != null) { setRelativeFilePath(file.getParent() + getFileSeparator() + file.getName()); } else { setRelativeFilePath(file.getName()); } // absolute path if (isAbsolute(newFileName)) { setAbsolute(true); setAbsoluteFilePath(newFileName); } else { setAbsolute(false); // construct a pseudo absolute filename that the file operations uses even for relative only String path = ObjectHelper.isEmpty(endpointPath) ? "" : endpointPath + getFileSeparator(); setAbsoluteFilePath(path + getRelativeFilePath()); } if (LOG.isTraceEnabled()) { LOG.trace("FileNameOnly: {}", getFileNameOnly()); LOG.trace("FileName: {}", getFileName()); LOG.trace("Absolute: {}", isAbsolute()); LOG.trace("Relative path: {}", getRelativeFilePath()); LOG.trace("Absolute path: {}", getAbsoluteFilePath()); LOG.trace("Name changed to: {}", this); } } public String getRelativeFilePath() { return relativeFilePath; } public void setRelativeFilePath(String relativeFilePath) { this.relativeFilePath = normalizePathToProtocol(relativeFilePath); } public String getFileName() { return fileName; } public void setFileName(String fileName) { this.fileName = normalizePathToProtocol(fileName); } public long getFileLength() { return fileLength; } public void setFileLength(long fileLength) { this.fileLength = fileLength; } public long getLastModified() { return lastModified; } public void setLastModified(long lastModified) { this.lastModified = lastModified; } public String getCharset() { return charset; } public void setCharset(String charset) { this.charset = charset; } public Map<String, Object> getExtendedAttributes() { return extendedAttributes; } public void setExtendedAttributes(Map<String, Object> extendedAttributes) { this.extendedAttributes = extendedAttributes; } @Override public T getFile() { return file; } public void setFile(T file) { this.file = file; } public Object getBody() { return getBinding().getBody(this); } public void setBody(Object os) { getBinding().setBody(this, os); } public String getParent() { String parent; if (isAbsolute()) { String name = getAbsoluteFilePath(); File path = new File(name); parent = path.getParent(); } else { String name = getRelativeFilePath(); File path; if (name != null) { path = new File(endpointPath, name); } else { path = new File(endpointPath); } parent = path.getParent(); } return normalizePathToProtocol(parent); } public GenericFileBinding<T> getBinding() { if (binding == null) { binding = new GenericFileDefaultBinding<T>(); } return binding; } public void setBinding(GenericFileBinding<T> binding) { this.binding = binding; } public void setAbsoluteFilePath(String absoluteFilePath) { this.absoluteFilePath = normalizePathToProtocol(absoluteFilePath); } public String getAbsoluteFilePath() { return absoluteFilePath; } public boolean isAbsolute() { return absolute; } public void setAbsolute(boolean absolute) { this.absolute = absolute; } public String getEndpointPath() { return endpointPath; } public void setEndpointPath(String endpointPath) { this.endpointPath = normalizePathToProtocol(endpointPath); } public String getFileNameOnly() { return fileNameOnly; } public void setFileNameOnly(String fileNameOnly) { this.fileNameOnly = fileNameOnly; } public boolean isDirectory() { return directory; } public void setDirectory(boolean directory) { this.directory = directory; } public String getCopyFromAbsoluteFilePath() { return copyFromAbsoluteFilePath; } public void setCopyFromAbsoluteFilePath(String copyFromAbsoluteFilePath) { this.copyFromAbsoluteFilePath = copyFromAbsoluteFilePath; } /** * Fixes the path separator to be according to the protocol */ protected String normalizePathToProtocol(String path) { if (ObjectHelper.isEmpty(path)) { return path; } path = path.replace('/', getFileSeparator()); path = path.replace('\\', getFileSeparator()); return path; } @Override public String toString() { return "GenericFile[" + (absolute ? absoluteFilePath : relativeFilePath) + "]"; } }