net.oneandone.lavender.modules.LavenderProperties Maven / Gradle / Ivy
/**
* Copyright 1&1 Internet AG, https://github.com/1and1/
*
* 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 net.oneandone.lavender.modules;
import net.oneandone.lavender.config.Docroot;
import net.oneandone.sushi.fs.FileNotFoundException;
import net.oneandone.sushi.fs.Node;
import net.oneandone.sushi.fs.World;
import net.oneandone.sushi.fs.filter.Filter;
import net.oneandone.sushi.util.Separator;
import net.oneandone.sushi.util.Strings;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.io.InputStream;
import java.net.NetworkInterface;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.UUID;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
public class LavenderProperties {
private static final Logger LOG = LoggerFactory.getLogger(LavenderProperties.class);
public static final String MODULE_PROPERTIES = "PUSTEFIX-INF/lavender.properties";
private static final String APP_PROPERTIES = "WEB-INF/lavender.properties";
public static final List DEFAULT_INCLUDES = new ArrayList<>(Arrays.asList(
"**/*.gif", "**/*.png", "**/*.jpg", "**/*.jpeg", "**/*.ico", "**/*.swf", "**/*.css", "**/*.js"));
public static final Filter defaultFilter() {
return new Filter().include(DEFAULT_INCLUDES);
}
public static LavenderProperties loadNode(boolean prod, Node root, Node pominfo) throws IOException {
return LavenderProperties.parse(prod, root.readProperties(), pominfoOpt(pominfo));
}
public static LavenderProperties loadModuleOpt(boolean prod, Node root) throws IOException {
Node src;
Properties properties;
if (root == null) {
return null;
}
src = root.join(LavenderProperties.MODULE_PROPERTIES);
if (!src.exists()) {
return null;
}
properties = src.readProperties();
return LavenderProperties.parse(prod, properties, pominfoOpt(root));
}
public static LavenderProperties loadApp(boolean prod, Node webapp) throws IOException {
Node src;
Properties pominfo;
src = webapp.join(LavenderProperties.APP_PROPERTIES);
src.checkFile();
pominfo = pominfoOpt(webapp.join("WEB-INF/classes"));
if (pominfo == null) {
// TODO: try target/classes - hack because pws deletes WEB-INF/classes to get virtual classpath working
pominfo = pominfoOpt(webapp.join("../classes"));
}
if (pominfo == null) {
throw new IOException("pominfo.properties for application not found");
}
return parse(prod, src.readProperties(), pominfo);
}
private static Properties pominfoOpt(Node root) throws IOException {
try {
return root.join("META-INF/pominfo.properties").readProperties();
} catch (FileNotFoundException e) {
return null;
}
}
// public only for testing
public static LavenderProperties parse(boolean prod, Properties properties, Properties pominfo) throws IOException {
return parse(prod, properties, pominfo, DEFAULT_INCLUDES);
}
private static LavenderProperties parse(boolean prod, Properties properties, Properties pominfo, List defaultIncludes) throws IOException {
LavenderProperties result;
String relative;
String source;
String configUrl;
String configSource;
relative = eat(properties, "pustefix.relative");
// TODO: enforce pominfo != null when enough modules have switched
if (!prod && pominfo != null && thisMachine(pominfo.getProperty("ethernet"))) {
source = join(pominfo.getProperty("basedir"), relative);
} else {
source = null;
}
result = new LavenderProperties(eatFilter(properties, "pustefix", defaultIncludes), source);
for (String prefix : svnPrefixes(properties)) {
configUrl = Strings.removeLeftOpt((String) properties.remove(prefix), "scm:svn:");
configSource = eatSvnSource(properties, prefix, source);
configSource = fallback(configUrl, configSource);
result.configs.add(
new SvnProperties(
prefix.substring(SvnProperties.SVN_PREFIX.length()),
eatFilter(properties, prefix, defaultIncludes),
configUrl,
eatType(properties, prefix),
eatBoolean(properties, prefix + ".lavendelize", true),
eatOpt(properties, prefix + ".resourcePathPrefix", ""),
eatTargetPathPrefix(properties, prefix),
configSource));
}
if (properties.size() > 0) {
throw new IllegalArgumentException("unknown properties: " + properties);
}
return result;
}
private static String eatSvnSource(Properties properties, String prefix, String source) {
String relative;
relative = eatOpt(properties, prefix + ".relative", null);
return source == null || relative == null ? null : join(source, relative);
}
private static String join(String left, String right) {
StringBuilder result;
result = new StringBuilder(left);
if (!left.endsWith("/")) {
result.append("/");
}
if (right.startsWith("/")) {
right = right.substring(1);
}
result.append(right);
return result.toString();
}
private static String eatType(Properties properties, String prefix) {
String type;
String value;
type = eatOpt(properties, prefix + ".type", Docroot.WEB);
if (type == null) {
value = eatOpt(properties, prefix + ".storage", null);
if (value.startsWith("flash-")) {
// TODO: dump
LOG.warn("CAUTION: out-dated storage configured - use type instead");
type = Docroot.FLASH;
} else {
throw new IllegalArgumentException("storage no longer supported: " + value);
}
}
return type;
}
private static String eatTargetPathPrefix(Properties properties, String prefix) {
String targetPathPrefix;
targetPathPrefix = eatOpt(properties, prefix + ".targetPathPrefix", "");
if (targetPathPrefix == null) {
LOG.warn("CAUTION: out-dated pathPrefix - use targetPathPrefix instead");
targetPathPrefix = eatOpt(properties, prefix + ".pathPrefix", null);
}
return targetPathPrefix;
}
private static Filter eatFilter(Properties properties, String prefix, List defaultIncludes) {
Filter result;
result = new Filter();
result.include(eatListOpt(properties, prefix + ".includes", defaultIncludes));
result.exclude(eatListOpt(properties, prefix + ".excludes", Collections.EMPTY_LIST));
return result;
}
private static List eatListOpt(Properties p, String key, List dflt) {
String result;
result = eatOpt(p, key, null);
return result == null ? dflt : Separator.COMMA.split(result);
}
private static boolean eatBoolean(Properties p, String key, boolean dflt) {
String result;
result = eatOpt(p, key, null);
if (result == null) {
return dflt;
}
switch (result) {
case "true": return true;
case "false": return false;
default: throw new IllegalArgumentException("true or false expected, got " + result);
}
}
private static String eat(Properties p, String key) {
String result;
result = eatOpt(p, key, null);
if (result == null) {
throw new IllegalArgumentException("key not found: " + key);
}
return result;
}
private static String eatOpt(Properties p, String key, String dflt) {
String result;
result = (String) p.remove(key);
return result == null ? dflt : result;
}
private static List svnPrefixes(Properties properties) {
List result;
result = new ArrayList<>();
for (String name : properties.stringPropertyNames()) {
if (name.startsWith(SvnProperties.SVN_PREFIX)) {
if (Strings.count(name, ".") == 1) {
result.add(name);
}
}
}
return result;
}
//--
public final Filter filter;
public final String source;
public final Collection configs;
public LavenderProperties(Filter filter, String source) {
if (filter == null) {
throw new IllegalArgumentException();
}
this.filter = filter;
this.source = source;
this.configs = new ArrayList<>();
}
public void addModules(World world, String svnUsername, String svnPassword, List result) throws IOException {
for (SvnProperties config : configs) {
result.add(config.create(world, svnUsername, svnPassword));
}
}
public Node live(Node root) throws IOException {
return source != null ? root.getWorld().file(source) : root;
}
//--
private static boolean thisMachine(String ethernet) throws IOException {
List thisEthernet;
List otherEthernet;
thisEthernet = ethernet();
otherEthernet = Separator.COMMA.split(ethernet);
for (String address : thisEthernet) {
if (otherEthernet.contains(address)) {
return true;
}
}
return false;
}
public static List ethernet() throws IOException {
Enumeration ifcs;
List result;
NetworkInterface ifc;
byte[] address;
ifcs = NetworkInterface.getNetworkInterfaces();
result = new ArrayList<>();
while (ifcs.hasMoreElements()) {
ifc = (NetworkInterface) ifcs.nextElement();
address = ifc.getHardwareAddress();
if (address != null) {
result.add(toHex(address));
} else {
// ignore -- not available (i.e. loopback device) or not accessible
}
}
return result;
}
public static String toHex(byte ... bytes) {
StringBuilder builder;
builder = new StringBuilder();
for (byte b : bytes) {
if (builder.length() > 0) {
builder.append(':');
}
builder.append(toHexChar(b >> 4 & 0xf));
builder.append(toHexChar(b & 0xf));
}
return builder.toString();
}
private static char toHexChar(int b) {
if (b >= 0 && b <= 9) {
return (char) ('0' + b);
} else if (b >= 10 && b <= 15) {
return (char) ('a' + b - 10);
} else {
throw new IllegalArgumentException("" + b);
}
}
//--
public static final Map FALLBACK_SOURCES;
static {
String str;
int idx;
String file;
FALLBACK_SOURCES = new HashMap<>();
str = System.getenv("LAVENDER_FALLBACKS");
if (str != null) {
for (String entry : Separator.COMMA.split(str)) {
idx = entry.indexOf('=');
if (idx == -1) {
throw new IllegalStateException("illegal fallback entry: " + entry);
}
file = entry.substring(idx + 1);
if (!new java.io.File(file).isDirectory()) {
throw new IllegalStateException("fallback directory not found: " + file);
}
FALLBACK_SOURCES.put(entry.substring(0, idx), file);
}
LOG.info("fallback configured: " + FALLBACK_SOURCES);
}
}
private static String fallback(String url, String source) {
String fallbackSource;
fallbackSource = FALLBACK_SOURCES.get(url);
if (!FALLBACK_SOURCES.isEmpty()) {
LOG.info("fallback for url " + url + ": " + fallbackSource);
}
return fallbackSource != null ? fallbackSource : source;
}
//--
/** To properly make jars available as a module, I have to load them into memory when the jar is itself packaged into a war. */
public static Node[] loadStreamNodes(Node jar, String ... names) throws IOException {
World world;
int count;
Node[] result;
ZipEntry entry;
String path;
Node dest;
int idx;
world = jar.getWorld();
count = 0;
result = new Node[names.length];
try (ZipInputStream src = new ZipInputStream(jar.createInputStream())) {
while ((entry = src.getNextEntry()) != null) {
path = entry.getName();
idx = indexOf(names, path);
if (idx != -1) {
count++;
dest = world.memoryNode();
result[idx] = dest;
world.getBuffer().copy(src, dest);
if (count == names.length) {
return result;
}
}
}
}
return result;
}
private static int indexOf(String[] all, String element) {
for (int i = 0; i < all.length; i++) {
if (element.equals(all[i])) {
return i;
}
}
return -1;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy