API Claritity: Documented org.opendaylight.yangtools.yang.model.util.repo
[yangtools.git] / yang / yang-model-util / src / main / java / org / opendaylight / yangtools / yang / model / util / repo / FilesystemSchemaCachingProvider.java
1 /*
2  * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/eplv10.html
7  */
8 package org.opendaylight.yangtools.yang.model.util.repo;
9
10 import java.io.ByteArrayInputStream;
11 import java.io.File;
12 import java.io.FileInputStream;
13 import java.io.FileNotFoundException;
14 import java.io.FileOutputStream;
15 import java.io.FilenameFilter;
16 import java.io.IOException;
17 import java.io.InputStream;
18 import java.io.OutputStreamWriter;
19 import java.text.DateFormat;
20 import java.text.ParseException;
21 import java.text.SimpleDateFormat;
22 import java.util.Date;
23 import java.util.TreeMap;
24 import java.util.regex.Matcher;
25 import java.util.regex.Pattern;
26
27 import org.slf4j.Logger;
28 import org.slf4j.LoggerFactory;
29
30 import com.google.common.base.Charsets;
31 import com.google.common.base.Function;
32 import com.google.common.base.Optional;
33 import com.google.common.base.Preconditions;
34
35 /**
36  * Filesystem-based schema caching source provider
37  *
38  * This schema source provider caches all YANG modules loaded from backing
39  * schema source providers (registered via
40  * {@link #createInstanceFor(SchemaSourceProvider)} to supplied folder.
41  *
42  * @param <I>
43  *            Input format in which schema source is represented.
44  *
45  */
46 public final class FilesystemSchemaCachingProvider<I> extends AbstractCachingSchemaSourceProvider<I, InputStream> {
47     private static final Logger LOG = LoggerFactory.getLogger(FilesystemSchemaCachingProvider.class);
48
49     private final File storageDirectory;
50     private final SchemaSourceTransformation<I, String> transformationFunction;
51
52     /**
53      *
54      * Construct filesystem caching schema source provider.
55      *
56      *
57      * @param delegate
58      *            Default delegate to lookup for missed entries in cache.
59      * @param directory
60      *            Directory where YANG files should be cached.
61      * @param transformationFunction
62      *            Transformation function which translates from input in format
63      *            <code>I</code> to InputStream.
64      * @throws IllegalArgumentException
65      *             If supplied directory does not exists or is not directory.
66      */
67     public FilesystemSchemaCachingProvider(final AdvancedSchemaSourceProvider<I> delegate, final File directory,
68             final SchemaSourceTransformation<I, String> transformationFunction) {
69         super(delegate);
70         Preconditions.checkNotNull(directory, "directory must not be null.");
71         Preconditions.checkArgument(directory.exists(), "directory must be directory.");
72         Preconditions.checkArgument(directory.isDirectory(), "directory must be directory.");
73         this.storageDirectory = directory;
74         this.transformationFunction = Preconditions.checkNotNull(transformationFunction,
75                 "transformationFunction must not be null.");
76     }
77
78     /**
79      *
80      * Construct filesystem caching schema source provider.
81      *
82      *
83      * @param delegate
84      *            Default delegate to lookup for missed entries in cache.
85      * @param directory
86      *            Directory where YANG files should be cached.
87      * @param transformationFunction
88      *            Transformation function which translates from input in format
89      *            <code>I</code> to InputStream.
90      * @throws IllegalArgumentException
91      *             If supplied directory does not exists or is not directory.
92      * @deprecated Use
93      *             {@link #FilesystemSchemaCachingProvider(AdvancedSchemaSourceProvider, File, SchemaSourceTransformation)}
94      *             with
95      *             {@link SchemaSourceProviders#schemaSourceTransformationFrom(Function)}
96      *             instead.
97      */
98     @Deprecated
99     public FilesystemSchemaCachingProvider(final AdvancedSchemaSourceProvider<I> delegate, final File directory,
100             final Function<I, String> transformationFunction) {
101         super(delegate);
102         Preconditions.checkNotNull(directory, "directory must not be null.");
103         Preconditions.checkArgument(directory.exists(), "directory must be directory.");
104         Preconditions.checkArgument(directory.isDirectory(), "directory must be directory.");
105         this.storageDirectory = directory;
106         this.transformationFunction = SchemaSourceProviders.schemaSourceTransformationFrom(transformationFunction);
107     }
108
109     @Override
110     protected synchronized Optional<InputStream> cacheSchemaSource(final SourceIdentifier identifier,
111             final Optional<I> source) {
112         File schemaFile = toFile(identifier);
113         try {
114             if (source.isPresent() && schemaFile.createNewFile()) {
115                 try (FileOutputStream outStream = new FileOutputStream(schemaFile);
116                         OutputStreamWriter writer = new OutputStreamWriter(outStream);) {
117                     writer.write(transformToString(source.get()));
118                     writer.flush();
119                 } catch (IOException e) {
120                     LOG.warn("Could not chache source for {}. Source: ",identifier,source.get(),e);
121                 }
122             }
123         } catch (IOException e) {
124             LOG.warn("Could not create cache file for {}. File: ",identifier,schemaFile,e);
125         }
126         return transformToStream(source);
127     }
128
129     private Optional<InputStream> transformToStream(final Optional<I> source) {
130         if (source.isPresent()) {
131             return Optional.<InputStream> of(new ByteArrayInputStream(transformToString(source.get()).getBytes(
132                     Charsets.UTF_8)));
133         }
134         return Optional.absent();
135     }
136
137     private String transformToString(final I input) {
138         return transformationFunction.transform(input);
139     }
140
141     @Override
142     protected Optional<InputStream> getCachedSchemaSource(final SourceIdentifier identifier) {
143         File inputFile = toFile(identifier);
144         try {
145             if (inputFile.exists() && inputFile.canRead()) {
146                 InputStream stream = new FileInputStream(inputFile);
147                 return Optional.of(stream);
148             }
149         } catch (FileNotFoundException e) {
150             return Optional.absent();
151         }
152         return Optional.absent();
153     }
154
155     private File toFile(final SourceIdentifier identifier) {
156         File file = null;
157         String rev = identifier.getRevision();
158         if (rev == null || rev.isEmpty()) {
159             file = findFileWithNewestRev(identifier);
160         } else {
161             file = new File(storageDirectory, identifier.toYangFilename());
162         }
163         return file;
164     }
165
166     private File findFileWithNewestRev(final SourceIdentifier identifier) {
167         File[] files = storageDirectory.listFiles(new FilenameFilter() {
168             final String regex = identifier.getName() + "(\\.yang|@\\d\\d\\d\\d-\\d\\d-\\d\\d.yang)";
169
170             @Override
171             public boolean accept(final File dir, final String name) {
172                 if (name.matches(regex)) {
173                     return true;
174                 } else {
175                     return false;
176                 }
177             }
178         });
179
180         if (files.length == 0) {
181             return new File(storageDirectory, identifier.toYangFilename());
182         }
183         if (files.length == 1) {
184             return files[0];
185         }
186
187         File file = null;
188         Pattern p = Pattern.compile("\\d\\d\\d\\d-\\d\\d-\\d\\d");
189         TreeMap<Date, File> map = new TreeMap<>();
190         for (File sorted : files) {
191             String fileName = sorted.getName();
192             Matcher m = p.matcher(fileName);
193             if (m.find()) {
194                 String revStr = m.group();
195                 DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
196                 try {
197                     Date d = df.parse(revStr);
198                     map.put(d, sorted);
199                 } catch (ParseException e) {
200                     LOG.info("Unable to parse date from yang file name");
201                     map.put(new Date(0L), sorted);
202                 }
203
204             } else {
205                 map.put(new Date(0L), sorted);
206             }
207         }
208         file = map.lastEntry().getValue();
209
210         return file;
211     }
212
213     public static FilesystemSchemaCachingProvider<String> createFromStringSourceProvider(
214             final SchemaSourceProvider<String> liveProvider, final File directory) {
215         Preconditions.checkNotNull(liveProvider);
216         Preconditions.checkNotNull(directory);
217         directory.mkdirs();
218         return new FilesystemSchemaCachingProvider<String>(
219                 SchemaSourceProviders.toAdvancedSchemaSourceProvider(liveProvider),//
220                 directory, //
221                 SchemaSourceProviders.<String>identityTransformation());
222     }
223 }