001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.juneau.server.config.repository; 018 019import static org.apache.juneau.commons.utils.FileUtils.*; 020import static org.apache.juneau.commons.utils.Shorts.*; 021 022import java.io.*; 023import java.nio.file.*; 024import java.util.*; 025 026import org.apache.juneau.config.*; 027 028/** 029 * Command that fetches configuration from a Git repository for a given project and branch. 030 * 031 * <p> 032 * Clones or pulls the configured Git repo, then loads application and project-specific 033 * configuration files into a map of {@link ConfigItem} values. 034 */ 035public class GetConfiguration implements Command, GetValue<Map<String,ConfigItem>> { 036 037 private static final String APPLICATION = "APPLICATION"; 038 private static final String PROJECT = "PROJECT"; 039 private static final String EXT = ".cfg"; 040 041 private final Map<String,ConfigItem> configs = new HashMap<>(); 042 043 @SuppressWarnings({ 044 "java:S1845" // Field name intentionally differs only by case from static field PROJECT 045 }) 046 private String project; 047 private final String branch; 048 049 /** 050 * Constructor. 051 * 052 * @param project Project name. 053 * @param branch Branch name. 054 */ 055 public GetConfiguration(String project, String branch) { 056 this.branch = branch; 057 this.project = project; 058 } 059 060 /** {@inheritDoc} */ 061 @Override 062 public void execute() throws Exception { 063 064 var config = Config.create().name("juneau-server-config.cfg").build(); 065 066 var pathStr = config.get("GitServer/pathLocal").orElse(null); 067 068 var git = config.get("GitServer/gitRemote").orElse(null); 069 070 // Fail fast with a clear message when the operator has not configured the local checkout directory, 071 // rather than surfacing an opaque NullPointerException from new File(null) below. 072 if (pathStr == null) 073 throw new IllegalStateException("Required configuration 'GitServer/pathLocal' is not set."); 074 075 // Trusted operator-configured local git checkout directory (GitServer/pathLocal). 076 // Serves as the confinement root for the project/application config file lookups below. 077 var root = new File(pathStr); 078 079 try (var gitControl = new GitControl(pathStr, git)) { 080 081 if (root.isDirectory()) { 082 gitControl.pullFromRepo(); 083 } else { 084 gitControl.cloneRepo(); 085 } 086 087 gitControl.branch(branch); 088 gitControl.pullFromRepo(); 089 } 090 091 var fileDefaultStr = lcr(APPLICATION).concat(EXT); 092 var fileProjectStr = this.project.concat(EXT); 093 094 // Resolve the config file names under the checkout root through the shared boundary check 095 // so a crafted project name cannot escape the repo directory (empty = file absent). 096 var fileDefault = resolveSafely(root, fileDefaultStr).orElse(null); 097 if (fileDefault != null) { 098 var lines = new String(Files.readAllBytes(fileDefault.toPath())); 099 configs.put(APPLICATION, new ConfigItem(lines)); 100 } 101 102 var fileProject = resolveSafely(root, fileProjectStr).orElse(null); 103 if (fileProject != null) { 104 var linesProject = new String(Files.readAllBytes(fileProject.toPath())); 105 configs.put(PROJECT, new ConfigItem(linesProject)); 106 } 107 } 108 109 /** {@inheritDoc} */ 110 @Override 111 public Map<String,ConfigItem> get() { 112 return Map.copyOf(configs); 113 } 114}