blob: 7d1c012103cdbd81d4fd1ed18fed2129b47829a2 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache license, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the license for the specific language governing permissions and
* limitations under the license.
*/
package org.apache.logging.log4j.catalog.git.dao;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.catalog.api.CatalogData;
import org.apache.logging.log4j.catalog.api.dao.AbstractCatalogReader;
import org.apache.logging.log4j.catalog.api.dao.CatalogDao;
import org.apache.logging.log4j.catalog.api.exception.CatalogModificationException;
import org.apache.logging.log4j.catalog.api.exception.CatalogNotFoundException;
import org.apache.logging.log4j.catalog.api.exception.CatalogReadException;
import org.apache.logging.log4j.catalog.api.util.CatalogEventFilter;
import org.eclipse.jgit.api.CloneCommand;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.PullCommand;
import org.eclipse.jgit.api.PushCommand;
import org.eclipse.jgit.api.TransportConfigCallback;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.internal.storage.file.FileRepository;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.transport.CredentialsProvider;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
public class GitCatalogDao extends AbstractCatalogReader implements CatalogDao {
private static final Logger LOGGER = LogManager.getLogger();
private static final String DEFAULT_CATALOG_PATH = "src/main/resources/catalog.json";
private final ObjectMapper mapper;
private CredentialsProvider credentialsProvider = null;
private TransportConfigCallback transportConfigCallback = null;
private String remoteRepoUri = null;
private String localRepoPath = null;
private String catalogPath = DEFAULT_CATALOG_PATH;
private String branch = null;
private Repository localRepo = null;
private Git git = null;
private File catalogFile = null;
public GitCatalogDao() {
JsonFactory factory = new JsonFactory();
factory.enable(JsonParser.Feature.ALLOW_COMMENTS);
mapper = new ObjectMapper(factory).enable(SerializationFeature.INDENT_OUTPUT);
SimpleFilterProvider filterProvider = new SimpleFilterProvider();
filterProvider.addFilter("catalogEvent", new CatalogEventFilter());
mapper.setFilterProvider(filterProvider);
}
public CredentialsProvider getCredentialsProvider() {
return credentialsProvider;
}
public void setCredentialsProvider(CredentialsProvider credentialsProvider) {
this.credentialsProvider = credentialsProvider;
}
public TransportConfigCallback getTransportConfigCallback() {
return transportConfigCallback;
}
public void setTransportConfigCallback(TransportConfigCallback transportConfigCallback) {
this.transportConfigCallback = transportConfigCallback;
}
public String getRemoteRepoUri() {
return remoteRepoUri;
}
public void setRemoteRepoUri(String remoteRepoUri) {
this.remoteRepoUri = remoteRepoUri;
}
public String getLocalRepoPath() {
return localRepoPath;
}
public void setLocalRepoPath(String localRepoPath) {
this.localRepoPath = localRepoPath;
}
public String getCatalogPath() {
return catalogPath;
}
public void setCatalogPath(String catalogPath) {
this.catalogPath = catalogPath;
}
public String getBranch() {
return branch;
}
public void setBranch(String branch) {
this.branch = branch;
}
@Override
public LocalDateTime getLastUpdated() {
if (localRepo == null) {
updateRepo();
}
return LocalDateTime.ofInstant(Instant.ofEpochMilli(catalogFile.lastModified()),
ZoneId.systemDefault());
}
@Override
public synchronized CatalogData read() {
updateRepo();
if (catalogFile == null) {
throw new CatalogNotFoundException();
}
if (!catalogFile.exists() || !catalogFile.canRead()) {
throw new CatalogReadException("Catalog " + catalogFile.getAbsolutePath() + " is not readable.");
}
try {
catalogData = mapper.readValue(catalogFile, CatalogData.class);
return catalogData;
} catch (IOException ioe) {
throw new CatalogReadException("Error reading catalog from " + catalogFile.getAbsolutePath());
}
}
@Override
public void write(CatalogData data) {
File localRepoFile = new File(localRepoPath);
if (!localRepoFile.exists() || !localRepoFile.canWrite()) {
throw new CatalogModificationException("Catalog is not writable: " + localRepoFile.getAbsolutePath());
}
FileWriter writer = null;
try {
String text = mapper.writeValueAsString(data);
writer = new FileWriter(catalogFile);
writer.write(text);
} catch (IOException ioException) {
throw new CatalogModificationException("Unable to write catalog file.", ioException);
} finally {
try { if (writer != null) writer.close(); } catch(Exception exception) { }
}
try (Git git = Git.open(localRepoFile)) {
git.add().addFilepattern(catalogPath).call();
git.commit().setMessage("Catalog updated").call();
updateRepo();
PushCommand pushCommand = git.push();
if (credentialsProvider != null) {
pushCommand.setCredentialsProvider(credentialsProvider);
}
if (transportConfigCallback != null) {
pushCommand.setTransportConfigCallback(transportConfigCallback);
}
pushCommand.call();
} catch (GitAPIException | IOException ex) {
throw new CatalogModificationException("Unable to modify catalog", ex);
}
}
@Override
public String readCatalog() {
return null;
}
private void updateRepo() {
File localRepoFile = new File(localRepoPath);
if (!localRepoFile.exists()) {
LOGGER.debug("local git repo {} does not exist - creating it", localRepoPath);
localRepoFile.getParentFile().mkdirs();
CloneCommand cloneCommand = Git.cloneRepository().setURI(remoteRepoUri).setDirectory(localRepoFile);
if (branch != null) {
cloneCommand.setBranch(branch);
}
if (credentialsProvider != null) {
cloneCommand.setCredentialsProvider(credentialsProvider);
}
if (transportConfigCallback != null) {
cloneCommand.setTransportConfigCallback(transportConfigCallback);
}
try (Git git = cloneCommand.call()) {
catalogFile = new File(localRepoFile, catalogPath);
} catch (Exception ex) {
throw new CatalogNotFoundException("Unable to clone remote catalog at " + remoteRepoUri + " to " + localRepoPath, ex);
}
} else {
try {
LOGGER.debug("local git repo {} exists - updating", localRepoPath);
localRepo = new FileRepository(localRepoPath + "/.git");
catalogFile = new File(localRepoFile, catalogPath);
git = new Git(localRepo);
PullCommand pullCommand = git.pull();
try {
if (credentialsProvider != null) {
pullCommand.setCredentialsProvider(credentialsProvider);
}
if (transportConfigCallback != null) {
pullCommand.setTransportConfigCallback(transportConfigCallback);
}
pullCommand.call();
} catch (GitAPIException gitApiException) {
LOGGER.error("Exception", gitApiException);
}
} catch (Exception exception) {
throw new CatalogReadException("Unable to pull remote catalog at " + remoteRepoUri + " to " + localRepoPath, exception);
}
}
}
}