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

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

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

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

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

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

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

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

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

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

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

    
497
    }
498

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

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

    
523
    }
524

    
525
}