001/* 002 * Copyright (c) 2006-2015 Nuxeo SA (http://nuxeo.com/) and others. 003 * 004 * All rights reserved. This program and the accompanying materials 005 * are made available under the terms of the Eclipse Public License v1.0 006 * which accompanies this distribution, and is available at 007 * http://www.eclipse.org/legal/epl-v10.html 008 * 009 * Contributors: 010 * Bogdan Stefanescu 011 * Florent Guillaume 012 */ 013package org.nuxeo.ecm.core.api.impl.blob; 014 015import java.io.File; 016import java.io.FileOutputStream; 017import java.io.IOException; 018import java.io.InputStream; 019import java.io.InputStreamReader; 020import java.io.OutputStream; 021import java.io.Reader; 022import java.io.Serializable; 023import java.nio.file.Files; 024 025import org.apache.commons.io.IOUtils; 026import org.apache.commons.lang.ObjectUtils; 027import org.apache.commons.lang.builder.HashCodeBuilder; 028import org.nuxeo.ecm.core.api.Blob; 029import org.nuxeo.ecm.core.api.CloseableFile; 030import org.nuxeo.runtime.api.Framework; 031 032/** 033 * Abstract implementation of a {@link Blob} storing the information other than the byte stream. 034 */ 035public abstract class AbstractBlob implements Blob, Serializable { 036 037 private static final long serialVersionUID = 1L; 038 039 public static final String UTF_8 = "UTF-8"; 040 041 public static final String TEXT_PLAIN = "text/plain"; 042 043 protected String mimeType; 044 045 protected String encoding; 046 047 protected String filename; 048 049 protected String digest; 050 051 @Override 052 public String getMimeType() { 053 return mimeType; 054 } 055 056 @Override 057 public String getEncoding() { 058 return encoding; 059 } 060 061 @Override 062 public String getFilename() { 063 return filename; 064 } 065 066 @Override 067 public String getDigestAlgorithm() { 068 return null; 069 } 070 071 @Override 072 public String getDigest() { 073 return digest; 074 } 075 076 @Override 077 public void setMimeType(String mimeType) { 078 this.mimeType = mimeType; 079 } 080 081 @Override 082 public void setEncoding(String encoding) { 083 this.encoding = encoding; 084 } 085 086 @Override 087 public void setFilename(String filename) { 088 this.filename = filename; 089 } 090 091 @Override 092 public void setDigest(String digest) { 093 this.digest = digest; 094 } 095 096 @Override 097 public File getFile() { 098 return null; 099 } 100 101 @Override 102 public byte[] getByteArray() throws IOException { 103 try (InputStream in = getStream()) { 104 return IOUtils.toByteArray(in); 105 } 106 } 107 108 @Override 109 public String getString() throws IOException { 110 try (Reader reader = new InputStreamReader(getStream(), getEncoding() == null ? UTF_8 : getEncoding())) { 111 return IOUtils.toString(reader); 112 } 113 } 114 115 @Override 116 public long getLength() { 117 return -1; 118 } 119 120 @Override 121 public CloseableFile getCloseableFile() throws IOException { 122 return getCloseableFile(null); 123 } 124 125 @Override 126 public CloseableFile getCloseableFile(String ext) throws IOException { 127 File file = getFile(); 128 if (file != null && (ext == null || file.getName().endsWith(ext))) { 129 return new CloseableFile(file, false); 130 } 131 File tmp = File.createTempFile("nxblob-", ext); 132 tmp.delete(); 133 if (file != null) { 134 // attempt to create a symbolic link, which would be cheaper than a copy 135 try { 136 Files.createSymbolicLink(tmp.toPath(), file.toPath().toAbsolutePath()); 137 } catch (IOException | UnsupportedOperationException e) { 138 // symbolic link not supported, do a copy instead 139 Files.copy(file.toPath(), tmp.toPath()); 140 } 141 } else { 142 try (InputStream in = getStream()) { 143 Files.copy(in, tmp.toPath()); 144 } 145 } 146 Framework.trackFile(tmp, tmp); 147 return new CloseableFile(tmp, true); 148 } 149 150 @Override 151 public void transferTo(OutputStream out) throws IOException { 152 try (InputStream in = getStream()) { 153 IOUtils.copy(in, out); 154 } 155 } 156 157 @Override 158 public void transferTo(File file) throws IOException { 159 try (OutputStream out = new FileOutputStream(file)) { 160 transferTo(out); 161 } 162 } 163 164 @Override 165 public boolean equals(Object object) { 166 if (object == this) { 167 return true; 168 } 169 if (!(object instanceof Blob)) { 170 return false; 171 } 172 Blob other = (Blob) object; 173 if (!ObjectUtils.equals(getFilename(), other.getFilename())) { 174 return false; 175 } 176 if (!ObjectUtils.equals(getMimeType(), other.getMimeType())) { 177 return false; 178 } 179 if (!ObjectUtils.equals(getEncoding(), other.getEncoding())) { 180 return false; 181 } 182 // ignore null digests, they are sometimes lazily computed 183 // therefore mutable 184 String digest = getDigest(); 185 String otherDigest = other.getDigest(); 186 if (digest != null && otherDigest != null && !digest.equals(otherDigest)) { 187 return false; 188 } 189 // compare streams 190 return equalsStream(other); 191 } 192 193 // overridden by StorageBlob for improved performance 194 protected boolean equalsStream(Blob other) { 195 InputStream is = null; 196 InputStream ois = null; 197 try { 198 is = getStream(); 199 ois = other.getStream(); 200 return IOUtils.contentEquals(is, ois); 201 } catch (IOException e) { 202 throw new RuntimeException(e); 203 } finally { 204 IOUtils.closeQuietly(is); 205 IOUtils.closeQuietly(ois); 206 } 207 } 208 209 // we don't implement a complex hashCode as we don't expect 210 // to put blobs as hashmap keys 211 @Override 212 public int hashCode() { 213 return new HashCodeBuilder() // 214 .append(getFilename()) // 215 .append(getMimeType()) // 216 .append(getEncoding()) // 217 .toHashCode(); 218 } 219 220}