1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.logging.log4j.catalog.git.dao;
18
19 import java.io.File;
20 import java.io.FileWriter;
21 import java.io.IOException;
22 import java.time.Instant;
23 import java.time.LocalDateTime;
24 import java.time.ZoneId;
25
26 import com.fasterxml.jackson.core.JsonFactory;
27 import com.fasterxml.jackson.core.JsonParser;
28 import com.fasterxml.jackson.databind.ObjectMapper;
29 import com.fasterxml.jackson.databind.SerializationFeature;
30 import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider;
31 import org.apache.logging.log4j.LogManager;
32 import org.apache.logging.log4j.Logger;
33 import org.apache.logging.log4j.catalog.api.CatalogData;
34 import org.apache.logging.log4j.catalog.api.dao.AbstractCatalogReader;
35 import org.apache.logging.log4j.catalog.api.dao.CatalogDao;
36 import org.apache.logging.log4j.catalog.api.exception.CatalogModificationException;
37 import org.apache.logging.log4j.catalog.api.exception.CatalogReadException;
38 import org.apache.logging.log4j.catalog.api.exception.CatalogNotFoundException;
39 import org.apache.logging.log4j.catalog.api.util.CatalogEventFilter;
40 import org.eclipse.jgit.api.CloneCommand;
41 import org.eclipse.jgit.api.Git;
42 import org.eclipse.jgit.api.PullCommand;
43 import org.eclipse.jgit.api.PushCommand;
44 import org.eclipse.jgit.api.TransportConfigCallback;
45 import org.eclipse.jgit.api.errors.GitAPIException;
46 import org.eclipse.jgit.internal.storage.file.FileRepository;
47 import org.eclipse.jgit.lib.Repository;
48 import org.eclipse.jgit.transport.CredentialsProvider;
49
50 public class GitCatalogDao extends AbstractCatalogReader implements CatalogDao {
51 private static final Logger LOGGER = LogManager.getLogger();
52 private static final String DEFAULT_CATALOG_PATH = "src/main/resources/catalog.json";
53
54 private final ObjectMapper mapper;
55
56 private CredentialsProvider credentialsProvider = null;
57 private TransportConfigCallback transportConfigCallback = null;
58 private String remoteRepoUri = null;
59 private String localRepoPath = null;
60 private String catalogPath = DEFAULT_CATALOG_PATH;
61 private String branch = null;
62
63 private Repository localRepo = null;
64 private Git git = null;
65 private File catalogFile = null;
66
67 public GitCatalogDao() {
68 JsonFactory factory = new JsonFactory();
69 factory.enable(JsonParser.Feature.ALLOW_COMMENTS);
70 mapper = new ObjectMapper(factory).enable(SerializationFeature.INDENT_OUTPUT);
71 SimpleFilterProvider filterProvider = new SimpleFilterProvider();
72 filterProvider.addFilter("catalogEvent", new CatalogEventFilter());
73 mapper.setFilterProvider(filterProvider);
74 }
75
76 public CredentialsProvider getCredentialsProvider() {
77 return credentialsProvider;
78 }
79
80 public void setCredentialsProvider(CredentialsProvider credentialsProvider) {
81 this.credentialsProvider = credentialsProvider;
82 }
83
84 public TransportConfigCallback getTransportConfigCallback() {
85 return transportConfigCallback;
86 }
87
88 public void setTransportConfigCallback(TransportConfigCallback transportConfigCallback) {
89 this.transportConfigCallback = transportConfigCallback;
90 }
91
92 public String getRemoteRepoUri() {
93 return remoteRepoUri;
94 }
95
96 public void setRemoteRepoUri(String remoteRepoUri) {
97 this.remoteRepoUri = remoteRepoUri;
98 }
99
100 public String getLocalRepoPath() {
101 return localRepoPath;
102 }
103
104 public void setLocalRepoPath(String localRepoPath) {
105 this.localRepoPath = localRepoPath;
106 }
107
108 public String getCatalogPath() {
109 return catalogPath;
110 }
111
112 public void setCatalogPath(String catalogPath) {
113 this.catalogPath = catalogPath;
114 }
115
116 public String getBranch() {
117 return branch;
118 }
119
120 public void setBranch(String branch) {
121 this.branch = branch;
122 }
123
124 @Override
125 public LocalDateTime getLastUpdated() {
126 if (localRepo == null) {
127 updateRepo();
128 }
129 return LocalDateTime.ofInstant(Instant.ofEpochMilli(catalogFile.lastModified()),
130 ZoneId.systemDefault());
131 }
132
133 @Override
134 public synchronized CatalogData read() {
135 updateRepo();
136 if (catalogFile == null || !catalogFile.exists() || !catalogFile.canRead()) {
137 throw new IllegalStateException("Catalog " + catalogFile.getAbsolutePath() + " is not readable.");
138 }
139
140 try {
141 catalogData = mapper.readValue(catalogFile, CatalogData.class);
142 return catalogData;
143 } catch (IOException ioe) {
144 throw new CatalogReadException("Error reading catalog from " + catalogFile.getAbsolutePath());
145 }
146 }
147
148 @Override
149 public void write(CatalogData data) {
150 File localRepoFile = new File(localRepoPath);
151 if (!localRepoFile.exists() || !localRepoFile.canWrite()) {
152 throw new IllegalStateException("Catalog is not writable.");
153 }
154
155 FileWriter writer = null;
156 try {
157 String text = mapper.writeValueAsString(data);
158 writer = new FileWriter(catalogFile);
159 writer.write(text);
160 } catch (IOException ioException) {
161 throw new CatalogModificationException("Unable to write catalog file.", ioException);
162 } finally {
163 try { if (writer != null) writer.close(); } catch(Exception exception) { }
164 }
165
166 try (Git git = Git.open(localRepoFile)) {
167 git.add().addFilepattern(catalogPath).call();
168 git.commit().setMessage("Catalog updated").call();
169 updateRepo();
170 PushCommand pushCommand = git.push();
171 if (credentialsProvider != null) {
172 pushCommand.setCredentialsProvider(credentialsProvider);
173 }
174 if (transportConfigCallback != null) {
175 pushCommand.setTransportConfigCallback(transportConfigCallback);
176 }
177 pushCommand.call();
178 } catch (GitAPIException | IOException ex) {
179 throw new CatalogModificationException("Unable to modify catalog", ex);
180 }
181 }
182
183 @Override
184 public String readCatalog() {
185 return null;
186 }
187
188 private void updateRepo() {
189
190 File localRepoFile = new File(localRepoPath);
191 if (!localRepoFile.exists()) {
192 LOGGER.debug("local git repo {} does not exist - creating it", localRepoPath);
193 localRepoFile.getParentFile().mkdirs();
194 CloneCommand cloneCommand = Git.cloneRepository().setURI(remoteRepoUri).setDirectory(localRepoFile);
195 if (branch != null) {
196 cloneCommand.setBranch(branch);
197 }
198 if (credentialsProvider != null) {
199 cloneCommand.setCredentialsProvider(credentialsProvider);
200 }
201 if (transportConfigCallback != null) {
202 cloneCommand.setTransportConfigCallback(transportConfigCallback);
203 }
204 try (Git git = cloneCommand.call()) {
205 catalogFile = new File(localRepoFile, catalogPath);
206 } catch (Exception ex) {
207 throw new CatalogNotFoundException("Unable to clone remote catalog at " + remoteRepoUri + " to " + localRepoPath, ex);
208 }
209 } else {
210 try {
211 LOGGER.debug("local git repo {} exists - updating", localRepoPath);
212 localRepo = new FileRepository(localRepoPath + "/.git");
213 catalogFile = new File(localRepoFile, catalogPath);
214 git = new Git(localRepo);
215 PullCommand pullCommand = git.pull();
216 try {
217 if (credentialsProvider != null) {
218 pullCommand.setCredentialsProvider(credentialsProvider);
219 }
220 if (transportConfigCallback != null) {
221 pullCommand.setTransportConfigCallback(transportConfigCallback);
222 }
223 pullCommand.call();
224 } catch (GitAPIException gitApiException) {
225 LOGGER.error("Exception", gitApiException);
226 }
227 } catch (Exception exception) {
228 throw new CatalogReadException("Unable to pull remote catalog at " + remoteRepoUri + " to " + localRepoPath, exception);
229 }
230 }
231 }
232 }