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