/* * (C) Copyright 2006-2015 Nuxeo SA (http://nuxeo.com/) and others. * * Licensed 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. * * Contributors: * Bogdan Stefanescu * Florent Guillaume */ package org.nuxeo.ecm.core.api.impl.blob; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.Reader; import java.io.Serializable; import java.nio.file.Files; import org.apache.commons.io.IOUtils; import org.apache.commons.lang.ObjectUtils; import org.apache.commons.lang.builder.HashCodeBuilder; import org.nuxeo.ecm.core.api.Blob; import org.nuxeo.ecm.core.api.CloseableFile; import org.nuxeo.runtime.api.Framework; /** * Abstract implementation of a {@link Blob} storing the information other than the byte stream. */ public abstract class AbstractBlob implements Blob, Serializable { private static final long serialVersionUID = 1L; public static final String UTF_8 = "UTF-8"; public static final String TEXT_PLAIN = "text/plain"; protected String mimeType; protected String encoding; protected String filename; protected String digest; @Override public String getMimeType() { return mimeType; } @Override public String getEncoding() { return encoding; } @Override public String getFilename() { return filename; } @Override public String getDigestAlgorithm() { return null; } @Override public String getDigest() { return digest; } @Override public void setMimeType(String mimeType) { this.mimeType = mimeType; } @Override public void setEncoding(String encoding) { this.encoding = encoding; } @Override public void setFilename(String filename) { this.filename = filename; } @Override public void setDigest(String digest) { this.digest = digest; } @Override public File getFile() { return null; } @Override public byte[] getByteArray() throws IOException { try (InputStream in = getStream()) { return IOUtils.toByteArray(in); } } @Override public String getString() throws IOException { try (Reader reader = new InputStreamReader(getStream(), getEncoding() == null ? UTF_8 : getEncoding())) { return IOUtils.toString(reader); } } @Override public long getLength() { return -1; } @Override public CloseableFile getCloseableFile() throws IOException { return getCloseableFile(null); } @Override public CloseableFile getCloseableFile(String ext) throws IOException { File file = getFile(); if (file != null && (ext == null || file.getName().endsWith(ext))) { return new CloseableFile(file, false); } File tmp = Framework.createTempFile("nxblob-", ext); tmp.delete(); if (file != null) { // attempt to create a symbolic link, which would be cheaper than a copy try { Files.createSymbolicLink(tmp.toPath(), file.toPath().toAbsolutePath()); } catch (IOException | UnsupportedOperationException e) { // symbolic link not supported, do a copy instead Files.copy(file.toPath(), tmp.toPath()); } } else { try (InputStream in = getStream()) { Files.copy(in, tmp.toPath()); } } Framework.trackFile(tmp, tmp); return new CloseableFile(tmp, true); } @Override public void transferTo(OutputStream out) throws IOException { try (InputStream in = getStream()) { IOUtils.copy(in, out); } } @Override public void transferTo(File file) throws IOException { try (OutputStream out = new FileOutputStream(file)) { transferTo(out); } } @Override public boolean equals(Object object) { if (object == this) { return true; } if (!(object instanceof Blob)) { return false; } Blob other = (Blob) object; if (!ObjectUtils.equals(getFilename(), other.getFilename())) { return false; } if (!ObjectUtils.equals(getMimeType(), other.getMimeType())) { return false; } if (!ObjectUtils.equals(getEncoding(), other.getEncoding())) { return false; } // ignore null digests, they are sometimes lazily computed // therefore mutable String digest = getDigest(); String otherDigest = other.getDigest(); if (digest != null && otherDigest != null && !digest.equals(otherDigest)) { return false; } // compare streams return equalsStream(other); } // overridden by StorageBlob for improved performance protected boolean equalsStream(Blob other) { InputStream is = null; InputStream ois = null; try { is = getStream(); ois = other.getStream(); return IOUtils.contentEquals(is, ois); } catch (IOException e) { throw new RuntimeException(e); } finally { IOUtils.closeQuietly(is); IOUtils.closeQuietly(ois); } } // we don't implement a complex hashCode as we don't expect // to put blobs as hashmap keys @Override public int hashCode() { return new HashCodeBuilder() // .append(getFilename()) // .append(getMimeType()) // .append(getEncoding()) // .toHashCode(); } }