Merge "BUG-509: make sure datastore commits are visible"
[controller.git] / opendaylight / config / config-persister-directory-adapter / src / main / java / org / opendaylight / controller / config / persist / storage / directory / DirectoryPersister.java
1 /*
2  * Copyright (c) 2013 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/epl-v10.html
7  */
8 package org.opendaylight.controller.config.persist.storage.directory;
9
10 import com.google.common.base.Charsets;
11 import com.google.common.io.Files;
12 import org.apache.commons.io.IOUtils;
13 import org.opendaylight.controller.config.persist.api.ConfigSnapshotHolder;
14 import org.opendaylight.controller.config.persist.api.ConfigSnapshotHolderImpl;
15 import org.opendaylight.controller.config.persist.api.Persister;
16 import org.slf4j.Logger;
17 import org.slf4j.LoggerFactory;
18
19 import java.io.File;
20 import java.io.IOException;
21 import java.nio.charset.Charset;
22 import java.util.ArrayList;
23 import java.util.Arrays;
24 import java.util.Collections;
25 import java.util.List;
26 import java.util.SortedSet;
27 import java.util.TreeSet;
28
29 import static com.google.common.base.Preconditions.checkArgument;
30 import static com.google.common.base.Preconditions.checkState;
31
32 public class DirectoryPersister implements Persister {
33     private static final Logger logger = LoggerFactory.getLogger(DirectoryPersister.class);
34     private static final Charset ENCODING = Charsets.UTF_8;
35
36     public static final String MODULES_START = "//MODULES START";
37     static final String SERVICES_START = "//SERVICES START";
38     static final String CAPABILITIES_START = "//CAPABILITIES START";
39
40
41     private final File storage;
42     private static final String header, middle, footer;
43
44     static {
45         header = readResource("header.txt");
46         middle = readResource("middle.txt");
47         footer = readResource("footer.txt");
48     }
49
50     public DirectoryPersister(File storage) {
51         checkArgument(storage.exists() && storage.isDirectory(), "Storage directory does not exist: " + storage);
52         this.storage = storage;
53     }
54
55     private static String readResource(String resource) {
56         try {
57             return IOUtils.toString(DirectoryPersister.class.getResourceAsStream("/" + resource));
58         } catch (IOException e) {
59             throw new IllegalStateException("Cannot load " + resource, e);
60         }
61     }
62
63     @Override
64     public void persistConfig(ConfigSnapshotHolder holder) throws IOException {
65         throw new UnsupportedOperationException("This adapter is read only. Please set readonly=true on " + getClass());
66     }
67
68     @Override
69     public List<ConfigSnapshotHolder> loadLastConfigs() throws IOException {
70         File[] filesArray = storage.listFiles();
71         if (filesArray == null || filesArray.length == 0) {
72             return Collections.emptyList();
73         }
74         List<File> sortedFiles = new ArrayList<>(Arrays.asList(filesArray));
75         Collections.sort(sortedFiles);
76         // combine all found files
77         logger.debug("Reading files in following order: {}", sortedFiles);
78
79         List<ConfigSnapshotHolder> result = new ArrayList<>();
80         for (File file : sortedFiles) {
81             logger.trace("Adding file '{}' to combined result", file);
82
83             ConfigSnapshotHolder configSnapshotHolder = loadLastConfig(file);
84             result.add(configSnapshotHolder);
85         }
86         return result;
87     }
88
89     public static ConfigSnapshotHolder loadLastConfig(File file) throws IOException {
90         final MyLineProcessor lineProcessor = new MyLineProcessor(file.getAbsolutePath());
91         Files.readLines(file, ENCODING, lineProcessor);
92         return lineProcessor.getConfigSnapshotHolder(header, middle, footer);
93     }
94
95
96     @Override
97     public void close() {
98
99     }
100
101     @Override
102     public String toString() {
103         return "FileStorageAdapter [storage=" + storage + "]";
104     }
105 }
106
107 class MyLineProcessor implements com.google.common.io.LineProcessor<String> {
108     private final String fileNameForReporting;
109
110     private boolean inModules, inServices, inCapabilities;
111     private final StringBuffer modulesBuffer = new StringBuffer(), servicesBuilder = new StringBuffer();
112     private final SortedSet<String> caps = new TreeSet<>();
113
114     MyLineProcessor(String fileNameForReporting) {
115         this.fileNameForReporting = fileNameForReporting;
116     }
117
118     @Override
119     public String getResult() {
120         return null;
121     }
122
123     @Override
124     public boolean processLine(String line) throws IOException {
125
126         String lineWithNewLine = line + System.lineSeparator();
127         if (line.equals(DirectoryPersister.MODULES_START)) {
128             checkState(inModules == false && inServices == false && inCapabilities == false);
129             inModules = true;
130         } else if (line.equals(DirectoryPersister.SERVICES_START)) {
131             checkState(inModules == true && inServices == false && inCapabilities == false);
132             inModules = false;
133             inServices = true;
134         } else if (line.equals(DirectoryPersister.CAPABILITIES_START)) {
135             checkState(inModules == false && inServices == true && inCapabilities == false);
136             inServices = false;
137             inCapabilities = true;
138         } else if (inModules) {
139             modulesBuffer.append(lineWithNewLine);
140         } else if (inServices) {
141             servicesBuilder.append(lineWithNewLine);
142         } else {
143             caps.add(line);
144         }
145         return true;
146     }
147
148     private void checkFileConsistency(){
149         checkState(inCapabilities, "File %s is missing delimiters in this order: %s", fileNameForReporting,
150                 Arrays.asList(DirectoryPersister.MODULES_START,
151                         DirectoryPersister.SERVICES_START,
152                         DirectoryPersister.CAPABILITIES_START));
153     }
154
155     String getModules() {
156         checkFileConsistency();
157         return modulesBuffer.toString();
158     }
159
160     String getServices() {
161         checkFileConsistency();
162         return servicesBuilder.toString();
163     }
164
165     SortedSet<String> getCapabilities() {
166         checkFileConsistency();
167         return caps;
168     }
169
170     ConfigSnapshotHolder getConfigSnapshotHolder(String header, String middle, String footer) {
171         String combinedSnapshot = header + getModules() + middle + getServices() + footer;
172         ConfigSnapshotHolder result = new ConfigSnapshotHolderImpl(combinedSnapshot, getCapabilities(), fileNameForReporting);
173         return result;
174     }
175
176 }
177