1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 package org.efaps.db.store;
22
23 import java.io.IOException;
24 import java.io.InputStream;
25 import java.io.OutputStream;
26 import java.util.zip.GZIPOutputStream;
27 import java.util.zip.ZipOutputStream;
28
29 import javax.naming.Context;
30 import javax.naming.InitialContext;
31 import javax.naming.NameClassPair;
32 import javax.naming.NamingEnumeration;
33 import javax.naming.NamingException;
34 import javax.transaction.xa.XAException;
35 import javax.transaction.xa.Xid;
36
37 import org.apache.commons.vfs2.FileContent;
38 import org.apache.commons.vfs2.FileObject;
39 import org.apache.commons.vfs2.FileSystemException;
40 import org.apache.commons.vfs2.impl.DefaultFileSystemManager;
41 import org.apache.commons.vfs2.provider.FileProvider;
42 import org.efaps.db.Instance;
43 import org.efaps.db.wrapper.SQLSelect;
44 import org.efaps.util.EFapsException;
45 import org.slf4j.Logger;
46 import org.slf4j.LoggerFactory;
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66 public class VFSStoreResource
67 extends AbstractStoreResource
68 {
69
70
71
72
73 private static final String EXTENSION_TEMP = ".tmp";
74
75
76
77
78 private static final String EXTENSION_NORMAL = "";
79
80
81
82
83 private static final String EXTENSION_BACKUP = ".bak";
84
85
86
87
88 private static final String PROPERTY_NUMBER_SUBDIRS = "VFSNumberSubDirectories";
89
90
91
92
93
94 private static final String PROPERTY_USE_TYPE = "VFSUseTypeIdInPath";
95
96
97
98
99
100 private static final String PROPERTY_NUMBER_BACKUP = "VFSNumberBackups";
101
102
103
104
105 private static final String PROPERTY_BASENAME = "VFSBaseName";
106
107
108
109
110 private static final String PROPERTY_PROVIDER = "VFSProvider";
111
112
113
114
115 private static final Logger LOG = LoggerFactory.getLogger(VFSStoreResource.class);
116
117
118
119
120
121
122 private final byte[] buffer = new byte[1024];
123
124
125
126
127 private String storeFileName = null;
128
129
130
131
132 private DefaultFileSystemManager manager;
133
134
135
136
137 private int numberBackup = 1;
138
139
140
141
142
143
144
145
146
147
148 @Override
149 public void initialize(final Instance _instance,
150 final Store _store)
151 throws EFapsException
152 {
153 super.initialize(_instance, _store);
154
155 final StringBuilder fileNameTmp = new StringBuilder();
156
157 final String useTypeIdStr = getStore().getResourceProperties().get(VFSStoreResource.PROPERTY_USE_TYPE);
158 if ("true".equalsIgnoreCase(useTypeIdStr)) {
159 fileNameTmp.append(getInstance().getType().getId()).append("/");
160 }
161
162 final String numberSubDirsStr = getStore().getResourceProperties().get(
163 VFSStoreResource.PROPERTY_NUMBER_SUBDIRS);
164 if (numberSubDirsStr != null) {
165 final long numberSubDirs = Long.parseLong(numberSubDirsStr);
166 final String pathFormat = "%0"
167 + Math.round(Math.log10(numberSubDirs) + 0.5d)
168 + "d";
169 fileNameTmp.append(String.format(pathFormat,
170 getInstance().getId() % numberSubDirs))
171 .append("/");
172 }
173 fileNameTmp.append(getInstance().getType().getId()).append(".").append(getInstance().getId());
174 this.storeFileName = fileNameTmp.toString();
175
176 final String numberBackupStr = getStore().getResourceProperties().get(VFSStoreResource.PROPERTY_NUMBER_BACKUP);
177 if (numberBackupStr != null) {
178 this.numberBackup = Integer.parseInt(numberBackupStr);
179 }
180
181 if (this.manager == null) {
182 try {
183 DefaultFileSystemManager tmpMan = null;
184 if (getStore().getResourceProperties().containsKey(Store.PROPERTY_JNDINAME)) {
185 final InitialContext initialContext = new InitialContext();
186 final Context context = (Context) initialContext.lookup("java:comp/env");
187 final NamingEnumeration<NameClassPair> nameEnum = context.list("");
188 while (nameEnum.hasMoreElements()) {
189 final NameClassPair namePair = nameEnum.next();
190 if (namePair.getName().equals(getStore().getResourceProperties().get(
191 Store.PROPERTY_JNDINAME))) {
192 tmpMan = (DefaultFileSystemManager) context.lookup(
193 getStore().getResourceProperties().get(Store.PROPERTY_JNDINAME));
194 break;
195 }
196 }
197 }
198 if (tmpMan == null && this.manager == null) {
199 this.manager = evaluateFileSystemManager();
200 }
201 } catch (final NamingException e) {
202 throw new EFapsException(VFSStoreResource.class, "initialize.NamingException", e);
203 }
204 }
205 }
206
207
208
209
210
211 private DefaultFileSystemManager evaluateFileSystemManager()
212 throws EFapsException
213 {
214 final DefaultFileSystemManager ret = new DefaultFileSystemManager();
215
216 final String baseName = getProperties().get(VFSStoreResource.PROPERTY_BASENAME);
217 final String provider = getProperties().get(VFSStoreResource.PROPERTY_PROVIDER);
218 try {
219 ret.init();
220 final FileProvider fileProvider = (FileProvider) Class.forName(provider).newInstance();
221 ret.addProvider(baseName, fileProvider);
222 ret.setBaseFile(fileProvider.findFile(null, baseName, null));
223 } catch (final FileSystemException e) {
224 throw new EFapsException(VFSStoreResource.class,
225 "evaluateFileSystemManager.FileSystemException",
226 e, provider, baseName);
227 } catch (final InstantiationException e) {
228 throw new EFapsException(VFSStoreResource.class,
229 "evaluateFileSystemManager.InstantiationException",
230 e, baseName, provider);
231 } catch (final IllegalAccessException e) {
232 throw new EFapsException(VFSStoreResource.class,
233 "evaluateFileSystemManager.IllegalAccessException",
234 e, provider);
235 } catch (final ClassNotFoundException e) {
236 throw new EFapsException(VFSStoreResource.class,
237 "evaluateFileSystemManager.ClassNotFoundException",
238 e, provider);
239 }
240 return ret;
241 }
242
243
244
245
246 @Override
247 protected int add2Select(final SQLSelect _select)
248 {
249 return 0;
250 }
251
252
253
254
255
256
257
258
259
260
261
262
263
264 public long write(final InputStream _in,
265 final long _size,
266 final String _fileName)
267 throws EFapsException
268 {
269 try {
270 long size = _size;
271 final FileObject tmpFile = this.manager.resolveFile(this.manager.getBaseFile(),
272 this.storeFileName + VFSStoreResource.EXTENSION_TEMP);
273 if (!tmpFile.exists()) {
274 tmpFile.createFile();
275 }
276 final FileContent content = tmpFile.getContent();
277 OutputStream out = content.getOutputStream(false);
278 if (getCompress().equals(Compress.GZIP)) {
279 out = new GZIPOutputStream(out);
280 } else if (getCompress().equals(Compress.ZIP)) {
281 out = new ZipOutputStream(out);
282 }
283
284
285 if (_size < 0) {
286 int length = 1;
287 size = 0;
288 while (length > 0) {
289 length = _in.read(this.buffer);
290 if (length > 0) {
291 out.write(this.buffer, 0, length);
292 size += length;
293 }
294 }
295 } else {
296 Long length = _size;
297 while (length > 0) {
298 final int readLength = length.intValue() < this.buffer.length
299 ? length.intValue() : this.buffer.length;
300 _in.read(this.buffer, 0, readLength);
301 out.write(this.buffer, 0, readLength);
302 length -= readLength;
303 }
304 }
305 if (getCompress().equals(Compress.GZIP) || getCompress().equals(Compress.ZIP)) {
306 out.close();
307 }
308 tmpFile.close();
309 setFileInfo(_fileName, size);
310 return size;
311 } catch (final IOException e) {
312 VFSStoreResource.LOG.error("write of content failed", e);
313 throw new EFapsException(VFSStoreResource.class, "write.IOException", e);
314 }
315
316 }
317
318
319
320
321 public void delete()
322 {
323
324 }
325
326
327
328
329
330
331
332 public InputStream read()
333 throws EFapsException
334 {
335 StoreResourceInputStream in = null;
336 try {
337 final FileObject file = this.manager.resolveFile(this.storeFileName + VFSStoreResource.EXTENSION_NORMAL);
338 if (!file.isReadable()) {
339 VFSStoreResource.LOG.error("file for " + this.storeFileName + " not readable");
340 throw new EFapsException(VFSStoreResource.class, "#####file not readable");
341 }
342 in = new VFSStoreResourceInputStream(this, file);
343 } catch (final FileSystemException e) {
344 VFSStoreResource.LOG.error("read of " + this.storeFileName + " failed", e);
345 throw new EFapsException(VFSStoreResource.class, "read.Throwable", e);
346 } catch (final IOException e) {
347 VFSStoreResource.LOG.error("read of " + this.storeFileName + " failed", e);
348 throw new EFapsException(VFSStoreResource.class, "read.Throwable", e);
349 }
350 return in;
351 }
352
353
354
355
356
357
358
359
360 public int prepare(final Xid _xid)
361 {
362 if (VFSStoreResource.LOG.isDebugEnabled()) {
363 VFSStoreResource.LOG.debug("prepare (xid=" + _xid + ")");
364 }
365 return 0;
366 }
367
368
369
370
371
372
373
374
375 private void backup(final FileObject _backup,
376 final int _number)
377 throws FileSystemException
378 {
379 if (_number < this.numberBackup) {
380 final FileObject backFile = this.manager.resolveFile(this.manager.getBaseFile(),
381 this.storeFileName + VFSStoreResource.EXTENSION_BACKUP + _number);
382 if (backFile.exists()) {
383 backup(backFile, _number + 1);
384 }
385 _backup.moveTo(backFile);
386 } else {
387 _backup.delete();
388 }
389 }
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413 public void commit(final Xid _xid,
414 final boolean _onePhase)
415 throws XAException
416 {
417 if (VFSStoreResource.LOG.isDebugEnabled()) {
418 VFSStoreResource.LOG.debug("transaction commit");
419 }
420 if (getStoreEvent() == VFSStoreResource.StoreEvent.WRITE) {
421 try {
422 final FileObject tmpFile = this.manager.resolveFile(this.manager.getBaseFile(),
423 this.storeFileName + VFSStoreResource.EXTENSION_TEMP);
424 final FileObject currentFile = this.manager.resolveFile(this.manager.getBaseFile(),
425 this.storeFileName + VFSStoreResource.EXTENSION_NORMAL);
426 final FileObject bakFile = this.manager.resolveFile(this.manager.getBaseFile(),
427 this.storeFileName + VFSStoreResource.EXTENSION_BACKUP);
428 if (bakFile.exists() && (this.numberBackup > 0)) {
429 backup(bakFile, 0);
430 }
431 if (currentFile.exists()) {
432 if (this.numberBackup > 0) {
433 currentFile.moveTo(bakFile);
434 } else {
435 currentFile.delete();
436 }
437 }
438 tmpFile.moveTo(currentFile);
439 tmpFile.close();
440 currentFile.close();
441 bakFile.close();
442 } catch (final FileSystemException e) {
443 VFSStoreResource.LOG.error("transaction commit fails for " + _xid
444 + " (one phase = " + _onePhase + ")", e);
445 final XAException xa = new XAException(XAException.XA_RBCOMMFAIL);
446 xa.initCause(e);
447 throw xa;
448 }
449 } else if (getStoreEvent() == VFSStoreResource.StoreEvent.DELETE) {
450 try {
451 final FileObject curFile = this.manager.resolveFile(this.manager.getBaseFile(),
452 this.storeFileName + VFSStoreResource.EXTENSION_NORMAL);
453 final FileObject bakFile = this.manager.resolveFile(this.manager.getBaseFile(),
454 this.storeFileName + VFSStoreResource.EXTENSION_BACKUP);
455 if (bakFile.exists()) {
456 bakFile.delete();
457 }
458 if (curFile.exists()) {
459 curFile.moveTo(bakFile);
460 }
461 bakFile.close();
462 curFile.close();
463 } catch (final FileSystemException e) {
464 VFSStoreResource.LOG.error("transaction commit fails for " + _xid
465 + " (one phase = " + _onePhase + ")", e);
466 final XAException xa = new XAException(XAException.XA_RBCOMMFAIL);
467 xa.initCause(e);
468 throw xa;
469 }
470 }
471 }
472
473
474
475
476
477
478
479
480
481
482
483
484 public void rollback(final Xid _xid)
485 throws XAException
486 {
487 if (VFSStoreResource.LOG.isDebugEnabled()) {
488 VFSStoreResource.LOG.debug("rollback (xid = " + _xid + ")");
489 }
490 try {
491 final FileObject tmpFile = this.manager.resolveFile(this.manager.getBaseFile(),
492 this.storeFileName + VFSStoreResource.EXTENSION_TEMP);
493 if (tmpFile.exists()) {
494 tmpFile.delete();
495 }
496 } catch (final FileSystemException e) {
497 VFSStoreResource.LOG.error("transaction rollback fails for " + _xid, e);
498 final XAException xa = new XAException(XAException.XA_RBCOMMFAIL);
499 xa.initCause(e);
500 throw xa;
501 }
502 }
503
504
505
506
507
508
509
510
511 public void forget(final Xid _xid)
512 {
513 if (VFSStoreResource.LOG.isDebugEnabled()) {
514 VFSStoreResource.LOG.debug("forget (xid = " + _xid + ")");
515 }
516 }
517
518
519
520
521
522
523
524 public int getTransactionTimeout()
525 {
526 if (VFSStoreResource.LOG.isDebugEnabled()) {
527 VFSStoreResource.LOG.debug("getTransactionTimeout");
528 }
529 return 0;
530 }
531
532
533
534
535
536
537
538 public Xid[] recover(final int _flag)
539 {
540 if (VFSStoreResource.LOG.isDebugEnabled()) {
541 VFSStoreResource.LOG.debug("recover (flag = " + _flag + ")");
542 }
543 return null;
544 }
545
546
547
548
549
550
551
552 public boolean setTransactionTimeout(final int _seconds)
553 {
554 if (VFSStoreResource.LOG.isDebugEnabled()) {
555 VFSStoreResource.LOG.debug("setTransactionTimeout (seconds = " + _seconds + ")");
556 }
557 return true;
558 }
559
560
561
562
563 private class VFSStoreResourceInputStream
564 extends StoreResourceInputStream
565 {
566
567
568
569 private final FileObject file;
570
571
572
573
574
575
576 protected VFSStoreResourceInputStream(final AbstractStoreResource _storeRes,
577 final FileObject _file)
578 throws IOException
579 {
580 super(_storeRes, _file.getContent().getInputStream());
581 this.file = _file;
582 }
583
584
585
586
587
588
589
590
591 @Override
592 protected void beforeClose() throws IOException
593 {
594 this.file.close();
595 }
596 }
597 }