]> git.argeo.org Git - lgpl/argeo-commons.git/blob - org.argeo.jcr/src/org/argeo/jcr/fs/BinaryChannel.java
Add SFTP support to sync
[lgpl/argeo-commons.git] / org.argeo.jcr / src / org / argeo / jcr / fs / BinaryChannel.java
1 package org.argeo.jcr.fs;
2
3 import java.io.ByteArrayInputStream;
4 import java.io.IOException;
5 import java.io.InputStream;
6 import java.nio.ByteBuffer;
7 import java.nio.channels.Channels;
8 import java.nio.channels.FileChannel;
9 import java.nio.channels.ReadableByteChannel;
10 import java.nio.channels.SeekableByteChannel;
11 import java.nio.file.Files;
12 import java.nio.file.Path;
13 import java.nio.file.StandardOpenOption;
14
15 import javax.activation.FileTypeMap;
16 import javax.activation.MimetypesFileTypeMap;
17 import javax.jcr.Binary;
18 import javax.jcr.Node;
19 import javax.jcr.Property;
20 import javax.jcr.RepositoryException;
21 import javax.jcr.Session;
22 import javax.jcr.nodetype.NodeType;
23
24 import org.argeo.jcr.JcrUtils;
25
26 public class BinaryChannel implements SeekableByteChannel {
27 private final Node file;
28 private Binary binary;
29 private boolean open = true;
30
31 private long position = 0;
32
33 // private ByteBuffer toWrite;
34 private FileChannel fc = null;
35
36 private static FileTypeMap fileTypeMap;
37
38 static {
39 try {
40 fileTypeMap = new MimetypesFileTypeMap("/etc/mime.types");
41 } catch (IOException e) {
42 fileTypeMap = FileTypeMap.getDefaultFileTypeMap();
43 }
44 }
45
46 public BinaryChannel(Node file) throws RepositoryException, IOException {
47 this.file = file;
48 // int capacity = 1024 * 1024;
49 // this.toWrite = ByteBuffer.allocate(capacity);
50 if (file.isNodeType(NodeType.NT_FILE)) {
51 if (file.hasNode(Property.JCR_CONTENT)) {
52 Node data = file.getNode(Property.JCR_CONTENT);
53 this.binary = data.getProperty(Property.JCR_DATA).getBinary();
54 } else {
55 Node data = file.addNode(Property.JCR_CONTENT, NodeType.NT_RESOURCE);
56 try (InputStream in = new ByteArrayInputStream(new byte[0])) {
57 this.binary = data.getSession().getValueFactory().createBinary(in);
58 }
59 data.setProperty(Property.JCR_DATA, this.binary);
60
61 // MIME type
62 String mime = fileTypeMap.getContentType(file.getName());
63 data.setProperty(Property.JCR_MIMETYPE, mime);
64
65 data.getSession().save();
66 }
67 } else {
68 throw new IllegalArgumentException(
69 "Unsupported file node " + file + " (" + file.getPrimaryNodeType() + ")");
70 }
71 }
72
73 @Override
74 public synchronized boolean isOpen() {
75 return open;
76 }
77
78 @Override
79 public synchronized void close() throws IOException {
80 if (isModified()) {
81 Binary newBinary = null;
82 try {
83 Session session = file.getSession();
84 // byte[] arr = new byte[(int) position];
85 // toWrite.flip();
86 // toWrite.get(arr);
87 fc.position(0);
88 InputStream in = Channels.newInputStream(fc);
89 newBinary = session.getValueFactory().createBinary(in);
90 file.getNode(Property.JCR_CONTENT).setProperty(Property.JCR_DATA, newBinary);
91 session.save();
92 open = false;
93 } catch (RepositoryException e) {
94 throw new IOException("Cannot close " + file, e);
95 } finally {
96 JcrUtils.closeQuietly(newBinary);
97 // IOUtils.closeQuietly(fc);
98 if (fc != null) {
99 fc.close();
100 }
101 }
102 } else {
103 clearReadState();
104 open = false;
105 }
106 }
107
108 @Override
109 public int read(ByteBuffer dst) throws IOException {
110 if (isModified()) {
111 return fc.read(dst);
112 } else {
113
114 try {
115 int read;
116 // int capacity = dst.capacity();
117 byte[] arr = dst.array();
118 read = binary.read(arr, position);
119 // dst.put(arr, 0, read);
120
121 // try {
122 // byte[] arr = dst.array();
123 // read = binary.read(arr, position);
124 // } catch (UnsupportedOperationException e) {
125 // int capacity = dst.capacity();
126 // byte[] arr = new byte[capacity];
127 // read = binary.read(arr, position);
128 // dst.put(arr);
129 // }
130 if (read != -1)
131 position = position + read;
132 return read;
133 } catch (RepositoryException e) {
134 throw new IOException("Cannot read into buffer", e);
135 }
136 }
137 }
138
139 @Override
140 public int write(ByteBuffer src) throws IOException {
141 int written = getFileChannel().write(src);
142 return written;
143 // int byteCount = src.remaining();
144 // if (toWrite.remaining() < byteCount)
145 // throw new JcrFsException("Write buffer is full");
146 // toWrite.put(src);
147 // if (position < binarySize)
148 // position = binarySize + byteCount;
149 // else
150 // position = position + byteCount;
151 // return byteCount;
152 }
153
154 @Override
155 public long position() throws IOException {
156 if (isModified())
157 return getFileChannel().position();
158 else
159 return position;
160 }
161
162 @Override
163 public SeekableByteChannel position(long newPosition) throws IOException {
164 if (isModified()) {
165 getFileChannel().position(position);
166 } else {
167 this.position = newPosition;
168 }
169 return this;
170 }
171
172 @Override
173 public long size() throws IOException {
174 if (isModified()) {
175 return getFileChannel().size();
176 } else {
177 try {
178 return binary.getSize();
179 } catch (RepositoryException e) {
180 throw new IOException("Cannot get size", e);
181 }
182 }
183 }
184
185 @Override
186 public SeekableByteChannel truncate(long size) throws IOException {
187 getFileChannel().truncate(size);
188 // if (size != size())
189 // throw new UnsupportedOperationException("Cannot truncate JCR
190 // binary");
191 return this;
192 }
193
194 private FileChannel getFileChannel() throws IOException {
195 try {
196 if (fc == null) {
197 Path tempPath = Files.createTempFile(getClass().getSimpleName(), null);
198 fc = FileChannel.open(tempPath, StandardOpenOption.WRITE, StandardOpenOption.READ,
199 StandardOpenOption.DELETE_ON_CLOSE, StandardOpenOption.SPARSE);
200 ReadableByteChannel readChannel = Channels.newChannel(binary.getStream());
201 fc.transferFrom(readChannel, 0, binary.getSize());
202 clearReadState();
203 }
204 return fc;
205 } catch (RepositoryException e) {
206 throw new IOException("Cannot get temp file channel", e);
207 }
208 }
209
210 private boolean isModified() {
211 return fc != null;
212 }
213
214 private void clearReadState() {
215 position = -1;
216 JcrUtils.closeQuietly(binary);
217 binary = null;
218 }
219 }