2 * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
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
8 package org.opendaylight.yangtools.yang.model.util.repo;
10 import java.io.ByteArrayInputStream;
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;
27 import org.slf4j.Logger;
28 import org.slf4j.LoggerFactory;
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;
36 * Filesystem-based schema caching source provider
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.
43 * Input format in which schema source is represented.
46 public final class FilesystemSchemaCachingProvider<I> extends AbstractCachingSchemaSourceProvider<I, InputStream> {
47 private static final Logger LOG = LoggerFactory.getLogger(FilesystemSchemaCachingProvider.class);
49 private final File storageDirectory;
50 private final SchemaSourceTransformation<I, String> transformationFunction;
54 * Construct filesystem caching schema source provider.
58 * Default delegate to lookup for missed entries in cache.
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.
67 public FilesystemSchemaCachingProvider(final AdvancedSchemaSourceProvider<I> delegate, final File directory,
68 final SchemaSourceTransformation<I, String> transformationFunction) {
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.");
80 * Construct filesystem caching schema source provider.
84 * Default delegate to lookup for missed entries in cache.
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.
93 * {@link #FilesystemSchemaCachingProvider(AdvancedSchemaSourceProvider, File, SchemaSourceTransformation)}
95 * {@link SchemaSourceProviders#schemaSourceTransformationFrom(Function)}
99 public FilesystemSchemaCachingProvider(final AdvancedSchemaSourceProvider<I> delegate, final File directory,
100 final Function<I, String> transformationFunction) {
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);
110 protected synchronized Optional<InputStream> cacheSchemaSource(final SourceIdentifier identifier,
111 final Optional<I> source) {
112 File schemaFile = toFile(identifier);
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()));
119 } catch (IOException e) {
120 LOG.warn("Could not chache source for {}. Source: ",identifier,source.get(),e);
123 } catch (IOException e) {
124 LOG.warn("Could not create cache file for {}. File: ",identifier,schemaFile,e);
126 return transformToStream(source);
129 private Optional<InputStream> transformToStream(final Optional<I> source) {
130 if (source.isPresent()) {
131 return Optional.<InputStream> of(new ByteArrayInputStream(transformToString(source.get()).getBytes(
134 return Optional.absent();
137 private String transformToString(final I input) {
138 return transformationFunction.transform(input);
142 protected Optional<InputStream> getCachedSchemaSource(final SourceIdentifier identifier) {
143 File inputFile = toFile(identifier);
145 if (inputFile.exists() && inputFile.canRead()) {
146 InputStream stream = new FileInputStream(inputFile);
147 return Optional.of(stream);
149 } catch (FileNotFoundException e) {
150 return Optional.absent();
152 return Optional.absent();
155 private File toFile(final SourceIdentifier identifier) {
157 String rev = identifier.getRevision();
158 if (rev == null || rev.isEmpty()) {
159 file = findFileWithNewestRev(identifier);
161 file = new File(storageDirectory, identifier.toYangFilename());
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)";
171 public boolean accept(final File dir, final String name) {
172 if (name.matches(regex)) {
180 if (files.length == 0) {
181 return new File(storageDirectory, identifier.toYangFilename());
183 if (files.length == 1) {
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);
194 String revStr = m.group();
195 DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
197 Date d = df.parse(revStr);
199 } catch (ParseException e) {
200 LOG.info("Unable to parse date from yang file name");
201 map.put(new Date(0L), sorted);
205 map.put(new Date(0L), sorted);
208 file = map.lastEntry().getValue();
213 public static FilesystemSchemaCachingProvider<String> createFromStringSourceProvider(
214 final SchemaSourceProvider<String> liveProvider, final File directory) {
215 Preconditions.checkNotNull(liveProvider);
216 Preconditions.checkNotNull(directory);
218 return new FilesystemSchemaCachingProvider<String>(
219 SchemaSourceProviders.toAdvancedSchemaSourceProvider(liveProvider),//
221 SchemaSourceProviders.<String>identityTransformation());