]> git.argeo.org Git - lgpl/argeo-commons.git/blob - org.argeo.jcr/src/org/argeo/jcr/fs/BinaryChannel.java
Improve localization framework.
[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 /** A read/write {@link SeekableByteChannel} based on a {@link Binary}. */
25 public class BinaryChannel implements SeekableByteChannel {
26 private final Node file;
27 private Binary binary;
28 private boolean open = true;
29
30 private long position = 0;
31
32 private FileChannel fc = null;
33
34 public BinaryChannel(Node file, Path path) throws RepositoryException, IOException {
35 this.file = file;
36 Session session = file.getSession();
37 synchronized (session) {
38 if (file.isNodeType(NodeType.NT_FILE)) {
39 if (file.hasNode(Node.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(Node.JCR_CONTENT, NodeType.NT_UNSTRUCTURED);
44 data.addMixin(NodeType.MIX_LAST_MODIFIED);
45 try (InputStream in = new ByteArrayInputStream(new byte[0])) {
46 this.binary = data.getSession().getValueFactory().createBinary(in);
47 }
48 data.setProperty(Property.JCR_DATA, this.binary);
49
50 // MIME type
51 String mime = Files.probeContentType(path);
52 // String mime = fileTypeMap.getContentType(file.getName());
53 data.setProperty(Property.JCR_MIMETYPE, mime);
54
55 session.refresh(true);
56 session.save();
57 session.notifyAll();
58 }
59 } else {
60 throw new IllegalArgumentException(
61 "Unsupported file node " + file + " (" + file.getPrimaryNodeType() + ")");
62 }
63 }
64 }
65
66 @Override
67 public synchronized boolean isOpen() {
68 return open;
69 }
70
71 @Override
72 public synchronized void close() throws IOException {
73 if (isModified()) {
74 Binary newBinary = null;
75 try {
76 Session session = file.getSession();
77 synchronized (session) {
78 fc.position(0);
79 InputStream in = Channels.newInputStream(fc);
80 newBinary = session.getValueFactory().createBinary(in);
81 file.getNode(Property.JCR_CONTENT).setProperty(Property.JCR_DATA, newBinary);
82 session.refresh(true);
83 session.save();
84 open = false;
85 session.notifyAll();
86 }
87 } catch (RepositoryException e) {
88 throw new IOException("Cannot close " + file, e);
89 } finally {
90 JcrUtils.closeQuietly(newBinary);
91 // IOUtils.closeQuietly(fc);
92 if (fc != null) {
93 fc.close();
94 }
95 }
96 } else {
97 clearReadState();
98 open = false;
99 }
100 }
101
102 @Override
103 public int read(ByteBuffer dst) throws IOException {
104 if (isModified()) {
105 return fc.read(dst);
106 } else {
107
108 try {
109 int read;
110 byte[] arr = dst.array();
111 read = binary.read(arr, position);
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 }
127
128 @Override
129 public long position() throws IOException {
130 if (isModified())
131 return getFileChannel().position();
132 else
133 return position;
134 }
135
136 @Override
137 public SeekableByteChannel position(long newPosition) throws IOException {
138 if (isModified()) {
139 getFileChannel().position(position);
140 } else {
141 this.position = newPosition;
142 }
143 return this;
144 }
145
146 @Override
147 public long size() throws IOException {
148 if (isModified()) {
149 return getFileChannel().size();
150 } else {
151 try {
152 return binary.getSize();
153 } catch (RepositoryException e) {
154 throw new IOException("Cannot get size", e);
155 }
156 }
157 }
158
159 @Override
160 public SeekableByteChannel truncate(long size) throws IOException {
161 getFileChannel().truncate(size);
162 return this;
163 }
164
165 private FileChannel getFileChannel() throws IOException {
166 try {
167 if (fc == null) {
168 Path tempPath = Files.createTempFile(getClass().getSimpleName(), null);
169 fc = FileChannel.open(tempPath, StandardOpenOption.WRITE, StandardOpenOption.READ,
170 StandardOpenOption.DELETE_ON_CLOSE, StandardOpenOption.SPARSE);
171 ReadableByteChannel readChannel = Channels.newChannel(binary.getStream());
172 fc.transferFrom(readChannel, 0, binary.getSize());
173 clearReadState();
174 }
175 return fc;
176 } catch (RepositoryException e) {
177 throw new IOException("Cannot get temp file channel", e);
178 }
179 }
180
181 private boolean isModified() {
182 return fc != null;
183 }
184
185 private void clearReadState() {
186 position = -1;
187 JcrUtils.closeQuietly(binary);
188 binary = null;
189 }
190 }