]> git.argeo.org Git - lgpl/argeo-commons.git/blob - org.argeo.jcr/src/org/argeo/jcr/fs/BinaryChannel.java
Improve RCP platform.
[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 FileChannel fc = null;
32
33 public BinaryChannel(Node file, Path path) throws RepositoryException, IOException {
34 this.file = file;
35 if (file.isNodeType(NodeType.NT_FILE)) {
36 if (file.hasNode(Property.JCR_CONTENT)) {
37 Node data = file.getNode(Property.JCR_CONTENT);
38 this.binary = data.getProperty(Property.JCR_DATA).getBinary();
39 } else {
40 Node data = file.addNode(Property.JCR_CONTENT, NodeType.NT_RESOURCE);
41 try (InputStream in = new ByteArrayInputStream(new byte[0])) {
42 this.binary = data.getSession().getValueFactory().createBinary(in);
43 }
44 data.setProperty(Property.JCR_DATA, this.binary);
45
46 // MIME type
47 String mime = Files.probeContentType(path);
48 // String mime = fileTypeMap.getContentType(file.getName());
49 data.setProperty(Property.JCR_MIMETYPE, mime);
50
51 data.getSession().save();
52 }
53 } else {
54 throw new IllegalArgumentException(
55 "Unsupported file node " + file + " (" + file.getPrimaryNodeType() + ")");
56 }
57 }
58
59 @Override
60 public synchronized boolean isOpen() {
61 return open;
62 }
63
64 @Override
65 public synchronized void close() throws IOException {
66 if (isModified()) {
67 Binary newBinary = null;
68 try {
69 Session session = file.getSession();
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 byte[] arr = dst.array();
100 read = binary.read(arr, position);
101
102 if (read != -1)
103 position = position + read;
104 return read;
105 } catch (RepositoryException e) {
106 throw new IOException("Cannot read into buffer", e);
107 }
108 }
109 }
110
111 @Override
112 public int write(ByteBuffer src) throws IOException {
113 int written = getFileChannel().write(src);
114 return written;
115 }
116
117 @Override
118 public long position() throws IOException {
119 if (isModified())
120 return getFileChannel().position();
121 else
122 return position;
123 }
124
125 @Override
126 public SeekableByteChannel position(long newPosition) throws IOException {
127 if (isModified()) {
128 getFileChannel().position(position);
129 } else {
130 this.position = newPosition;
131 }
132 return this;
133 }
134
135 @Override
136 public long size() throws IOException {
137 if (isModified()) {
138 return getFileChannel().size();
139 } else {
140 try {
141 return binary.getSize();
142 } catch (RepositoryException e) {
143 throw new IOException("Cannot get size", e);
144 }
145 }
146 }
147
148 @Override
149 public SeekableByteChannel truncate(long size) throws IOException {
150 getFileChannel().truncate(size);
151 return this;
152 }
153
154 private FileChannel getFileChannel() throws IOException {
155 try {
156 if (fc == null) {
157 Path tempPath = Files.createTempFile(getClass().getSimpleName(), null);
158 fc = FileChannel.open(tempPath, StandardOpenOption.WRITE, StandardOpenOption.READ,
159 StandardOpenOption.DELETE_ON_CLOSE, StandardOpenOption.SPARSE);
160 ReadableByteChannel readChannel = Channels.newChannel(binary.getStream());
161 fc.transferFrom(readChannel, 0, binary.getSize());
162 clearReadState();
163 }
164 return fc;
165 } catch (RepositoryException e) {
166 throw new IOException("Cannot get temp file channel", e);
167 }
168 }
169
170 private boolean isModified() {
171 return fc != null;
172 }
173
174 private void clearReadState() {
175 position = -1;
176 JcrUtils.closeQuietly(binary);
177 binary = null;
178 }
179 }