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 @ 45796

History | View | Annotate | Download (19.5 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.feature.EditableFeature;
55
import org.gvsig.fmap.dal.feature.Feature;
56
import org.gvsig.fmap.dal.feature.FeatureStore;
57
import org.gvsig.fmap.dal.store.jdbc.JDBCStoreParameters;
58
import org.gvsig.tools.ToolsLocator;
59
import org.gvsig.tools.dispose.DisposeUtils;
60
import org.gvsig.tools.folders.FoldersManager;
61
import org.gvsig.tools.resourcesstorage.AbstractResourcesStorage;
62
import org.gvsig.tools.resourcesstorage.ResourcesStorage;
63
import org.gvsig.tools.util.CachedValue;
64
import org.slf4j.Logger;
65
import org.slf4j.LoggerFactory;
66

    
67
/**
68
 *
69
 * @author jjdelcerro
70
 */
71
@SuppressWarnings("UseSpecificCatch")
72
public class JDBCResourcesStorage extends AbstractResourcesStorage {
73

    
74
    private static final Logger LOGGER = LoggerFactory.getLogger(JDBCResourcesStorage.class);
75

    
76
    private static class ExistsResourcesCache extends CachedValue<Map<String,Boolean>> {
77
        @Override
78
        protected void reload() {
79
            this.setValue(new HashMap<>());
80
        }        
81
    }
82
    
83
    private static class DataBaseResource implements Resource {
84

    
85
        private class ResourceInputStream extends InputStream {
86

    
87
            @Override
88
            public int read() throws IOException {
89
                return in.read();
90
            }
91

    
92
            @Override
93
            public void close() throws IOException {
94
                DataBaseResource.this.close();
95
            }
96
        }
97

    
98
        private class ResourceOutputStream extends OutputStream {
99

    
100
            @Override
101
            public void write(int b) throws IOException {
102
                out.write(b);
103
            }
104

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

    
110
            @Override
111
            public void close() throws IOException {
112
                DataBaseResource.this.close();
113
            }
114
        }
115

    
116
        private final String name;
117
        private final JDBCResourcesStorage storage;
118

    
119
        private InputStream in;
120
        private OutputStream out;
121

    
122
        public DataBaseResource(JDBCResourcesStorage storage, String name) {
123
            this.storage = storage;                    
124
            this.name = name;
125
        }
126

    
127
        @Override
128
        public String getName() {
129
          return name;
130
        }
131
        
132
        private JDBCStoreParameters getStoreParameters() {
133
            return this.storage.resourcesStoreParameters;
134
        }
135
        
136
        private String getTableName() {
137
            return this.storage.tableName;
138
        }
139
        
140
        private Map<String,Boolean> getExistsResourcesCache() {
141
            return  this.storage.existsResourcesCache.get();
142
        }
143
        
144
        @Override
145
        public boolean isReadOnly() {
146
            return this.storage.readonly;
147
        }
148
        @Override
149
        public URL getURL() {
150
            try {
151
                String url = this.getStoreParameters().getUrl();
152
                return new URL(url + "&tableName=" + this.getTableName() + "&resourceName=" + this.name);
153
            } catch (Throwable ex) {
154
                return null;
155
            }
156
        }
157

    
158
        @Override
159
        public boolean exists() {
160
            FeatureStore store = null;
161
            try {
162
                FoldersManager fm = ToolsLocator.getFoldersManager();
163
                Pair<String, String> key = this.getCacheID();
164
                String key_s = key.getLeft() + "/"+ key.getRight();
165
                Map<String, Boolean> erc = this.getExistsResourcesCache();
166
                if( erc.containsKey(key_s) ) {
167
                    return this.getExistsResourcesCache().get(key_s);
168
                }
169
                File f = fm.getTemporaryFile("resources-storage","jdbc", key.getLeft(), key.getRight());
170
                if( f.exists() ) {
171
                  return true;
172
                }
173
                DataManager dataManager = DALLocator.getDataManager();
174
                store = (FeatureStore) dataManager.openStore(
175
                        this.getStoreParameters().getDataStoreName(),
176
                        this.getStoreParameters()
177
                );
178
                ExpressionBuilder builder = ExpressionUtils.createExpressionBuilder();
179
                String filter = builder.eq(
180
                        builder.column(FIELD_RESOURCES_NAME),
181
                        builder.constant(this.getTableName()+"."+this.name)
182
                ).toString();
183
                Feature feature = store.findFirst(filter);
184
                boolean exists = feature!=null;
185
                this.getExistsResourcesCache().put(key_s, exists);
186
                return exists;
187
            } catch (Throwable ex) {
188
                LOGGER.warn("Can't access to the resoure '" + this.getURL() + "'.", ex);
189
            } finally {
190
                DisposeUtils.disposeQuietly(store);
191
            }
192
            return false;
193
        }
194

    
195
        public boolean remove() {
196
            FeatureStore store = null;
197
            try {
198
                FoldersManager fm = ToolsLocator.getFoldersManager();
199
                Pair<String, String> key = this.getCacheID();
200
                File f = fm.getTemporaryFile("resources-storage","jdbc", key.getLeft(), key.getRight());
201
                if( f.exists() ) {
202
                  f.delete();
203
                }
204
                DataManager dataManager = DALLocator.getDataManager();
205
                store = (FeatureStore) dataManager.openStore(
206
                        this.getStoreParameters().getDataStoreName(),
207
                        this.getStoreParameters()
208
                );
209
                ExpressionBuilder builder = ExpressionUtils.createExpressionBuilder();
210
                String filter = builder.eq(
211
                        builder.column(FIELD_RESOURCES_NAME),
212
                        builder.constant(this.getTableName()+"."+this.name)
213
                ).toString();
214
                Feature feature = store.findFirst(filter);
215
                if(feature != null){
216
                    store.edit();
217
                    store.delete(feature);
218
                    store.finishEditing();
219
                    return true;
220
                }
221
                return false;
222
            } catch (Throwable ex) {
223
                FeatureStore.cancelEditingQuietly(store);
224
                LOGGER.warn("Can't access to the resoure '" + this.getURL() + "'.", ex);
225
            } finally {
226
                DisposeUtils.disposeQuietly(store);
227
            }
228
            return false;
229
        }
230

    
231
        private Pair<String,String> getCacheID() {
232
          byte[] data = this.getStoreParameters().toByteArray();
233
          ImmutablePair<String, String> r = new ImmutablePair<>(
234
                  DigestUtils.md5Hex(data), 
235
                  this.getTableName()+"."+this.name
236
          );
237
          return r;
238
        }
239
        
240
        private boolean removeCache() {
241
          FoldersManager fm = ToolsLocator.getFoldersManager();
242
          Pair<String, String> key = this.getCacheID();
243
          File f = fm.getTemporaryFile("resources-storage","jdbc", key.getLeft(), key.getRight());
244
          if( f.exists() ) {
245
              try {
246
                  FileUtils.forceDelete(f);
247
              } catch (IOException ex) {
248
                  return false;
249
              }
250
          }
251
          return true;
252
        }
253
        
254
        private InputStream getInputStreamFromCache() {
255
          FoldersManager fm = ToolsLocator.getFoldersManager();
256
          Pair<String, String> key = this.getCacheID();
257
          File f = fm.getTemporaryFile("resources-storage","jdbc", key.getLeft(), key.getRight());
258
          if( !f.exists() ) {
259
            InputStream is = null;
260
            FileOutputStream os = null;
261
            File f2 = null;
262
            try {
263
              is = this.getInputStream();
264
              if (is==null) {
265
                  return null;
266
              }
267
              FileUtils.forceMkdir(f.getParentFile());
268
              os = new FileOutputStream(f);
269
              IOUtils.copy(is, os);             
270
              IOUtils.closeQuietly(os);
271

    
272
              f2 = fm.getTemporaryFile("resources-storage","jdbc", key.getLeft(), "parameters");              
273
              byte[] data = this.getStoreParameters().toByteArray();
274
              os = new FileOutputStream(f2);
275
              IOUtils.write(data, os);
276
            } catch (IOException ex) {
277
              FileUtils.deleteQuietly(f);
278
              FileUtils.deleteQuietly(f2);
279
              return null;
280
            } finally {
281
              IOUtils.closeQuietly(is);
282
              IOUtils.closeQuietly(os);
283
            }
284
          }
285
          InputStream is = null;
286
          try {
287
            is = new FileInputStream(f);
288
          } catch (FileNotFoundException ex) {
289
          }
290
          return is;
291
        }
292
        
293
        private InputStream getInputStream() throws IOException {
294
            FeatureStore store = null;
295
            try {
296
                DataManager dataManager = DALLocator.getDataManager();
297
                store = (FeatureStore) dataManager.openStore(
298
                        this.getStoreParameters().getDataStoreName(),
299
                        this.getStoreParameters()
300
                );
301
                ExpressionBuilder builder = ExpressionUtils.createExpressionBuilder();
302
                String filter = builder.eq(
303
                        builder.column(FIELD_RESOURCES_NAME),
304
                        builder.constant(this.getTableName()+"."+this.name)
305
                ).toString();
306
                Feature feature = store.findFirst(filter);
307
                if (feature == null) {
308
                    return null;
309
                }
310
                byte[] resource = feature.getByteArray(FIELD_RESOURCES_RESOURCE);
311
                InputStream is = new ByteArrayInputStream(resource);
312
                return is;
313
            } catch (Throwable ex) {
314
                LOGGER.warn("Can't access to the resoure '" + this.getURL() + "'.", ex);
315
            } finally {
316
                DisposeUtils.disposeQuietly(store);
317
            }
318
            return null;
319
        }
320

    
321
        @Override
322
        public InputStream asInputStream() throws IOException {
323
            if (this.in != null || this.out != null) {
324
                throw new IllegalStateException("Resource is already open (" + this.getURL() + ")");
325
            }
326
            InputStream is = this.getInputStreamFromCache();
327
            if( is==null ) {
328
                is = this.getInputStream();
329
                if ( is==null) {
330
                    return null;
331
                }
332
            }
333
            this.in = is;
334
            return new ResourceInputStream();
335
        }
336

    
337
        @Override
338
        public OutputStream asOutputStream() throws IOException {
339
            if (this.in != null || this.out != null) {
340
                throw new IllegalStateException("Resource is already open (" + this.getURL() + ").");
341
            }
342
            if( this.isReadOnly()) {
343
                this.out = NullOutputStream.NULL_OUTPUT_STREAM;
344
            } else {
345
                this.out = new ByteArrayOutputStream();
346
            }
347
            return new ResourceOutputStream();
348
        }
349

    
350
        @Override
351
        public void close() {
352
            if (this.in != null) {
353
                IOUtils.closeQuietly(this.in);
354
                this.in = null;
355
            }
356
            if (!this.isReadOnly()&& this.out != null) {
357
                FeatureStore store = null;
358
                try {
359
                    DataManager dataManager = DALLocator.getDataManager();
360
                    store = (FeatureStore) dataManager.openStore(
361
                            this.getStoreParameters().getDataStoreName(),
362
                            this.getStoreParameters()
363
                    );
364
                    store.edit();
365
                    ExpressionBuilder builder = ExpressionUtils.createExpressionBuilder();
366
                    String filter = builder.eq(
367
                            builder.column(FIELD_RESOURCES_NAME),
368
                            builder.constant(this.getTableName()+"."+this.name)
369
                    ).toString();
370
                    Feature feature = store.findFirst(filter);
371
                    EditableFeature efeature;
372
                    if (feature == null) {
373
                        efeature = store.createNewFeature();
374
                        efeature.set(FIELD_RESOURCES_NAME, this.getTableName()+"."+this.name);
375
                        efeature.set(FIELD_RESOURCES_RESOURCE, ((ByteArrayOutputStream)this.out).toByteArray());
376
                        store.insert(efeature);
377
                    } else {
378
                        efeature = feature.getEditable();
379
                        efeature.set(FIELD_RESOURCES_RESOURCE, ((ByteArrayOutputStream)this.out).toByteArray());
380
                        store.update(efeature);
381
                    }
382
                    store.finishEditing();
383
                    removeCache();
384
                } catch (Throwable ex) {
385
                    FeatureStore.cancelEditingQuietly(store);
386
                    LOGGER.warn("Can't write the resoure '" + this.getURL() + "'.", ex);
387
                } finally {
388
                    DisposeUtils.disposeQuietly(store);
389
                }
390
            }
391
        }
392
    }
393

    
394
    private final ResourcesStorage alternativeStorge;
395
    private final JDBCStoreParameters resourcesStoreParameters;
396
    private final String tableName;
397
    private final boolean readonly;
398
    private final ExistsResourcesCache existsResourcesCache;
399

    
400
    public JDBCResourcesStorage(
401
                ResourcesStorage alternativeStorge, 
402
                JDBCStoreParameters resourcesStoreParameters, 
403
                String tableName
404
        ) {
405
        this(alternativeStorge, resourcesStoreParameters, tableName, false);
406
    }
407

    
408
    public JDBCResourcesStorage(
409
                ResourcesStorage alternativeStorge, 
410
                JDBCStoreParameters resourcesStoreParameters, 
411
                String tableName,
412
                boolean readonly
413
        ) {
414
        this.readonly = readonly;
415
        this.alternativeStorge = alternativeStorge;
416
        if( StringUtils.equals(TABLE_RESOURCES_NAME, tableName) ) {
417
            // No podemos buscar recursos de la tabla de recursos, ya que si no
418
            // al abrise la tabla de recursos entraria en bucle.
419
            this.resourcesStoreParameters = null;
420
        } else {
421
            this.resourcesStoreParameters = resourcesStoreParameters;
422
        }
423
        this.tableName = tableName;
424
        this.existsResourcesCache = new ExistsResourcesCache();
425
        this.existsResourcesCache.setExpireTime(60000); // 1 min
426
    }
427

    
428
    @Override
429
    public boolean isEmpty() {
430
        return this.resourcesStoreParameters == null;
431
    }
432

    
433
    @Override
434
    public Resource getResource(String name) {
435
        if( this.alternativeStorge!=null ) {
436
            Resource r = this.alternativeStorge.getResource(name);
437
            if( r.exists() ) {
438
                return r;
439
            }
440
        }
441
        if (this.resourcesStoreParameters == null) {
442
            return null;
443
        }
444
        return new DataBaseResource(this,name);
445
    }
446

    
447
    @Override
448
    public List<Resource> getResources(String name) {
449
        if (this.resourcesStoreParameters == null) {
450
            return null;
451
        }
452
        if( this.alternativeStorge!=null ) {
453
            List<Resource> r = this.alternativeStorge.getResources(name);
454
            if( r!=null && !r.isEmpty() ) {
455
                return r;
456
            }
457
        }
458
        FeatureStore store = null;
459
        try {
460
            DataManager dataManager = DALLocator.getDataManager();
461
            store = (FeatureStore) dataManager.openStore(
462
                    this.resourcesStoreParameters.getDataStoreName(),
463
                    this.resourcesStoreParameters
464
            );
465
            ExpressionBuilder builder = ExpressionUtils.createExpressionBuilder();
466
            List<ResourcesStorage.Resource> ress = new ArrayList<>();
467
            int n = 0;
468
            while (true) {
469
                String multiresourceName;
470
                if (n == 0) {
471
                    multiresourceName = name;
472
                } else {
473
                    multiresourceName = String.valueOf(n) + "." + name ;
474
                }
475
                String filter = builder.eq(
476
                        builder.column(FIELD_RESOURCES_NAME),
477
                        builder.constant(this.tableName+"."+multiresourceName)
478
                ).toString();
479
                Feature feature = store.findFirst(filter);
480
                if( feature==null ) {
481
                    break;
482
                }
483
                ress.add(new DataBaseResource(this,multiresourceName));
484
                n++;
485
            }
486
            if (ress.isEmpty()) {
487
                return null;
488
            }
489
            return ress;
490
        } catch (Throwable ex) {
491
            LOGGER.warn("Can't get resources for '" + this.resourcesStoreParameters.getUrl()+"&resourceName="+name + "'.", ex);
492
            return null;
493
            
494
        } finally {
495
            DisposeUtils.disposeQuietly(store);
496
        }
497

    
498
    }
499

    
500
    @Override
501
    public boolean remove(String resourceName) {
502
        if( this.alternativeStorge!=null ) {
503
            try {
504
                this.alternativeStorge.remove(resourceName);
505
            } catch (Exception e) {
506
                //Do nothing
507
            }
508
        }
509
        if (this.resourcesStoreParameters == null) {
510
            return false;
511
        }
512
        DataBaseResource resource = new DataBaseResource(this,resourceName);
513
        
514
        return resource.remove();
515
    }
516
    
517
    void clearCache() throws IOException {
518
        byte[] data = this.resourcesStoreParameters.toByteArray();
519

    
520
        FoldersManager fm = ToolsLocator.getFoldersManager();
521
        File f = fm.getTemporaryFile("resources-storage", "jdbc", DigestUtils.md5Hex(data));
522
        FileUtils.deleteDirectory(f);
523

    
524
    }
525

    
526
}