/*
* Copyright 2012 James Moger
*
* Licensed 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.moxie.proxy;
import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.moxie.Dependency;
import org.moxie.IMavenCache;
import org.moxie.MavenCache;
import org.moxie.MoxieCache;
import org.moxie.Proxy;
import org.moxie.RemoteRepository;
import org.moxie.maxml.Maxml;
import org.moxie.maxml.MaxmlMap;
import org.moxie.utils.FileUtils;
import org.moxie.utils.StringUtils;
/**
* Read and manage the configuration.
*/
public class ProxyConfig {
public static final Logger log = Logger.getLogger(ProxyConfig.class.getSimpleName());
private File configFile;
private long configLastModified;
private boolean isLoaded;
private File moxieRoot;
private File localArtifactsRoot;
private File remoteArtifactsRoot;
private int httpPort;
private int httpsPort;
private int proxyPort;
private int shutdownPort;
private List<String> bindAddresses;
private boolean accesslog;
private String dateFormat;
private int atomCount;
private int searchCount;
private String keystorePassword;
private String userAgent;
private List<String> localRepositories;
private List<RemoteRepository> remoteRepositories;
private Map<String, RemoteRepository> remoteRepositoryLookup;
private Map<String, String> repositorySizeCache;
private List<Proxy> proxies;
private List<Redirect> redirects;
private List<AllowDeny> allowDeny;
private MoxieCache moxieCache;
public ProxyConfig() {
shutdownPort = 8079;
httpPort = 8080;
httpsPort = 8443;
proxyPort = 8081;
dateFormat = "yyyy-MM-dd";
bindAddresses = Collections.emptyList();
proxies = Collections.emptyList();
redirects = Collections.emptyList();
allowDeny = Collections.emptyList();
localRepositories = Collections.emptyList();
remoteRepositories = Collections.emptyList();
remoteRepositoryLookup = new HashMap<String, RemoteRepository>();
repositorySizeCache = new ConcurrentHashMap<String, String>();
atomCount = 50;
searchCount = 50;
keystorePassword = "";
userAgent = "";
}
public void setUserDefaults() {
setMoxieRoot(new File(System.getProperty("user.home"), ".moxie"));
moxieRoot.mkdirs();
localArtifactsRoot.mkdirs();
remoteArtifactsRoot.mkdirs();
// default local repositories
localRepositories = Arrays.asList("releases", "snapshots");
for (String repository : localRepositories) {
new File(localArtifactsRoot, repository).mkdirs();
}
// default proxy of central
remoteRepositories = new ArrayList<RemoteRepository>();
remoteRepositories.add(new RemoteRepository("central", "http://repo1.maven.org/maven2", false));
remoteRepositoryLookup = new HashMap<String, RemoteRepository>();
for (RemoteRepository repository : remoteRepositories) {
remoteRepositoryLookup.put(repository.id, repository);
remoteRepositoryLookup.put(StringUtils.urlToFolder(repository.url), repository);
}
// default ssl keystore password
keystorePassword = "moxieproxy";
}
public void parse(File file) {
configFile = file;
reload();
}
public synchronized void reload() {
if (configFile == null || !configFile.exists()) {
return;
}
long lastModified = configFile.lastModified();
if (lastModified != configLastModified) {
log.info((isLoaded ? "reloading" : "loading") + " config from " + configFile.getAbsolutePath());
configLastModified = lastModified;
try {
MaxmlMap map = Maxml.parse(configFile);
if (!isLoaded) {
// only read these settings on a cold start
httpPort = map.getInt("httpPort", httpPort);
httpsPort = map.getInt("httpsPort", httpPort);
proxyPort = map.getInt("proxyPort", proxyPort);
shutdownPort = map.getInt("shutdownPort", shutdownPort);
bindAddresses = map.getStrings("bindAddresses", bindAddresses);
keystorePassword = map.getString("keystorePassword", keystorePassword);
userAgent = map.getString("userAgent", userAgent);
moxieRoot = new File(map.getString("rootFolder", "moxie"));
setMoxieRoot(moxieRoot);
localRepositories = map.getStrings("localRepositories", localRepositories);
remoteRepositories = parseRemoteRepositories(map);
for (RemoteRepository repository : remoteRepositories) {
remoteRepositoryLookup.put(repository.id, repository);
remoteRepositoryLookup.put(StringUtils.urlToFolder(repository.url), repository);
}
// do not reload these immutable settings
isLoaded = true;
}
proxies = parseProxies(map);
dateFormat = map.getString("dateFormat", dateFormat);
redirects = parseRedirects(map);
allowDeny = parseAllowDeny(map);
atomCount = map.getInt("atomCount", atomCount);
searchCount = map.getInt("searchCount", searchCount);
} catch (Exception e) {
log.log(Level.SEVERE, "failed to parse " + configFile, e);
}
}
}
List<RemoteRepository> parseRemoteRepositories(MaxmlMap map) {
List<RemoteRepository> remotes = new ArrayList<RemoteRepository>();
if (map.containsKey("remoteRepositories")) {
for (Object o : map.getList("remoteRepositories", Collections.emptyList())) {
MaxmlMap repoMap = (MaxmlMap) o;
String id = repoMap.getString("id", null);
String url = repoMap.getString("url", null);
boolean allowSnapshots = repoMap.getBoolean("allowSnapshots", false);
RemoteRepository repo = new RemoteRepository(id, url, allowSnapshots);
repo.connectTimeout = repoMap.getInt("connectTimeout", repo.connectTimeout);
repo.readTimeout = repoMap.getInt("readTimeout", repo.readTimeout);
repo.username = repoMap.getString("username", null);
repo.password = repoMap.getString("password", null);
remotes.add(repo);
}
}
return remotes;
}
@SuppressWarnings("unchecked")
List<Proxy> parseProxies(MaxmlMap map) {
List<Proxy> list = new ArrayList<Proxy>();
if (map.containsKey("proxies")) {
List<MaxmlMap> values = (List<MaxmlMap>) map.get("proxies");
for (MaxmlMap definition : values) {
Proxy proxy = new Proxy();
proxy.id = definition.getString("id", "");
proxy.active = definition.getBoolean("active", true);
proxy.protocol = definition.getString("protocol", "http");
proxy.host = definition.getString("host", "");
proxy.port = definition.getInt("port", 80);
proxy.username = definition.getString("username", "");
proxy.password = definition.getString("password", "");
proxy.repositories = definition.getStrings("repositories", new ArrayList<String>());
proxy.proxyHosts = definition.getStrings("proxyHosts", new ArrayList<String>());
proxy.nonProxyHosts = definition.getStrings("nonProxyHosts", new ArrayList<String>());
list.add(proxy);
}
}
return list;
}
@SuppressWarnings("unchecked")
List<Redirect> parseRedirects(MaxmlMap map) {
List<Redirect> list = new ArrayList<Redirect>();
if (map.containsKey("redirects")) {
List<MaxmlMap> values = (List<MaxmlMap>) map.get("redirects");
for (MaxmlMap definition : values) {
String from = definition.getString("from", null);
String to = definition.getString("to", null);
if (StringUtils.isEmpty(from) || StringUtils.isEmpty(to)) {
log.warning(MessageFormat.format("Dropping incomplete redirect definition! from: \"{0}\" to: \"{1}\"", from, to));
continue;
}
Redirect redirect = new Redirect(from, to);
list.add(redirect);
}
}
return list;
}
List<AllowDeny> parseAllowDeny(MaxmlMap map) {
List<AllowDeny> list = new ArrayList<AllowDeny>();
if (map.containsKey("allow")) {
for (String value : map.getStrings("allow", new ArrayList<String>())) {
AllowDeny rule = new AllowDeny(value, true);
list.add(rule);
}
}
if (map.containsKey("deny")) {
for (String value : map.getStrings("deny", new ArrayList<String>())) {
AllowDeny rule = new AllowDeny(value, false);
list.add(rule);
}
}
return list;
}
public int getAtomCount() {
return atomCount;
}
public int getSearchCount() {
return searchCount;
}
public int getHttpPort() {
return httpPort;
}
public void setHttpPort(int val) {
this.httpPort = val;
}
public int getHttpsPort() {
return httpsPort;
}
public void setHttpsPort(int val) {
this.httpsPort = val;
}
public int getProxyPort() {
return proxyPort;
}
public void setProxyPort(int val) {
this.proxyPort = val;
}
public boolean isProxyEnabled() {
return proxyPort > 0;
}
public int getShutdownPort() {
return shutdownPort;
}
public void setShutdownPort(int val) {
this.shutdownPort = val;
}
public List<String> getBindAddresses() {
return bindAddresses;
}
public boolean getAccessLog() {
return accesslog;
}
public void setAccessLog(boolean val) {
this.accesslog = val;
}
public String getDateFormat() {
return dateFormat;
}
public String getKeystorePassword() {
return keystorePassword;
}
public String getUserAgent() {
return userAgent;
}
public void setStorePassword(String val) {
this.keystorePassword = val;
}
public File getMoxieRoot() {
return moxieRoot;
}
public void setMoxieRoot(File val) {
this.moxieRoot = val;
localArtifactsRoot = new File(moxieRoot, org.moxie.Constants.LOCAL);
remoteArtifactsRoot = new File(moxieRoot, org.moxie.Constants.REMOTE);
}
public File getArtifactRoot(String relativePath) {
String repo;
if (relativePath.startsWith(org.moxie.Constants.REMOTE + "/")) {
// strip out remote/
relativePath = relativePath.substring(org.moxie.Constants.REMOTE.length() + 1);
}
if (relativePath.indexOf('/') > -1) {
// strip out basepath
repo = relativePath.substring(0, relativePath.indexOf('/'));
} else {
repo = relativePath;
}
if (remoteRepositoryLookup.containsKey(repo)) {
RemoteRepository repository = remoteRepositoryLookup.get(repo);
return new File(remoteArtifactsRoot, StringUtils.urlToFolder(repository.url));
}
return new File(localArtifactsRoot, repo);
}
public boolean isRemoteRepository(String repository) {
return remoteRepositoryLookup.containsKey(repository);
}
public RemoteRepository getRemoteRepository(String repository) {
return remoteRepositoryLookup.get(repository);
}
public String getRepositoryId(File artifactFile) {
for (RemoteRepository repository : remoteRepositories) {
String remote = StringUtils.urlToFolder(repository.url.toString());
File remoteFolder = new File(remoteArtifactsRoot, remote);
if (artifactFile.getAbsolutePath().startsWith(remoteFolder.getAbsolutePath())) {
return repository.id;
}
}
return null;
}
public String getRepositoryId(URL artifactUrl) {
String url = artifactUrl.toExternalForm();
for (RemoteRepository repository : remoteRepositories) {
if (url.startsWith(repository.url)) {
return repository.id;
}
}
return null;
}
public File getRemoteArtifact(URL artifactUrl) {
String url = artifactUrl.toExternalForm();
for (RemoteRepository repository : remoteRepositories) {
if (url.startsWith(repository.url)) {
String remote = StringUtils.urlToFolder(repository.url.toString());
File remoteFolder = new File(remoteArtifactsRoot, remote);
String path = StringUtils.getRelativePath(repository.url, url);
return new File(remoteFolder, path);
}
}
return null;
}
public MoxieCache getMoxieCache() {
if (moxieCache == null) {
moxieCache = new MoxieCache(moxieRoot);
}
return moxieCache;
}
public IMavenCache getMavenCache(File file) {
String path = FileUtils.getRelativePath(moxieRoot, file);
File folder = getArtifactRoot(path);
return new MavenCache(folder);
}
public IMavenCache getMavenCache(String repository) {
for (String repo : localRepositories) {
if (repo.equalsIgnoreCase(repository)) {
File file = new File(localArtifactsRoot, repo);
return new MavenCache(file);
}
}
if (remoteRepositoryLookup.containsKey(repository)) {
RemoteRepository repo = remoteRepositoryLookup.get(repository);
String folder = StringUtils.urlToFolder(repo.url);
File file = new File(remoteArtifactsRoot, folder);
return new MavenCache(file);
}
return null;
}
public DependencyLink find(Dependency dependency) {
String path = null;
for (String repository : localRepositories) {
File cacheRoot = new File(localArtifactsRoot, repository);
IMavenCache cache = new MavenCache(cacheRoot);
File file = cache.getArtifact(dependency, dependency.extension);
if (file != null && file.exists()) {
path = repository + "/" + FileUtils.getRelativePath(cacheRoot, file.getParentFile());
}
}
for (RemoteRepository repository : remoteRepositories) {
String folder = StringUtils.urlToFolder(repository.url);
File cacheRoot = new File(remoteArtifactsRoot, folder);
IMavenCache cache = new MavenCache(cacheRoot);
File file = cache.getArtifact(dependency, dependency.extension);
if (file != null && file.exists()) {
path = repository.id + "/" + FileUtils.getRelativePath(cacheRoot, file.getParentFile());
}
}
if (StringUtils.isEmpty(path)) {
return null;
}
return new DependencyLink(dependency.getCoordinates(), path);
}
public List<String> getLocalRepositories() {
return localRepositories;
}
public Collection<RemoteRepository> getRemoteRepositories() {
return remoteRepositories;
}
public String getRepositorySize(String repository) {
if (!repositorySizeCache.containsKey(repository)) {
IMavenCache cache = getMavenCache(repository);
File folder = cache.getRootFolder();
long size = FileUtils.folderSize(folder);
String value = FileUtils.formatSize(size);
repositorySizeCache.put(repository, value);
}
return repositorySizeCache.get(repository);
}
public void resetRepositorySize(String repository) {
repositorySizeCache.remove(repository);
}
public List<Redirect> getRedirects() {
return redirects;
}
public URL getRedirect(URL url) throws MalformedURLException {
String s = url.toString();
for (Redirect entry : getRedirects()) {
URL to = entry.getRedirectURL(s);
if (to != null) {
log.info("Redirecting request to " + to.toString());
return to;
}
}
return url;
}
public boolean useProxy(URL url) {
for (Proxy proxy : proxies) {
if (proxy.active && proxy.matches(null, url.toExternalForm())) {
return true;
}
}
return false;
}
public Proxy getProxy(URL url) {
for (Proxy proxy : proxies) {
if (proxy.active && proxy.matches(null, url.toExternalForm())) {
return proxy;
}
}
return null;
}
public List<AllowDeny> getAllowDeny() {
return allowDeny;
}
public boolean isAllowed(URL url) {
String s = url.toString();
for (AllowDeny rule : getAllowDeny()) {
if (rule.matches(s)) {
log.info((rule.isAllowed() ? "Allowing" : "Denying") + " access to " + url
+ " because of config rule");
return rule.isAllowed();
}
}
return true;
}
}