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);
48 public static final Pattern REVISION_PATTERN = Pattern.compile("\\d\\d\\d\\d-\\d\\d-\\d\\d");
50 private final File storageDirectory;
51 private final SchemaSourceTransformation<I, String> transformationFunction;
55 * Construct filesystem caching schema source provider.
59 * Default delegate to lookup for missed entries in cache.
61 * Directory where YANG files should be cached.
62 * @param transformationFunction
63 * Transformation function which translates from input in format
64 * <code>I</code> to InputStream.
65 * @throws IllegalArgumentException
66 * If supplied directory does not exists or is not directory.
68 public FilesystemSchemaCachingProvider(final AdvancedSchemaSourceProvider<I> delegate, final File directory,
69 final SchemaSourceTransformation<I, String> transformationFunction) {
71 Preconditions.checkNotNull(directory, "directory must not be null.");
72 Preconditions.checkArgument(directory.exists(), "directory must be directory.");
73 Preconditions.checkArgument(directory.isDirectory(), "directory must be directory.");
74 this.storageDirectory = directory;
75 this.transformationFunction = Preconditions.checkNotNull(transformationFunction,
76 "transformationFunction must not be null.");
81 * Construct filesystem caching schema source provider.
85 * Default delegate to lookup for missed entries in cache.
87 * Directory where YANG files should be cached.
88 * @param transformationFunction
89 * Transformation function which translates from input in format
90 * <code>I</code> to InputStream.
91 * @throws IllegalArgumentException
92 * If supplied directory does not exists or is not directory.
94 * {@link #FilesystemSchemaCachingProvider(AdvancedSchemaSourceProvider, File, SchemaSourceTransformation)}
96 * {@link SchemaSourceProviders#schemaSourceTransformationFrom(Function)}
100 public FilesystemSchemaCachingProvider(final AdvancedSchemaSourceProvider<I> delegate, final File directory,
101 final Function<I, String> transformationFunction) {
103 Preconditions.checkNotNull(directory, "directory must not be null.");
104 Preconditions.checkArgument(directory.exists(), "directory must be directory.");
105 Preconditions.checkArgument(directory.isDirectory(), "directory must be directory.");
106 this.storageDirectory = directory;
107 this.transformationFunction = SchemaSourceProviders.schemaSourceTransformationFrom(transformationFunction);
111 protected synchronized Optional<InputStream> cacheSchemaSource(final SourceIdentifier identifier,
112 final Optional<I> source) {
113 File schemaFile = toFile(identifier);
115 if (source.isPresent() && schemaFile.createNewFile()) {
116 try (FileOutputStream outStream = new FileOutputStream(schemaFile);
117 OutputStreamWriter writer = new OutputStreamWriter(outStream);) {
118 writer.write(transformToString(source.get()));
120 } catch (IOException e) {
121 LOG.warn("Could not chache source for {}. Source: ",identifier,source.get(),e);
124 } catch (IOException e) {
125 LOG.warn("Could not create cache file for {}. File: ",identifier,schemaFile,e);
127 return transformToStream(source);
130 private Optional<InputStream> transformToStream(final Optional<I> source) {
131 if (source.isPresent()) {
132 return Optional.<InputStream> of(new ByteArrayInputStream(transformToString(source.get()).getBytes(
135 return Optional.absent();
138 private String transformToString(final I input) {
139 return transformationFunction.transform(input);
143 protected Optional<InputStream> getCachedSchemaSource(final SourceIdentifier identifier) {
144 File inputFile = toFile(identifier);
146 if (inputFile.exists() && inputFile.canRead()) {
147 InputStream stream = new FileInputStream(inputFile);
148 return Optional.of(stream);
150 } catch (FileNotFoundException e) {
151 return Optional.absent();
153 return Optional.absent();
156 private File toFile(final SourceIdentifier identifier) {
157 return sourceIdToFile(identifier, storageDirectory);
160 public static File sourceIdToFile(final SourceIdentifier identifier, final File storageDirectory) {
162 String rev = identifier.getRevision();
163 if (rev == null || rev.isEmpty()) {
164 file = findFileWithNewestRev(identifier, storageDirectory);
166 file = new File(storageDirectory, identifier.toYangFilename());
171 private static File findFileWithNewestRev(final SourceIdentifier identifier, final File storageDirectory) {
172 File[] files = storageDirectory.listFiles(new FilenameFilter() {
173 final Pattern p = Pattern.compile(Pattern.quote(identifier.getName()) + "(\\.yang|@\\d\\d\\d\\d-\\d\\d-\\d\\d.yang)");
176 public boolean accept(final File dir, final String name) {
177 return p.matcher(name).matches();
181 if (files.length == 0) {
182 return new File(storageDirectory, identifier.toYangFilename());
184 if (files.length == 1) {
189 TreeMap<Date, File> map = new TreeMap<>();
190 for (File sorted : files) {
191 String fileName = sorted.getName();
192 Matcher m = REVISION_PATTERN.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());