Statistics
| Revision:

svn-gvsig-desktop / trunk / org.gvsig.desktop / org.gvsig.desktop.compat.cdc / org.gvsig.fmap.dal / org.gvsig.fmap.dal.db / org.gvsig.fmap.dal.db.jdbc / src / main / java / org / gvsig / fmap / dal / store / jdbc2 / spi / JDBCResourcesStorage.java @ 46277

History | View | Annotate | Download (23 KB)

1
/**
2
 * gvSIG. Desktop Geographic Information System.
3
 *
4
 * Copyright (C) 2007-2020 gvSIG Association.
5
 *
6
 * This program is free software; you can redistribute it and/or
7
 * modify it under the terms of the GNU General Public License
8
 * as published by the Free Software Foundation; either version 3
9
 * of the License, or (at your option) any later version.
10
 *
11
 * This program is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU General Public License
17
 * along with this program; if not, write to the Free Software
18
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19
 * MA  02110-1301, USA.
20
 *
21
 * For any additional information, do not hesitate to contact us
22
 * at info AT gvsig.com, or visit our website www.gvsig.com.
23
 */
24
package org.gvsig.fmap.dal.store.jdbc2.spi;
25

    
26
import java.io.ByteArrayInputStream;
27
import java.io.ByteArrayOutputStream;
28
import java.io.File;
29
import java.io.FileInputStream;
30
import java.io.FileNotFoundException;
31
import java.io.FileOutputStream;
32
import java.io.IOException;
33
import java.io.InputStream;
34
import java.io.OutputStream;
35
import java.net.URL;
36
import java.util.ArrayList;
37
import java.util.HashMap;
38
import java.util.List;
39
import java.util.Map;
40
import org.apache.commons.codec.digest.DigestUtils;
41
import org.apache.commons.io.FileUtils;
42
import org.apache.commons.io.IOUtils;
43
import org.apache.commons.io.output.NullOutputStream;
44
import org.apache.commons.lang3.StringUtils;
45
import org.apache.commons.lang3.tuple.ImmutablePair;
46
import org.apache.commons.lang3.tuple.Pair;
47
import org.gvsig.expressionevaluator.ExpressionBuilder;
48
import org.gvsig.expressionevaluator.ExpressionUtils;
49
import org.gvsig.fmap.dal.DALLocator;
50
import org.gvsig.fmap.dal.DataManager;
51
import static org.gvsig.fmap.dal.DatabaseWorkspaceManager.FIELD_RESOURCES_NAME;
52
import static org.gvsig.fmap.dal.DatabaseWorkspaceManager.FIELD_RESOURCES_RESOURCE;
53
import static org.gvsig.fmap.dal.DatabaseWorkspaceManager.TABLE_RESOURCES_NAME;
54
import org.gvsig.fmap.dal.exception.InitializeException;
55
import org.gvsig.fmap.dal.feature.EditableFeature;
56
import org.gvsig.fmap.dal.feature.Feature;
57
import org.gvsig.fmap.dal.feature.FeatureStore;
58
import org.gvsig.fmap.dal.store.jdbc.JDBCStoreParameters;
59
import org.gvsig.fmap.dal.store.jdbc2.JDBCHelper;
60
import org.gvsig.fmap.dal.store.jdbc2.OperationsFactory;
61
import org.gvsig.fmap.dal.store.jdbc2.spi.operations.RetrieveValueOperation;
62
import org.gvsig.tools.ToolsLocator;
63
import org.gvsig.tools.dispose.DisposeUtils;
64
import org.gvsig.tools.folders.FoldersManager;
65
import org.gvsig.tools.resourcesstorage.AbstractResourcesStorage;
66
import org.gvsig.tools.resourcesstorage.ResourcesStorage;
67
import org.gvsig.tools.util.CachedValue;
68
import org.slf4j.Logger;
69
import org.slf4j.LoggerFactory;
70

    
71
/**
72
 *
73
 * @author jjdelcerro
74
 */
75
@SuppressWarnings("UseSpecificCatch")
76
public class JDBCResourcesStorage extends AbstractResourcesStorage {
77

    
78
    private static final Logger LOGGER = LoggerFactory.getLogger(JDBCResourcesStorage.class);
79
    private final JDBCHelper helper;
80

    
81
    private static class ExistsResourcesCache extends CachedValue<Map<String,Boolean>> {
82
        @Override
83
        protected void reload() {
84
            this.setValue(new HashMap<>());
85
        }        
86
    }
87
    
88
    private static class DataBaseResource implements Resource {
89

    
90
        private class ResourceInputStream extends InputStream {
91

    
92
            @Override
93
            public int read() throws IOException {
94
                return in.read();
95
            }
96

    
97
            @Override
98
            public void close() throws IOException {
99
                DataBaseResource.this.close();
100
            }
101
        }
102

    
103
        private class ResourceOutputStream extends OutputStream {
104

    
105
            @Override
106
            public void write(int b) throws IOException {
107
                out.write(b);
108
            }
109

    
110
            @Override
111
            public void flush() throws IOException {
112
                out.flush();
113
            }
114

    
115
            @Override
116
            public void close() throws IOException {
117
                DataBaseResource.this.close();
118
            }
119
        }
120

    
121
        private final String name;
122
        private final JDBCResourcesStorage storage;
123

    
124
        private InputStream in;
125
        private OutputStream out;
126

    
127
        public DataBaseResource(JDBCResourcesStorage storage, String name) {
128
            this.storage = storage;                    
129
            this.name = name;
130
        }
131

    
132
        @Override
133
        public String getName() {
134
          return name;
135
        }
136
        
137
        private JDBCStoreParameters getStoreParameters() {
138
            return this.storage.resourcesStoreParameters;
139
        }
140
        
141
        private String getTableName() {
142
            return this.storage.tableName;
143
        }
144
        
145
        private Map<String,Boolean> getExistsResourcesCache() {
146
            return  this.storage.existsResourcesCache.get();
147
        }
148
        
149
        @Override
150
        public boolean isReadOnly() {
151
            return this.storage.readonly;
152
        }
153
        @Override
154
        public URL getURL() {
155
            try {
156
                String url = this.getStoreParameters().getUrl();
157
                return new URL(url + "&tableName=" + this.getTableName() + "&resourceName=" + this.name);
158
            } catch (Throwable ex) {
159
                return null;
160
            }
161
        }
162
        
163
        private byte[] retrieveResource() throws Exception {
164
            ExpressionBuilder builder = ExpressionUtils.createExpressionBuilder();
165
            String filter = builder.eq(
166
                    builder.column(FIELD_RESOURCES_NAME),
167
                    builder.constant(this.getTableName() + "." + this.name)
168
            ).toString();
169
            OperationsFactory operations = this.storage.helper.getOperations();
170
            RetrieveValueOperation op = operations.createRetrieveValue(
171
                    operations.createTableReference(this.getStoreParameters()),
172
                    filter,
173
                    null, //order
174
                    FIELD_RESOURCES_RESOURCE
175
            );
176
            return (byte[] ) op.perform();
177
        }
178
        
179
        private byte[] retrieveResource0() throws Exception {
180
            FeatureStore store = null;
181
            try {
182
                DataManager dataManager = DALLocator.getDataManager();
183
                store = (FeatureStore) dataManager.openStore(
184
                        this.getStoreParameters().getDataStoreName(),
185
                        this.getStoreParameters()
186
                );
187
                ExpressionBuilder builder = ExpressionUtils.createExpressionBuilder();
188
                String filter = builder.eq(
189
                        builder.column(FIELD_RESOURCES_NAME),
190
                        builder.constant(this.getTableName() + "." + this.name)
191
                ).toString();
192
                Feature feature = store.findFirst(filter);
193
                if (feature == null) {
194
                    return null;
195
                }
196
                byte[] resource = feature.getByteArray(FIELD_RESOURCES_RESOURCE);                
197
                return resource;
198
            } finally {
199
                DisposeUtils.disposeQuietly(store);
200
            }
201
        }
202

    
203
        @Override
204
        public boolean exists() {
205
//            FeatureStore store = null;
206
            try {
207
                FoldersManager fm = ToolsLocator.getFoldersManager();
208
                Pair<String, String> key = this.getCacheID();
209
                String key_s = key.getLeft() + "/"+ key.getRight();
210
                Map<String, Boolean> erc = this.getExistsResourcesCache();
211
                if( erc.containsKey(key_s) ) {
212
                    return this.getExistsResourcesCache().get(key_s);
213
                }
214
                File f = fm.getTemporaryFile("resources-storage","jdbc", key.getLeft(), key.getRight());
215
                if( f.exists() ) {
216
                  return true;
217
                }
218
//                DataManager dataManager = DALLocator.getDataManager();
219
//                store = (FeatureStore) dataManager.openStore(
220
//                        this.getStoreParameters().getDataStoreName(),
221
//                        this.getStoreParameters()
222
//                );
223
//                ExpressionBuilder builder = ExpressionUtils.createExpressionBuilder();
224
//                String filter = builder.eq(
225
//                        builder.column(FIELD_RESOURCES_NAME),
226
//                        builder.constant(this.getTableName()+"."+this.name)
227
//                ).toString();
228
//                Feature feature = store.findFirst(filter);
229
                byte[] resource = retrieveResource();
230
                boolean exists = resource!=null;
231
                if( resource!=null ) {
232
                    putResourceInCache(resource);
233
                }
234
                this.getExistsResourcesCache().put(key_s, exists);
235
                return exists;
236
            } catch (Throwable ex) {
237
                LOGGER.warn("Can't access to the resoure '" + this.getURL() + "'.", ex);
238
//            } finally {
239
//                DisposeUtils.disposeQuietly(store);
240
            }
241
            return false;
242
        }
243

    
244
        public boolean remove() {
245
            FeatureStore store = null;
246
            try {
247
                FoldersManager fm = ToolsLocator.getFoldersManager();
248
                Pair<String, String> key = this.getCacheID();
249
                File f = fm.getTemporaryFile("resources-storage","jdbc", key.getLeft(), key.getRight());
250
                if( f.exists() ) {
251
                  f.delete();
252
                }
253
                DataManager dataManager = DALLocator.getDataManager();
254
                store = (FeatureStore) dataManager.openStore(
255
                        this.getStoreParameters().getDataStoreName(),
256
                        this.getStoreParameters()
257
                );
258
                ExpressionBuilder builder = ExpressionUtils.createExpressionBuilder();
259
                String filter = builder.eq(
260
                        builder.column(FIELD_RESOURCES_NAME),
261
                        builder.constant(this.getTableName()+"."+this.name)
262
                ).toString();
263
                Feature feature = store.findFirst(filter);
264
                if(feature != null){
265
                    store.edit();
266
                    store.delete(feature);
267
                    store.finishEditing();
268
                    return true;
269
                }
270
                return false;
271
            } catch (Throwable ex) {
272
                FeatureStore.cancelEditingQuietly(store);
273
                LOGGER.warn("Can't access to the resoure '" + this.getURL() + "'.", ex);
274
            } finally {
275
                DisposeUtils.disposeQuietly(store);
276
            }
277
            return false;
278
        }
279

    
280
        private Pair<String,String> getCacheID() {
281
          byte[] data = this.getStoreParameters().toByteArray();
282
          ImmutablePair<String, String> r = new ImmutablePair<>(
283
                  DigestUtils.md5Hex(data), 
284
                  this.getTableName()+"."+this.name
285
          );
286
          return r;
287
        }
288
        
289
        private boolean removeCache() {
290
          FoldersManager fm = ToolsLocator.getFoldersManager();
291
          Pair<String, String> key = this.getCacheID();
292
          File f = fm.getTemporaryFile("resources-storage","jdbc", key.getLeft(), key.getRight());
293
          if( f.exists() ) {
294
              try {
295
                  FileUtils.forceDelete(f);
296
              } catch (IOException ex) {
297
                  return false;
298
              }
299
          }
300
          return true;
301
        }
302
        
303
        private InputStream getInputStreamFromCache() {
304
          FoldersManager fm = ToolsLocator.getFoldersManager();
305
          Pair<String, String> key = this.getCacheID();
306
          File f = fm.getTemporaryFile("resources-storage","jdbc", key.getLeft(), key.getRight());
307
          if( !f.exists() ) {
308
            InputStream is = null;
309
            FileOutputStream os = null;
310
            File f2 = null;
311
            try {
312
              is = this.getInputStream();
313
              if (is==null) {
314
                  return null;
315
              }
316
              FileUtils.forceMkdir(f.getParentFile());
317
              os = new FileOutputStream(f);
318
              IOUtils.copy(is, os);             
319
              IOUtils.closeQuietly(os);
320

    
321
              f2 = fm.getTemporaryFile("resources-storage","jdbc", key.getLeft(), "parameters");              
322
              byte[] data = this.getStoreParameters().toByteArray();
323
              os = new FileOutputStream(f2);
324
              IOUtils.write(data, os);
325
            } catch (IOException ex) {
326
              FileUtils.deleteQuietly(f);
327
              FileUtils.deleteQuietly(f2);
328
              return null;
329
            } finally {
330
              IOUtils.closeQuietly(is);
331
              IOUtils.closeQuietly(os);
332
            }
333
          }
334
          InputStream is = null;
335
          try {
336
            is = new FileInputStream(f);
337
          } catch (FileNotFoundException ex) {
338
          }
339
          return is;
340
        }
341

    
342
        private void putResourceInCache(byte[] resource) {
343
            FoldersManager fm = ToolsLocator.getFoldersManager();
344
            Pair<String, String> key = this.getCacheID();
345
            File f = fm.getTemporaryFile("resources-storage", "jdbc", key.getLeft(), key.getRight());
346
            FileOutputStream os = null;
347
            File f2 = null;
348
            try {
349
                FileUtils.forceMkdir(f.getParentFile());
350
                os = new FileOutputStream(f);
351
                IOUtils.write(resource, os);
352
                IOUtils.closeQuietly(os);
353

    
354
                f2 = fm.getTemporaryFile("resources-storage", "jdbc", key.getLeft(), "parameters");
355
                byte[] data = this.getStoreParameters().toByteArray();
356
                os = new FileOutputStream(f2);
357
                IOUtils.write(data, os);
358
            } catch (IOException ex) {
359
                FileUtils.deleteQuietly(f);
360
                FileUtils.deleteQuietly(f2);
361
            } finally {
362
                IOUtils.closeQuietly(os);
363
            }
364
        }
365
        
366
        private InputStream getInputStream() throws IOException {
367
//            FeatureStore store = null;
368
            try {
369
//                DataManager dataManager = DALLocator.getDataManager();
370
//                store = (FeatureStore) dataManager.openStore(
371
//                        this.getStoreParameters().getDataStoreName(),
372
//                        this.getStoreParameters()
373
//                );
374
//                ExpressionBuilder builder = ExpressionUtils.createExpressionBuilder();
375
//                String filter = builder.eq(
376
//                        builder.column(FIELD_RESOURCES_NAME),
377
//                        builder.constant(this.getTableName()+"."+this.name)
378
//                ).toString();
379
//                Feature feature = store.findFirst(filter);
380
//                if (feature == null) {
381
//                    return null;
382
//                }
383
//                byte[] resource = feature.getByteArray(FIELD_RESOURCES_RESOURCE);
384
                byte[] resource = retrieveResource();
385
                InputStream is = new ByteArrayInputStream(resource);
386
                return is;
387
            } catch (Throwable ex) {
388
                LOGGER.warn("Can't access to the resoure '" + this.getURL() + "'.", ex);
389
//            } finally {
390
//                DisposeUtils.disposeQuietly(store);
391
            }
392
            return null;
393
        }
394

    
395
        @Override
396
        public InputStream asInputStream() throws IOException {
397
            if (this.in != null || this.out != null) {
398
                throw new IllegalStateException("Resource is already open (" + this.getURL() + ")");
399
            }
400
            InputStream is = this.getInputStreamFromCache();
401
            if( is==null ) {
402
                is = this.getInputStream();
403
                if ( is==null) {
404
                    return null;
405
                }
406
            }
407
            this.in = is;
408
            return new ResourceInputStream();
409
        }
410

    
411
        @Override
412
        public OutputStream asOutputStream() throws IOException {
413
            if (this.in != null || this.out != null) {
414
                throw new IllegalStateException("Resource is already open (" + this.getURL() + ").");
415
            }
416
            if( this.isReadOnly()) {
417
                this.out = NullOutputStream.NULL_OUTPUT_STREAM;
418
            } else {
419
                this.out = new ByteArrayOutputStream();
420
            }
421
            return new ResourceOutputStream();
422
        }
423

    
424
        @Override
425
        public void close() {
426
            if (this.in != null) {
427
                IOUtils.closeQuietly(this.in);
428
                this.in = null;
429
            }
430
            if (!this.isReadOnly()&& this.out != null) {
431
                FeatureStore store = null;
432
                try {
433
                    DataManager dataManager = DALLocator.getDataManager();
434
                    store = (FeatureStore) dataManager.openStore(
435
                            this.getStoreParameters().getDataStoreName(),
436
                            this.getStoreParameters()
437
                    );
438
                    store.edit();
439
                    ExpressionBuilder builder = ExpressionUtils.createExpressionBuilder();
440
                    String filter = builder.eq(
441
                            builder.column(FIELD_RESOURCES_NAME),
442
                            builder.constant(this.getTableName()+"."+this.name)
443
                    ).toString();
444
                    Feature feature = store.findFirst(filter);
445
                    EditableFeature efeature;
446
                    if (feature == null) {
447
                        efeature = store.createNewFeature();
448
                        efeature.set(FIELD_RESOURCES_NAME, this.getTableName()+"."+this.name);
449
                        efeature.set(FIELD_RESOURCES_RESOURCE, ((ByteArrayOutputStream)this.out).toByteArray());
450
                        store.insert(efeature);
451
                    } else {
452
                        efeature = feature.getEditable();
453
                        efeature.set(FIELD_RESOURCES_RESOURCE, ((ByteArrayOutputStream)this.out).toByteArray());
454
                        store.update(efeature);
455
                    }
456
                    store.finishEditing();
457
                    removeCache();
458
                } catch (Throwable ex) {
459
                    FeatureStore.cancelEditingQuietly(store);
460
                    LOGGER.warn("Can't write the resoure '" + this.getURL() + "'.", ex);
461
                } finally {
462
                    DisposeUtils.disposeQuietly(store);
463
                }
464
            }
465
        }
466
    }
467

    
468
    private final ResourcesStorage alternativeStorage;
469
    private final JDBCStoreParameters resourcesStoreParameters;
470
    private final String tableName;
471
    private final boolean readonly;
472
    private final ExistsResourcesCache existsResourcesCache;
473

    
474
    public JDBCResourcesStorage(
475
                JDBCHelper helper,
476
                ResourcesStorage alternativeStorage, 
477
                JDBCStoreParameters resourcesStoreParameters, 
478
                String tableName
479
        ) {
480
        this(helper, alternativeStorage, resourcesStoreParameters, tableName, false);
481
    }
482

    
483
    public JDBCResourcesStorage(
484
                JDBCHelper helper,
485
                ResourcesStorage alternativeStorage, 
486
                JDBCStoreParameters resourcesStoreParameters, 
487
                String tableName,
488
                boolean readonly
489
        ) {
490
        this.helper = helper;
491
        this.readonly = readonly;
492
        this.alternativeStorage = alternativeStorage;
493
        if( StringUtils.equals(TABLE_RESOURCES_NAME, tableName) ) {
494
            // No podemos buscar recursos de la tabla de recursos, ya que si no
495
            // al abrise la tabla de recursos entraria en bucle.
496
            this.resourcesStoreParameters = null;
497
        } else {
498
            this.resourcesStoreParameters = resourcesStoreParameters;
499
        }
500
        this.tableName = tableName;
501
        this.existsResourcesCache = new ExistsResourcesCache();
502
        this.existsResourcesCache.setExpireTime(60000); // 1 min
503
    }
504

    
505
    @Override
506
    public boolean isEmpty() {
507
        return this.resourcesStoreParameters == null;
508
    }
509

    
510
    @Override
511
    public Resource getResource(String name) {
512
        if( this.alternativeStorage!=null ) {
513
            Resource r = this.alternativeStorage.getResource(name);
514
            if( r.exists() ) {
515
                return r;
516
            }
517
        }
518
        if (this.resourcesStoreParameters == null) {
519
            return null;
520
        }
521
        return new DataBaseResource(this,name);
522
    }
523

    
524
    @Override
525
    public List<Resource> getResources(String name) {
526
        if (this.resourcesStoreParameters == null) {
527
            return null;
528
        }
529
        if( this.alternativeStorage!=null ) {
530
            List<Resource> r = this.alternativeStorage.getResources(name);
531
            if( r!=null && !r.isEmpty() ) {
532
                return r;
533
            }
534
        }
535
        FeatureStore store = null;
536
        try {
537
            DataManager dataManager = DALLocator.getDataManager();
538
            store = (FeatureStore) dataManager.openStore(
539
                    this.resourcesStoreParameters.getDataStoreName(),
540
                    this.resourcesStoreParameters
541
            );
542
            ExpressionBuilder builder = ExpressionUtils.createExpressionBuilder();
543
            List<ResourcesStorage.Resource> ress = new ArrayList<>();
544
            int n = 0;
545
            while (true) {
546
                String multiresourceName;
547
                if (n == 0) {
548
                    multiresourceName = name;
549
                } else {
550
                    multiresourceName = String.valueOf(n) + "." + name ;
551
                }
552
                String filter = builder.eq(
553
                        builder.column(FIELD_RESOURCES_NAME),
554
                        builder.constant(this.tableName+"."+multiresourceName)
555
                ).toString();
556
                Feature feature = store.findFirst(filter);
557
                if( feature==null ) {
558
                    break;
559
                }
560
                ress.add(new DataBaseResource(this,multiresourceName));
561
                n++;
562
            }
563
            if (ress.isEmpty()) {
564
                return null;
565
            }
566
            return ress;
567
        } catch (Throwable ex) {
568
            LOGGER.warn("Can't get resources for '" + this.resourcesStoreParameters.getUrl()+"&resourceName="+name + "'.", ex);
569
            return null;
570
            
571
        } finally {
572
            DisposeUtils.disposeQuietly(store);
573
        }
574

    
575
    }
576

    
577
    @Override
578
    public boolean remove(String resourceName) {
579
        if( this.alternativeStorage!=null ) {
580
            try {
581
                this.alternativeStorage.remove(resourceName);
582
            } catch (Exception e) {
583
                //Do nothing
584
            }
585
        }
586
        if (this.resourcesStoreParameters == null) {
587
            return false;
588
        }
589
        DataBaseResource resource = new DataBaseResource(this,resourceName);
590
        
591
        return resource.remove();
592
    }
593
    
594
    void clearCache() throws IOException {
595
        byte[] data = this.resourcesStoreParameters.toByteArray();
596

    
597
        FoldersManager fm = ToolsLocator.getFoldersManager();
598
        File f = fm.getTemporaryFile("resources-storage", "jdbc", DigestUtils.md5Hex(data));
599
        FileUtils.deleteDirectory(f);
600

    
601
    }
602

    
603
}