![JAR search and dependency download from the Maven repository](/logo.png)
net.oneandone.stool.util.Session 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.stool.util;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import net.oneandone.inline.ArgumentException;
import net.oneandone.inline.Console;
import net.oneandone.maven.embedded.Maven;
import net.oneandone.setenv.Setenv;
import net.oneandone.stool.cli.EnumerationFailed;
import net.oneandone.stool.cli.Main;
import net.oneandone.stool.configuration.Bedroom;
import net.oneandone.stool.configuration.Expire;
import net.oneandone.stool.configuration.Option;
import net.oneandone.stool.configuration.Property;
import net.oneandone.stool.configuration.StageConfiguration;
import net.oneandone.stool.configuration.StoolConfiguration;
import net.oneandone.stool.configuration.adapter.ExpireTypeAdapter;
import net.oneandone.stool.configuration.adapter.ExtensionsAdapter;
import net.oneandone.stool.configuration.adapter.FileNodeTypeAdapter;
import net.oneandone.stool.extensions.ExtensionsFactory;
import net.oneandone.stool.locking.LockManager;
import net.oneandone.stool.scm.Scm;
import net.oneandone.stool.stage.Stage;
import net.oneandone.stool.users.User;
import net.oneandone.stool.users.UserNotFound;
import net.oneandone.stool.users.Users;
import net.oneandone.sushi.fs.LinkException;
import net.oneandone.sushi.fs.ModeException;
import net.oneandone.sushi.fs.ReadLinkException;
import net.oneandone.sushi.fs.World;
import net.oneandone.sushi.fs.file.FileNode;
import net.oneandone.sushi.launcher.Failure;
import net.oneandone.sushi.launcher.Launcher;
import net.oneandone.sushi.util.Separator;
import net.oneandone.sushi.util.Strings;
import org.codehaus.plexus.DefaultPlexusContainer;
import javax.mail.MessagingException;
import javax.naming.NamingException;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.management.ManagementFactory;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/** Home with some session information. */
public class Session {
public static Session load(boolean setenv, FileNode home, Logging logging, String command, Console console, World world,
String svnuser, String svnpassword) throws IOException {
Session session;
session = loadWithoutBackstageWipe(setenv, home, logging, command, console, world, svnuser, svnpassword);
// Stale backstage wiping: how to detect backstage directories who's stage directory was removed.
//
// My first thought was to watch for filesystem events to trigger backstage wiping.
// But there's quite a big delay and rmdir+mkdir is reported as modification. Plus the code is quite complex and
// I don't know how to handle overflow events.
// So I simply wipe them whenever I load stool a session. That's a well-defined timing and that's before stool might
// use a stale stage.
session.wipeStaleBackstages();
return session;
}
public void wipeStaleBackstages() throws IOException {
long s;
s = System.currentTimeMillis();
for (FileNode backstage : backstages.list()) {
if (backstage.exists() && !backstage.isFile() && !backstage.isDirectory()) {
console.info.println("removing stale backstage: " + backstage);
backstage.deleteTree();
}
}
console.verbose.println("wipeStaleBackstages done, ms=" + ((System.currentTimeMillis() - s)));
}
private static Session loadWithoutBackstageWipe(boolean setenv, FileNode home, Logging logging, String command, Console console,
World world, String svnuser, String svnpassword) throws IOException {
ExtensionsFactory factory;
Gson gson;
Session result;
Environment environment;
factory = ExtensionsFactory.create(world);
gson = gson(world, factory);
environment = Environment.loadSystem();
result = new Session(setenv, factory, gson, logging, command, home, console, world, environment,
StoolConfiguration.load(gson, home), Bedroom.loadOrCreate(gson, home), svnuser, svnpassword);
return result;
}
private static final int MEM_RESERVED_OS = 500;
//--
private final boolean setenv;
public final ExtensionsFactory extensionsFactory;
public final Gson gson;
public final Logging logging;
public final String user;
private final String command;
public final FileNode home;
private String lazyGroup;
public final Console console;
public final World world;
public final Environment environment;
public final StoolConfiguration configuration;
public final Bedroom bedroom;
private final FileNode backstages;
private final Credentials svnCredentials;
private final String stageIdPrefix;
private int nextStageId;
public final Users users;
public final LockManager lockManager;
private Pool lazyPool;
public Session(boolean setenv, ExtensionsFactory extensionsFactory, Gson gson, Logging logging, String command,
FileNode home, Console console, World world, Environment environment, StoolConfiguration configuration,
Bedroom bedroom, String svnuser, String svnpassword) {
this.setenv = setenv;
this.extensionsFactory = extensionsFactory;
this.gson = gson;
this.logging = logging;
this.user = logging.getUser();
this.command = command;
this.home = home;
this.lazyGroup = null;
this.console = console;
this.world = world;
this.environment = environment;
this.configuration = configuration;
this.bedroom = bedroom;
this.backstages = home.join("backstages");
this.svnCredentials = new Credentials(svnuser, svnpassword);
this.stageIdPrefix = logging.id + ".";
this.nextStageId = 0;
if (configuration.ldapUrl.isEmpty()) {
this.users = Users.fromLogin();
} else {
this.users = Users.fromLdap(configuration.ldapUrl, configuration.ldapPrincipal, configuration.ldapCredentials);
}
this.lockManager = LockManager.create(home.join("run/locks"), user + ":" + command.replace("\n", "\\n"), 10);
this.lazyPool= null;
}
public Map properties() {
Map result;
Option option;
result = new LinkedHashMap<>();
for (java.lang.reflect.Field field : StageConfiguration.class.getFields()) {
option = field.getAnnotation(Option.class);
if (option != null) {
result.put(option.key(), new Property(option.key(), field, null));
}
}
extensionsFactory.fields(result);
return result;
}
public void add(FileNode backstage, String id) throws LinkException {
backstage.link(backstages.join(id));
}
public FileNode backstageLink(String id) throws ReadLinkException {
return backstages.join(id);
}
public FileNode findStageDirectory(FileNode dir) {
do {
if (Stage.backstageDirectory(dir).exists()) {
return dir;
}
dir = dir.getParent();
} while (dir != null);
return null;
}
//--
/** logs an error for administrators, i.e. the user is not expected to understand/fix this problem. */
public void reportException(String context, Throwable e) {
String subject;
StringWriter body;
PrintWriter writer;
logging.error("[" + command + "] " + context + ": " + e.getMessage(), e);
if (!configuration.admin.isEmpty()) {
subject = "[stool exception] " + e.getMessage();
body = new StringWriter();
body.write("stool: " + Main.versionString(world) + "\n");
body.write("command: " + command + "\n");
body.write("context: " + context + "\n");
body.write("user: " + user + "\n");
body.write("hostname: " + configuration.hostname + "\n");
writer = new PrintWriter(body);
while (true) {
e.printStackTrace(writer);
e = e.getCause();
if (e == null) {
break;
}
body.append("Caused by:\n");
}
try {
configuration.mailer().send(configuration.admin, new String[]{configuration.admin}, subject, body.toString());
} catch (MessagingException suppressed) {
logging.error("cannot send exception email: " + suppressed.getMessage(), suppressed);
}
}
}
//--
public String group() throws ModeException {
if (lazyGroup == null) {
lazyGroup = backstages.getGroup().toString();
}
return lazyGroup;
}
public FileNode bin(String name) {
return home.join("bin", name);
}
//-- environment handling
public static int memTotal() {
long result;
result = ((com.sun.management.OperatingSystemMXBean) ManagementFactory.getOperatingSystemMXBean()).getTotalPhysicalMemorySize();
return (int) (result / 1024 / 1024);
}
//--
public List list(EnumerationFailed problems, Predicate predicate) throws IOException {
List result;
Stage stage;
FileNode backstage;
result = new ArrayList<>();
for (FileNode link : backstages.list()) {
backstage = link.resolveLink();
if (StageConfiguration.file(backstage).exists()) {
try {
stage = Stage.load(this, link);
} catch (IOException e) {
problems.add(link.getName(), e);
continue;
}
if (predicate.matches(stage)) {
result.add(stage);
}
} else {
// stage is being created, we're usually waiting the the checkout to complete
}
}
return result;
}
public List listWithoutSystem() throws IOException {
List result;
EnumerationFailed problems;
problems = new EnumerationFailed();
result = list(problems, new Predicate() {
@Override
public boolean matches(Stage stage) {
return !stage.isSystem();
}
});
for (Map.Entry entry : problems.problems.entrySet()) {
reportException(entry.getKey() + ": Session.listWithoutDashboard", entry.getValue());
}
return result;
}
public Scm scm(String url) throws IOException {
return Scm.forUrl(url, svnCredentials);
}
public Scm scmOpt(String url) {
return Scm.forUrlOpt(url, svnCredentials);
}
public Credentials svnCredentials() {
return svnCredentials;
}
public Stage load(String id) throws IOException {
return Stage.load(this, backstages.join(id));
}
public Stage loadByName(String stageName) throws IOException {
List links;
FileNode bs;
StageConfiguration config;
links = backstages.list();
for (FileNode link : links) {
bs = link.resolveLink();
config = StageConfiguration.load(gson, StageConfiguration.file(bs));
if (stageName.equals(config.name)) {
return load(link.getName());
}
}
throw new IllegalArgumentException("stage not found: " + stageName);
}
public List stageNames() throws IOException {
List links;
FileNode bs;
StageConfiguration config;
List result;
links = backstages.list();
result = new ArrayList<>(links.size());
for (FileNode link : links) {
bs = link.resolveLink();
config = StageConfiguration.load(gson, StageConfiguration.file(bs));
result.add(config.name);
}
return result;
}
/** return directory or null */
public FileNode lookup(String stageName) throws IOException {
List links;
FileNode bs;
StageConfiguration config;
links = backstages.list();
for (FileNode link : links) {
bs = link.resolveLink();
config = StageConfiguration.load(gson, StageConfiguration.file(bs));
if (stageName.equals(config.name)) {
return bs.getParent();
}
}
return null;
}
private static final String UNKNOWN = "../unknown/..";
private String lazySelectedId = UNKNOWN;
public String getSelectedStageId() throws IOException {
FileNode directory;
FileNode bs;
if (lazySelectedId == UNKNOWN) {
directory = findStageDirectory(world.getWorking());
if (directory == null) {
lazySelectedId = null;
} else {
bs = Stage.backstageDirectory(directory);
for (FileNode link : backstages.list()) {
if (link.resolveLink().equals(bs)) {
lazySelectedId = link.getName();
break;
}
}
}
}
return lazySelectedId;
}
public Environment environment(Stage stage) {
Environment env;
String mavenOpts;
if (stage == null) {
mavenOpts = "";
} else {
mavenOpts = stage.macros().replace(stage.config().mavenOpts);
}
env = new Environment();
// note that both MAVEN and ANT use JAVA_HOME to locate their JVM - it's not necessary to add java to the PATH variable
env.set(Environment.JAVA_HOME, stage != null ? stage.config().javaHome : null);
env.set(Environment.MAVEN_HOME, (stage != null && stage.config().mavenHome() != null) ? stage.config().mavenHome() : null);
env.set(Environment.MAVEN_OPTS, mavenOpts);
return env;
}
//--
/** @return memory not yet reserved */
public int memUnreserved() throws IOException {
return memTotal() - MEM_RESERVED_OS - memReservedTomcats();
}
/** used for running tomcat */
private int memReservedTomcats() throws IOException {
int reserved;
StageConfiguration stage;
FileNode backstage;
reserved = 0;
for (FileNode link : backstages.list()) {
backstage = link.resolveLink();
if (backstage.join("run/tomcat.pid").exists()) {
stage = loadStageConfiguration(backstage);
reserved += stage.tomcatHeap;
}
}
return reserved;
}
public void checkDiskFree(FileNode directory) {
int free;
int min;
free = diskFree(directory);
min = configuration.diskMin;
if (free < min) {
throw new ArgumentException("Disk almost full. Currently available " + free + " mb, required " + min + " mb.");
}
}
/** @return Free disk space in partition used for stool lib. TODO: not necessarily the partition used for stages. */
public int diskFree(FileNode directory) {
return (int) (directory.toPath().toFile().getUsableSpace() / 1024 / 1024);
}
public User lookupUser(String login) throws NamingException, UserNotFound {
if (configuration.shared) {
return users.byLogin(login);
} else {
return null;
}
}
public void chown(Stage stage, String newOwner) throws Failure {
chown(newOwner, stage.getDirectory());
}
public void chown(String newOwner, FileNode ... dirs) throws Failure {
Launcher launcher;
launcher = new Launcher(home, "sudo", bin("chowntree.sh").getAbsolute(), newOwner);
for (FileNode dir : dirs) {
launcher.arg(dir.getAbsolute());
}
launcher.exec(console.info);
}
public FileNode ports() {
return home.join("run/ports");
}
public boolean isSelected(Stage stage) throws IOException {
return stage.getId().equals(getSelectedStageId());
}
//-- stage properties
public void saveStageProperties(StageConfiguration stageConfiguration, FileNode backstage) throws IOException {
stageConfiguration.save(gson, StageConfiguration.file(backstage));
}
public StageConfiguration loadStageConfiguration(FileNode backstage) throws IOException {
return StageConfiguration.load(gson, StageConfiguration.file(backstage));
}
//-- stool properties
public List stageDirectories() throws IOException {
List result;
result = new ArrayList<>();
for (FileNode link : backstages.list()) {
result.add(link.resolveLink().getParent());
}
return result;
}
public Pool pool() throws IOException {
if (lazyPool == null) {
lazyPool = Pool.loadOpt(ports(), configuration.portFirst, configuration.portLast, backstages);
}
return lazyPool;
}
public void updatePool() { // TODO: hack to see updates application urls
lazyPool = null;
}
public StageConfiguration createStageConfiguration(String url) {
String mavenHome;
StageConfiguration result;
Scm scm;
String refresh;
try {
mavenHome = Maven.locateMaven(world).getAbsolute();
} catch (IOException e) {
mavenHome = "";
}
scm = scmOpt(url);
refresh = scm == null ? "" : scm.refresh();
result = new StageConfiguration(javaHome(), mavenHome, refresh, extensionsFactory.newInstance());
configuration.setDefaults(properties(), result, url);
return result;
}
public String nextStageId() {
nextStageId++;
return stageIdPrefix + nextStageId;
}
public static String javaHome() {
String result;
result = System.getProperty("java.home");
if (result == null) {
throw new IllegalStateException();
}
result = Strings.removeRightOpt(result, "/");
// prefer jdk, otherwise Java tools like jar while report an error on Mac OS ("unable to locate executable at $JAVA_HOME")
result = Strings.removeRightOpt(result, "/jre");
return result;
}
private DefaultPlexusContainer lazyPlexus;
public DefaultPlexusContainer plexus() {
if (lazyPlexus == null) {
lazyPlexus = Maven.container();
}
return lazyPlexus;
}
public static Gson gson(World world, ExtensionsFactory factory) {
return new GsonBuilder()
.registerTypeAdapter(FileNode.class, new FileNodeTypeAdapter(world))
.registerTypeAdapter(Expire.class, new ExpireTypeAdapter())
.registerTypeAdapterFactory(ExtensionsAdapter.factory(factory))
.disableHtmlEscaping()
.serializeNulls()
.excludeFieldsWithModifiers(Modifier.STATIC, Modifier.TRANSIENT)
.setPrettyPrinting()
.create();
}
public FileNode downloadCache() {
return configuration.downloadCache;
}
public List search(String search) throws IOException {
FileNode working;
List cmd;
List result;
int idx;
working = world.getWorking();
result = new ArrayList<>();
if (configuration.search.isEmpty()) {
throw new IOException("no search tool configured");
}
cmd = Separator.SPACE.split(configuration.search);
idx = cmd.indexOf("()");
if (idx == -1) {
throw new IOException("search tool configured without () placeholder");
}
cmd.set(idx, search);
for (String line : Separator.RAW_LINE.split(working.exec(Strings.toArray(cmd)))) {
line = line.trim();
result.add(line);
}
return result;
}
public void cd(FileNode dir) {
if (setenv) {
Setenv.get().cd(dir.getAbsolute());
}
}
public void checkVersion() throws IOException {
String homeVersion;
String binVersion;
homeVersion = home.join("version").readString().trim();
binVersion = Main.versionString(world);
if (!homeVersion.equals(binVersion)) {
throw new IOException("Cannot use home directory version " + homeVersion + " with Stool " + binVersion
+ "\nTry 'stool setup'");
}
}
public static final String majorMinor(String version) {
int major;
int minor;
major = version.indexOf('.');
minor = version.indexOf('.', major + 1);
if (minor == -1) {
throw new IllegalArgumentException(version);
}
return version.substring(0, minor);
}
public Property property(String name) {
return properties().get(name);
}
public Info[] fieldsAndName() {
Field[] fields;
Info[] result;
fields = Field.values();
result = new Info[fields.length + 1];
System.arraycopy(fields, 0, result, 1, fields.length);
result[0] = property("name");
return result;
}
public int quotaReserved() throws IOException {
int reserved;
StageConfiguration config;
reserved = 0;
for (FileNode stage : stageDirectories()) {
config = loadStageConfiguration(Stage.backstageDirectory(stage));
reserved += Math.max(0, config.quota);
}
return reserved;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy