include sdk.mk
-.PHONY: clean all osgi
-
-all: osgi
- $(MAKE) -f Makefile-rcp.mk all
-
-install: osgi-install
-
-uninstall: osgi-uninstall
A2_CATEGORY = org.argeo.cms
org.argeo.api.uuid \
org.argeo.api.register \
org.argeo.api.acr \
-org.argeo.api.cli \
org.argeo.api.cms \
org.argeo.cms \
org.argeo.cms.ux \
org.argeo.api.acr \
org.argeo.api.cms
-clean:
- rm -rf $(BUILD_BASE)
+all: osgi-all
+ $(MAKE) -f Makefile-rcp.mk all
+
+clean: osgi-clean
$(MAKE) -f Makefile-rcp.mk clean
-A2_BUNDLES_CLASSPATH = $(subst $(space),$(pathsep),$(strip $(A2_BUNDLES)))
+install: osgi-install
+ $(MAKE) -f Makefile-rcp.mk install
+
+uninstall: osgi-uninstall
+ $(MAKE) -f Makefile-rcp.mk uninstall
+
+include $(SDK_SRC_BASE)/sdk/argeo-build/osgi.mk
-include $(SDK_SRC_BASE)/sdk/argeo-build/osgi.mk
\ No newline at end of file
+.PHONY: clean all install uninstall
include sdk.mk
-.PHONY: clean all osgi
-
-all: osgi
A2_CATEGORY = org.argeo.cms
swt/rcp/org.argeo.tp.swt \
lib/linux/x86_64/swt/rcp/org.argeo.tp.swt \
-clean:
- rm -rf $(BUILD_BASE)
-
VPATH = .:swt/rcp
-include $(SDK_SRC_BASE)/sdk/argeo-build/osgi.mk
\ No newline at end of file
+all: osgi-all
+
+clean: osgi-clean
+
+install: osgi-install
+
+uninstall: osgi-uninstall
+
+include $(SDK_SRC_BASE)/sdk/argeo-build/osgi.mk
+
+.PHONY: clean all install uninstall
import java.io.Closeable;
import java.io.IOException;
-import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import javax.xml.namespace.QName;
/**
- * A semi-structured content, with attributes, within a hierarchical structure.
+ * A semi-structured content, with attributes, within a hierarchical structure
+ * whose nodes are named.
*/
-public interface Content extends Iterable<Content>, Map<QName, Object> {
- /** The base of a repository path. */
- String ROOT_PATH = "/";
+public interface Content extends QualifiedData<Content> {
+ /** The path separator: '/' */
+ char PATH_SEPARATOR = '/';
- QName getName();
+ /** The base of a repository path. */
+ String ROOT_PATH = Character.toString(PATH_SEPARATOR);
String getPath();
- Content getParent();
-
- /*
- * ATTRIBUTES OPERATIONS
- */
-
- <A> Optional<A> get(QName key, Class<A> clss);
-
- Class<?> getType(QName key);
-
- boolean isMultiple(QName key);
-
- <A> List<A> getMultiple(QName key, Class<A> clss);
-
- /*
- * ATTRIBUTES OPERATION HELPERS
- */
- default boolean containsKey(QNamed key) {
- return containsKey(key.qName());
- }
-
- default <A> Optional<A> get(QNamed key, Class<A> clss) {
- return get(key.qName(), clss);
- }
-
- default Object get(QNamed key) {
- return get(key.qName());
- }
-
- default Object put(QNamed key, Object value) {
- return put(key.qName(), value);
- }
-
- default Object remove(QNamed key) {
- return remove(key.qName());
- }
-
- // TODO do we really need the helpers below?
-
- default Object get(String key) {
- return get(unqualified(key));
- }
-
- default Object put(String key, Object value) {
- return put(unqualified(key), value);
- }
-
- default Object remove(String key) {
- return remove(unqualified(key));
- }
-
- @SuppressWarnings("unchecked")
- default <A> List<A> getMultiple(QName key) {
- Class<A> type;
- try {
- type = (Class<A>) getType(key);
- } catch (ClassCastException e) {
- throw new IllegalArgumentException("Requested type is not the default type");
- }
- List<A> res = getMultiple(key, type);
- return res;
+ /** MUST be {@link Content#PATH_SEPARATOR}. */
+ default char getPathSeparator() {
+ return PATH_SEPARATOR;
}
/*
return res;
}
- /*
- * SIBLINGS
- */
-
- default int getSiblingIndex() {
- return 1;
- }
-
/*
* DEFAULT METHODS
*/
- default <A> A adapt(Class<A> clss) {
- throw new UnsupportedOperationException("Cannot adapt content " + this + " to " + clss.getName());
- }
-
default <C extends Closeable> C open(Class<C> clss) throws IOException {
throw new UnsupportedOperationException("Cannot open content " + this + " as " + clss.getName());
}
/*
* CHILDREN
*/
-
- default boolean hasChild(QName name) {
- for (Content child : this) {
- if (child.getName().equals(name))
- return true;
- }
- return false;
- }
-
- default boolean hasChild(QNamed name) {
- return hasChild(name.qName());
- }
-
default Content anyOrAddChild(QName name, QName... classes) {
Content child = anyChild(name);
if (child != null)
return anyOrAddChild(unqualified(name), classes);
}
- /** Any child with this name, or null if there is none */
- default Content anyChild(QName name) {
- for (Content child : this) {
- if (child.getName().equals(name))
- return child;
- }
- return null;
- }
-
- default List<Content> children(QName name) {
- List<Content> res = new ArrayList<>();
- for (Content child : this) {
- if (child.getName().equals(name))
- res.add(child);
- }
- return res;
- }
-
- default List<Content> children(QNamed name) {
- return children(name.qName());
- }
-
- default Optional<Content> soleChild(QNamed name) {
- return soleChild(name.qName());
- }
-
- default Optional<Content> soleChild(QName name) {
- List<Content> res = children(name);
- if (res.isEmpty())
- return Optional.empty();
- if (res.size() > 1)
- throw new IllegalStateException(this + " has multiple children with name " + name);
- return Optional.of(res.get(0));
- }
-
default Content soleOrAddChild(QName name, QName... classes) {
return soleChild(name).orElseGet(() -> this.add(name, classes));
}
- default Content child(QName name) {
- return soleChild(name).orElseThrow();
- }
-
- default Content child(QNamed name) {
- return child(name.qName());
- }
-
- /*
- * ATTR AS STRING
- */
- /**
- * Convenience method returning an attribute as a {@link String}.
- *
- * @param key the attribute name
- * @return the attribute value as a {@link String} or <code>null</code>.
- *
- * @see Object#toString()
- */
- default String attr(QName key) {
- return get(key, String.class).orElse(null);
- }
-
- /**
- * Convenience method returning an attribute as a {@link String}.
- *
- * @param key the attribute name
- * @return the attribute value as a {@link String} or <code>null</code>.
- *
- * @see Object#toString()
- */
- default String attr(QNamed key) {
- return attr(key.qName());
- }
-
- /**
- * Convenience method returning an attribute as a {@link String}.
- *
- * @param key the attribute name
- * @return the attribute value as a {@link String} or <code>null</code>.
- *
- * @see Object#toString()
- */
- default String attr(String key) {
- return attr(unqualified(key));
- }
-
/*
* CONTEXT
*/
--- /dev/null
+package org.argeo.api.acr;
+
+import static org.argeo.api.acr.NamespaceUtils.unqualified;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+
+import javax.xml.namespace.QName;
+
+/** A {@link StructuredData} whose attributes have qualified keys. */
+public interface QualifiedData<CHILD extends QualifiedData<CHILD>> extends StructuredData<QName, Object, CHILD> {
+ QName getName();
+
+ CHILD getParent();
+
+ /*
+ * ATTRIBUTES OPERATIONS
+ */
+
+ <A> Optional<A> get(QName key, Class<A> clss);
+
+ Class<?> getType(QName key);
+
+ boolean isMultiple(QName key);
+
+ <A> List<A> getMultiple(QName key, Class<A> clss);
+
+ /*
+ * PATH
+ */
+ char getPathSeparator();
+
+ /*
+ * ATTRIBUTES OPERATION HELPERS
+ */
+ default boolean containsKey(QNamed key) {
+ return containsKey(key.qName());
+ }
+
+ default <A> Optional<A> get(QNamed key, Class<A> clss) {
+ return get(key.qName(), clss);
+ }
+
+ default Object get(QNamed key) {
+ return get(key.qName());
+ }
+
+ default Object put(QNamed key, Object value) {
+ return put(key.qName(), value);
+ }
+
+ default Object remove(QNamed key) {
+ return remove(key.qName());
+ }
+
+ // TODO do we really need the helpers below?
+
+ default Object get(String key) {
+ return get(unqualified(key));
+ }
+
+ default Object put(String key, Object value) {
+ return put(unqualified(key), value);
+ }
+
+ default Object remove(String key) {
+ return remove(unqualified(key));
+ }
+
+ @SuppressWarnings("unchecked")
+ default <A> List<A> getMultiple(QName key) {
+ Class<A> type;
+ try {
+ type = (Class<A>) getType(key);
+ } catch (ClassCastException e) {
+ throw new IllegalArgumentException("Requested type is not the default type");
+ }
+ List<A> res = getMultiple(key, type);
+ return res;
+ }
+
+ /*
+ * CHILDREN
+ */
+
+ default boolean hasChild(QName name) {
+ for (CHILD child : this) {
+ if (child.getName().equals(name))
+ return true;
+ }
+ return false;
+ }
+
+ default boolean hasChild(QNamed name) {
+ return hasChild(name.qName());
+ }
+
+ /** Any child with this name, or null if there is none */
+ default CHILD anyChild(QName name) {
+ for (CHILD child : this) {
+ if (child.getName().equals(name))
+ return child;
+ }
+ return null;
+ }
+
+ default List<CHILD> children(QName name) {
+ List<CHILD> res = new ArrayList<>();
+ for (CHILD child : this) {
+ if (child.getName().equals(name))
+ res.add(child);
+ }
+ return res;
+ }
+
+ default List<CHILD> children(QNamed name) {
+ return children(name.qName());
+ }
+
+ default Optional<CHILD> soleChild(QNamed name) {
+ return soleChild(name.qName());
+ }
+
+ default Optional<CHILD> soleChild(QName name) {
+ List<CHILD> res = children(name);
+ if (res.isEmpty())
+ return Optional.empty();
+ if (res.size() > 1)
+ throw new IllegalStateException(this + " has multiple children with name " + name);
+ return Optional.of(res.get(0));
+ }
+
+ default CHILD child(QName name) {
+ return soleChild(name).orElseThrow();
+ }
+
+ default CHILD child(QNamed name) {
+ return child(name.qName());
+ }
+
+ /*
+ * ATTR AS STRING
+ */
+ /**
+ * Convenience method returning an attribute as a {@link String}.
+ *
+ * @param key the attribute name
+ * @return the attribute value as a {@link String} or <code>null</code>.
+ *
+ * @see Object#toString()
+ */
+ default String attr(QName key) {
+ return get(key, String.class).orElse(null);
+ }
+
+ /**
+ * Convenience method returning an attribute as a {@link String}.
+ *
+ * @param key the attribute name
+ * @return the attribute value as a {@link String} or <code>null</code>.
+ *
+ * @see Object#toString()
+ */
+ default String attr(QNamed key) {
+ return attr(key.qName());
+ }
+
+ /**
+ * Convenience method returning an attribute as a {@link String}.
+ *
+ * @param key the attribute name
+ * @return the attribute value as a {@link String} or <code>null</code>.
+ *
+ * @see Object#toString()
+ */
+ default String attr(String key) {
+ return attr(unqualified(key));
+ }
+
+ /*
+ * SIBLINGS
+ */
+ default int getSiblingIndex() {
+ return 1;
+ }
+}
/**
* Programmatically defined {@link NamespaceContext}, which is valid at runtime
* (when the software is running). Code contributing namespaces MUST register
- * here with a single default prefix, nad MUST make sure that stored data
- * contains the fully qualified namespace URI.
+ * here with a single default prefix, and MUST make sure that stored data
+ * contains the fully qualified namespace URI.</br>
+ * </br>
+ * All environments sharing the classloader of this class MUST use strictly the
+ * same default prefix / namespace mappings, as a static reference to the
+ * mapping is kept.
*/
public class RuntimeNamespaceContext implements NamespaceContext {
public final static String XSD_DEFAULT_PREFIX = "xs";
public final static String XSD_INSTANCE_DEFAULT_PREFIX = "xsi";
+ private final static RuntimeNamespaceContext INSTANCE = new RuntimeNamespaceContext();
+
private NavigableMap<String, String> prefixes = new TreeMap<>();
private NavigableMap<String, String> namespaces = new TreeMap<>();
/*
* STATIC
*/
- private final static RuntimeNamespaceContext INSTANCE = new RuntimeNamespaceContext();
static {
// Standard
register(XMLConstants.XMLNS_ATTRIBUTE_NS_URI, XMLConstants.XMLNS_ATTRIBUTE);
// Common
+ // FIXME shouldn't it be registered externally?
register(XMLConstants.W3C_XML_SCHEMA_NS_URI, XSD_DEFAULT_PREFIX);
register(XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI, XSD_INSTANCE_DEFAULT_PREFIX);
--- /dev/null
+package org.argeo.api.acr;
+
+import java.util.Map;
+
+/** A hierarchical structure of unnamed mappings. */
+public interface StructuredData<KEY, VALUE, CHILD> extends Map<KEY, VALUE>, Iterable<CHILD> {
+ /*
+ * DEFAULT METHODS
+ */
+ default <A> A adapt(Class<A> clss) {
+ throw new UnsupportedOperationException("Cannot adapt content " + this + " to " + clss.getName());
+ }
+}
--- /dev/null
+package org.argeo.api.acr.ldap;
+
+import javax.naming.InvalidNameException;
+import javax.naming.ldap.LdapName;
+import javax.naming.ldap.Rdn;
+
+/** Utilities to simplify using {@link LdapName}. */
+public class LdapNameUtils {
+
+ public static LdapName relativeName(LdapName prefix, LdapName dn) {
+ try {
+ if (!dn.startsWith(prefix))
+ throw new IllegalArgumentException("Prefix " + prefix + " not consistent with " + dn);
+ LdapName res = (LdapName) dn.clone();
+ for (int i = 0; i < prefix.size(); i++) {
+ res.remove(0);
+ }
+ return res;
+ } catch (InvalidNameException e) {
+ throw new IllegalStateException("Cannot find realtive name", e);
+ }
+ }
+
+ public static LdapName getParent(LdapName dn) {
+ try {
+ LdapName parent = (LdapName) dn.clone();
+ parent.remove(parent.size() - 1);
+ return parent;
+ } catch (InvalidNameException e) {
+ throw new IllegalArgumentException("Cannot get parent of " + dn, e);
+ }
+ }
+
+ public static Rdn getParentRdn(LdapName dn) {
+ if (dn.size() < 2)
+ throw new IllegalArgumentException(dn + " has no parent");
+ Rdn parentRdn = dn.getRdn(dn.size() - 2);
+ return parentRdn;
+ }
+
+ public static LdapName toLdapName(String distinguishedName) {
+ try {
+ return new LdapName(distinguishedName);
+ } catch (InvalidNameException e) {
+ throw new IllegalArgumentException("Cannot parse " + distinguishedName + " as LDAP name", e);
+ }
+ }
+
+ public static Rdn getLastRdn(LdapName dn) {
+ return dn.getRdn(dn.size() - 1);
+ }
+
+ public static String getLastRdnAsString(LdapName dn) {
+ return getLastRdn(dn).toString();
+ }
+
+ public static String getLastRdnValue(String dn) {
+ return getLastRdnValue(toLdapName(dn));
+ }
+
+ public static String getLastRdnValue(LdapName dn) {
+ return getLastRdn(dn).getValue().toString();
+ }
+
+ /** singleton */
+ private LdapNameUtils() {
+
+ }
+}
@Override
default Optional<Content> getContent(String path) {
String absolutePath;
- if (path.startsWith(Content.ROOT_PATH)) {// absolute
+ if (path.startsWith(ROOT_PATH)) {// absolute
absolutePath = path;
} else {// relative
- absolutePath = getPath() + '/' + path;
+ absolutePath = getPath() + PATH_SEPARATOR + path;
}
return getSession().exists(absolutePath) ? Optional.of(getSession().get(absolutePath)) : Optional.empty();
}
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<classpath>
- <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-17"/>
- <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
- <classpathentry kind="src" path="src"/>
- <classpathentry kind="output" path="bin"/>
-</classpath>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<projectDescription>
- <name>org.argeo.api.cli</name>
- <comment></comment>
- <projects>
- </projects>
- <buildSpec>
- <buildCommand>
- <name>org.eclipse.jdt.core.javabuilder</name>
- <arguments>
- </arguments>
- </buildCommand>
- <buildCommand>
- <name>org.eclipse.pde.ManifestBuilder</name>
- <arguments>
- </arguments>
- </buildCommand>
- <buildCommand>
- <name>org.eclipse.pde.SchemaBuilder</name>
- <arguments>
- </arguments>
- </buildCommand>
- </buildSpec>
- <natures>
- <nature>org.eclipse.pde.PluginNature</nature>
- <nature>org.eclipse.jdt.core.javanature</nature>
- </natures>
-</projectDescription>
+++ /dev/null
-source.. = src/
-output.. = bin/
-bin.includes = META-INF/,\
- .
+++ /dev/null
-package org.argeo.api.cli;
-
-/** Exception thrown when the provided arguments are not correct. */
-public class CommandArgsException extends IllegalArgumentException {
- private static final long serialVersionUID = -7271050747105253935L;
- private String commandName;
- private volatile CommandsCli commandsCli;
-
- public CommandArgsException(Exception cause) {
- super(cause.getMessage(), cause);
- }
-
- public CommandArgsException(String message) {
- super(message);
- }
-
- public String getCommandName() {
- return commandName;
- }
-
- public void setCommandName(String commandName) {
- this.commandName = commandName;
- }
-
- public CommandsCli getCommandsCli() {
- return commandsCli;
- }
-
- public void setCommandsCli(CommandsCli commandsCli) {
- this.commandsCli = commandsCli;
- }
-
-}
+++ /dev/null
-package org.argeo.api.cli;
-
-import java.util.List;
-
-/** {@link RuntimeException} referring during a command run. */
-public class CommandRuntimeException extends RuntimeException {
- private static final long serialVersionUID = 5595999301269377128L;
-
- private final DescribedCommand<?> command;
- private final List<String> arguments;
-
- public CommandRuntimeException(Throwable e, DescribedCommand<?> command, List<String> arguments) {
- this(null, e, command, arguments);
- }
-
- public CommandRuntimeException(String message, DescribedCommand<?> command, List<String> arguments) {
- this(message, null, command, arguments);
- }
-
- public CommandRuntimeException(String message, Throwable e, DescribedCommand<?> command, List<String> arguments) {
- super(message == null ? "(" + command.getClass().getName() + " " + arguments.toString() + ")"
- : message + " (" + command.getClass().getName() + " " + arguments.toString() + ")", e);
- this.command = command;
- this.arguments = arguments;
- }
-
- public DescribedCommand<?> getCommand() {
- return command;
- }
-
- public List<String> getArguments() {
- return arguments;
- }
-
-}
+++ /dev/null
-package org.argeo.api.cli;
-
-import java.io.StringWriter;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.TreeMap;
-import java.util.function.Function;
-
-import org.apache.commons.cli.CommandLine;
-import org.apache.commons.cli.CommandLineParser;
-import org.apache.commons.cli.DefaultParser;
-import org.apache.commons.cli.MissingOptionException;
-import org.apache.commons.cli.Options;
-import org.apache.commons.cli.ParseException;
-
-/** Base class for a CLI managing sub commands. */
-public abstract class CommandsCli implements DescribedCommand<Object> {
- private final String commandName;
- private Map<String, Function<List<String>, ?>> commands = new TreeMap<>();
-
- protected final Options options = new Options();
-
- public CommandsCli(String commandName) {
- this.commandName = commandName;
- }
-
- @Override
- public Object apply(List<String> args) {
- String cmd = null;
- List<String> newArgs = new ArrayList<>();
- boolean isHelpOption = false;
- try {
- CommandLineParser clParser = new DefaultParser();
- CommandLine commonCl = clParser.parse(getOptions(), args.toArray(new String[args.size()]), true);
- List<String> leftOvers = commonCl.getArgList();
- for (String arg : leftOvers) {
- if (arg.equals("--" + HelpCommand.HELP_OPTION.getLongOpt())) {
- isHelpOption = true;
- // TODO break?
- }
-
- if (!arg.startsWith("-") && cmd == null) {
- cmd = arg;
- } else {
- newArgs.add(arg);
- }
- }
- } catch (ParseException e) {
- CommandArgsException cae = new CommandArgsException(e);
- throw cae;
- }
-
- Function<List<String>, ?> function = cmd != null ? getCommand(cmd) : getDefaultCommand();
-
- // --help option
- if (!(function instanceof CommandsCli))
- if (function instanceof DescribedCommand<?> command)
- if (isHelpOption) {
- throw new PrintHelpRequestException(cmd, this);
-// StringWriter out = new StringWriter();
-// HelpCommand.printHelp(command, out);
-// System.out.println(out.toString());
-// return null;
- }
-
- if (function == null)
- throw new IllegalArgumentException("Uknown command " + cmd);
- try {
- Object value = function.apply(newArgs);
- return value != null ? value.toString() : null;
- } catch (CommandArgsException e) {
- if (e.getCommandName() == null) {
- e.setCommandName(cmd);
- e.setCommandsCli(this);
- }
- throw e;
- } catch (IllegalArgumentException e) {
- CommandArgsException cae = new CommandArgsException(e);
- cae.setCommandName(cmd);
- throw cae;
- }
- }
-
- @Override
- public Options getOptions() {
- return options;
- }
-
- protected void addCommand(String cmd, Function<List<String>, ?> function) {
- commands.put(cmd, function);
-
- }
-
- @Override
- public String getUsage() {
- return "[command]";
- }
-
- protected void addCommandsCli(CommandsCli commandsCli) {
- addCommand(commandsCli.getCommandName(), commandsCli);
- commandsCli.addCommand(HelpCommand.HELP, new HelpCommand(this, commandsCli));
- }
-
- public String getCommandName() {
- return commandName;
- }
-
- public Set<String> getSubCommands() {
- return commands.keySet();
- }
-
- public Function<List<String>, ?> getCommand(String command) {
- return commands.get(command);
- }
-
- public HelpCommand getHelpCommand() {
- return (HelpCommand) getCommand(HelpCommand.HELP);
- }
-
- public Function<List<String>, String> getDefaultCommand() {
- return getHelpCommand();
- }
-
- /** In order to implement quickly a main method. */
- public static void mainImpl(CommandsCli cli, String[] args) {
- try {
- cli.addCommand(HelpCommand.HELP, new HelpCommand(null, cli));
- Object output = cli.apply(Arrays.asList(args));
- if (output != null)
- System.out.println(output);
- System.exit(0);
- } catch (PrintHelpRequestException e) {
- StringWriter out = new StringWriter();
- HelpCommand.printHelp(e.getCommandsCli(), e.getCommandName(), out);
- System.out.println(out.toString());
- } catch (CommandArgsException e) {
- System.err.println("Wrong arguments " + Arrays.toString(args) + ": " + e.getMessage());
- Throwable cause = e.getCause();
- if (!(cause instanceof MissingOptionException))
- e.printStackTrace();
- if (e.getCommandName() != null) {
- StringWriter out = new StringWriter();
- HelpCommand.printHelp(e.getCommandsCli(), e.getCommandName(), out);
- System.err.println(out.toString());
- } else {
- e.printStackTrace();
- }
- System.exit(1);
- } catch (Exception e) {
- e.printStackTrace();
- System.exit(1);
- }
- }
-}
+++ /dev/null
-package org.argeo.api.cli;
-
-import java.io.StringWriter;
-import java.util.Arrays;
-import java.util.List;
-import java.util.function.Function;
-
-import org.apache.commons.cli.CommandLine;
-import org.apache.commons.cli.DefaultParser;
-import org.apache.commons.cli.Options;
-import org.apache.commons.cli.ParseException;
-
-/** A command that can be described. */
-public interface DescribedCommand<T> extends Function<List<String>, T> {
- default Options getOptions() {
- return new Options();
- }
-
- String getDescription();
-
- default String getUsage() {
- return null;
- }
-
- default String getExamples() {
- return null;
- }
-
- default CommandLine toCommandLine(List<String> args) {
- try {
- DefaultParser parser = new DefaultParser();
- return parser.parse(getOptions(), args.toArray(new String[args.size()]));
- } catch (ParseException e) {
- throw new CommandArgsException(e);
- }
- }
-
- /** In order to implement quickly a main method. */
- public static void mainImpl(DescribedCommand<?> command, String[] args) {
- try {
- Object output = command.apply(Arrays.asList(args));
- System.out.println(output);
- System.exit(0);
- } catch (PrintHelpRequestException e) {
- StringWriter out = new StringWriter();
- HelpCommand.printHelp(command, out);
- System.out.println(out.toString());
- System.exit(1);
- } catch (IllegalArgumentException e) {
- StringWriter out = new StringWriter();
- HelpCommand.printHelp(command, out);
- System.err.println(out.toString());
- System.exit(1);
- } catch (Exception e) {
- e.printStackTrace();
- System.exit(1);
- }
- }
-
-}
+++ /dev/null
-package org.argeo.api.cli;
-
-import java.io.PrintWriter;
-import java.io.StringWriter;
-import java.util.List;
-import java.util.function.Function;
-
-import org.apache.commons.cli.HelpFormatter;
-import org.apache.commons.cli.Option;
-import org.apache.commons.cli.Options;
-
-/** A special command that can describe {@link DescribedCommand}. */
-public class HelpCommand implements DescribedCommand<String> {
- /**
- * System property forcing the root command to this value (typically the name of
- * a script).
- */
- public final static String ROOT_COMMAND_PROPERTY = "org.argeo.api.cli.rootCommand";
-
- final static String HELP = "help";
- final static Option HELP_OPTION = Option.builder().longOpt(HELP).desc("print this help").build();
-
- private CommandsCli commandsCli;
- private CommandsCli parentCommandsCli;
-
- // Help formatting
- private static int helpWidth = 80;
- private static int helpLeftPad = 4;
- private static int helpDescPad = 20;
-
- public HelpCommand(CommandsCli parentCommandsCli, CommandsCli commandsCli) {
- super();
- this.parentCommandsCli = parentCommandsCli;
- this.commandsCli = commandsCli;
- }
-
- @Override
- public String apply(List<String> args) {
- StringWriter out = new StringWriter();
-
- if (args.size() == 0) {// overview
- printHelp(commandsCli, out);
- } else {
- String cmd = args.get(0);
- Function<List<String>, ?> function = commandsCli.getCommand(cmd);
- if (function == null)
- return "Command " + cmd + " not found.";
- Options options;
- String examples;
- DescribedCommand<?> command = null;
- if (function instanceof DescribedCommand) {
- command = (DescribedCommand<?>) function;
- options = command.getOptions();
- examples = command.getExamples();
- } else {
- options = new Options();
- examples = null;
- }
- String description = getShortDescription(function);
- String commandCall = getCommandUsage(cmd, command);
- HelpFormatter formatter = new HelpFormatter();
- formatter.printHelp(new PrintWriter(out), helpWidth, commandCall, description, options, helpLeftPad,
- helpDescPad, examples, false);
- }
- return out.toString();
- }
-
- private static String getShortDescription(Function<List<String>, ?> function) {
- if (function instanceof DescribedCommand) {
- return ((DescribedCommand<?>) function).getDescription();
- } else {
- return function.toString();
- }
- }
-
- public String getCommandUsage(String cmd, DescribedCommand<?> command) {
- String commandCall = getCommandCall(commandsCli) + " " + cmd;
- assert command != null;
- if (command != null && command.getUsage() != null) {
- commandCall = commandCall + " " + command.getUsage();
- }
- return commandCall;
- }
-
- @Override
- public String getDescription() {
- return "Shows this help or describes a command";
- }
-
- @Override
- public String getUsage() {
- return "[command]";
- }
-
- public CommandsCli getParentCommandsCli() {
- return parentCommandsCli;
- }
-
- protected String getCommandCall(CommandsCli commandsCli) {
- HelpCommand hc = commandsCli.getHelpCommand();
- if (hc.getParentCommandsCli() != null) {
- return getCommandCall(hc.getParentCommandsCli()) + " " + commandsCli.getCommandName();
- } else {
- String rootCommand = System.getProperty(ROOT_COMMAND_PROPERTY);
- if (rootCommand != null)
- return rootCommand;
- return commandsCli.getCommandName();
- }
- }
-
- public static void printHelp(DescribedCommand<?> command, StringWriter out) {
- String usage = "java " + command.getClass().getName()
- + (command.getUsage() != null ? " " + command.getUsage() : "");
- HelpFormatter formatter = new HelpFormatter();
- Options options = command.getOptions();
- options.addOption(HelpCommand.HELP_OPTION);
- formatter.printHelp(new PrintWriter(out), helpWidth, usage, command.getDescription(), options, helpLeftPad,
- helpDescPad, command.getExamples(), false);
-
- }
-
- public static void printHelp(CommandsCli commandsCli, String commandName, StringWriter out) {
- if (commandName == null) {
- printHelp(commandsCli, out);
- return;
- }
- DescribedCommand<?> command = (DescribedCommand<?>) commandsCli.getCommand(commandName);
- String usage = commandsCli.getHelpCommand().getCommandUsage(commandName, command);
- HelpFormatter formatter = new HelpFormatter();
- Options options = command.getOptions();
- options.addOption(HelpCommand.HELP_OPTION);
- formatter.printHelp(new PrintWriter(out), helpWidth, usage, command.getDescription(), options, helpLeftPad,
- helpDescPad, command.getExamples(), false);
-
- }
-
- public static void printHelp(CommandsCli commandsCli, StringWriter out) {
- out.append(commandsCli.getDescription()).append('\n');
- String leftPad = spaces(helpLeftPad);
- for (String cmd : commandsCli.getSubCommands()) {
- Function<List<String>, ?> function = commandsCli.getCommand(cmd);
- assert function != null;
- out.append(leftPad);
- out.append(cmd);
- // TODO deal with long commands
- out.append(spaces(helpDescPad - cmd.length()));
- out.append(getShortDescription(function));
- out.append('\n');
- }
- }
-
- private static String spaces(int count) {
- // Java 11
- // return " ".repeat(count);
- if (count <= 0)
- return "";
- else {
- StringBuilder sb = new StringBuilder(count);
- for (int i = 0; i < count; i++)
- sb.append(' ');
- return sb.toString();
- }
- }
-}
+++ /dev/null
-package org.argeo.api.cli;
-
-/** An exception indicating that help should be printed. */
-class PrintHelpRequestException extends RuntimeException {
-
- private static final long serialVersionUID = -9029122270660656639L;
-
- private String commandName;
- private volatile CommandsCli commandsCli;
-
- public PrintHelpRequestException(String commandName, CommandsCli commandsCli) {
- super();
- this.commandName = commandName;
- this.commandsCli = commandsCli;
- }
-
- public static long getSerialversionuid() {
- return serialVersionUID;
- }
-
- public String getCommandName() {
- return commandName;
- }
-
- public CommandsCli getCommandsCli() {
- return commandsCli;
- }
-
-}
+++ /dev/null
-/** Command line API. */
-package org.argeo.api.cli;
\ No newline at end of file
+++ /dev/null
-/MANIFEST.MF
Import-Package: \
javax.transaction.xa,\
javax.security.*,\
-org.osgi.service.useradmin,\
*
Export-Package: org.argeo.api.cms.*
\ No newline at end of file
--- /dev/null
+package org.argeo.api.cms.auth;
+
+import java.security.Principal;
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.xml.namespace.QName;
+
+/**
+ * A {@link Principal} which has been implied by an authorisation. If it is
+ * empty it means this is an additional identity, otherwise it lists the users
+ * (typically the logged-in user but possibly empty {@link ImpliedByPrincipal}s)
+ * which have implied it. When an additional identity is removed, the related
+ * {@link ImpliedByPrincipal}s can thus be removed.
+ */
+public final class ImpliedByPrincipal implements Principal {
+ private final String name;
+ private final QName roleName;
+ private final boolean systemRole;
+ private final String context;
+
+ private Set<Principal> causes = new HashSet<Principal>();
+
+ public ImpliedByPrincipal(String name, Principal userPrincipal) {
+ this.name = name;
+ roleName = RoleNameUtils.getLastRdnAsName(name);
+ systemRole = RoleNameUtils.isSystemRole(roleName);
+ context = RoleNameUtils.getContext(name);
+ if (userPrincipal != null)
+ causes.add(userPrincipal);
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ /*
+ * OBJECT
+ */
+
+ public QName getRoleName() {
+ return roleName;
+ }
+
+ public String getContext() {
+ return context;
+ }
+
+ public boolean isSystemRole() {
+ return systemRole;
+ }
+
+ @Override
+ public int hashCode() {
+ return name.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj instanceof ImpliedByPrincipal) {
+ ImpliedByPrincipal that = (ImpliedByPrincipal) obj;
+ // TODO check members too?
+ return name.equals(that.name);
+ }
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return name.toString();
+ }
+}
--- /dev/null
+package org.argeo.api.cms.auth;
+
+import static org.argeo.api.acr.RuntimeNamespaceContext.getNamespaceContext;
+
+import javax.xml.namespace.QName;
+
+import org.argeo.api.acr.ArgeoNamespace;
+import org.argeo.api.acr.NamespaceUtils;
+import org.argeo.api.acr.ldap.LdapNameUtils;
+
+/** Simplifies analysis of system roles. */
+public class RoleNameUtils {
+ public static String getLastRdnValue(String dn) {
+ return LdapNameUtils.getLastRdnValue(dn);
+// // we don't use LdapName for portability with Android
+// // TODO make it more robust
+// String[] parts = dn.split(",");
+// String[] rdn = parts[0].split("=");
+// return rdn[1];
+ }
+
+ public static QName getLastRdnAsName(String dn) {
+ String cn = getLastRdnValue(dn);
+ QName roleName = NamespaceUtils.parsePrefixedName(getNamespaceContext(), cn);
+ return roleName;
+ }
+
+ public static boolean isSystemRole(QName roleName) {
+ return roleName.getNamespaceURI().equals(ArgeoNamespace.ROLE_NAMESPACE_URI);
+ }
+
+ public static String getParent(String dn) {
+ int index = dn.indexOf(',');
+ return dn.substring(index + 1);
+ }
+
+ /** Up two levels. */
+ public static String getContext(String dn) {
+ return getParent(getParent(dn));
+ }
+}
--- /dev/null
+package org.argeo.api.cms.auth;
+
+import java.util.Set;
+
+import javax.security.auth.Subject;
+import javax.xml.namespace.QName;
+
+import org.argeo.api.cms.CmsConstants;
+
+/** A programmatic role. */
+public interface SystemRole {
+ QName qName();
+
+ /** Whether this role is implied for this authenticated user. */
+ default boolean implied(Subject subject, String context) {
+ return implied(qName(), subject, context);
+ }
+
+ /** Whether this role is implied for this distinguished name. */
+ default boolean implied(String dn, String context) {
+ String roleContext = RoleNameUtils.getContext(dn);
+ QName roleName = RoleNameUtils.getLastRdnAsName(dn);
+ return roleContext.equalsIgnoreCase(context) && qName().equals(roleName);
+ }
+
+ /**
+ * Whether this role is implied for this authenticated subject. If context is
+ * <code>null</code>, it is not considered; this should be used to build user
+ * interfaces, but not to authorise.
+ */
+ static boolean implied(QName name, Subject subject, String context) {
+ Set<ImpliedByPrincipal> roles = subject.getPrincipals(ImpliedByPrincipal.class);
+ for (ImpliedByPrincipal role : roles) {
+ if (role.isSystemRole()) {
+ if (role.getRoleName().equals(name)) {
+ // !! if context is not specified, it is considered irrelevant
+ if (context == null)
+ return true;
+ if (role.getContext().equalsIgnoreCase(context)
+ || role.getContext().equals(CmsConstants.NODE_BASEDN))
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+}
package org.argeo.api.cms.directory;
-import org.osgi.service.useradmin.Authorization;
-
/** An authorisation to a CMS system. */
-public interface CmsAuthorization extends Authorization {
+public interface CmsAuthorization {
/** The role which did imply this role, <code>null</code> if a direct role. */
default String getImplyingRole(String role) {
return null;
package org.argeo.api.cms.directory;
-import org.osgi.service.useradmin.Group;
+import java.util.Set;
-/** A group in a user directroy. */
-public interface CmsGroup extends Group, CmsUser {
-// List<LdapName> getMemberNames();
+/** A group in a user directory. */
+public interface CmsGroup extends CmsUser {
+ Set<? extends CmsRole> getDirectMembers();
}
--- /dev/null
+package org.argeo.api.cms.directory;
+
+import java.util.Dictionary;
+
+/** Parent of user/group hierarchy */
+public interface CmsRole {
+ String getName();
+
+ // TODO replace with Map or ACR content
+ @Deprecated
+ Dictionary<String, Object> getProperties();
+}
package org.argeo.api.cms.directory;
-import org.osgi.service.useradmin.User;
-
/**
* An entity with credentials which can log in to a system. Can be a real person
* or not.
*/
-public interface CmsUser extends User {
+public interface CmsUser extends CmsRole {
+ String getDisplayName();
}
import javax.security.auth.Subject;
import javax.xml.namespace.QName;
-import org.osgi.service.useradmin.Role;
-import org.osgi.service.useradmin.User;
-
/**
* Provide method interfaces to manage user concepts without accessing directly
* the userAdmin.
Set<UserDirectory> getUserDirectories();
// CurrentUser
- /** Returns the e-mail of the current logged in user */
- String getMyMail();
+// /** Returns the e-mail of the current logged in user */
+// String getMyMail();
// Other users
- /** Returns a {@link User} given a username */
+ /** Returns a {@link CmsUser} given a username */
CmsUser getUser(String username);
/** Can be a group or a user */
// Search
/** Returns a filtered list of roles */
- Role[] getRoles(String filter);
+ CmsRole[] getRoles(String filter);
/** Recursively lists users in a given group. */
Set<CmsUser> listUsersInGroup(String groupDn, String filter);
CmsGroup getOrCreateSystemRole(HierarchyUnit roles, QName systemRole);
/** Add additional object classes to this role. */
- void addObjectClasses(Role role, Set<String> objectClasses, Map<String, Object> additionalProperties);
+ void addObjectClasses(CmsRole role, Set<String> objectClasses, Map<String, Object> additionalProperties);
/** Add additional object classes to this hierarchy unit. */
void addObjectClasses(HierarchyUnit hierarchyUnit, Set<String> objectClasses,
Map<String, Object> additionalProperties);
/** Add a member to this group. */
- void addMember(CmsGroup group, Role role);
+ void addMember(CmsGroup group, CmsRole role);
/** Remove a member from this group. */
- void removeMember(CmsGroup group, Role role);
-
+ void removeMember(CmsGroup group, CmsRole role);
+
void edit(Runnable action);
/* MISCELLANEOUS */
String getDefaultDomainName();
/**
- * Search for a {@link User} (might also be a group) whose uid or cn is equals
+ * Search for a {@link CmsUser} (might also be a group) whose uid or cn is equals
* to localId within the various user repositories defined in the current
* context.
*/
void expireAuthTokens(Subject subject);
- UserDirectory getDirectory(Role role);
+ UserDirectory getDirectory(CmsRole role);
/** Create a new hierarchy unit. Does nothing if it already exists. */
HierarchyUnit getOrCreateHierarchyUnit(UserDirectory directory, String path);
package org.argeo.api.cms.directory;
-import org.osgi.service.useradmin.Role;
-
/** Information about a user directory. */
public interface UserDirectory extends CmsDirectory {
- HierarchyUnit getHierarchyUnit(Role role);
+ HierarchyUnit getHierarchyUnit(CmsRole role);
- Iterable<? extends Role> getHierarchyUnitRoles(HierarchyUnit hierarchyUnit, String filter, boolean deep);
+ Iterable<? extends CmsRole> getHierarchyUnitRoles(HierarchyUnit hierarchyUnit, String filter, boolean deep);
- String getRolePath(Role role);
+ String getRolePath(CmsRole role);
- String getRoleSimpleName(Role role);
+ String getRoleSimpleName(CmsRole role);
- Role getRoleByPath(String path);
+ CmsRole getRoleByPath(String path);
}
return UuidBinaryUtils.fromBytes(arr);
}
- /*
- * SPI UTILITIES
- */
- /** Guarantees that a byte array of length 6 will be returned. */
- protected static byte[] toNodeIdBytes(byte[] source, int offset) {
- if (source == null)
- return null;
- if (offset < 0 || offset + 6 > source.length)
- throw new ArrayIndexOutOfBoundsException(offset);
- byte[] nodeId = new byte[6];
- System.arraycopy(source, offset, nodeId, 0, 6);
- return nodeId;
- }
-
- /**
- * Force this node id to be identified as no MAC address.
- *
- * @see "https://datatracker.ietf.org/doc/html/rfc4122#section-4.5"
- */
- protected static void forceToNoMacAddress(byte[] nodeId, int offset) {
- assert nodeId != null && offset < nodeId.length;
- nodeId[offset] = (byte) (nodeId[offset] | 1);
- }
-
/*
* DIGEST UTILITIES
*/
Objects.requireNonNull(nodeId);
if (offset + 6 > nodeId.length)
throw new IllegalArgumentException("Offset too big: " + offset);
- byte[] defaultNodeId = toNodeIdBytes(nodeId, offset);
+ byte[] defaultNodeId = NodeIdSupplier.toNodeIdBytes(nodeId, offset);
long nodeIdBase = NodeIdSupplier.toNodeIdBase(defaultNodeId);
setNodeIdSupplier(() -> nodeIdBase, initialClockRange);
}
try {
localHost = InetAddress.getLocalHost();
NetworkInterface nic = NetworkInterface.getByInetAddress(localHost);
- if (nic != null)
+ if (nic != null && nic.getHardwareAddress() != null)
return hardwareAddressToNodeId(nic);
Enumeration<NetworkInterface> netInterfaces = null;
- try {
- netInterfaces = NetworkInterface.getNetworkInterfaces();
- } catch (SocketException e) {
- throw new IllegalStateException(e);
- }
+ netInterfaces = NetworkInterface.getNetworkInterfaces();
if (netInterfaces == null || !netInterfaces.hasMoreElements())
throw new IllegalStateException("No interfaces");
- return hardwareAddressToNodeId(netInterfaces.nextElement());
+ while (netInterfaces.hasMoreElements()) {
+ // TODO find out public/physical interfaces
+ nic = netInterfaces.nextElement();
+ if (nic.getHardwareAddress() != null)
+ return hardwareAddressToNodeId(nic);
+ }
+ throw new IllegalStateException("No interfaces with a MAC address");
} catch (UnknownHostException | SocketException e) {
throw new IllegalStateException(e);
}
}
- public static byte[] hardwareAddressToNodeId(NetworkInterface nic) {
+ public static byte[] hardwareAddressToNodeId(NetworkInterface nic) throws IllegalStateException {
try {
byte[] hardwareAddress = nic.getHardwareAddress();
final int length = 6;
random.nextBytes(nodeId);
return nodeId;
}
+
+ /**
+ * Force this node id to be identified as no MAC address.
+ *
+ * @see "https://datatracker.ietf.org/doc/html/rfc4122#section-4.5"
+ */
+ static void forceToNoMacAddress(byte[] nodeId, int offset) {
+ assert nodeId != null && offset < nodeId.length;
+ nodeId[offset] = (byte) (nodeId[offset] | 1);
+ }
+
+ /*
+ * SPI UTILITIES
+ */
+ /** Guarantees that a byte array of length 6 will be returned. */
+ static byte[] toNodeIdBytes(byte[] source, int offset) {
+ if (source == null)
+ return null;
+ if (offset < 0 || offset + 6 > source.length)
+ throw new ArrayIndexOutOfBoundsException(offset);
+ byte[] nodeId = new byte[6];
+ System.arraycopy(source, offset, nodeId, 0, 6);
+ return nodeId;
+ }
}
--- /dev/null
+package org.argeo.api.uuid.libuuid;
+
+import java.io.Serializable;
+
+/** Package metadata for this package. */
+class APM implements Serializable {
+ /** Major version (equality means backward compatibility). */
+ static final int MAJOR = 2;
+ /** Minor version (if even, equality means forward compatibility). */
+ static final int MINOR = 3;
+ /** serialVersionUID to use for {@link Serializable} classes in this package. */
+ static final long SERIAL = (long) MAJOR << 32 | MINOR & 0xFFFFFFFFL;
+ /** Metadata version. */
+ private static final long serialVersionUID = 2L;
+}
* concept that using shared memory in order to limit the JNI
* overhead does not yield any significant performance gain. But it
* could be an approach for computing and transferring bulk UUIDs
- * computations in one go, vi
+ * computations in one go, via
* {@link ByteBuffer#allocateDirect(int)}.
*/
public class DirectLibuuidFactory implements UuidFactory {
static {
- System.loadLibrary("Java_org_argeo_api_uuid_libuuid");
+ System.loadLibrary("Java_org_argeo_api_uuid_libuuid." + APM.MAJOR + "." + APM.MINOR);
}
@Override
/** An {@link UuidFactory} based on a native library. */
public class LibuuidFactory implements UuidFactory, TypedUuidFactory {
static {
- System.loadLibrary("Java_org_argeo_api_uuid_libuuid");
+ System.loadLibrary("Java_org_argeo_api_uuid_libuuid." + APM.MAJOR + "." + APM.MINOR);
}
@Override
Class-Path: \
org.argeo.api.acr.2.1.jar \
-org.argeo.api.cli.2.1.jar \
org.argeo.api.cms.2.1.jar \
org.argeo.api.register.2.1.jar \
org.argeo.api.uuid.2.1.jar \
../crypto/fips/org.argeo.tp.crypto/bcpkix-fips.1.0.src.jar \
../crypto/fips/org.argeo.tp.crypto/bctls-fips.1.0.jar \
../crypto/fips/org.argeo.tp.crypto/bctls-fips.1.0.src.jar \
-../osgi/equinox/org.argeo.cms/org.argeo.cms.lib.equinox.2.1.jar \
+../osgi/equinox/org.argeo.cms/org.argeo.cms.lib.equinox.2.3.jar \
../osgi/equinox/org.argeo.tp.osgi/org.apache.felix.gogo.command.1.1.jar \
../osgi/equinox/org.argeo.tp.osgi/org.apache.felix.gogo.runtime.1.1.jar \
../osgi/equinox/org.argeo.tp.osgi/org.apache.felix.gogo.shell.1.1.jar \
org.osgi.service.http.whiteboard;version=0.0.0,\
org.osgi.framework.namespace;version=0.0.0,\
org.argeo.cms.osgi,\
-javax.servlet.*;version="[3,5)",\
*
+# javax.servlet.*;version="[3,5)",\
+
+
Service-Component:\
OSGI-INF/pkgServletContext.xml,\
OSGI-INF/pkgServlet.xml,\
static Path bundleToPath(Path frameworkLocation, Bundle bundle) throws IOException {
String location = bundle.getLocation();
- if (location.startsWith("initial@reference:file:")) {
+ if (location.startsWith("initial@reference:file:")) {// Eclipse IDE environment
location = location.substring("initial@reference:file:".length());
Path p = frameworkLocation.getParent().resolve(location).toAbsolutePath();
if (Files.exists(p)) {
log.warn("Ignore bundle " + p + " as it does not exist");
return null;
}
+ } else if (location.startsWith("reference:file:")) {// a2+reference
+ location = location.substring("reference:".length());
+ Path p = Paths.get(URI.create(location));
+ if (Files.exists(p)) {
+ return p;
+ } else {
+ log.warn("Ignore bundle " + p + " as it does not exist");
+ return null;
+ }
}
Path p = Paths.get(location);
return p;
server.stop();
// TODO delete temp dir
started = false;
+ log.debug(() -> "Stopped Jetty server");
} catch (Exception e) {
log.error("Cannot stop Jetty HTTP server", e);
}
if (httpContext instanceof ContextHandlerHttpContext contextHandlerHttpContext) {
// TODO stop handler first?
// FIXME understand compatibility with Jetty 12
- //contextHandlerCollection.removeHandler(contextHandlerHttpContext.getServletContextHandler());
+ // contextHandlerCollection.removeHandler(contextHandlerHttpContext.getServletContextHandler());
} else {
// FIXME apparently servlets cannot be removed in Jetty, we should replace the
// handler
}
private String httpPortsMsg() {
+ String hostStr = getHost();
+ hostStr = hostStr == null ? "*:" : hostStr + ":";
+ return (httpConnector != null ? "# HTTP " + hostStr + getHttpPort() + " " : "")
+ + (httpsConnector != null ? "# HTTPS " + hostStr + getHttpsPort() : "");
+ }
- return (httpConnector != null ? "HTTP " + getHttpPort() + " " : "")
- + (httpsConnector != null ? "HTTPS " + getHttpsPort() : "");
+ public String getHost() {
+ if (httpConnector == null)
+ return null;
+ return httpConnector.getHost();
}
public Integer getHttpPort() {
.,\
OSGI-INF/
source.. = src/
-additional.bundles = org.argeo.tp.syslogger,\
- org.apache.tomcat.jni
-
\ No newline at end of file
+additional.bundles = org.argeo.tp.syslogger
\ No newline at end of file
import java.io.Console;
import java.io.IOException;
+import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Arrays;
}
public void authenticate() {
+ authenticate(System.in);
+ }
+
+ public void authenticate(InputStream in) {
if (sshKeyPair != null) {
session.addPublicKeyIdentity(sshKeyPair.asKeyPair());
} else {
- if (!passwordSet) {
+ if (!passwordSet && in != null) {
String password;
Console console = System.console();
if (console == null) {// IDE
System.out.print("Password: ");
- try (Scanner s = new Scanner(System.in)) {
+ try (Scanner s = new Scanner(in)) {
password = s.next();
}
} else {
// start
sshd.start();
- log.debug(() -> "CMS SSH server started on port " + port + (host != null ? " of host " + host : ""));
+ log.info("# SSH " + (host != null ? host + ":" : "*:") + port);
} catch (IOException e) {
throw new RuntimeException("Cannot start SSH server on port " + port, e);
}
} catch (IOException | KeyStoreException | NoSuchProviderException | NoSuchAlgorithmException
| CertificateException | IllegalArgumentException | UnrecoverableKeyException e) {
if (log.isTraceEnabled())
- log.error("Cannot add node public key to SSH authorized keys", e);
+ log.warn("Cannot add node public key to SSH authorized keys", e);
else
- log.error("Cannot add node public key to SSH authorized keys: " + e.getMessage());
+ log.warn("Cannot add node public key to SSH authorized keys: " + e);
return null;
}
import org.argeo.api.acr.spi.ProvidedContent;
import org.argeo.api.acr.spi.ProvidedSession;
import org.argeo.api.cms.ux.CmsEditable;
-import org.argeo.cms.acr.ContentUtils;
+import org.argeo.cms.acr.CmsContent;
import org.argeo.cms.ux.AbstractCmsEditable;
/** {@link CmsEditable} semantics for a {@link Content}. */
canEdit = providedContent.canEdit();
session = providedContent.getSession();
provider = providedContent.getProvider();
- relativePath = ContentUtils.relativize(provider.getMountPath(), content.getPath());
+ relativePath = CmsContent.relativize(provider.getMountPath(), content.getPath());
}
@Override
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" activate="start" deactivate="stop" immediate="false" name="CMS State">
- <implementation class="org.argeo.cms.internal.runtime.CmsStateImpl"/>
- <service>
- <provide interface="org.argeo.api.cms.CmsState"/>
- </service>
- <reference bind="setUuidFactory" cardinality="1..1" interface="org.argeo.api.uuid.UuidFactory" name="UuidFactory" policy="static"/>
-</scr:component>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="UUID Factory">
+ <implementation class="org.argeo.api.uuid.ConcurrentUuidFactory"/>
+ <service>
+ <provide interface="org.argeo.api.uuid.UuidFactory"/>
+ </service>
+ <reference bind="setNodeIdSupplier" cardinality="1..1" interface="org.argeo.api.uuid.NodeIdSupplier" name="NodeIdSupplier" policy="static"/>
+</scr:component>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="UUID Factory">
- <implementation class="org.argeo.cms.acr.CmsUuidFactory"/>
- <service>
- <provide interface="org.argeo.api.uuid.UuidFactory"/>
- </service>
-</scr:component>
Bundle-Activator: org.argeo.cms.internal.osgi.CmsActivator
Import-Package: \
-org.osgi.*;version=0.0.0,\
+*
+
+Export-Package:\
+org.argeo.cms.acr.schemas,\
*
Service-Component:\
OSGI-INF/cmsOsgiLogger.xml,\
-OSGI-INF/uuidFactory.xml,\
+OSGI-INF/cmsUuidFactory.xml,\
OSGI-INF/cmsEventBus.xml,\
-OSGI-INF/cmsState.xml,\
OSGI-INF/transactionManager.xml,\
OSGI-INF/cmsUserAdmin.xml,\
OSGI-INF/cmsUserManager.xml,\
--- /dev/null
+package org.argeo.api.cli;
+
+/** Exception thrown when the provided arguments are not correct. */
+public class CommandArgsException extends IllegalArgumentException {
+ private static final long serialVersionUID = -7271050747105253935L;
+ private String commandName;
+ private volatile CommandsCli commandsCli;
+
+ public CommandArgsException(Exception cause) {
+ super(cause.getMessage(), cause);
+ }
+
+ public CommandArgsException(String message) {
+ super(message);
+ }
+
+ public String getCommandName() {
+ return commandName;
+ }
+
+ public void setCommandName(String commandName) {
+ this.commandName = commandName;
+ }
+
+ public CommandsCli getCommandsCli() {
+ return commandsCli;
+ }
+
+ public void setCommandsCli(CommandsCli commandsCli) {
+ this.commandsCli = commandsCli;
+ }
+
+}
--- /dev/null
+package org.argeo.api.cli;
+
+import java.util.List;
+
+/** {@link RuntimeException} referring during a command run. */
+public class CommandRuntimeException extends RuntimeException {
+ private static final long serialVersionUID = 5595999301269377128L;
+
+ private final DescribedCommand<?> command;
+ private final List<String> arguments;
+
+ public CommandRuntimeException(Throwable e, DescribedCommand<?> command, List<String> arguments) {
+ this(null, e, command, arguments);
+ }
+
+ public CommandRuntimeException(String message, DescribedCommand<?> command, List<String> arguments) {
+ this(message, null, command, arguments);
+ }
+
+ public CommandRuntimeException(String message, Throwable e, DescribedCommand<?> command, List<String> arguments) {
+ super(message == null ? "(" + command.getClass().getName() + " " + arguments.toString() + ")"
+ : message + " (" + command.getClass().getName() + " " + arguments.toString() + ")", e);
+ this.command = command;
+ this.arguments = arguments;
+ }
+
+ public DescribedCommand<?> getCommand() {
+ return command;
+ }
+
+ public List<String> getArguments() {
+ return arguments;
+ }
+
+}
--- /dev/null
+package org.argeo.api.cli;
+
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.function.Function;
+
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.CommandLineParser;
+import org.apache.commons.cli.DefaultParser;
+import org.apache.commons.cli.MissingOptionException;
+import org.apache.commons.cli.Options;
+import org.apache.commons.cli.ParseException;
+
+/** Base class for a CLI managing sub commands. */
+public abstract class CommandsCli implements DescribedCommand<Object> {
+ private final String commandName;
+ private Map<String, Function<List<String>, ?>> commands = new TreeMap<>();
+
+ protected final Options options = new Options();
+
+ public CommandsCli(String commandName) {
+ this.commandName = commandName;
+ }
+
+ @Override
+ public Object apply(List<String> args) {
+ String cmd = null;
+ List<String> newArgs = new ArrayList<>();
+ boolean isHelpOption = false;
+ try {
+ CommandLineParser clParser = new DefaultParser();
+ CommandLine commonCl = clParser.parse(getOptions(), args.toArray(new String[args.size()]), true);
+ List<String> leftOvers = commonCl.getArgList();
+ for (String arg : leftOvers) {
+ if (arg.equals("--" + HelpCommand.HELP_OPTION.getLongOpt())) {
+ isHelpOption = true;
+ // TODO break?
+ }
+
+ if (!arg.startsWith("-") && cmd == null) {
+ cmd = arg;
+ } else {
+ newArgs.add(arg);
+ }
+ }
+ } catch (ParseException e) {
+ CommandArgsException cae = new CommandArgsException(e);
+ throw cae;
+ }
+
+ Function<List<String>, ?> function = cmd != null ? getCommand(cmd) : getDefaultCommand();
+
+ // --help option
+ if (!(function instanceof CommandsCli))
+ if (function instanceof DescribedCommand<?> command)
+ if (isHelpOption) {
+ throw new PrintHelpRequestException(cmd, this);
+// StringWriter out = new StringWriter();
+// HelpCommand.printHelp(command, out);
+// System.out.println(out.toString());
+// return null;
+ }
+
+ if (function == null)
+ throw new IllegalArgumentException("Uknown command " + cmd);
+ try {
+ Object value = function.apply(newArgs);
+ return value != null ? value.toString() : null;
+ } catch (CommandArgsException e) {
+ if (e.getCommandName() == null) {
+ e.setCommandName(cmd);
+ e.setCommandsCli(this);
+ }
+ throw e;
+ } catch (IllegalArgumentException e) {
+ CommandArgsException cae = new CommandArgsException(e);
+ cae.setCommandName(cmd);
+ throw cae;
+ }
+ }
+
+ @Override
+ public Options getOptions() {
+ return options;
+ }
+
+ protected void addCommand(String cmd, Function<List<String>, ?> function) {
+ commands.put(cmd, function);
+
+ }
+
+ @Override
+ public String getUsage() {
+ return "[command]";
+ }
+
+ protected void addCommandsCli(CommandsCli commandsCli) {
+ addCommand(commandsCli.getCommandName(), commandsCli);
+ commandsCli.addCommand(HelpCommand.HELP, new HelpCommand(this, commandsCli));
+ }
+
+ public String getCommandName() {
+ return commandName;
+ }
+
+ public Set<String> getSubCommands() {
+ return commands.keySet();
+ }
+
+ public Function<List<String>, ?> getCommand(String command) {
+ return commands.get(command);
+ }
+
+ public HelpCommand getHelpCommand() {
+ return (HelpCommand) getCommand(HelpCommand.HELP);
+ }
+
+ public Function<List<String>, String> getDefaultCommand() {
+ return getHelpCommand();
+ }
+
+ /** In order to implement quickly a main method. */
+ public static void mainImpl(CommandsCli cli, String[] args) {
+ try {
+ cli.addCommand(HelpCommand.HELP, new HelpCommand(null, cli));
+ Object output = cli.apply(Arrays.asList(args));
+ if (output != null)
+ System.out.println(output);
+ System.exit(0);
+ } catch (PrintHelpRequestException e) {
+ StringWriter out = new StringWriter();
+ HelpCommand.printHelp(e.getCommandsCli(), e.getCommandName(), out);
+ System.out.println(out.toString());
+ } catch (CommandArgsException e) {
+ System.err.println("Wrong arguments " + Arrays.toString(args) + ": " + e.getMessage());
+ Throwable cause = e.getCause();
+ if (!(cause instanceof MissingOptionException))
+ e.printStackTrace();
+ if (e.getCommandName() != null) {
+ StringWriter out = new StringWriter();
+ HelpCommand.printHelp(e.getCommandsCli(), e.getCommandName(), out);
+ System.err.println(out.toString());
+ } else {
+ e.printStackTrace();
+ }
+ System.exit(1);
+ } catch (Exception e) {
+ e.printStackTrace();
+ System.exit(1);
+ }
+ }
+}
--- /dev/null
+package org.argeo.api.cli;
+
+import java.io.StringWriter;
+import java.util.Arrays;
+import java.util.List;
+import java.util.function.Function;
+
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.DefaultParser;
+import org.apache.commons.cli.Options;
+import org.apache.commons.cli.ParseException;
+
+/** A command that can be described. */
+public interface DescribedCommand<T> extends Function<List<String>, T> {
+ default Options getOptions() {
+ return new Options();
+ }
+
+ String getDescription();
+
+ default String getUsage() {
+ return null;
+ }
+
+ default String getExamples() {
+ return null;
+ }
+
+ default CommandLine toCommandLine(List<String> args) {
+ try {
+ DefaultParser parser = new DefaultParser();
+ return parser.parse(getOptions(), args.toArray(new String[args.size()]));
+ } catch (ParseException e) {
+ throw new CommandArgsException(e);
+ }
+ }
+
+ /** In order to implement quickly a main method. */
+ public static void mainImpl(DescribedCommand<?> command, String[] args) {
+ try {
+ Object output = command.apply(Arrays.asList(args));
+ System.out.println(output);
+ System.exit(0);
+ } catch (PrintHelpRequestException e) {
+ StringWriter out = new StringWriter();
+ HelpCommand.printHelp(command, out);
+ System.out.println(out.toString());
+ System.exit(1);
+ } catch (IllegalArgumentException e) {
+ StringWriter out = new StringWriter();
+ HelpCommand.printHelp(command, out);
+ System.err.println(out.toString());
+ System.exit(1);
+ } catch (Exception e) {
+ e.printStackTrace();
+ System.exit(1);
+ }
+ }
+
+}
--- /dev/null
+package org.argeo.api.cli;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.List;
+import java.util.function.Function;
+
+import org.apache.commons.cli.HelpFormatter;
+import org.apache.commons.cli.Option;
+import org.apache.commons.cli.Options;
+
+/** A special command that can describe {@link DescribedCommand}. */
+public class HelpCommand implements DescribedCommand<String> {
+ /**
+ * System property forcing the root command to this value (typically the name of
+ * a script).
+ */
+ public final static String ROOT_COMMAND_PROPERTY = "org.argeo.api.cli.rootCommand";
+
+ final static String HELP = "help";
+ final static Option HELP_OPTION = Option.builder().longOpt(HELP).desc("print this help").build();
+
+ private CommandsCli commandsCli;
+ private CommandsCli parentCommandsCli;
+
+ // Help formatting
+ private static int helpWidth = 80;
+ private static int helpLeftPad = 4;
+ private static int helpDescPad = 20;
+
+ public HelpCommand(CommandsCli parentCommandsCli, CommandsCli commandsCli) {
+ super();
+ this.parentCommandsCli = parentCommandsCli;
+ this.commandsCli = commandsCli;
+ }
+
+ @Override
+ public String apply(List<String> args) {
+ StringWriter out = new StringWriter();
+
+ if (args.size() == 0) {// overview
+ printHelp(commandsCli, out);
+ } else {
+ String cmd = args.get(0);
+ Function<List<String>, ?> function = commandsCli.getCommand(cmd);
+ if (function == null)
+ return "Command " + cmd + " not found.";
+ Options options;
+ String examples;
+ DescribedCommand<?> command = null;
+ if (function instanceof DescribedCommand) {
+ command = (DescribedCommand<?>) function;
+ options = command.getOptions();
+ examples = command.getExamples();
+ } else {
+ options = new Options();
+ examples = null;
+ }
+ String description = getShortDescription(function);
+ String commandCall = getCommandUsage(cmd, command);
+ HelpFormatter formatter = new HelpFormatter();
+ formatter.printHelp(new PrintWriter(out), helpWidth, commandCall, description, options, helpLeftPad,
+ helpDescPad, examples, false);
+ }
+ return out.toString();
+ }
+
+ private static String getShortDescription(Function<List<String>, ?> function) {
+ if (function instanceof DescribedCommand) {
+ return ((DescribedCommand<?>) function).getDescription();
+ } else {
+ return function.toString();
+ }
+ }
+
+ public String getCommandUsage(String cmd, DescribedCommand<?> command) {
+ String commandCall = getCommandCall(commandsCli) + " " + cmd;
+ assert command != null;
+ if (command != null && command.getUsage() != null) {
+ commandCall = commandCall + " " + command.getUsage();
+ }
+ return commandCall;
+ }
+
+ @Override
+ public String getDescription() {
+ return "Shows this help or describes a command";
+ }
+
+ @Override
+ public String getUsage() {
+ return "[command]";
+ }
+
+ public CommandsCli getParentCommandsCli() {
+ return parentCommandsCli;
+ }
+
+ protected String getCommandCall(CommandsCli commandsCli) {
+ HelpCommand hc = commandsCli.getHelpCommand();
+ if (hc.getParentCommandsCli() != null) {
+ return getCommandCall(hc.getParentCommandsCli()) + " " + commandsCli.getCommandName();
+ } else {
+ String rootCommand = System.getProperty(ROOT_COMMAND_PROPERTY);
+ if (rootCommand != null)
+ return rootCommand;
+ return commandsCli.getCommandName();
+ }
+ }
+
+ public static void printHelp(DescribedCommand<?> command, StringWriter out) {
+ String usage = "java " + command.getClass().getName()
+ + (command.getUsage() != null ? " " + command.getUsage() : "");
+ HelpFormatter formatter = new HelpFormatter();
+ Options options = command.getOptions();
+ options.addOption(HelpCommand.HELP_OPTION);
+ formatter.printHelp(new PrintWriter(out), helpWidth, usage, command.getDescription(), options, helpLeftPad,
+ helpDescPad, command.getExamples(), false);
+
+ }
+
+ public static void printHelp(CommandsCli commandsCli, String commandName, StringWriter out) {
+ if (commandName == null) {
+ printHelp(commandsCli, out);
+ return;
+ }
+ DescribedCommand<?> command = (DescribedCommand<?>) commandsCli.getCommand(commandName);
+ String usage = commandsCli.getHelpCommand().getCommandUsage(commandName, command);
+ HelpFormatter formatter = new HelpFormatter();
+ Options options = command.getOptions();
+ options.addOption(HelpCommand.HELP_OPTION);
+ formatter.printHelp(new PrintWriter(out), helpWidth, usage, command.getDescription(), options, helpLeftPad,
+ helpDescPad, command.getExamples(), false);
+
+ }
+
+ public static void printHelp(CommandsCli commandsCli, StringWriter out) {
+ out.append(commandsCli.getDescription()).append('\n');
+ String leftPad = spaces(helpLeftPad);
+ for (String cmd : commandsCli.getSubCommands()) {
+ Function<List<String>, ?> function = commandsCli.getCommand(cmd);
+ assert function != null;
+ out.append(leftPad);
+ out.append(cmd);
+ // TODO deal with long commands
+ out.append(spaces(helpDescPad - cmd.length()));
+ out.append(getShortDescription(function));
+ out.append('\n');
+ }
+ }
+
+ private static String spaces(int count) {
+ // Java 11
+ // return " ".repeat(count);
+ if (count <= 0)
+ return "";
+ else {
+ StringBuilder sb = new StringBuilder(count);
+ for (int i = 0; i < count; i++)
+ sb.append(' ');
+ return sb.toString();
+ }
+ }
+}
--- /dev/null
+package org.argeo.api.cli;
+
+/** An exception indicating that help should be printed. */
+class PrintHelpRequestException extends RuntimeException {
+
+ private static final long serialVersionUID = -9029122270660656639L;
+
+ private String commandName;
+ private volatile CommandsCli commandsCli;
+
+ public PrintHelpRequestException(String commandName, CommandsCli commandsCli) {
+ super();
+ this.commandName = commandName;
+ this.commandsCli = commandsCli;
+ }
+
+ public static long getSerialversionuid() {
+ return serialVersionUID;
+ }
+
+ public String getCommandName() {
+ return commandName;
+ }
+
+ public CommandsCli getCommandsCli() {
+ return commandsCli;
+ }
+
+}
--- /dev/null
+/** Command line API. */
+package org.argeo.api.cli;
\ No newline at end of file
import org.argeo.api.cms.CmsConstants;
import org.argeo.api.cms.CmsSession;
import org.argeo.api.cms.CmsSessionId;
+import org.argeo.api.cms.auth.ImpliedByPrincipal;
+import org.argeo.api.cms.auth.RoleNameUtils;
+import org.argeo.api.cms.auth.SystemRole;
import org.argeo.cms.internal.auth.CmsSessionImpl;
-import org.argeo.cms.internal.auth.ImpliedByPrincipal;
import org.argeo.cms.internal.runtime.CmsContextImpl;
import org.argeo.cms.util.CurrentSubject;
import org.osgi.service.useradmin.Authorization;
+++ /dev/null
-package org.argeo.cms;
-
-import static org.argeo.api.acr.RuntimeNamespaceContext.getNamespaceContext;
-
-import javax.xml.namespace.QName;
-
-import org.argeo.api.acr.ArgeoNamespace;
-import org.argeo.api.acr.NamespaceUtils;
-import org.argeo.cms.directory.ldap.LdapNameUtils;
-
-/** Simplifies analysis of system roles. */
-public class RoleNameUtils {
- public static String getLastRdnValue(String dn) {
- return LdapNameUtils.getLastRdnValue(dn);
-// // we don't use LdapName for portability with Android
-// // TODO make it more robust
-// String[] parts = dn.split(",");
-// String[] rdn = parts[0].split("=");
-// return rdn[1];
- }
-
- public static QName getLastRdnAsName(String dn) {
- String cn = getLastRdnValue(dn);
- QName roleName = NamespaceUtils.parsePrefixedName(getNamespaceContext(), cn);
- return roleName;
- }
-
- public static boolean isSystemRole(QName roleName) {
- return roleName.getNamespaceURI().equals(ArgeoNamespace.ROLE_NAMESPACE_URI);
- }
-
- public static String getParent(String dn) {
- int index = dn.indexOf(',');
- return dn.substring(index + 1);
- }
-
- /** Up two levels. */
- public static String getContext(String dn) {
- return getParent(getParent(dn));
- }
-}
+++ /dev/null
-package org.argeo.cms;
-
-import java.util.Set;
-
-import javax.security.auth.Subject;
-import javax.xml.namespace.QName;
-
-import org.argeo.api.cms.CmsConstants;
-import org.argeo.cms.internal.auth.ImpliedByPrincipal;
-
-/** A programmatic role. */
-public interface SystemRole {
- QName qName();
-
- /** Whether this role is implied for this authenticated user. */
- default boolean implied(Subject subject, String context) {
- return implied(qName(), subject, context);
- }
-
- /** Whether this role is implied for this distinguished name. */
- default boolean implied(String dn, String context) {
- String roleContext = RoleNameUtils.getContext(dn);
- QName roleName = RoleNameUtils.getLastRdnAsName(dn);
- return roleContext.equalsIgnoreCase(context) && qName().equals(roleName);
- }
-
- /**
- * Whether this role is implied for this authenticated subject. If context is
- * <code>null</code>, it is not considered; this should be used to build user
- * interfaces, but not to authorise.
- */
- static boolean implied(QName name, Subject subject, String context) {
- Set<ImpliedByPrincipal> roles = subject.getPrincipals(ImpliedByPrincipal.class);
- for (ImpliedByPrincipal role : roles) {
- if (role.isSystemRole()) {
- if (role.getRoleName().equals(name)) {
- // !! if context is not specified, it is considered irrelevant
- if (context == null)
- return true;
- if (role.getContext().equalsIgnoreCase(context)
- || role.getContext().equals(CmsConstants.NODE_BASEDN))
- return true;
- }
- }
- }
- return false;
- }
-}
import org.argeo.cms.util.LangUtils;
/** Partial reference implementation of a {@link ProvidedContent}. */
-public abstract class AbstractContent extends AbstractMap<QName, Object> implements ProvidedContent {
+public abstract class AbstractContent extends AbstractMap<QName, Object> implements CmsContent {
private final ProvidedSession session;
// cache
if (CrName.root.qName().equals(name))
continue ancestors;
- path.append('/');
+ path.append(PATH_SEPARATOR);
path.append(NamespaceUtils.toPrefixedName(name));
int siblingIndex = c.getSiblingIndex();
if (siblingIndex != 1)
--- /dev/null
+package org.argeo.cms.acr;
+
+import java.util.Objects;
+
+import org.argeo.api.acr.Content;
+import org.argeo.api.acr.spi.ProvidedContent;
+
+/** A content within a CMS system. */
+public interface CmsContent extends ProvidedContent {
+
+ /**
+ * Split a path (with {@link Content#PATH_SEPARATOR} separator) in an array of
+ * length 2, the first part being the parent path (which could be either
+ * absolute or relative), the second one being the last segment, (guaranteed to
+ * be without a '/').
+ */
+ static String[] getParentPath(String path) {
+ if (path == null)
+ throw new IllegalArgumentException("Path cannot be null");
+ if (path.length() == 0)
+ throw new IllegalArgumentException("Path cannot be empty");
+ ContentUtils.checkDoubleSlash(path);
+ int parentIndex = path.lastIndexOf(PATH_SEPARATOR);
+ if (parentIndex == path.length() - 1) {// trailing '/'
+ path = path.substring(0, path.length() - 1);
+ parentIndex = path.lastIndexOf(PATH_SEPARATOR);
+ }
+
+ if (parentIndex == -1) // no '/'
+ return new String[] { "", path };
+
+ return new String[] { parentIndex != 0 ? path.substring(0, parentIndex) : ContentUtils.PATH_SEPARATOR_STRING,
+ path.substring(parentIndex + 1) };
+ }
+
+ /**
+ * Constructs a relative path between a base path and a given path.
+ *
+ * @throws IllegalArgumentException if the base path is not an ancestor of the
+ * path
+ */
+ static String relativize(String basePath, String path) throws IllegalArgumentException {
+ Objects.requireNonNull(basePath);
+ Objects.requireNonNull(path);
+ if (!path.startsWith(basePath))
+ throw new IllegalArgumentException(basePath + " is not an ancestor of " + path);
+ String relativePath = path.substring(basePath.length());
+ if (relativePath.length() > 0 && relativePath.charAt(0) == PATH_SEPARATOR)
+ relativePath = relativePath.substring(1);
+ return relativePath;
+ }
+
+}
package org.argeo.cms.acr;
+import static java.lang.System.Logger.Level.ERROR;
+
import java.net.MalformedURLException;
+import java.net.URI;
import java.net.URL;
import java.util.Objects;
Objects.requireNonNull(namespace);
this.namespace = namespace;
if (resourceFileName != null) {
- resource = getClass().getResource(RESOURCE_BASE + resourceFileName);
- Objects.requireNonNull(resource);
+ // resource = getClass().getResource(RESOURCE_BASE + resourceFileName);
+ try {
+ // FIXME workaround when in nested OSGi frameworks
+ resource = URI.create("platform:/plugin/org.argeo.cms" + RESOURCE_BASE + resourceFileName).toURL();
+ } catch (MalformedURLException e) {
+ resource = null;
+ System.getLogger(CmsContentNamespace.class.getName()).log(ERROR,
+ "Cannot load " + resourceFileName + ": " + e.getMessage());
+ // throw new IllegalArgumentException("Cannot convert " + resourceFileName + "
+ // to URL");
+ }
+ // Objects.requireNonNull(resource);
}
if (publicUrl != null)
try {
throw new IllegalArgumentException(path + " is not an absolute path");
ContentProvider contentProvider = contentRepository.getMountManager().findContentProvider(path);
String mountPath = contentProvider.getMountPath();
- String relativePath = ContentUtils.relativize(mountPath, path);
+ String relativePath = CmsContent.relativize(mountPath, path);
ProvidedContent content = contentProvider.get(CmsContentSession.this, relativePath);
return content;
}
throw new IllegalArgumentException(path + " is not an absolute path");
ContentProvider contentProvider = contentRepository.getMountManager().findContentProvider(path);
String mountPath = contentProvider.getMountPath();
- String relativePath = ContentUtils.relativize(mountPath, path);
+ String relativePath = CmsContent.relativize(mountPath, path);
return contentProvider.exists(this, relativePath);
}
*/
@Override
public Content getMountPoint(String path) {
- String[] parent = ContentUtils.getParentPath(path);
+ String[] parent = CmsContent.getParentPath(path);
ProvidedContent mountParent = (ProvidedContent) get(parent[0]);
// Content mountPoint = mountParent.getProvider().get(CmsContentSession.this, null, path);
return mountParent.getMountPoint(parent[1]);
+++ /dev/null
-package org.argeo.cms.acr;
-
-import java.net.Inet4Address;
-import java.net.Inet6Address;
-import java.net.InetAddress;
-import java.net.InterfaceAddress;
-import java.net.NetworkInterface;
-import java.net.SocketException;
-import java.util.BitSet;
-import java.util.Enumeration;
-
-import org.argeo.api.cms.CmsLog;
-import org.argeo.api.uuid.ConcurrentUuidFactory;
-import org.argeo.api.uuid.NodeIdSupplier;
-import org.argeo.api.uuid.UuidBinaryUtils;
-
-public class CmsUuidFactory extends ConcurrentUuidFactory {
- private final static CmsLog log = CmsLog.getLog(CmsUuidFactory.class);
-
- public CmsUuidFactory(byte[] nodeId) {
- super(0, nodeId);
- assert createTimeUUID().node() == BitSet.valueOf(toNodeIdBytes(nodeId, 0)).toLongArray()[0];
- }
-
- public CmsUuidFactory() {
- this(getIpBytes());
- }
-
- /** Returns an SHA1 digest of one of the IP addresses. */
- protected static byte[] getIpBytes() {
- Enumeration<NetworkInterface> netInterfaces = null;
- try {
- netInterfaces = NetworkInterface.getNetworkInterfaces();
- } catch (SocketException e) {
- throw new IllegalStateException(e);
- }
- if (netInterfaces == null)
- throw new IllegalStateException("No interfaces");
-
- InetAddress selectedIpv6 = null;
- InetAddress selectedIpv4 = null;
- netInterfaces: while (netInterfaces.hasMoreElements()) {
- NetworkInterface netInterface = netInterfaces.nextElement();
- byte[] hardwareAddress = null;
- try {
- hardwareAddress = netInterface.getHardwareAddress();
- if (hardwareAddress != null) {
- // first IPv6
- addr: for (InterfaceAddress addr : netInterface.getInterfaceAddresses()) {
- InetAddress ip = addr.getAddress();
- if (ip instanceof Inet6Address) {
- Inet6Address ipv6 = (Inet6Address) ip;
- if (ipv6.isAnyLocalAddress() || ipv6.isLinkLocalAddress() || ipv6.isLoopbackAddress())
- continue addr;
- selectedIpv6 = ipv6;
- break netInterfaces;
- }
-
- }
- // then IPv4
- addr: for (InterfaceAddress addr : netInterface.getInterfaceAddresses()) {
- InetAddress ip = addr.getAddress();
- if (ip instanceof Inet4Address) {
- Inet4Address ipv4 = (Inet4Address) ip;
- if (ipv4.isAnyLocalAddress() || ipv4.isLinkLocalAddress() || ipv4.isLoopbackAddress())
- continue addr;
- selectedIpv4 = ipv4;
- }
-
- }
- }
- } catch (SocketException e) {
- throw new IllegalStateException(e);
- }
- }
- InetAddress selectedIp = selectedIpv6 != null ? selectedIpv6 : selectedIpv4;
- if (selectedIp == null) {
- log.warn("No IP address found, using a random node id for UUID generation");
- return NodeIdSupplier.randomNodeId();
- }
- byte[] digest = sha1(selectedIp.getAddress());
- log.info("Use IP " + selectedIp + " hashed as " + UuidBinaryUtils.toHexString(digest) + " as node id");
- byte[] nodeId = toNodeIdBytes(digest, 0);
- // marks that this is not based on MAC address
- forceToNoMacAddress(nodeId, 0);
- return nodeId;
- }
-
-}
package org.argeo.cms.acr;
+import static org.argeo.api.acr.Content.PATH_SEPARATOR;
+
import java.io.PrintStream;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
-import java.util.Objects;
import java.util.StringJoiner;
import java.util.StringTokenizer;
import java.util.function.BiConsumer;
import org.argeo.api.cms.CmsConstants;
import org.argeo.api.cms.CmsSession;
import org.argeo.api.cms.directory.CmsDirectory;
+import org.argeo.api.cms.directory.CmsRole;
import org.argeo.api.cms.directory.CmsUserManager;
import org.argeo.api.cms.directory.HierarchyUnit;
import org.argeo.api.cms.directory.UserDirectory;
import org.argeo.cms.util.CurrentSubject;
-import org.osgi.service.useradmin.Role;
/** Utilities and routines around {@link Content}. */
public class ContentUtils {
+ // Optimisations
+ static final String PATH_SEPARATOR_STRING = Character.toString(PATH_SEPARATOR);
+ private static final String DOUBLE_PATH_SEPARATOR = PATH_SEPARATOR_STRING + PATH_SEPARATOR_STRING;
+
public static void traverse(Content content, BiConsumer<Content, Integer> doIt) {
traverse(content, doIt, (Integer) null);
}
sb.append(" ");
}
String prefix = sb.toString();
- out.println(prefix + content.getName());
- for (QName key : content.keySet()) {
- out.println(prefix + " " + key + "=" + content.get(key));
- }
+ String txt = "";
if (printText) {
if (content.hasText()) {
- out.println("<![CDATA[" + content.getText().trim() + "]]>");
+ final int MAX_LENGTH = 64;
+ txt = content.getText().trim();
+ if (txt.length() > MAX_LENGTH)
+ txt = txt.substring(0, 64) + " ...";
+ txt = " : " + txt;
}
}
+ out.println(prefix + content.getName() + txt);
+ for (QName key : content.keySet()) {
+ out.println(prefix + " " + key + "=" + content.get(key));
+ }
}
// public static <T> boolean isString(T t) {
// return t instanceof String;
// }
- public static final char SLASH = '/';
- public static final String SLASH_STRING = Character.toString(SLASH);
- public static final String EMPTY = "";
-
- /**
- * Split a path (with '/' separator) in an array of length 2, the first part
- * being the parent path (which could be either absolute or relative), the
- * second one being the last segment, (guaranteed to be without a '/').
- */
- public static String[] getParentPath(String path) {
- if (path == null)
- throw new IllegalArgumentException("Path cannot be null");
- if (path.length() == 0)
- throw new IllegalArgumentException("Path cannot be empty");
- checkDoubleSlash(path);
- int parentIndex = path.lastIndexOf(SLASH);
- if (parentIndex == path.length() - 1) {// trailing '/'
- path = path.substring(0, path.length() - 1);
- parentIndex = path.lastIndexOf(SLASH);
- }
-
- if (parentIndex == -1) // no '/'
- return new String[] { EMPTY, path };
-
- return new String[] { parentIndex != 0 ? path.substring(0, parentIndex) : "" + SLASH,
- path.substring(parentIndex + 1) };
- }
-
public static String toPath(List<String> segments) {
// TODO checks
- StringJoiner sj = new StringJoiner("/");
+ StringJoiner sj = new StringJoiner(PATH_SEPARATOR_STRING);
segments.forEach((s) -> sj.add(s));
return sj.toString();
}
- public static List<String> toPathSegments(String path) {
+ static List<String> toPathSegments(String path) {
List<String> res = new ArrayList<>();
- if (EMPTY.equals(path) || Content.ROOT_PATH.equals(path))
+ if ("".equals(path) || Content.ROOT_PATH.equals(path))
return res;
collectPathSegments(path, res);
return res;
}
- private static void collectPathSegments(String path, List<String> segments) {
- String[] parent = getParentPath(path);
- if (EMPTY.equals(parent[1])) // root
+ static void collectPathSegments(String path, List<String> segments) {
+ String[] parent = CmsContent.getParentPath(path);
+ if ("".equals(parent[1])) // root
return;
segments.add(0, parent[1]);
- if (EMPTY.equals(parent[0])) // end
+ if ("".equals(parent[0])) // end
return;
collectPathSegments(parent[0], segments);
}
- public static void checkDoubleSlash(String path) {
- if (path.contains(SLASH + "" + SLASH))
+ static void checkDoubleSlash(String path) {
+ if (path.contains(DOUBLE_PATH_SEPARATOR))
throw new IllegalArgumentException("Path " + path + " contains //");
}
* DIRECTORY
*/
- public static Content roleToContent(CmsUserManager userManager, ContentSession contentSession, Role role) {
+ public static Content roleToContent(CmsUserManager userManager, ContentSession contentSession, CmsRole role) {
UserDirectory userDirectory = userManager.getDirectory(role);
String path = directoryPath(userDirectory) + userDirectory.getRolePath(role);
Content content = contentSession.get(path);
public static Content hierarchyUnitToContent(ContentSession contentSession, HierarchyUnit hierarchyUnit) {
CmsDirectory directory = hierarchyUnit.getDirectory();
- StringJoiner relativePath = new StringJoiner(SLASH_STRING);
+ StringJoiner relativePath = new StringJoiner(PATH_SEPARATOR_STRING);
buildHierarchyUnitPath(hierarchyUnit, relativePath);
String path = directoryPath(directory) + relativePath.toString();
Content content = contentSession.get(path);
/** The path to this {@link CmsDirectory}. Ends with a /. */
private static String directoryPath(CmsDirectory directory) {
- return CmsContentRepository.DIRECTORY_BASE + SLASH + directory.getName() + SLASH;
+ return CmsContentRepository.DIRECTORY_BASE + PATH_SEPARATOR + directory.getName() + PATH_SEPARATOR;
}
/** Recursively build a relative path of a {@link HierarchyUnit}. */
return content;
}
} else {
- String[] parentPath = getParentPath(path);
+ String[] parentPath = CmsContent.getParentPath(path);
Content parent = createCollections(session, parentPath[0]);
Content content = parent.add(parentPath[1], DName.collection.qName());
return content;
return CurrentSubject.callAs(cmsSession.getSubject(), () -> contentRepository.get());
}
- /**
- * Constructs a relative path between a base path and a given path.
- *
- * @throws IllegalArgumentException if the base path is not an ancestor of the
- * path
- */
- public static String relativize(String basePath, String path) throws IllegalArgumentException {
- Objects.requireNonNull(basePath);
- Objects.requireNonNull(path);
- if (!path.startsWith(basePath))
- throw new IllegalArgumentException(basePath + " is not an ancestor of " + path);
- String relativePath = path.substring(basePath.length());
- if (relativePath.length() > 0 && relativePath.charAt(0) == '/')
- relativePath = relativePath.substring(1);
- return relativePath;
- }
-
/** A path in the node repository */
public static String getDataPath(Content node) {
// TODO make it more configurable?
partitions.put(mountPath, contentProvider);
if ("/".equals(mountPath))// root
return;
- String[] parentPath = ContentUtils.getParentPath(mountPath);
+ String[] parentPath = CmsContent.getParentPath(mountPath);
Content parent = systemSession.get(parentPath[0]);
Content mount = parent.add(parentPath[1]);
mount.put(CrName.mount.qName(), "true");
String mountPath = floorEntry.getKey();
if (!path.startsWith(mountPath)) {
// FIXME make it more robust and find when there is no content provider
- String[] parent = ContentUtils.getParentPath(path);
+ String[] parent = CmsContent.getParentPath(path);
return findContentProvider(parent[0]);
// throw new IllegalArgumentException("Path " + path + " doesn't have a content
// provider");
import javax.xml.validation.Validator;
import org.apache.xerces.impl.xs.XSImplementationImpl;
-import org.apache.xerces.impl.xs.util.StringListImpl;
import org.apache.xerces.jaxp.DocumentBuilderFactoryImpl;
import org.apache.xerces.xs.StringList;
import org.apache.xerces.xs.XSAttributeDeclaration;
// cached
private Schema schema;
private DocumentBuilderFactory documentBuilderFactory;
- private XSModel xsModel;
+ // private XSModel xsModel;
private SortedMap<QName, Map<QName, CrAttributeType>> types;
private boolean validating = false;
List<StreamSource> sourcesToUse = new ArrayList<>();
for (URL sourceUrl : sources) {
sourcesToUse.add(new StreamSource(sourceUrl.toExternalForm()));
+// try {
+// sourcesToUse.add(new StreamSource(sourceUrl.openStream()));
+// } catch (IOException e) {
+// log.error("Cannot open schema source " + sourceUrl);
+// }
}
schema = schemaFactory.newSchema(sourcesToUse.toArray(new Source[sourcesToUse.size()]));
// for (StreamSource source : sourcesToUse) {
for (URL sourceUrl : sources) {
systemIds.add(sourceUrl.toExternalForm());
}
- StringList sl = new StringListImpl(systemIds.toArray(new String[systemIds.size()]), systemIds.size());
- xsModel = xsLoader.loadURIList(sl);
+ StringList sl = xsImplementation.createStringList(systemIds.toArray(new String[systemIds.size()]));
+ XSModel xsModel = xsLoader.loadURIList(sl);
// types
// XSNamedMap map = xsModel.getComponents(XSConstants.ELEMENT_DECLARATION);
// QName type = new QName(eDec.getNamespace(), eDec.getName());
// types.add(type);
// }
- collectTypes();
-
+ collectTypes(xsModel);
+
log.debug("Created XS model");
+ // printTypes();
}
} catch (XSException | SAXException e) {
throw new IllegalStateException("Cannot reload types", e);
}
}
- private void collectTypes() {
+ private void collectTypes(XSModel xsModel) {
types.clear();
// elements
XSNamedMap topLevelElements = xsModel.getComponents(XSConstants.ELEMENT_DECLARATION);
}
}
- public void printTypes() {
+ void printTypes() {
+ for (QName type : types.keySet()) {
+ Map<QName, CrAttributeType> attrs = types.get(type);
+ System.out.println("## " + type);
+ for (QName attr : attrs.keySet()) {
+ System.out.println(" " + attr + " : " + attrs.get(attr));
+ }
+ }
+ }
+
+ void printTypes(XSModel xsModel) {
if (xsModel != null)
try {
import org.argeo.api.acr.spi.ContentProvider;
import org.argeo.api.acr.spi.ProvidedSession;
import org.argeo.cms.acr.AbstractContent;
-import org.argeo.cms.acr.ContentUtils;
+import org.argeo.cms.acr.CmsContent;
import org.argeo.cms.dav.DavResponse;
import org.argeo.cms.http.HttpStatus;
@Override
public QName getName() {
- String fileName = ContentUtils.getParentPath(uri.getPath())[1];
+ String fileName = CmsContent.getParentPath(uri.getPath())[1];
ContentName name = NamespaceUtils.parsePrefixedName(provider, fileName);
return name;
}
@Override
public Content getParent() {
try {
- String parentPath = ContentUtils.getParentPath(uri.getPath())[0];
+ String parentPath = CmsContent.getParentPath(uri.getPath())[0];
URI parentUri = new URI(uri.getScheme(), uri.getHost(), parentPath, null);
return provider.getDavContent(getSession(), parentUri);
} catch (URISyntaxException e) {
import org.argeo.api.acr.spi.ContentProvider;
import org.argeo.api.acr.spi.ProvidedContent;
import org.argeo.api.acr.spi.ProvidedSession;
+import org.argeo.api.cms.directory.CmsUser;
import org.argeo.api.cms.directory.CmsUserManager;
import org.argeo.api.cms.directory.HierarchyUnit;
import org.argeo.api.cms.directory.UserDirectory;
import org.argeo.cms.acr.AbstractSimpleContentProvider;
import org.argeo.cms.acr.ContentUtils;
-import org.osgi.service.useradmin.User;
/** A {@link ContentProvider} based on a {@link CmsUserManager} service. */
public class DirectoryContentProvider extends AbstractSimpleContentProvider<CmsUserManager> {
// } catch (InvalidNameException e) {
// throw new IllegalStateException("Cannot interpret " + segments + " as DN", e);
// }
- User user = (User) userDirectory.getRoleByPath(pathWithinUserDirectory);
+ CmsUser user = (CmsUser) userDirectory.getRoleByPath(pathWithinUserDirectory);
if (user != null) {
HierarchyUnit parent = userDirectory.getHierarchyUnit(user);
return new RoleContent(session, this, new HierarchyUnitContent(session, this, parent), user);
import org.argeo.api.acr.Content;
import org.argeo.api.acr.ContentName;
-import org.argeo.api.acr.CrName;
import org.argeo.api.acr.DName;
import org.argeo.api.acr.spi.ProvidedSession;
import org.argeo.api.cms.directory.CmsDirectory;
+import org.argeo.api.cms.directory.CmsRole;
import org.argeo.api.cms.directory.HierarchyUnit;
import org.argeo.api.cms.directory.UserDirectory;
-import org.osgi.service.useradmin.Role;
class HierarchyUnitContent extends AbstractDirectoryContent {
private HierarchyUnit hierarchyUnit;
for (HierarchyUnit hu : hierarchyUnit.getDirectHierarchyUnits(false))
lst.add(new HierarchyUnitContent(getSession(), provider, hu));
- for (Role role : ((UserDirectory) hierarchyUnit.getDirectory()).getHierarchyUnitRoles(hierarchyUnit, null,
+ for (CmsRole role : ((UserDirectory) hierarchyUnit.getDirectory()).getHierarchyUnitRoles(hierarchyUnit, null,
false))
lst.add(new RoleContent(getSession(), provider, this, role));
return lst.iterator();
import org.argeo.api.acr.Content;
import org.argeo.api.acr.ContentName;
import org.argeo.api.acr.spi.ProvidedSession;
+import org.argeo.api.cms.directory.CmsRole;
import org.argeo.api.cms.directory.UserDirectory;
import org.osgi.service.useradmin.Role;
class RoleContent extends AbstractDirectoryContent {
private HierarchyUnitContent parent;
- private Role role;
+ private CmsRole role;
public RoleContent(ProvidedSession session, DirectoryContentProvider provider, HierarchyUnitContent parent,
- Role role) {
+ CmsRole role) {
super(session, provider);
this.parent = parent;
this.role = role;
}
@Override
+ @Deprecated
Dictionary<String, Object> doGetProperties() {
return role.getProperties();
}
@SuppressWarnings("unchecked")
@Override
public <A> A adapt(Class<A> clss) {
- if (Role.class.isAssignableFrom(clss))
+ if (CmsRole.class.isAssignableFrom(clss))
return (A) role;
+ // TODO do we need this?
+// if (Role.class.isAssignableFrom(clss))
+// return (A) role;
return super.adapt(clss);
}
import org.argeo.api.acr.spi.ProvidedContent;
import org.argeo.api.acr.spi.ProvidedSession;
import org.argeo.cms.acr.AbstractContent;
-import org.argeo.cms.acr.ContentUtils;
+import org.argeo.cms.acr.CmsContent;
import org.argeo.cms.util.FsUtils;
/** Content persisted as a filesystem {@link Path}. */
String mountPath = provider.getMountPath();
if (mountPath == null || mountPath.equals("/"))
return null;
- String[] parent = ContentUtils.getParentPath(mountPath);
+ String[] parent = CmsContent.getParentPath(mountPath);
return getSession().get(parent[0]);
}
return new FsContent(this, path.getParent());
import org.argeo.api.acr.spi.ProvidedContent;
import org.argeo.api.acr.spi.ProvidedSession;
import org.argeo.cms.acr.AbstractContent;
-import org.argeo.cms.acr.ContentUtils;
+import org.argeo.cms.acr.CmsContent;
import org.w3c.dom.Attr;
import org.w3c.dom.DOMException;
import org.w3c.dom.Document;
if (Content.ROOT_PATH.equals(mountPath)) {
return null;
}
- String[] parent = ContentUtils.getParentPath(mountPath);
- if (ContentUtils.EMPTY.equals(parent[0]))
+ String[] parent = CmsContent.getParentPath(mountPath);
+ if ("".equals(parent[0]))
return null;
return getSession().get(parent[0]);
}
protected NodeList findContent(String relativePath) {
if (relativePath.startsWith("/"))
throw new IllegalArgumentException("Relative path cannot start with /");
- String xPathExpression = '/' + relativePath;
- if (Content.ROOT_PATH.equals(mountPath)) // repository root
- xPathExpression = "/" + CrName.root.get() + xPathExpression;
+ String xPathExpression;
+ if (Content.ROOT_PATH.equals(mountPath)) {// repository root
+ xPathExpression = "/" + CrName.root.get() + '/' + relativePath;
+ } else {
+ String documentNodeName = document.getDocumentElement().getNodeName();
+ xPathExpression = '/' + documentNodeName + '/' + relativePath;
+ }
try {
NodeList nodes = (NodeList) xPath.get().evaluate(xPathExpression, document, XPathConstants.NODESET);
return nodes;
import org.argeo.api.cms.CmsSession;
import org.argeo.api.cms.CmsSessionId;
import org.argeo.api.cms.DataAdminPrincipal;
+import org.argeo.api.cms.auth.ImpliedByPrincipal;
import org.argeo.cms.internal.auth.CmsSessionImpl;
-import org.argeo.cms.internal.auth.ImpliedByPrincipal;
import org.argeo.cms.internal.auth.RemoteCmsSessionImpl;
import org.argeo.cms.internal.runtime.CmsContextImpl;
import org.argeo.cms.osgi.useradmin.AuthenticatingUser;
+++ /dev/null
-package org.argeo.cms.auth;
-
-import javax.xml.namespace.QName;
-
-import org.argeo.api.acr.ArgeoNamespace;
-import org.argeo.api.acr.ContentName;
-import org.argeo.cms.SystemRole;
-
-/** Standard CMS system roles. */
-public enum CmsRole implements SystemRole {
- userAdmin, //
- groupAdmin, //
- //
- ;
-
- private final static String QUALIFIER = "cms.";
-
- private final ContentName name;
-
- CmsRole() {
- name = new ContentName(ArgeoNamespace.ROLE_NAMESPACE_URI, QUALIFIER + name());
- }
-
- @Override
- public QName qName() {
- return name;
- }
-
- @Override
- public String toString() {
- return name.toPrefixedString();
- }
-}
--- /dev/null
+package org.argeo.cms.auth;
+
+import javax.xml.namespace.QName;
+
+import org.argeo.api.acr.ArgeoNamespace;
+import org.argeo.api.acr.ContentName;
+import org.argeo.api.cms.auth.SystemRole;
+
+/** Standard CMS system roles. */
+public enum CmsSystemRole implements SystemRole {
+ userAdmin, //
+ groupAdmin, //
+ //
+ ;
+
+ private final static String QUALIFIER = "cms.";
+
+ private final ContentName name;
+
+ CmsSystemRole() {
+ name = new ContentName(ArgeoNamespace.ROLE_NAMESPACE_URI, QUALIFIER + name());
+ }
+
+ @Override
+ public QName qName() {
+ return name;
+ }
+
+ @Override
+ public String toString() {
+ return name.toPrefixedString();
+ }
+}
CmsAuthUtils.addAuthorization(subject, authorization);
} else {
// next step with user admin will properly populate
+ authorization = new SingleUserAuthorization(authorizationName);
+ CmsAuthUtils.addAuthorization(subject, authorization);
}
// Add standard Java OS login
import org.osgi.service.useradmin.UserAdmin;
/** Centralise common patterns to manage users with a {@link UserAdmin} */
+@Deprecated
+// TODO use CmsRole after migrating to qualified properties
public class UserAdminUtils {
// CURRENTUSER HELPERS
return getUserDisplayName(user);
}
+ public static String getUserDisplayName(org.argeo.api.cms.directory.CmsRole user) {
+ return getUserDisplayName((Role) user);
+ }
+
public static String getUserDisplayName(Role user) {
String dName = getProperty(user, LdapAttr.displayName.name());
if (isEmpty(dName))
package org.argeo.cms.directory.ldap;
-import static org.argeo.cms.directory.ldap.LdapNameUtils.toLdapName;
+import static org.argeo.api.acr.ldap.LdapNameUtils.toLdapName;
import java.io.File;
import java.net.URI;
import javax.transaction.xa.XAResource;
import org.argeo.api.acr.ldap.LdapAttr;
+import org.argeo.api.acr.ldap.LdapNameUtils;
import org.argeo.api.acr.ldap.LdapObj;
import org.argeo.api.cms.directory.CmsDirectory;
import org.argeo.api.cms.directory.HierarchyUnit;
import javax.naming.directory.BasicAttribute;
import javax.naming.ldap.LdapName;
+import org.argeo.api.acr.QNamed;
import org.argeo.api.acr.ldap.LdapAttr;
import org.argeo.api.acr.ldap.LdapObj;
import org.argeo.api.cms.directory.DirectoryDigestUtils;
// modifiedAttributes = (Attributes) publishedAttributes.clone();
}
- public synchronized void publishAttributes(Attributes modifiedAttributes) {
-// publishedAttributes = modifiedAttributes;
- }
+// public synchronized void publishAttributes(Attributes modifiedAttributes) {
+//// publishedAttributes = modifiedAttributes;
+// }
/*
* PROPERTIES
return credentials;
}
+ protected String getPropertyAsString(QNamed key) {
+ return getPropertyAsString(key.localName());
+ }
+
+ protected String getPropertyAsString(String key) {
+ Object res = getProperties().get(key);
+ if (res == null)
+ return null;
+ return res.toString();
+ }
+
/*
* CREDENTIALS
*/
import javax.naming.ldap.Rdn;
import org.argeo.api.acr.ldap.LdapAttr;
+import org.argeo.api.acr.ldap.LdapNameUtils;
import org.argeo.api.acr.ldap.LdapObj;
import org.argeo.api.cms.directory.HierarchyUnit;
Attributes getAttributes();
- void publishAttributes(Attributes modifiedAttributes);
+ //void publishAttributes(Attributes modifiedAttributes);
List<LdapName> getReferences(String attributeId);
import javax.naming.ldap.LdapName;
import javax.naming.ldap.Rdn;
+import org.argeo.api.acr.ldap.LdapNameUtils;
import org.argeo.api.cms.directory.HierarchyUnit;
/** LDIF/LDAP based implementation of {@link HierarchyUnit}. */
+++ /dev/null
-package org.argeo.cms.directory.ldap;
-
-import javax.naming.InvalidNameException;
-import javax.naming.ldap.LdapName;
-import javax.naming.ldap.Rdn;
-
-/** Utilities to simplify using {@link LdapName}. */
-public class LdapNameUtils {
-
- public static LdapName relativeName(LdapName prefix, LdapName dn) {
- try {
- if (!dn.startsWith(prefix))
- throw new IllegalArgumentException("Prefix " + prefix + " not consistent with " + dn);
- LdapName res = (LdapName) dn.clone();
- for (int i = 0; i < prefix.size(); i++) {
- res.remove(0);
- }
- return res;
- } catch (InvalidNameException e) {
- throw new IllegalStateException("Cannot find realtive name", e);
- }
- }
-
- public static LdapName getParent(LdapName dn) {
- try {
- LdapName parent = (LdapName) dn.clone();
- parent.remove(parent.size() - 1);
- return parent;
- } catch (InvalidNameException e) {
- throw new IllegalArgumentException("Cannot get parent of " + dn, e);
- }
- }
-
- public static Rdn getParentRdn(LdapName dn) {
- if (dn.size() < 2)
- throw new IllegalArgumentException(dn + " has no parent");
- Rdn parentRdn = dn.getRdn(dn.size() - 2);
- return parentRdn;
- }
-
- public static LdapName toLdapName(String distinguishedName) {
- try {
- return new LdapName(distinguishedName);
- } catch (InvalidNameException e) {
- throw new IllegalArgumentException("Cannot parse " + distinguishedName + " as LDAP name", e);
- }
- }
-
- public static Rdn getLastRdn(LdapName dn) {
- return dn.getRdn(dn.size() - 1);
- }
-
- public static String getLastRdnAsString(LdapName dn) {
- return getLastRdn(dn).toString();
- }
-
- public static String getLastRdnValue(String dn) {
- return getLastRdnValue(toLdapName(dn));
- }
-
- public static String getLastRdnValue(LdapName dn) {
- return getLastRdn(dn).getValue().toString();
- }
-
- /** singleton */
- private LdapNameUtils() {
-
- }
-}
}
if (user == null)
throw new IllegalStateException("User to modify no found " + dn);
- user.publishAttributes(modifiedAttrs);
+ publishAttributes(dn, modifiedAttrs);
}
}
+ protected void publishAttributes(LdapName dn, Attributes modifiedAttributes) {
+ values.put(dn, modifiedAttributes);
+ }
+
@Override
public void commit(LdapEntryWorkingCopy wc) {
save();
import java.util.NavigableMap;
import java.util.TreeMap;
-import org.argeo.cms.acr.ContentUtils;
+import org.argeo.cms.acr.CmsContent;
import org.argeo.cms.http.HttpHeader;
import org.argeo.cms.http.HttpStatus;
import org.argeo.cms.util.StreamUtils;
String mountPath = entry.getKey();
if (!path.startsWith(mountPath)) {
// FIXME make it more robust and find when there is no content provider
- String[] parent = ContentUtils.getParentPath(path);
+ String[] parent = CmsContent.getParentPath(path);
return findBind(parent[0]);
}
return entry;
+++ /dev/null
-package org.argeo.cms.internal.auth;
-
-import java.security.Principal;
-import java.util.HashSet;
-import java.util.Set;
-
-import javax.xml.namespace.QName;
-
-import org.argeo.cms.RoleNameUtils;
-import org.osgi.service.useradmin.Authorization;
-
-/**
- * A {@link Principal} which has been implied by an {@link Authorization}. If it
- * is empty it means this is an additional identity, otherwise it lists the
- * users (typically the logged in user but possibly empty
- * {@link ImpliedByPrincipal}s) which have implied it. When an additional
- * identity is removed, the related {@link ImpliedByPrincipal}s can thus be
- * removed.
- */
-public final class ImpliedByPrincipal implements Principal {
- private final String name;
- private final QName roleName;
- private final boolean systemRole;
- private final String context;
-
- private Set<Principal> causes = new HashSet<Principal>();
-
- public ImpliedByPrincipal(String name, Principal userPrincipal) {
- this.name = name;
- roleName = RoleNameUtils.getLastRdnAsName(name);
- systemRole = RoleNameUtils.isSystemRole(roleName);
- context = RoleNameUtils.getContext(name);
- if (userPrincipal != null)
- causes.add(userPrincipal);
- }
-
- public String getName() {
- return name;
- }
-
- /*
- * OBJECT
- */
-
- public QName getRoleName() {
- return roleName;
- }
-
- public String getContext() {
- return context;
- }
-
- public boolean isSystemRole() {
- return systemRole;
- }
-
- @Override
- public int hashCode() {
- return name.hashCode();
- }
-
- @Override
- public boolean equals(Object obj) {
- if (obj instanceof ImpliedByPrincipal) {
- ImpliedByPrincipal that = (ImpliedByPrincipal) obj;
- // TODO check members too?
- return name.equals(that.name);
- }
- return false;
- }
-
- @Override
- public String toString() {
- return name.toString();
- }
-}
package org.argeo.cms.internal.osgi;
import java.security.AllPermission;
-import java.util.Dictionary;
-import org.argeo.api.cms.CmsLog;
+import org.argeo.api.cms.CmsState;
+import org.argeo.api.uuid.NodeIdSupplier;
+import org.argeo.cms.internal.runtime.CmsStateImpl;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
* bundle (and only it)
*/
public class CmsActivator implements BundleActivator {
- private final static CmsLog log = CmsLog.getLog(CmsActivator.class);
+// private final static CmsLog log = CmsLog.getLog(CmsActivator.class);
+ private final static String PROP_ARGEO_OSGI_PARENT_UUID = "argeo.osgi.parent.uuid";
// TODO make it configurable
private boolean hardened = false;
private static BundleContext bundleContext;
- void init() {
- }
-
- void destroy() {
- try {
- bundleContext = null;
-// this.logReaderService = null;
- } catch (Exception e) {
- log.error("CMS activator shutdown failed", e);
- }
-
- new GogoShellKiller().start();
- }
-
protected void initSecurity() {
// code-level permissions
String osgiSecurity = bundleContext.getProperty(Constants.FRAMEWORK_SECURITY);
}
- public static <T> void registerService(Class<T> clss, T service, Dictionary<String, ?> properties) {
- if (bundleContext != null) {
- bundleContext.registerService(clss, service, properties);
- }
-
- }
-
- public static <T> T getService(Class<T> clss) {
+// static <T> void registerService(Class<T> clss, T service, Dictionary<String, ?> properties) {
+// if (bundleContext != null) {
+// bundleContext.registerService(clss, service, properties);
+// }
+//
+// }
+//
+ static <T> T getService(Class<T> clss) {
if (bundleContext != null) {
return bundleContext.getService(bundleContext.getServiceReference(clss));
} else {
@Override
public void start(BundleContext bc) throws Exception {
bundleContext = bc;
-
- init();
-
+ CmsStateImpl cmsState = new CmsStateImpl();
+ cmsState.start();
+ bundleContext.registerService(new String[] { CmsState.class.getName(), NodeIdSupplier.class.getName() },
+ cmsState, null);
}
@Override
public void stop(BundleContext bc) throws Exception {
-
- destroy();
- bundleContext = null;
+ try {
+ CmsStateImpl cmsState = (CmsStateImpl) getService(CmsState.class);
+ String parentFrameworkuuid = bc.getProperty(PROP_ARGEO_OSGI_PARENT_UUID);
+ if (parentFrameworkuuid == null)
+ new GogoShellKiller().start();
+ cmsState.stop();
+ } finally {
+ bundleContext = null;
+ }
}
- public static BundleContext getBundleContext() {
+ static BundleContext getBundleContext() {
return bundleContext;
}
+ public static String getFrameworkProperty(String key) {
+ if (bundleContext == null)
+ return null;
+ return getBundleContext().getProperty(key);
+ }
}
import org.osgi.framework.Bundle;
import org.osgi.framework.Constants;
import org.osgi.framework.ServiceReference;
-import org.osgi.service.cm.ConfigurationAdmin;
import org.osgi.service.log.LogEntry;
import org.osgi.service.log.LogLevel;
import org.osgi.service.log.LogListener;
public class CmsOsgiLogger implements LogListener {
private final static String WHITEBOARD_PATTERN_PROP = "osgi.http.whiteboard.servlet.pattern";
private final static String CONTEXT_NAME_PROP = "contextName";
-
- private LogReaderService logReaderService;
-
-// /** Internal debug for development purposes. */
-// private static Boolean debug = false;
-
-// private Boolean disabled = false;
-//
-// private String level = null;
-
-// private Level log4jLevel = null;
-
-// private Properties configuration;
-
-// private AppenderImpl appender;
-// private BlockingQueue<LogEvent> events;
-// private LogDispatcherThread logDispatcherThread = new LogDispatcherThread();
-
-// private Integer maxLastEventsCount = 10 * 1000;
-//
-// /** Marker to prevent stack overflow */
-// private ThreadLocal<Boolean> dispatching = new ThreadLocal<Boolean>() {
-//
-// @Override
-// protected Boolean initialValue() {
-// return false;
-// }
-// };
-
-// public CmsOsgiLogger(LogReaderService lrs) {
-// }
+ private LogReaderService logReaderService;
public void start() {
if (logReaderService != null) {
while (logEntries.hasMoreElements())
logged(logEntries.nextElement());
logReaderService.addLogListener(this);
-
- // configure log4j watcher
-// String log4jConfiguration = KernelUtils.getFrameworkProp("log4j.configuration");
-// if (log4jConfiguration != null && log4jConfiguration.startsWith("file:")) {
-// if (log4jConfiguration.contains("..")) {
-// if (log4jConfiguration.startsWith("file://"))
-// log4jConfiguration = log4jConfiguration.substring("file://".length());
-// else if (log4jConfiguration.startsWith("file:"))
-// log4jConfiguration = log4jConfiguration.substring("file:".length());
-// }
-// try {
-// Path log4jconfigPath;
-// if (log4jConfiguration.startsWith("file:"))
-// log4jconfigPath = Paths.get(new URI(log4jConfiguration));
-// else
-// log4jconfigPath = Paths.get(log4jConfiguration);
-// Thread log4jConfWatcher = new Log4jConfWatcherThread(log4jconfigPath);
-// log4jConfWatcher.start();
-// } catch (Exception e) {
-// stdErr("Badly formatted log4j configuration URI " + log4jConfiguration + ": " + e.getMessage());
-// }
-// }
}
-// try {
-//// events = new LinkedBlockingQueue<LogEvent>();
-////
-//// // if (layout != null)
-//// // setLayout(layout);
-//// // else
-//// // setLayout(new PatternLayout(pattern));
-////// appender = new AppenderImpl();
-//// reloadConfiguration();
-////// Logger.getRootLogger().addAppender(appender);
-////
-//// logDispatcherThread = new LogDispatcherThread();
-//// logDispatcherThread.start();
-// } catch (Exception e) {
-// throw new IllegalStateException("Cannot initialize log4j");
-// }
}
public void stop() throws Exception {
-// events.clear();
-// events = null;
-// logDispatcherThread.interrupt();
logReaderService.removeLogListener(this);
}
//
@Override
public void logged(LogEntry status) {
- CmsLog pluginLog = CmsLog.getLog(status.getBundle().getSymbolicName());
+ String loggerName = status.getBundle().getSymbolicName();
+ if (loggerName == null)
+ loggerName = "org.argeo.ext.osgi";
+ CmsLog pluginLog = CmsLog.getLog(loggerName);
LogLevel severity = status.getLogLevel();
if (severity.equals(LogLevel.ERROR) && pluginLog.isErrorEnabled()) {
// FIXME Fix Argeo TP
Object cn = sr.getProperty(CmsConstants.CN);
if (cn != null)
sb.append(" " + CmsConstants.CN + ": " + cn);
- Object factoryPid = sr.getProperty(ConfigurationAdmin.SERVICE_FACTORYPID);
- if (factoryPid != null)
- sb.append(" " + ConfigurationAdmin.SERVICE_FACTORYPID + ": " + factoryPid);
+// Object factoryPid = sr.getProperty(ConfigurationAdmin.SERVICE_FACTORYPID);
+// if (factoryPid != null)
+// sb.append(" " + ConfigurationAdmin.SERVICE_FACTORYPID + ": " + factoryPid);
// else {
// Object servicePid = sr.getProperty(Constants.SERVICE_PID);
// if (servicePid != null)
this.logReaderService = logReaderService;
}
-
- //
- // ARGEO LOGGER
- //
-
-// public synchronized void register(ArgeoLogListener listener, Integer numberOfPreviousEvents) {
-// String username = CurrentUser.getUsername();
-// if (username == null)
-// throw new IllegalStateException("Only authenticated users can register a log listener");
-//
-// if (!userListeners.containsKey(username)) {
-// List<ArgeoLogListener> lst = Collections.synchronizedList(new ArrayList<ArgeoLogListener>());
-// userListeners.put(username, lst);
-// }
-// userListeners.get(username).add(listener);
-// List<LogEvent> lastEvents = logDispatcherThread.getLastEvents(username, numberOfPreviousEvents);
-// for (LogEvent evt : lastEvents)
-// dispatchEvent(listener, evt);
-// }
-//
-// public synchronized void registerForAll(ArgeoLogListener listener, Integer numberOfPreviousEvents,
-// boolean everything) {
-// if (everything)
-// everythingListeners.add(listener);
-// else
-// allUsersListeners.add(listener);
-// List<LogEvent> lastEvents = logDispatcherThread.getLastEvents(null, numberOfPreviousEvents);
-// for (LogEvent evt : lastEvents)
-// if (everything || evt.getUsername() != null)
-// dispatchEvent(listener, evt);
-// }
-//
-// public synchronized void unregister(ArgeoLogListener listener) {
-// String username = CurrentUser.getUsername();
-// if (username == null)// FIXME
-// return;
-// if (!userListeners.containsKey(username))
-// throw new IllegalStateException("No user listeners " + listener + " registered for user " + username);
-// if (!userListeners.get(username).contains(listener))
-// throw new IllegalStateException("No user listeners " + listener + " registered for user " + username);
-// userListeners.get(username).remove(listener);
-// if (userListeners.get(username).isEmpty())
-// userListeners.remove(username);
-//
-// }
-//
-// public synchronized void unregisterForAll(ArgeoLogListener listener) {
-// everythingListeners.remove(listener);
-// allUsersListeners.remove(listener);
-// }
-
-// /** For development purpose, since using regular logging is not easy here */
-// private static void stdOut(Object obj) {
-// System.out.println(obj);
-// }
-//
-// private static void stdErr(Object obj) {
-// System.err.println(obj);
-// }
-//
-// private static void debug(Object obj) {
-// if (debug)
-// System.out.println(obj);
-// }
-//
-// private static boolean isInternalDebugEnabled() {
-// return debug;
-// }
-
- // public void setPattern(String pattern) {
- // this.pattern = pattern;
- // }
-
-// public void setDisabled(Boolean disabled) {
-// this.disabled = disabled;
-// }
-//
-// public void setLevel(String level) {
-// this.level = level;
-// }
-
-// public void setConfiguration(Properties configuration) {
-// this.configuration = configuration;
-// }
-//
-// public void updateConfiguration(Properties configuration) {
-// setConfiguration(configuration);
-// reloadConfiguration();
-// }
-//
-// public Properties getConfiguration() {
-// return configuration;
-// }
-//
-// /**
-// * Reloads configuration (if the configuration {@link Properties} is set)
-// */
-// protected void reloadConfiguration() {
-// if (configuration != null) {
-//// LogManager.resetConfiguration();
-//// PropertyConfigurator.configure(configuration);
-// }
-// }
-
-// protected synchronized void processLoggingEvent(LogEvent event) {
-// if (disabled)
-// return;
-//
-// if (dispatching.get())
-// return;
-//
-// if (level != null && !level.trim().equals("")) {
-//// if (log4jLevel == null || !log4jLevel.toString().equals(level))
-//// try {
-//// log4jLevel = Level.toLevel(level);
-//// } catch (Exception e) {
-//// System.err.println("Log4j level could not be set for level '" + level + "', resetting it to null.");
-//// e.printStackTrace();
-//// level = null;
-//// }
-////
-//// if (log4jLevel != null && !event.getLoggingEvent().getLevel().isGreaterOrEqual(log4jLevel)) {
-//// return;
-//// }
-// }
-//
-//// try {
-//// // admin listeners
-//// Iterator<ArgeoLogListener> everythingIt = everythingListeners.iterator();
-//// while (everythingIt.hasNext())
-//// dispatchEvent(everythingIt.next(), event);
-////
-//// if (event.getUsername() != null) {
-//// Iterator<ArgeoLogListener> allUsersIt = allUsersListeners.iterator();
-//// while (allUsersIt.hasNext())
-//// dispatchEvent(allUsersIt.next(), event);
-////
-//// if (userListeners.containsKey(event.getUsername())) {
-//// Iterator<ArgeoLogListener> userIt = userListeners.get(event.getUsername()).iterator();
-//// while (userIt.hasNext())
-//// dispatchEvent(userIt.next(), event);
-//// }
-//// }
-//// } catch (Exception e) {
-//// stdOut("Cannot process logging event");
-//// e.printStackTrace();
-//// }
-// }
-
-// protected void dispatchEvent(ArgeoLogListener logListener, LogEvent evt) {
-//// LoggingEvent event = evt.getLoggingEvent();
-//// logListener.appendLog(evt.getUsername(), event.getTimeStamp(), event.getLevel().toString(),
-//// event.getLoggerName(), event.getThreadName(), event.getMessage(), event.getThrowableStrRep());
-// }
-
-// private class AppenderImpl { // extends AppenderSkeleton {
-// public boolean requiresLayout() {
-// return false;
-// }
-//
-// public void close() {
-// }
-//
-//// @Override
-//// protected void append(LoggingEvent event) {
-//// if (events != null) {
-//// try {
-//// String username = CurrentUser.getUsername();
-//// events.put(new LogEvent(username, event));
-//// } catch (InterruptedException e) {
-//// // silent
-//// }
-//// }
-//// }
-//
-// }
-
-// private class LogDispatcherThread extends Thread {
-// /** encapsulated in order to simplify concurrency management */
-// private LinkedList<LogEvent> lastEvents = new LinkedList<LogEvent>();
-//
-// public LogDispatcherThread() {
-// super("Argeo Logging Dispatcher Thread");
-// }
-//
-// public void run() {
-// while (events != null) {
-// try {
-// LogEvent loggingEvent = events.take();
-// processLoggingEvent(loggingEvent);
-// addLastEvent(loggingEvent);
-// } catch (InterruptedException e) {
-// if (events == null)
-// return;
-// }
-// }
-// }
-//
-// protected synchronized void addLastEvent(LogEvent loggingEvent) {
-// if (lastEvents.size() >= maxLastEventsCount)
-// lastEvents.poll();
-// lastEvents.add(loggingEvent);
-// }
-//
-// public synchronized List<LogEvent> getLastEvents(String username, Integer maxCount) {
-// LinkedList<LogEvent> evts = new LinkedList<LogEvent>();
-// ListIterator<LogEvent> it = lastEvents.listIterator(lastEvents.size());
-// int count = 0;
-// while (it.hasPrevious() && (count < maxCount)) {
-// LogEvent evt = it.previous();
-// if (username == null || username.equals(evt.getUsername())) {
-// evts.push(evt);
-// count++;
-// }
-// }
-// return evts;
-// }
-// }
-
-// private class LogEvent {
-// private final String username;
-//// private final LoggingEvent loggingEvent;
-//
-// public LogEvent(String username) {
-// super();
-// this.username = username;
-//// this.loggingEvent = loggingEvent;
-// }
-//
-//// @Override
-//// public int hashCode() {
-//// return loggingEvent.hashCode();
-//// }
-////
-//// @Override
-//// public boolean equals(Object obj) {
-//// return loggingEvent.equals(obj);
-//// }
-////
-//// @Override
-//// public String toString() {
-//// return username + "@ " + loggingEvent.toString();
-//// }
-//
-// public String getUsername() {
-// return username;
-// }
-//
-//// public LoggingEvent getLoggingEvent() {
-//// return loggingEvent;
-//// }
-//
-// }
-//
-// private class Log4jConfWatcherThread extends Thread {
-// private Path log4jConfigurationPath;
-//
-// public Log4jConfWatcherThread(Path log4jConfigurationPath) {
-// super("Log4j Configuration Watcher");
-// try {
-// this.log4jConfigurationPath = log4jConfigurationPath.toRealPath();
-// } catch (IOException e) {
-// this.log4jConfigurationPath = log4jConfigurationPath.toAbsolutePath();
-// stdOut("Cannot determine real path for " + log4jConfigurationPath + ": " + e.getMessage());
-// }
-// }
-//
-// public void run() {
-// Path parentDir = log4jConfigurationPath.getParent();
-// try (final WatchService watchService = FileSystems.getDefault().newWatchService()) {
-// parentDir.register(watchService, StandardWatchEventKinds.ENTRY_MODIFY);
-// WatchKey wk;
-// watching: while ((wk = watchService.take()) != null) {
-// for (WatchEvent<?> event : wk.pollEvents()) {
-// final Path changed = (Path) event.context();
-// if (log4jConfigurationPath.equals(parentDir.resolve(changed))) {
-// if (isInternalDebugEnabled())
-// debug(log4jConfigurationPath + " has changed, reloading.");
-//// PropertyConfigurator.configure(log4jConfigurationPath.toUri().toURL());
-// }
-// }
-// // reset the key
-// boolean valid = wk.reset();
-// if (!valid) {
-// break watching;
-// }
-// }
-// } catch (IOException | InterruptedException e) {
-// stdErr("Log4j configuration watcher failed: " + e.getMessage());
-// }
-// }
-// }
}
return;
System.exit(0);
// No non-deamon threads left, forcibly halting the VM
- //Runtime.getRuntime().halt(0);
+ // Runtime.getRuntime().halt(0);
}
private ThreadGroup getRootThreadGroup(ThreadGroup tg) {
Thread[] threads = new Thread[rootThreadGroup.activeCount()];
rootThreadGroup.enumerate(threads, true);
for (Thread thread : threads) {
- if (thread.getName().equals("pipe-gosh --login --noshutdown"))
+ if (thread.getName() != null && thread.getName().equals("pipe-gosh --login --noshutdown"))
return thread;
}
return null;
import org.osgi.framework.BundleContext;
import org.osgi.framework.FrameworkUtil;
import org.osgi.framework.ServicePermission;
-import org.osgi.service.cm.ConfigurationPermission;
import org.osgi.service.condpermadmin.BundleLocationCondition;
import org.osgi.service.condpermadmin.ConditionInfo;
import org.osgi.service.condpermadmin.ConditionalPermissionAdmin;
// DS
Bundle dsBundle = findBundle("org.eclipse.equinox.ds");
- update.getConditionalPermissionInfos().add(permissionAdmin.newConditionalPermissionInfo(null,
- new ConditionInfo[] { new ConditionInfo(BundleLocationCondition.class.getName(),
- new String[] { dsBundle.getLocation() }) },
- new PermissionInfo[] { new PermissionInfo(ConfigurationPermission.class.getName(), "*", "configure"),
- new PermissionInfo(AdminPermission.class.getName(), "*", "*"),
- new PermissionInfo(ServicePermission.class.getName(), "*", "get"),
- new PermissionInfo(ServicePermission.class.getName(), "*", "register"),
- new PermissionInfo(PropertyPermission.class.getName(), "osgi.*", "read"),
- new PermissionInfo(PropertyPermission.class.getName(), "xml.*", "read"),
- new PermissionInfo(PropertyPermission.class.getName(), "equinox.*", "read"),
- new PermissionInfo(RuntimePermission.class.getName(), "accessDeclaredMembers", null),
- new PermissionInfo(RuntimePermission.class.getName(), "getClassLoader", null),
- new PermissionInfo(ReflectPermission.class.getName(), "suppressAccessChecks", null), },
- ConditionalPermissionInfo.ALLOW));
+ update.getConditionalPermissionInfos()
+ .add(permissionAdmin.newConditionalPermissionInfo(null,
+ new ConditionInfo[] { new ConditionInfo(BundleLocationCondition.class.getName(),
+ new String[] { dsBundle.getLocation() }) },
+ new PermissionInfo[] {
+ new PermissionInfo("org.osgi.service.cm.ConfigurationPermission", "*", "configure"),
+ new PermissionInfo(AdminPermission.class.getName(), "*", "*"),
+ new PermissionInfo(ServicePermission.class.getName(), "*", "get"),
+ new PermissionInfo(ServicePermission.class.getName(), "*", "register"),
+ new PermissionInfo(PropertyPermission.class.getName(), "osgi.*", "read"),
+ new PermissionInfo(PropertyPermission.class.getName(), "xml.*", "read"),
+ new PermissionInfo(PropertyPermission.class.getName(), "equinox.*", "read"),
+ new PermissionInfo(RuntimePermission.class.getName(), "accessDeclaredMembers", null),
+ new PermissionInfo(RuntimePermission.class.getName(), "getClassLoader", null),
+ new PermissionInfo(ReflectPermission.class.getName(), "suppressAccessChecks", null), },
+ ConditionalPermissionInfo.ALLOW));
// Jetty
// Bundle jettyUtilBundle = findBundle("org.eclipse.equinox.http.jetty");
availableSince = System.currentTimeMillis();
long jvmUptime = ManagementFactory.getRuntimeMXBean().getUptime();
String jvmUptimeStr = " in " + (jvmUptime / 1000) + "." + (jvmUptime % 1000) + "s";
- log.info("## ARGEO CMS AVAILABLE" + (log.isDebugEnabled() ? jvmUptimeStr : "") + " ##");
+ log.info("## ARGEO CMS " + cmsState.getUuid() + " AVAILABLE" + (log.isDebugEnabled() ? jvmUptimeStr : "")
+ + " ##");
if (log.isDebugEnabled()) {
log.debug("## state: " + state);
if (data != null)
public void setHttpServer(HttpServer httpServer) {
Objects.requireNonNull(httpServer);
- this.httpServer.complete(httpServer);
- // create contexts whose handles had already been published
- for (String contextPath : httpHandlers.keySet()) {
- HttpHandler httpHandler = httpHandlers.get(contextPath);
- CmsAuthenticator authenticator = httpAuthenticators.get(contextPath);
- createHttpContext(contextPath, httpHandler, authenticator);
+ if (this.httpServer.isDone())
+ if (httpExpected)
+ throw new IllegalStateException("HTTP server is already set");
+ else
+ return;// ignore
+ // create contexts whose handlers had already been published
+ synchronized (httpHandlers) {
+ synchronized (httpAuthenticators) {
+ this.httpServer.complete(httpServer);
+ for (String contextPath : httpHandlers.keySet()) {
+ HttpHandler httpHandler = httpHandlers.get(contextPath);
+ CmsAuthenticator authenticator = httpAuthenticators.get(contextPath);
+ createHttpContext(contextPath, httpHandler, authenticator);
+ }
+ }
}
}
}
boolean isPublic = Boolean.parseBoolean(properties.get(CmsConstants.CONTEXT_PUBLIC));
CmsAuthenticator authenticator = isPublic ? new PublicCmsAuthenticator() : new CmsAuthenticator();
- httpHandlers.put(contextPath, httpHandler);
- httpAuthenticators.put(contextPath, authenticator);
- if (httpServer.join() == null) {
+ synchronized (httpHandlers) {
+ synchronized (httpAuthenticators) {
+ httpHandlers.put(contextPath, httpHandler);
+ httpAuthenticators.put(contextPath, authenticator);
+ }
+ }
+ if (!httpServer.isDone()) {
return;
} else {
createHttpContext(contextPath, httpHandler, authenticator);
log.warn("Ignore HTTP context " + contextPath + " as we don't provide an HTTP server");
return;
}
+ if (!this.httpServer.isDone())
+ throw new IllegalStateException("HTTP server is not set");
+ // TODO use resultNow when switching to Java 21
HttpContext httpContext = httpServer.join().createContext(contextPath);
// we want to set the authenticator BEFORE the handler actually becomes active
httpContext.setAuthenticator(authenticator);
if (contextPath == null)
return; // ignore silently
httpHandlers.remove(contextPath);
- if (httpServer.join() == null)
+ if (!httpExpected || !httpServer.isDone())
return;
+ // TODO use resultNow when switching to Java 21
httpServer.join().removeContext(contextPath);
log.debug(() -> "Removed handler " + contextPath + " : " + httpHandler.getClass().getName());
}
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.Reader;
+import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.URL;
import java.net.UnknownHostException;
import org.argeo.api.cms.CmsConstants;
import org.argeo.api.cms.CmsLog;
import org.argeo.api.cms.CmsState;
-import org.argeo.api.uuid.UuidFactory;
+import org.argeo.api.uuid.NodeIdSupplier;
+import org.argeo.api.uuid.UuidBinaryUtils;
import org.argeo.cms.CmsDeployProperty;
import org.argeo.cms.auth.ident.IdentClient;
+import org.argeo.cms.util.DigestUtils;
import org.argeo.cms.util.FsUtils;
import org.argeo.cms.util.OS;
/**
* Implementation of a {@link CmsState}, initialising the required services.
*/
-public class CmsStateImpl implements CmsState {
+public class CmsStateImpl implements CmsState, NodeIdSupplier {
private final static CmsLog log = CmsLog.getLog(CmsStateImpl.class);
// REFERENCES
private UUID uuid;
// private final boolean cleanState;
private String hostname;
+ private InetAddress inetAddress;
- private UuidFactory uuidFactory;
+// private UuidFactory uuidFactory;
private final Map<CmsDeployProperty, String> deployPropertyDefaults;
if (log.isTraceEnabled())
log.trace("CMS State started");
- this.uuid = uuidFactory.timeUUID();
+ String frameworkUuid = KernelUtils.getFrameworkProp(KernelUtils.OSGI_FRAMEWORK_UUID);
+ this.uuid = frameworkUuid != null ? UUID.fromString(frameworkUuid) : UUID.randomUUID();
// hostname
this.hostname = getDeployProperty(CmsDeployProperty.HOST);
final String LOCALHOST_IP = "::1";
ForkJoinTask<String> hostnameFJT = ForkJoinPool.commonPool().submit(() -> {
try {
- String hostname = InetAddress.getLocalHost().getHostName();
+ this.inetAddress = InetAddress.getLocalHost();
+ String hostname = this.inetAddress.getHostName();
return hostname;
} catch (UnknownHostException e) {
throw new IllegalStateException("Cannot get local hostname", e);
this.hostname = LOCALHOST_IP;
log.warn("Could not get local hostname, using " + this.hostname);
}
+ } else {
+ InetAddress[] addresses = InetAddress.getAllByName(this.hostname);
+ InetAddress selectedAddr = null;
+ addresses: for (InetAddress addr : addresses) {
+ if (selectedAddr == null)
+ selectedAddr = addr;
+ if (selectedAddr instanceof Inet6Address)
+ break addresses;
+ }
+ this.inetAddress = selectedAddr;
}
availableSince = System.currentTimeMillis();
}
}
}
- log.debug("## CMS starting... (" + uuid + ")\n" + sb + "\n");
+ log.debug("## CMS starting on " + hostname + " ... (" + uuid + ")\n" + sb + "\n");
}
if (log.isTraceEnabled()) {
} catch (RuntimeException | IOException e) {
log.error("## FATAL: CMS state failed", e);
+ throw new IllegalStateException(e);
}
}
log.debug("CMS stopping... (" + this.uuid + ")");
long duration = ((System.currentTimeMillis() - availableSince) / 1000) / 60;
- log.info("## ARGEO CMS STOPPED after " + (duration / 60) + "h " + (duration % 60) + "min uptime ##");
+ log.info("## ARGEO CMS " + uuid + " STOPPED after " + (duration / 60) + "h " + (duration % 60)
+ + "min uptime ##");
}
private void firstInit() throws IOException {
return availableSince;
}
+ /*
+ * NodeID supplier
+ */
+
+ @Override
+ public Long get() {
+ return NodeIdSupplier.toNodeIdBase(getIpBytes());
+ }
+
+ /** Returns an SHA1 digest of one of the IP addresses. */
+ protected byte[] getIpBytes() {
+// Enumeration<NetworkInterface> netInterfaces = null;
+// try {
+// netInterfaces = NetworkInterface.getNetworkInterfaces();
+// } catch (SocketException e) {
+// throw new IllegalStateException(e);
+// }
+//
+// InetAddress selectedIpv6 = null;
+// InetAddress selectedIpv4 = null;
+// if (netInterfaces != null) {
+// netInterfaces: while (netInterfaces.hasMoreElements()) {
+// NetworkInterface netInterface = netInterfaces.nextElement();
+// byte[] hardwareAddress = null;
+// try {
+// hardwareAddress = netInterface.getHardwareAddress();
+// if (hardwareAddress != null) {
+// // first IPv6
+// addr: for (InterfaceAddress addr : netInterface.getInterfaceAddresses()) {
+// InetAddress ip = addr.getAddress();
+// if (ip instanceof Inet6Address) {
+// Inet6Address ipv6 = (Inet6Address) ip;
+// if (ipv6.isAnyLocalAddress() || ipv6.isLinkLocalAddress() || ipv6.isLoopbackAddress())
+// continue addr;
+// selectedIpv6 = ipv6;
+// break netInterfaces;
+// }
+//
+// }
+// // then IPv4
+// addr: for (InterfaceAddress addr : netInterface.getInterfaceAddresses()) {
+// InetAddress ip = addr.getAddress();
+// if (ip instanceof Inet4Address) {
+// Inet4Address ipv4 = (Inet4Address) ip;
+// if (ipv4.isAnyLocalAddress() || ipv4.isLinkLocalAddress() || ipv4.isLoopbackAddress())
+// continue addr;
+// selectedIpv4 = ipv4;
+// // we keep searching for IPv6
+// }
+//
+// }
+// }
+// } catch (SocketException e) {
+// throw new IllegalStateException(e);
+// }
+// }
+// }
+// InetAddress selectedIp = selectedIpv6 != null ? selectedIpv6 : selectedIpv4;
+ if (this.inetAddress.isLoopbackAddress()) {
+ log.warn("No IP address found, using a random node id for UUID generation");
+ return NodeIdSupplier.randomNodeId();
+ }
+ InetAddress selectedIp = this.inetAddress;
+ byte[] digest = DigestUtils.sha1(selectedIp.getAddress());
+ log.debug("Use IP " + selectedIp + " hashed as " + UuidBinaryUtils.toHexString(digest) + " as node id");
+ byte[] nodeId = NodeIdSupplier.toNodeIdBytes(digest, 0);
+ // marks that this is not based on MAC address
+ NodeIdSupplier.forceToNoMacAddress(nodeId, 0);
+ return nodeId;
+ }
+
/*
* ACCESSORS
*/
return uuid;
}
- public void setUuidFactory(UuidFactory uuidFactory) {
- this.uuidFactory = uuidFactory;
- }
+// public void setUuidFactory(UuidFactory uuidFactory) {
+// this.uuidFactory = uuidFactory;
+// }
public String getHostname() {
return hostname;
// TODO make passphrase more configurable
return new IdentClient(remoteAddr);
}
+
}
import org.argeo.api.cms.CmsConstants;
import org.argeo.api.cms.CmsLog;
import org.argeo.api.cms.directory.CmsGroup;
+import org.argeo.api.cms.directory.CmsRole;
import org.argeo.api.cms.directory.CmsUser;
import org.argeo.api.cms.directory.CmsUserManager;
import org.argeo.api.cms.directory.HierarchyUnit;
}
- @Override
- public String getMyMail() {
- return getUserMail(CurrentUser.getUsername());
- }
+// @Override
+// public String getMyMail() {
+// return getUserMail(CurrentUser.getUsername());
+// }
@Override
- public Role[] getRoles(String filter) {
+ public CmsRole[] getRoles(String filter) {
try {
- return userAdmin.getRoles(filter);
+ Role[] roles = userAdmin.getRoles(filter);
+ CmsRole[] res = new CmsRole[roles.length];
+ for (int i = 0; i < roles.length; i++)
+ res[i] = (CmsRole) roles[i];
+ return res;
} catch (InvalidSyntaxException e) {
throw new IllegalArgumentException("Invalid filter " + filter, e);
}
/** Lists all roles of the given user */
@Override
public String[] getUserRoles(String dn) {
- Authorization currAuth = getUserAdmin().getAuthorization(getUser(dn));
+ Authorization currAuth = getUserAdmin().getAuthorization((User) getUser(dn));
return currAuth.getRoles();
}
return users;
}
-// @Override
-// public Set<User> listAccounts(HierarchyUnit hierarchyUnit, boolean deep) {
-// if(!hierarchyUnit.isFunctional())
-// throw new IllegalArgumentException("Hierarchy unit "+hierarchyUnit.getBase()+" is not functional");
-// UserDirectory directory = (UserDirectory)hierarchyUnit.getDirectory();
-// Set<User> res = new HashSet<>();
-// for(HierarchyUnit technicalHu:hierarchyUnit.getDirectHierarchyUnits(false)) {
-// if(technicalHu.isFunctional())
-// continue;
-// for(Role role:directory.getHierarchyUnitRoles(technicalHu, null, false)) {
-// if(role)
-// }
-// }
-// return res;
-// }
-
/** Recursively add users to list */
private void addUsers(Set<CmsUser> users, Group group, String filter) {
- Role[] roles = group.getMembers();
+ Role[] roles = ((Group) group).getMembers();
for (Role role : roles) {
if (role.getType() == Role.GROUP) {
- addUsers(users, (CmsGroup) role, filter);
+ addUsers(users, (Group) role, filter);
} else if (role.getType() == Role.USER) {
if (match(role, filter))
users.add((CmsUser) role);
public CmsUser createUser(String username, Map<String, Object> properties, Map<String, Object> credentials) {
try {
userTransaction.begin();
- CmsUser user = (CmsUser) userAdmin.createRole(username, Role.USER);
+ User user = (User) userAdmin.createRole(username, Role.USER);
if (properties != null) {
for (String key : properties.keySet())
user.getProperties().put(key, properties.get(key));
user.getCredentials().put(key, credentials.get(key));
}
userTransaction.commit();
- return user;
+ return (CmsUser) user;
} catch (Exception e) {
try {
userTransaction.rollback();
}
@Override
- public void addObjectClasses(Role role, Set<String> objectClasses, Map<String, Object> additionalProperties) {
+ public void addObjectClasses(CmsRole role, Set<String> objectClasses, Map<String, Object> additionalProperties) {
try {
userTransaction.begin();
LdapEntry.addObjectClasses(role.getProperties(), objectClasses);
}
@Override
- public void addMember(CmsGroup group, Role role) {
+ public void addMember(CmsGroup group, CmsRole role) {
try {
userTransaction.begin();
- group.addMember(role);
+ ((Group) group).addMember((Role) role);
userTransaction.commit();
} catch (Exception e1) {
try {
}
@Override
- public void removeMember(CmsGroup group, Role role) {
+ public void removeMember(CmsGroup group, CmsRole role) {
try {
userTransaction.begin();
- group.removeMember(role);
+ ((Group) group).removeMember((Role) role);
userTransaction.commit();
} catch (Exception e1) {
try {
}
@Override
- public UserDirectory getDirectory(Role user) {
+ public UserDirectory getDirectory(CmsRole user) {
String name = user.getName();
NavigableMap<String, UserDirectory> possible = new TreeMap<>();
for (UserDirectory userDirectory : userDirectories) {
--- /dev/null
+package org.argeo.cms.internal.runtime;
+
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.net.InterfaceAddress;
+import java.net.NetworkInterface;
+import java.net.SocketException;
+import java.util.BitSet;
+import java.util.Enumeration;
+
+import org.argeo.api.cms.CmsLog;
+import org.argeo.api.uuid.ConcurrentUuidFactory;
+import org.argeo.api.uuid.NodeIdSupplier;
+import org.argeo.api.uuid.UuidBinaryUtils;
+
+@Deprecated
+class CmsUuidFactory extends ConcurrentUuidFactory {
+ private final static CmsLog log = CmsLog.getLog(CmsUuidFactory.class);
+
+ public CmsUuidFactory(byte[] nodeId) {
+ super(0, nodeId);
+ assert createTimeUUID().node() == BitSet.valueOf(NodeIdSupplier.toNodeIdBytes(nodeId, 0)).toLongArray()[0];
+ }
+
+ public CmsUuidFactory() {
+ this(getIpBytes());
+ }
+
+ /** Returns an SHA1 digest of one of the IP addresses. */
+ protected static byte[] getIpBytes() {
+ Enumeration<NetworkInterface> netInterfaces = null;
+ try {
+ netInterfaces = NetworkInterface.getNetworkInterfaces();
+ } catch (SocketException e) {
+ throw new IllegalStateException(e);
+ }
+
+ InetAddress selectedIpv6 = null;
+ InetAddress selectedIpv4 = null;
+ if (netInterfaces != null) {
+ netInterfaces: while (netInterfaces.hasMoreElements()) {
+ NetworkInterface netInterface = netInterfaces.nextElement();
+ byte[] hardwareAddress = null;
+ try {
+ hardwareAddress = netInterface.getHardwareAddress();
+ if (hardwareAddress != null) {
+ // first IPv6
+ addr: for (InterfaceAddress addr : netInterface.getInterfaceAddresses()) {
+ InetAddress ip = addr.getAddress();
+ if (ip instanceof Inet6Address) {
+ Inet6Address ipv6 = (Inet6Address) ip;
+ if (ipv6.isAnyLocalAddress() || ipv6.isLinkLocalAddress() || ipv6.isLoopbackAddress())
+ continue addr;
+ selectedIpv6 = ipv6;
+ break netInterfaces;
+ }
+
+ }
+ // then IPv4
+ addr: for (InterfaceAddress addr : netInterface.getInterfaceAddresses()) {
+ InetAddress ip = addr.getAddress();
+ if (ip instanceof Inet4Address) {
+ Inet4Address ipv4 = (Inet4Address) ip;
+ if (ipv4.isAnyLocalAddress() || ipv4.isLinkLocalAddress() || ipv4.isLoopbackAddress())
+ continue addr;
+ selectedIpv4 = ipv4;
+ // we keep searching for IPv6
+ }
+
+ }
+ }
+ } catch (SocketException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+ }
+ InetAddress selectedIp = selectedIpv6 != null ? selectedIpv6 : selectedIpv4;
+ if (selectedIp == null) {
+ log.warn("No IP address found, using a random node id for UUID generation");
+ return NodeIdSupplier.randomNodeId();
+ }
+ byte[] digest = sha1(selectedIp.getAddress());
+ log.debug("Use IP " + selectedIp + " hashed as " + UuidBinaryUtils.toHexString(digest) + " as node id");
+ byte[] nodeId = NodeIdSupplier.toNodeIdBytes(digest, 0);
+ // marks that this is not based on MAC address
+ NodeIdSupplier.forceToNoMacAddress(nodeId, 0);
+ return nodeId;
+ }
+}
final static String OSGI_INSTANCE_AREA = "osgi.instance.area";
final static String OSGI_CONFIGURATION_AREA = "osgi.configuration.area";
+ final static String OSGI_FRAMEWORK_UUID = "org.osgi.framework.uuid";
+
static void setJaasConfiguration(URL jaasConfigurationUrl) {
try {
URIParameter uriParameter = new URIParameter(jaasConfigurationUrl.toURI());
static String getFrameworkProp(String key, String def) {
String value;
- if (CmsActivator.getBundleContext() != null)
- value = CmsActivator.getBundleContext().getProperty(key);
- else
+ value = CmsActivator.getFrameworkProperty(key);
+ if (value == null)
value = System.getProperty(key);
if (value == null)
return def;
import org.osgi.service.useradmin.Authorization;
/** An {@link Authorization} which combines roles form various auth sources. */
-class AggregatingAuthorization implements CmsAuthorization {
+class AggregatingAuthorization implements CmsAuthorization, Authorization {
private final String name;
private final String displayName;
private final Set<String> systemRoles;
--- /dev/null
+package org.argeo.cms.osgi.useradmin;
+
+import org.argeo.api.cms.directory.CmsAuthorization;
+import org.osgi.service.useradmin.Authorization;
+
+/** Merging interface between CMS and OSGi user management APIs. */
+interface CmsOsgiAuthorization extends CmsAuthorization, Authorization {
+
+}
--- /dev/null
+package org.argeo.cms.osgi.useradmin;
+
+import org.argeo.api.cms.directory.CmsGroup;
+import org.osgi.service.useradmin.Group;
+
+/** Merging interface between CMS and OSGi user management APIs. */
+interface CmsOsgiGroup extends CmsOsgiUser, CmsGroup, Group {
+
+}
--- /dev/null
+package org.argeo.cms.osgi.useradmin;
+
+import org.argeo.api.cms.directory.CmsRole;
+import org.osgi.service.useradmin.Role;
+
+/** Merging interface between CMS and OSGi user management APIs. */
+interface CmsOsgiRole extends CmsRole, Role {
+
+}
--- /dev/null
+package org.argeo.cms.osgi.useradmin;
+
+import org.argeo.api.cms.directory.CmsUser;
+import org.osgi.service.useradmin.User;
+
+/** Merging interface between CMS and OSGi user management APIs. */
+interface CmsOsgiUser extends CmsOsgiRole, CmsUser, User {
+
+}
import javax.security.auth.Subject;
import javax.security.auth.kerberos.KerberosTicket;
+import org.argeo.api.acr.ldap.LdapNameUtils;
+import org.argeo.api.cms.directory.CmsRole;
import org.argeo.api.cms.directory.DirectoryDigestUtils;
-import org.argeo.api.cms.directory.CmsUser;
import org.argeo.api.cms.directory.HierarchyUnit;
import org.argeo.api.cms.directory.UserDirectory;
import org.argeo.cms.directory.ldap.AbstractLdapDirectory;
import org.argeo.cms.directory.ldap.LdapDao;
import org.argeo.cms.directory.ldap.LdapEntry;
import org.argeo.cms.directory.ldap.LdapEntryWorkingCopy;
-import org.argeo.cms.directory.ldap.LdapNameUtils;
import org.argeo.cms.directory.ldap.LdifDao;
import org.argeo.cms.runtime.DirectoryConf;
import org.argeo.cms.util.CurrentSubject;
}
@Override
- public String getRolePath(Role role) {
+ public String getRolePath(CmsRole role) {
return nameToRelativePath(LdapNameUtils.toLdapName(role.getName()));
}
@Override
- public String getRoleSimpleName(Role role) {
+ public String getRoleSimpleName(CmsRole role) {
LdapName dn = LdapNameUtils.toLdapName(role.getName());
String name = LdapNameUtils.getLastRdnValue(dn);
return name;
}
@Override
- public Role getRoleByPath(String path) {
+ public CmsRole getRoleByPath(String path) {
LdapEntry entry = doGetRole(pathToName(path));
- if (!(entry instanceof Role)) {
+ if (!(entry instanceof CmsRole)) {
return null;
// throw new IllegalStateException("Path must be a UserAdmin Role.");
} else {
- return (Role) entry;
+ return (CmsRole) entry;
}
}
- protected List<Role> getAllRoles(CmsUser user) {
- List<Role> allRoles = new ArrayList<Role>();
+ protected List<CmsOsgiRole> getAllRoles(CmsOsgiUser user) {
+ List<CmsOsgiRole> allRoles = new ArrayList<CmsOsgiRole>();
if (user != null) {
collectRoles((LdapEntry) user, allRoles);
allRoles.add(user);
return allRoles;
}
- private void collectRoles(LdapEntry user, List<Role> allRoles) {
+ private void collectRoles(LdapEntry user, List<CmsOsgiRole> allRoles) {
List<LdapEntry> allEntries = new ArrayList<>();
LdapEntry entry = user;
collectGroups(entry, allEntries);
for (LdapEntry e : allEntries) {
- if (e instanceof Role)
- allRoles.add((Role) e);
+ if (e instanceof CmsOsgiRole)
+ allRoles.add((CmsOsgiRole) e);
}
}
- private void collectAnonymousRoles(List<Role> allRoles) {
+ private void collectAnonymousRoles(List<CmsOsgiRole> allRoles) {
// TODO gather anonymous roles
}
return res.toArray(new Role[res.size()]);
}
- List<CmsUser> getRoles(LdapName searchBase, String filter, boolean deep) throws InvalidSyntaxException {
+ List<CmsOsgiUser> getRoles(LdapName searchBase, String filter, boolean deep) throws InvalidSyntaxException {
LdapEntryWorkingCopy wc = getWorkingCopy();
// Filter f = filter != null ? FrameworkUtil.createFilter(filter) : null;
List<LdapEntry> searchRes = getDirectoryDao().doGetEntries(searchBase, filter, deep);
- List<CmsUser> res = new ArrayList<>();
+ List<CmsOsgiUser> res = new ArrayList<>();
for (LdapEntry entry : searchRes)
- res.add((CmsUser) entry);
+ res.add((CmsOsgiUser) entry);
if (wc != null) {
- for (Iterator<CmsUser> it = res.iterator(); it.hasNext();) {
- CmsUser user = (CmsUser) it.next();
+ for (Iterator<CmsOsgiUser> it = res.iterator(); it.hasNext();) {
+ CmsOsgiUser user = it.next();
LdapName dn = LdapNameUtils.toLdapName(user.getName());
if (wc.getDeletedData().containsKey(dn))
it.remove();
}
Filter f = filter != null ? FrameworkUtil.createFilter(filter) : null;
for (LdapEntry ldapEntry : wc.getNewData().values()) {
- CmsUser user = (CmsUser) ldapEntry;
+ CmsOsgiUser user = (CmsOsgiUser) ldapEntry;
if (f == null || f.match(user.getProperties()))
res.add(user);
}
@Override
public User getUser(String key, String value) {
// TODO check value null or empty
- List<CmsUser> collectedUsers = new ArrayList<CmsUser>();
+ List<CmsOsgiUser> collectedUsers = new ArrayList<>();
if (key != null) {
doGetUser(key, value, collectedUsers);
} else {
return null;
}
- protected void doGetUser(String key, String value, List<CmsUser> collectedUsers) {
+ protected void doGetUser(String key, String value, List<CmsOsgiUser> collectedUsers) {
String f = "(" + key + "=" + value + ")";
List<LdapEntry> users = getDirectoryDao().doGetEntries(getBaseDn(), f, true);
for (LdapEntry entry : users)
- collectedUsers.add((CmsUser) entry);
+ collectedUsers.add((CmsOsgiUser) entry);
}
@Override
return new LdifAuthorization(user, getAllRoles(null));
}
LdapName userName = toLdapName(user.getName());
- if (isExternal(userName) && user instanceof LdapEntry) {
- List<Role> allRoles = new ArrayList<Role>();
- collectRoles((LdapEntry) user, allRoles);
+ if (isExternal(userName) && user instanceof LdapEntry ldapEntry) {
+ List<CmsOsgiRole> allRoles = new ArrayList<>();
+ collectRoles(ldapEntry, allRoles);
return new LdifAuthorization(user, allRoles);
} else {
return getAuthorizationFromScoped(scopedUserAdmin, user);
}
- if (user instanceof CmsUser) {
- return new LdifAuthorization(user, getAllRoles((CmsUser) user));
+ if (user instanceof CmsOsgiUser u) {
+ return new LdifAuthorization(user, getAllRoles(u));
} else {
// bind with authenticating user
DirectoryUserAdmin scopedUserAdmin = scope(user).orElseThrow();
private Authorization getAuthorizationFromScoped(DirectoryUserAdmin scopedUserAdmin, User user) {
try {
- CmsUser directoryUser = (CmsUser) scopedUserAdmin.getRole(user.getName());
+ CmsOsgiUser directoryUser = (CmsOsgiUser) scopedUserAdmin.getRole(user.getName());
if (directoryUser == null)
throw new IllegalStateException("No scoped user found for " + user);
LdifAuthorization authorization = new LdifAuthorization(directoryUser,
* HIERARCHY
*/
@Override
- public HierarchyUnit getHierarchyUnit(Role role) {
+ public HierarchyUnit getHierarchyUnit(CmsRole role) {
LdapName dn = LdapNameUtils.toLdapName(role.getName());
LdapName huDn = LdapNameUtils.getParent(dn);
HierarchyUnit hierarchyUnit = getDirectoryDao().doGetHierarchyUnit(huDn);
}
@Override
- public Iterable<? extends Role> getHierarchyUnitRoles(HierarchyUnit hierarchyUnit, String filter, boolean deep) {
+ public Iterable<? extends CmsRole> getHierarchyUnitRoles(HierarchyUnit hierarchyUnit, String filter, boolean deep) {
LdapName dn = LdapNameUtils.toLdapName(hierarchyUnit.getBase());
try {
return getRoles(dn, filter, deep);
import org.argeo.api.acr.ldap.LdapAttr;
import org.osgi.service.useradmin.Authorization;
-import org.osgi.service.useradmin.Role;
import org.osgi.service.useradmin.User;
/** Basic authorization. */
-class LdifAuthorization implements Authorization {
+class LdifAuthorization implements CmsOsgiAuthorization {
private final String name;
private final String displayName;
private final List<String> allRoles;
- public LdifAuthorization(User user, List<Role> allRoles) {
+ public LdifAuthorization(User user, List<CmsOsgiRole> allRoles) {
if (user == null) {
this.name = null;
this.displayName = "anonymous";
package org.argeo.cms.osgi.useradmin;
-import java.util.ArrayList;
-import java.util.List;
+import java.util.HashSet;
+import java.util.Set;
import javax.naming.InvalidNameException;
import javax.naming.directory.Attribute;
import javax.naming.ldap.LdapName;
-import org.argeo.api.cms.directory.CmsGroup;
+import org.argeo.api.cms.directory.CmsRole;
import org.argeo.cms.directory.ldap.AbstractLdapDirectory;
import org.osgi.service.useradmin.Role;
/** Directory group implementation */
-class LdifGroup extends LdifUser implements CmsGroup {
+class LdifGroup extends LdifUser implements CmsOsgiGroup {
private final String memberAttributeId;
LdifGroup(AbstractLdapDirectory userAdmin, LdapName dn) {
@Override
public Role[] getMembers() {
- List<Role> directMembers = new ArrayList<Role>();
+ Set<CmsOsgiRole> directMembers = new HashSet<>();
for (LdapName ldapName : getReferences(memberAttributeId)) {
- Role role = findRole(ldapName);
+ CmsOsgiRole role = findRole(ldapName);
if (role == null) {
throw new IllegalStateException("Role " + ldapName + " not found.");
}
return directMembers.toArray(new Role[directMembers.size()]);
}
+ @Override
+ public Set<? extends CmsRole> getDirectMembers() {
+ return doGetDirectMembers();
+ }
+
+ protected Set<CmsOsgiRole> doGetDirectMembers() {
+ Set<CmsOsgiRole> directMembers = new HashSet<>();
+ for (LdapName ldapName : getReferences(memberAttributeId)) {
+ CmsOsgiRole role = findRole(ldapName);
+ if (role == null) {
+ throw new IllegalStateException("Role " + ldapName + " not found.");
+ }
+ directMembers.add(role);
+ }
+ return directMembers;
+ }
+
/**
* Whether a role with this name can be found from this context.
*
* @return The related {@link Role} or <code>null</code>.
*/
- protected Role findRole(LdapName ldapName) {
+ protected CmsOsgiRole findRole(LdapName ldapName) {
Role role = getUserAdmin().getRole(ldapName.toString());
if (role == null) {
if (getUserAdmin().getExternalRoles() != null)
role = getUserAdmin().getExternalRoles().getRole(ldapName.toString());
}
- return role;
+ return (CmsOsgiRole) role;
}
// @Override
import javax.naming.ldap.LdapName;
-import org.argeo.api.cms.directory.CmsUser;
+import org.argeo.api.acr.ldap.LdapAttr;
+import org.argeo.cms.auth.UserAdminUtils;
import org.argeo.cms.directory.ldap.AbstractLdapDirectory;
import org.argeo.cms.directory.ldap.DefaultLdapEntry;
+import org.argeo.cms.util.LangUtils;
/** Directory user implementation */
-class LdifUser extends DefaultLdapEntry implements CmsUser {
+class LdifUser extends DefaultLdapEntry implements CmsOsgiUser {
LdifUser(AbstractLdapDirectory userAdmin, LdapName dn) {
super(userAdmin, dn);
}
return USER;
}
+ public String getDisplayName() {
+ String dName = getPropertyAsString(LdapAttr.displayName);
+ if (LangUtils.isEmpty(dName))
+ dName = getPropertyAsString(LdapAttr.cn);
+ if (LangUtils.isEmpty(dName))
+ dName = getPropertyAsString(LdapAttr.uid);
+ if (LangUtils.isEmpty(dName))
+ dName = UserAdminUtils.getUserLocalId(getName());
+ return dName;
+ }
+
}
import org.argeo.api.register.Component;
import org.argeo.api.register.ComponentRegister;
import org.argeo.api.register.SimpleRegister;
+import org.argeo.api.uuid.ConcurrentUuidFactory;
+import org.argeo.api.uuid.NodeIdSupplier;
import org.argeo.api.uuid.UuidFactory;
-import org.argeo.cms.acr.CmsUuidFactory;
import org.argeo.cms.internal.runtime.CmsContextImpl;
import org.argeo.cms.internal.runtime.CmsDeploymentImpl;
import org.argeo.cms.internal.runtime.CmsStateImpl;
private CompletableFuture<Void> stopped = new CompletableFuture<Void>();
public void start() {
- // UID factory
- CmsUuidFactory uuidFactory = new CmsUuidFactory();
- Component<CmsUuidFactory> uuidFactoryC = new Component.Builder<>(uuidFactory) //
- .addType(UuidFactory.class) //
- .build(register);
-
// CMS State
CmsStateImpl cmsState = new CmsStateImpl();
Component<CmsStateImpl> cmsStateC = new Component.Builder<>(cmsState) //
.addType(CmsState.class) //
+ .addType(NodeIdSupplier.class) //
.addActivation(cmsState::start) //
.addDeactivation(cmsState::stop) //
- .addDependency(uuidFactoryC.getType(UuidFactory.class), cmsState::setUuidFactory, null) //
+ .build(register);
+
+ // UID factory
+ ConcurrentUuidFactory uuidFactory = new ConcurrentUuidFactory();
+ Component<ConcurrentUuidFactory> uuidFactoryC = new Component.Builder<>(uuidFactory) //
+ .addType(UuidFactory.class) //
+ .addDependency(cmsStateC.getType(NodeIdSupplier.class), uuidFactory::setNodeIdSupplier, null) //
.build(register);
// Transaction manager
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
-import java.util.Comparator;
import java.util.Dictionary;
import java.util.Enumeration;
import java.util.HashMap;
/** Utilities around Java basic features. */
public class LangUtils {
/*
- * NON-API OSGi
+ * OBJECTS and STRINGS
*/
/**
- * Returns an array with the names of the provided classes. Useful when
- * registering services with multiple interfaces in OSGi.
+ * Whether this {@link String} is <code>null</null>, empty, or only white
+ * spaces.
*/
- public static String[] names(Class<?>... clzz) {
- String[] res = new String[clzz.length];
- for (int i = 0; i < clzz.length; i++)
- res[i] = clzz[i].getName();
- return res;
+ public static boolean isEmpty(String str) {
+ return str == null || "".equals(str.strip());
}
-// /*
-// * MAP
-// */
-// /**
-// * Creates a new {@link Map} with one key-value pair. Key should not be null,
-// * but if the value is null, it returns an empty {@link Map}.
-// *
-// * @deprecated Use {@link Collections#singletonMap(Object, Object)} instead.
-// */
-// @Deprecated
-// public static Map<String, Object> map(String key, Object value) {
-// assert key != null;
-// HashMap<String, Object> props = new HashMap<>();
-// if (value != null)
-// props.put(key, value);
-// return props;
-// }
-
/*
* DICTIONARY
*/
return count > 1 ? count + " seconds" : count + " second";
}
+ /*
+ * NON-API OSGi
+ */
+ /**
+ * Returns an array with the names of the provided classes. Useful when
+ * registering services with multiple interfaces in OSGi.
+ */
+ public static String[] names(Class<?>... clzz) {
+ String[] res = new String[clzz.length];
+ for (int i = 0; i < clzz.length; i++)
+ res[i] = clzz[i].getName();
+ return res;
+ }
+
/** Singleton constructor. */
private LangUtils() {
Import-Package: \
org.osgi.*;version=0.0.0,\
java.util.logging;resolution:=optional
+
+Export-Package: \
+org.argeo.api.*,\
+org.argeo.init.osgi,\
\ No newline at end of file
--- /dev/null
+package org.argeo.api.a2;
+
+import java.lang.System.Logger;
+import java.lang.System.Logger.Level;
+import java.util.Collections;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+import org.osgi.framework.Version;
+
+/**
+ * A logical linear sequence of versions of a given {@link A2Component}. This is
+ * typically a combination of major and minor version, indicating backward
+ * compatibility.
+ */
+public class A2Branch implements Comparable<A2Branch> {
+ private final static Logger logger = System.getLogger(A2Branch.class.getName());
+
+ private final A2Component component;
+ private final String id;
+
+ final SortedMap<Version, A2Module> modules = Collections.synchronizedSortedMap(new TreeMap<>());
+
+ public A2Branch(A2Component component, String id) {
+ this.component = component;
+ this.id = id;
+ component.branches.put(id, this);
+ }
+
+ public Iterable<A2Module> listModules(Object filter) {
+ return modules.values();
+ }
+
+ A2Module getOrAddModule(Version version, Object locator) {
+ if (modules.containsKey(version)) {
+ A2Module res = modules.get(version);
+ if (!res.getLocator().equals(locator)) {
+ logger.log(Level.TRACE,
+ () -> "Inconsistent locator " + locator + " (registered: " + res.getLocator() + ")");
+ }
+ return res;
+ } else
+ return new A2Module(this, version, locator);
+ }
+
+ public A2Module last() {
+ return modules.get(modules.lastKey());
+ }
+
+ public A2Module first() {
+ return modules.get(modules.firstKey());
+ }
+
+ public A2Component getComponent() {
+ return component;
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ @Override
+ public int compareTo(A2Branch o) {
+ return id.compareTo(id);
+ }
+
+ @Override
+ public int hashCode() {
+ return id.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj instanceof A2Branch) {
+ A2Branch o = (A2Branch) obj;
+ return component.equals(o.component) && id.equals(o.id);
+ } else
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return getCoordinates();
+ }
+
+ public String getCoordinates() {
+ return component + ":" + id;
+ }
+
+ static String versionToBranchId(Version version) {
+ return version.getMajor() + "." + version.getMinor();
+ }
+}
--- /dev/null
+package org.argeo.api.a2;
+
+import java.util.Collections;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+import org.osgi.framework.Version;
+
+/**
+ * The logical name of a software package. In OSGi's case this is
+ * <code>Bundle-SymbolicName</code>. This is the equivalent of Maven's artifact
+ * id.
+ */
+public class A2Component implements Comparable<A2Component> {
+ private final A2Contribution contribution;
+ private final String id;
+
+ final SortedMap<String, A2Branch> branches = Collections.synchronizedSortedMap(new TreeMap<>());
+
+ public A2Component(A2Contribution contribution, String id) {
+ this.contribution = contribution;
+ this.id = id;
+ contribution.components.put(id, this);
+ }
+
+ public Iterable<A2Branch> listBranches(Object filter) {
+ return branches.values();
+ }
+
+ A2Branch getOrAddBranch(String branchId) {
+ if (!branches.containsKey(branchId)) {
+ A2Branch a2Branch = new A2Branch(this, branchId);
+ branches.put(branchId, a2Branch);
+ }
+ return branches.get(branchId);
+ }
+
+ A2Module getOrAddModule(Version version, Object locator) {
+ A2Branch branch = getOrAddBranch(A2Branch.versionToBranchId(version));
+ A2Module module = branch.getOrAddModule(version, locator);
+ return module;
+ }
+
+ public A2Branch last() {
+ return branches.get(branches.lastKey());
+ }
+
+ public A2Contribution getContribution() {
+ return contribution;
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ @Override
+ public int compareTo(A2Component o) {
+ return id.compareTo(o.id);
+ }
+
+ @Override
+ public int hashCode() {
+ return id.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj instanceof A2Component) {
+ A2Component o = (A2Component) obj;
+ return contribution.equals(o.contribution) && id.equals(o.id);
+ } else
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return contribution.getId() + ":" + id;
+ }
+
+ void asTree(String prefix, StringBuffer buf) {
+ if (prefix == null)
+ prefix = "";
+ A2Branch lastBranch = last();
+ SortedMap<String, A2Branch> displayMap = new TreeMap<>(Collections.reverseOrder());
+ displayMap.putAll(branches);
+ for (String branchId : displayMap.keySet()) {
+ A2Branch branch = displayMap.get(branchId);
+ if (!lastBranch.equals(branch)) {
+ buf.append('\n');
+ buf.append(prefix);
+ } else {
+ buf.append(" -");
+ }
+ buf.append(prefix);
+ buf.append(branchId);
+ A2Module first = branch.first();
+ A2Module last = branch.last();
+ buf.append(" (").append(last.getVersion());
+ if (!first.equals(last))
+ buf.append(" ... ").append(first.getVersion());
+ buf.append(')');
+ }
+ }
+
+}
--- /dev/null
+package org.argeo.api.a2;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.TreeMap;
+
+/**
+ * A category grouping a set of {@link A2Component}, typically based on the
+ * provider of these components. This is the equivalent of Maven's group Id.
+ */
+public class A2Contribution implements Comparable<A2Contribution> {
+ final static String BOOT = "boot";
+ final static String RUNTIME = "runtime";
+ final static String CLASSPATH = "classpath";
+
+ final static String DEFAULT = "default";
+ final static String LIB = "lib";
+
+ private final ProvisioningSource source;
+ private final String id;
+
+ final Map<String, A2Component> components = Collections.synchronizedSortedMap(new TreeMap<>());
+
+ /**
+ * The contribution must be added to the source. Rather use
+ * {@link AbstractProvisioningSource#getOrAddContribution(String)} than this
+ * contructor directly.
+ */
+ public A2Contribution(ProvisioningSource context, String id) {
+ this.source = context;
+ this.id = id;
+// if (context != null)
+// context.contributions.put(id, this);
+ }
+
+ public Iterable<A2Component> listComponents(Object filter) {
+ return components.values();
+ }
+
+ A2Component getOrAddComponent(String componentId) {
+ if (components.containsKey(componentId))
+ return components.get(componentId);
+ else
+ return new A2Component(this, componentId);
+ }
+
+ public ProvisioningSource getSource() {
+ return source;
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ @Override
+ public int compareTo(A2Contribution o) {
+ return id.compareTo(o.id);
+ }
+
+ @Override
+ public int hashCode() {
+ return id.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj instanceof A2Contribution) {
+ A2Contribution o = (A2Contribution) obj;
+ return id.equals(o.id);
+ } else
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return id;
+ }
+
+ void asTree(String prefix, StringBuffer buf) {
+ if (prefix == null)
+ prefix = "";
+ for (String componentId : components.keySet()) {
+ buf.append(prefix);
+ buf.append(componentId);
+ A2Component component = components.get(componentId);
+ component.asTree(prefix, buf);
+ buf.append('\n');
+ }
+ }
+
+ static String localOsArchRelativePath() {
+ return Os.local().toString() + "/" + Arch.local().toString();
+ }
+
+ /** Well-known operating systems. */
+ static enum Os {
+ LINUX, WIN32, MACOSX, UNKOWN;
+
+ @Override
+ public String toString() {
+ return name().toLowerCase();
+ }
+
+ /** The local operating system. */
+ public static Os local() {
+ String osStr = System.getProperty("os.name").toLowerCase();
+ if (osStr.startsWith("linux"))
+ return LINUX;
+ if (osStr.startsWith("win"))
+ return WIN32;
+ if (osStr.startsWith("mac"))
+ return MACOSX;
+ return UNKOWN;
+ }
+
+ }
+
+ /** Well-known processor architectures. */
+ static enum Arch {
+ X86_64, AARCH64, X86, POWERPC, UNKOWN;
+
+ @Override
+ public String toString() {
+ return name().toLowerCase();
+ }
+
+ /** The locla processor architecture. */
+ public static Arch local() {
+ String archStr = System.getProperty("os.arch").toLowerCase();
+ return switch (archStr) {
+ case "x86_64":
+ case "amd64":
+ case "x86-64": {
+ yield X86_64;
+ }
+ case "aarch64":
+ case "arm64": {
+ yield AARCH64;
+ }
+ case "x86":
+ case "i386":
+ case "i686": {
+ yield X86;
+ }
+ case "powerpc":
+ case "ppc": {
+ yield POWERPC;
+ }
+ default:
+ yield UNKOWN;
+ };
+ }
+ }
+
+}
--- /dev/null
+package org.argeo.api.a2;
+
+/** Unchecked A2 provisioning exception. */
+public class A2Exception extends RuntimeException {
+ private static final long serialVersionUID = 1927603558545397360L;
+
+ public A2Exception(String message, Throwable e) {
+ super(message, e);
+ }
+
+ public A2Exception(String message) {
+ super(message);
+ }
+
+}
--- /dev/null
+package org.argeo.api.a2;
+
+import org.osgi.framework.Version;
+
+/**
+ * An identified software package. In OSGi's case this is the combination of
+ * <code>Bundle-SymbolicName</code> and <code>Bundle-version</code>. This is the
+ * equivalent of the full coordinates of a Maven artifact version.
+ */
+public class A2Module implements Comparable<A2Module> {
+ private final A2Branch branch;
+ private final Version version;
+ private final Object locator;
+
+ public A2Module(A2Branch branch, Version version, Object locator) {
+ this.branch = branch;
+ this.version = version;
+ this.locator = locator;
+ branch.modules.put(version, this);
+ }
+
+ public A2Branch getBranch() {
+ return branch;
+ }
+
+ public Version getVersion() {
+ return version;
+ }
+
+ Object getLocator() {
+ return locator;
+ }
+
+ @Override
+ public int compareTo(A2Module o) {
+ return version.compareTo(o.version);
+ }
+
+ @Override
+ public int hashCode() {
+ return version.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj instanceof A2Module) {
+ A2Module o = (A2Module) obj;
+ return branch.equals(o.branch) && version.equals(o.version);
+ } else
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return getCoordinates();
+ }
+
+ public String getCoordinates() {
+ return branch.getComponent() + ":" + version;
+ }
+
+}
--- /dev/null
+package org.argeo.api.a2;
+
+import java.net.URI;
+
+/** A provisioning source in A2 format. */
+public interface A2Source extends ProvisioningSource {
+ /** Use standard a2 protocol, installing from source URL. */
+ final static String SCHEME_A2 = "a2";
+ /**
+ * Use equinox-specific reference: installation, which does not copy the bundle
+ * content.
+ */
+ final static String SCHEME_A2_REFERENCE = "a2+reference";
+ final static String DEFAULT_A2_URI = SCHEME_A2 + ":///";
+ final static String DEFAULT_A2_REFERENCE_URI = SCHEME_A2_REFERENCE + ":///";
+
+ final static String INCLUDE = "include";
+ final static String EXCLUDE = "exclude";
+
+ URI getUri();
+}
--- /dev/null
+package org.argeo.api.a2;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.nio.file.FileVisitResult;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.SimpleFileVisitor;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.util.Collections;
+import java.util.Map;
+import java.util.SortedMap;
+import java.util.TreeMap;
+import java.util.jar.JarInputStream;
+import java.util.jar.JarOutputStream;
+import java.util.jar.Manifest;
+import java.util.zip.ZipEntry;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.Constants;
+import org.osgi.framework.Version;
+
+/** Where components are retrieved from. */
+public abstract class AbstractProvisioningSource implements ProvisioningSource {
+ protected final Map<String, A2Contribution> contributions = Collections.synchronizedSortedMap(new TreeMap<>());
+
+ private final boolean usingReference;
+
+ public AbstractProvisioningSource(boolean usingReference) {
+ this.usingReference = usingReference;
+ }
+
+ public Iterable<A2Contribution> listContributions(Object filter) {
+ return contributions.values();
+ }
+
+ @Override
+ public Bundle install(BundleContext bc, A2Module module) {
+ try {
+ Object locator = module.getLocator();
+ if (usingReference && locator instanceof Path locatorPath) {
+ String referenceUrl = "reference:file:" + locatorPath.toString();
+ Bundle bundle = bc.installBundle(referenceUrl);
+ return bundle;
+ } else {
+ Path locatorPath = (Path) locator;
+ Path pathToUse;
+ boolean isTemp = false;
+ if (locator instanceof Path && Files.isDirectory(locatorPath)) {
+ pathToUse = toTempJar(locatorPath);
+ isTemp = true;
+ } else {
+ pathToUse = locatorPath;
+ }
+ Bundle bundle;
+ try (InputStream in = newInputStream(pathToUse)) {
+ bundle = bc.installBundle(locatorPath.toAbsolutePath().toString(), in);
+ }
+
+ if (isTemp && pathToUse != null)
+ Files.deleteIfExists(pathToUse);
+ return bundle;
+ }
+ } catch (BundleException | IOException e) {
+ throw new A2Exception("Cannot install module " + module, e);
+ }
+ }
+
+ @Override
+ public void update(Bundle bundle, A2Module module) {
+ try {
+ Object locator = module.getLocator();
+ if (usingReference && locator instanceof Path) {
+ try (InputStream in = newInputStream(locator)) {
+ bundle.update(in);
+ }
+ } else {
+ Path locatorPath = (Path) locator;
+ Path pathToUse;
+ boolean isTemp = false;
+ if (locator instanceof Path && Files.isDirectory(locatorPath)) {
+ pathToUse = toTempJar(locatorPath);
+ isTemp = true;
+ } else {
+ pathToUse = locatorPath;
+ }
+ try (InputStream in = newInputStream(pathToUse)) {
+ bundle.update(in);
+ }
+ if (isTemp && pathToUse != null)
+ Files.deleteIfExists(pathToUse);
+ }
+ } catch (BundleException | IOException e) {
+ throw new A2Exception("Cannot update module " + module, e);
+ }
+ }
+
+ @Override
+ public A2Branch findBranch(String componentId, Version version) {
+ A2Component component = findComponent(componentId);
+ if (component == null)
+ return null;
+ String branchId = version.getMajor() + "." + version.getMinor();
+ if (!component.branches.containsKey(branchId))
+ return null;
+ return component.branches.get(branchId);
+ }
+
+ protected A2Contribution getOrAddContribution(String contributionId) {
+ if (contributions.containsKey(contributionId))
+ return contributions.get(contributionId);
+ else {
+ A2Contribution contribution = new A2Contribution(this, contributionId);
+ contributions.put(contributionId, contribution);
+ return contribution;
+ }
+ }
+
+ protected void asTree(String prefix, StringBuffer buf) {
+ if (prefix == null)
+ prefix = "";
+ for (String contributionId : contributions.keySet()) {
+ buf.append(prefix);
+ buf.append(contributionId);
+ buf.append('\n');
+ A2Contribution contribution = contributions.get(contributionId);
+ contribution.asTree(prefix + " ", buf);
+ }
+ }
+
+ protected void asTree() {
+ StringBuffer buf = new StringBuffer();
+ asTree("", buf);
+ System.out.println(buf);
+ }
+
+ protected A2Component findComponent(String componentId) {
+ SortedMap<A2Contribution, A2Component> res = new TreeMap<>();
+ for (A2Contribution contribution : contributions.values()) {
+ components: for (String componentIdKey : contribution.components.keySet()) {
+ if (componentId.equals(componentIdKey)) {
+ res.put(contribution, contribution.components.get(componentIdKey));
+ break components;
+ }
+ }
+ }
+ if (res.size() == 0)
+ return null;
+ // TODO explicit contribution priorities
+ return res.get(res.lastKey());
+
+ }
+
+ protected String[] readNameVersionFromModule(Path modulePath) {
+ Manifest manifest;
+ if (Files.isDirectory(modulePath)) {
+ manifest = findManifest(modulePath);
+ } else {
+ try (JarInputStream in = new JarInputStream(newInputStream(modulePath))) {
+ manifest = in.getManifest();
+ } catch (IOException e) {
+ throw new A2Exception("Cannot read manifest from " + modulePath, e);
+ }
+ }
+ String versionStr = manifest.getMainAttributes().getValue(Constants.BUNDLE_VERSION);
+ String symbolicName = manifest.getMainAttributes().getValue(Constants.BUNDLE_SYMBOLICNAME);
+ int semiColIndex = symbolicName.indexOf(';');
+ if (semiColIndex >= 0)
+ symbolicName = symbolicName.substring(0, semiColIndex);
+ return new String[] { symbolicName, versionStr };
+ }
+
+ protected String readVersionFromModule(Path modulePath) {
+ Manifest manifest;
+ if (Files.isDirectory(modulePath)) {
+ manifest = findManifest(modulePath);
+ } else {
+ try (JarInputStream in = new JarInputStream(newInputStream(modulePath))) {
+ manifest = in.getManifest();
+ } catch (IOException e) {
+ throw new A2Exception("Cannot read manifest from " + modulePath, e);
+ }
+ }
+ String versionStr = manifest.getMainAttributes().getValue(Constants.BUNDLE_VERSION);
+ return versionStr;
+ }
+
+ protected String readSymbolicNameFromModule(Path modulePath) {
+ Manifest manifest;
+ if (Files.isDirectory(modulePath)) {
+ manifest = findManifest(modulePath);
+ } else {
+ try (JarInputStream in = new JarInputStream(newInputStream(modulePath))) {
+ manifest = in.getManifest();
+ } catch (IOException e) {
+ throw new A2Exception("Cannot read manifest from " + modulePath, e);
+ }
+ }
+ String symbolicName = manifest.getMainAttributes().getValue(Constants.BUNDLE_SYMBOLICNAME);
+ int semiColIndex = symbolicName.indexOf(';');
+ if (semiColIndex >= 0)
+ symbolicName = symbolicName.substring(0, semiColIndex);
+ return symbolicName;
+ }
+
+ protected boolean isUsingReference() {
+ return usingReference;
+ }
+
+ private InputStream newInputStream(Object locator) throws IOException {
+ if (locator instanceof Path) {
+ return Files.newInputStream((Path) locator);
+ } else if (locator instanceof URL) {
+ return ((URL) locator).openStream();
+ } else {
+ throw new IllegalArgumentException("Unsupported module locator type " + locator.getClass());
+ }
+ }
+
+ private static Manifest findManifest(Path currentPath) {
+ Path metaInfPath = currentPath.resolve("META-INF");
+ if (Files.exists(metaInfPath) && Files.isDirectory(metaInfPath)) {
+ Path manifestPath = metaInfPath.resolve("MANIFEST.MF");
+ try {
+ try (InputStream in = Files.newInputStream(manifestPath)) {
+ Manifest manifest = new Manifest(in);
+ return manifest;
+ }
+ } catch (IOException e) {
+ throw new A2Exception("Cannot read manifest from " + manifestPath, e);
+ }
+ } else {
+ Path parentPath = currentPath.getParent();
+ if (parentPath == null)
+ throw new A2Exception("MANIFEST.MF file not found.");
+ return findManifest(currentPath.getParent());
+ }
+ }
+
+ private static Path toTempJar(Path dir) {
+ try {
+ Manifest manifest = findManifest(dir);
+ Path jarPath = Files.createTempFile("a2Source", ".jar");
+ try (JarOutputStream zos = new JarOutputStream(new FileOutputStream(jarPath.toFile()), manifest)) {
+ Files.walkFileTree(dir, new SimpleFileVisitor<Path>() {
+ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
+ Path relPath = dir.relativize(file);
+ // skip MANIFEST from folder
+ if (relPath.toString().contentEquals("META-INF/MANIFEST.MF"))
+ return FileVisitResult.CONTINUE;
+ zos.putNextEntry(new ZipEntry(relPath.toString()));
+ Files.copy(file, zos);
+ zos.closeEntry();
+ return FileVisitResult.CONTINUE;
+ }
+ });
+ }
+ return jarPath;
+ } catch (IOException e) {
+ throw new A2Exception("Cannot install OSGi bundle from " + dir, e);
+ }
+
+ }
+
+}
--- /dev/null
+package org.argeo.api.a2;
+
+import java.io.File;
+import java.io.IOException;
+import java.lang.System.Logger;
+import java.lang.System.Logger.Level;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Arrays;
+import java.util.List;
+
+import org.osgi.framework.Version;
+
+/**
+ * A provisioning source based on the linear classpath with which the JVM has
+ * been started.
+ */
+public class ClasspathSource extends AbstractProvisioningSource {
+ private final static Logger logger = System.getLogger(ClasspathSource.class.getName());
+
+ public ClasspathSource() {
+ super(true);
+ }
+
+ void load() throws IOException {
+ A2Contribution classpathContribution = getOrAddContribution(A2Contribution.CLASSPATH);
+ List<String> classpath = Arrays.asList(System.getProperty("java.class.path").split(File.pathSeparator));
+ parts: for (String part : classpath) {
+ Path file = Paths.get(part);
+ Version version;
+ try {
+ version = new Version(readVersionFromModule(file));
+ } catch (Exception e) {
+ // ignore non OSGi
+ continue parts;
+ }
+ String moduleName = readSymbolicNameFromModule(file);
+ A2Component component = classpathContribution.getOrAddComponent(moduleName);
+ A2Module module = component.getOrAddModule(version, file);
+ logger.log(Level.TRACE, () -> "Registered " + module);
+ }
+
+ }
+}
--- /dev/null
+package org.argeo.api.a2;
+
+import java.io.IOException;
+import java.lang.System.Logger;
+import java.lang.System.Logger.Level;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.nio.file.DirectoryStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.SortedMap;
+import java.util.StringJoiner;
+import java.util.TreeMap;
+
+import org.osgi.framework.Version;
+
+/** A file system {@link AbstractProvisioningSource} in A2 format. */
+public class FsA2Source extends AbstractProvisioningSource implements A2Source {
+ private final static Logger logger = System.getLogger(FsA2Source.class.getName());
+
+ private final Path base;
+ private final Map<String, String> variantsXOr;
+
+ private final List<String> includes;
+ private final List<String> excludes;
+
+ public FsA2Source(Path base, Map<String, String> variantsXOr, boolean usingReference, List<String> includes,
+ List<String> excludes) {
+ super(usingReference);
+ this.base = base;
+ this.variantsXOr = new HashMap<>(variantsXOr);
+ this.includes = includes;
+ this.excludes = excludes;
+ }
+
+ void load() throws IOException {
+ SortedMap<Path, A2Contribution> contributions = new TreeMap<>();
+
+ DirectoryStream<Path> contributionPaths = Files.newDirectoryStream(base);
+ contributions: for (Path contributionPath : contributionPaths) {
+ if (Files.isDirectory(contributionPath)) {
+ String contributionId = contributionPath.getFileName().toString();
+ if (A2Contribution.BOOT.equals(contributionId))// skip boot
+ continue contributions;
+ if (contributionId.contains(".")) {
+ A2Contribution contribution = getOrAddContribution(contributionId);
+ contributions.put(contributionPath, contribution);
+ } else {// variants
+ Path variantPath = null;
+ // is it an explicit variant?
+ String variant = variantsXOr.get(contributionPath.getFileName().toString());
+ if (variant != null) {
+ variantPath = contributionPath.resolve(variant);
+ }
+
+ // is there a default variant?
+ if (variantPath == null) {
+ Path defaultPath = contributionPath.resolve(A2Contribution.DEFAULT);
+ if (Files.exists(defaultPath)) {
+ variantPath = defaultPath;
+ }
+ }
+
+ if (variantPath == null)
+ continue contributions;
+
+ // a variant was found, let's collect its contributions (also common ones in its
+ // parent)
+ if (Files.exists(variantPath.getParent())) {
+ for (Path variantContributionPath : Files.newDirectoryStream(variantPath.getParent())) {
+ String variantContributionId = variantContributionPath.getFileName().toString();
+ if (variantContributionId.contains(".")) {
+ A2Contribution contribution = getOrAddContribution(variantContributionId);
+ contributions.put(variantContributionPath, contribution);
+ }
+ }
+ }
+ if (Files.exists(variantPath)) {
+ for (Path variantContributionPath : Files.newDirectoryStream(variantPath)) {
+ String variantContributionId = variantContributionPath.getFileName().toString();
+ if (variantContributionId.contains(".")) {
+ A2Contribution contribution = getOrAddContribution(variantContributionId);
+ contributions.put(variantContributionPath, contribution);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ contributions: for (Path contributionPath : contributions.keySet()) {
+ String contributionId = contributionPath.getFileName().toString();
+ if (includes != null && !includes.contains(contributionId))
+ continue contributions;
+ if (excludes != null && excludes.contains(contributionId))
+ continue contributions;
+ A2Contribution contribution = getOrAddContribution(contributionId);
+ DirectoryStream<Path> modulePaths = Files.newDirectoryStream(contributionPath);
+ modules: for (Path modulePath : modulePaths) {
+ if (!Files.isDirectory(modulePath)) {
+ // OsgiBootUtils.debug("Registering " + modulePath);
+ String moduleFileName = modulePath.getFileName().toString();
+ int lastDot = moduleFileName.lastIndexOf('.');
+ String ext = moduleFileName.substring(lastDot + 1);
+ if (!"jar".equals(ext))
+ continue modules;
+ Version version;
+ // TODO optimise? check attributes?
+ String[] nameVersion = readNameVersionFromModule(modulePath);
+ String componentName = nameVersion[0];
+ String versionStr = nameVersion[1];
+ if (versionStr != null) {
+ version = new Version(versionStr);
+ } else {
+ logger.log(Level.TRACE, () -> "Ignore " + modulePath + " since version cannot be found");
+ continue modules;
+ }
+// }
+ A2Component component = contribution.getOrAddComponent(componentName);
+ A2Module module = component.getOrAddModule(version, modulePath);
+ logger.log(Level.TRACE, () -> "Registered " + module);
+ }
+ }
+ }
+
+ }
+
+ @Override
+ public URI getUri() {
+ URI baseUri = base.toUri();
+ try {
+ if (baseUri.getScheme().equals("file")) {
+ String queryPart = "";
+ if (!getVariantsXOr().isEmpty()) {
+ StringJoiner sj = new StringJoiner("&");
+ for (String key : getVariantsXOr().keySet()) {
+ sj.add(key + "=" + getVariantsXOr().get(key));
+ }
+ queryPart = sj.toString();
+ }
+ return new URI(isUsingReference() ? SCHEME_A2_REFERENCE : SCHEME_A2, null, base.toString(), queryPart,
+ null);
+ } else {
+ throw new UnsupportedOperationException("Unsupported scheme " + baseUri.getScheme());
+ }
+ } catch (URISyntaxException e) {
+ throw new IllegalStateException("Cannot build URI from " + baseUri, e);
+ }
+ }
+
+ protected Map<String, String> getVariantsXOr() {
+ return variantsXOr;
+ }
+
+// public static void main(String[] args) {
+// if (args.length == 0)
+// throw new IllegalArgumentException("Usage: <path to A2 base>");
+// try {
+// Map<String, String> xOr = new HashMap<>();
+// xOr.put("osgi", "equinox");
+// xOr.put("swt", "rap");
+// FsA2Source context = new FsA2Source(Paths.get(args[0]), xOr);
+// context.load();
+// context.asTree();
+// } catch (Exception e) {
+// e.printStackTrace();
+// }
+// }
+
+}
--- /dev/null
+package org.argeo.api.a2;
+
+import java.io.File;
+import java.io.IOException;
+import java.lang.System.Logger;
+import java.lang.System.Logger.Level;
+import java.nio.file.FileVisitResult;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.SimpleFileVisitor;
+import java.nio.file.attribute.BasicFileAttributes;
+
+import org.osgi.framework.Version;
+
+/** A file system {@link AbstractProvisioningSource} in Maven 2 format. */
+public class FsM2Source extends AbstractProvisioningSource {
+ private final static Logger logger = System.getLogger(FsM2Source.class.getName());
+
+ private final Path base;
+
+ public FsM2Source(Path base) {
+ super(false);
+ this.base = base;
+ }
+
+ void load() throws IOException {
+ Files.walkFileTree(base, new ArtifactFileVisitor());
+ }
+
+ class ArtifactFileVisitor extends SimpleFileVisitor<Path> {
+
+ @Override
+ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
+ // OsgiBootUtils.debug("Processing " + file);
+ if (file.toString().endsWith(".jar")) {
+ Version version;
+ try {
+ version = new Version(readVersionFromModule(file));
+ } catch (Exception e) {
+ // ignore non OSGi
+ return FileVisitResult.CONTINUE;
+ }
+ String moduleName = readSymbolicNameFromModule(file);
+ Path groupPath = file.getParent().getParent().getParent();
+ Path relGroupPath = base.relativize(groupPath);
+ String contributionName = relGroupPath.toString().replace(File.separatorChar, '.');
+ A2Contribution contribution = getOrAddContribution(contributionName);
+ A2Component component = contribution.getOrAddComponent(moduleName);
+ A2Module module = component.getOrAddModule(version, file);
+ logger.log(Level.TRACE, () -> "Registered " + module);
+ }
+ return super.visitFile(file, attrs);
+ }
+
+ }
+
+ public static void main(String[] args) {
+ try {
+ FsM2Source context = new FsM2Source(Paths.get("/home/mbaudier/.m2/repository"));
+ context.load();
+ context.asTree();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+}
--- /dev/null
+package org.argeo.api.a2;
+
+import java.lang.System.Logger;
+import java.lang.System.Logger.Level;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.framework.Version;
+
+/**
+ * A running OSGi bundle context seen as a {@link AbstractProvisioningSource}.
+ */
+class OsgiContext extends AbstractProvisioningSource {
+ private final static Logger logger = System.getLogger(OsgiContext.class.getName());
+
+ private final BundleContext bc;
+
+ private A2Contribution runtimeContribution;
+
+ public OsgiContext(BundleContext bc) {
+ super(false);
+ this.bc = bc;
+ runtimeContribution = getOrAddContribution(A2Contribution.RUNTIME);
+ }
+
+ public OsgiContext() {
+ super(false);
+ Bundle bundle = FrameworkUtil.getBundle(OsgiContext.class);
+ if (bundle == null)
+ throw new IllegalArgumentException(
+ "OSGi Boot bundle must be started or a bundle context must be specified");
+ this.bc = bundle.getBundleContext();
+ }
+
+ void load() {
+ for (Bundle bundle : bc.getBundles()) {
+ registerBundle(bundle);
+ }
+
+ }
+
+ void registerBundle(Bundle bundle) {
+ String componentId = bundle.getSymbolicName();
+ Version version = bundle.getVersion();
+ A2Component component = runtimeContribution.getOrAddComponent(componentId);
+ A2Module module = component.getOrAddModule(version, bundle);
+ logger.log(Level.TRACE,
+ () -> "Registered bundle module " + module + " (location id: " + bundle.getLocation() + ")");
+
+ }
+}
--- /dev/null
+package org.argeo.api.a2;
+
+import static java.lang.System.Logger.Level.DEBUG;
+import static java.lang.System.Logger.Level.ERROR;
+import static java.lang.System.Logger.Level.INFO;
+import static java.lang.System.Logger.Level.TRACE;
+import static org.argeo.api.a2.A2Source.SCHEME_A2;
+import static org.argeo.api.a2.A2Source.SCHEME_A2_REFERENCE;
+
+import java.io.File;
+import java.io.UnsupportedEncodingException;
+import java.lang.System.Logger;
+import java.net.URI;
+import java.net.URLDecoder;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.Version;
+import org.osgi.framework.wiring.FrameworkWiring;
+
+/** Loads provisioning sources into an OSGi context. */
+public class ProvisioningManager {
+ private final static Logger logger = System.getLogger(ProvisioningManager.class.getName());
+
+ private BundleContext bc;
+ private OsgiContext osgiContext;
+ private List<ProvisioningSource> sources = Collections.synchronizedList(new ArrayList<>());
+
+ public ProvisioningManager(BundleContext bc) {
+ this.bc = bc;
+ osgiContext = new OsgiContext(bc);
+ osgiContext.load();
+ }
+
+ protected void addSource(ProvisioningSource source) {
+ sources.add(source);
+ }
+
+ void installWholeSource(ProvisioningSource source) {
+ Set<Bundle> updatedBundles = new HashSet<>();
+ for (A2Contribution contribution : source.listContributions(null)) {
+ for (A2Component component : contribution.components.values()) {
+ A2Module module = component.last().last();
+ Bundle bundle = installOrUpdate(module);
+ if (bundle != null)
+ updatedBundles.add(bundle);
+ }
+ }
+// FrameworkWiring frameworkWiring = bc.getBundle(0).adapt(FrameworkWiring.class);
+// frameworkWiring.refreshBundles(updatedBundles);
+ }
+
+ public void registerSource(String uri) {
+ try {
+ URI u = new URI(uri);
+
+ // XOR
+ Map<String, List<String>> properties = queryToMap(u);
+ Map<String, String> xOr = new HashMap<>();
+ List<String> includes = null;
+ List<String> excludes = null;
+ for (String key : properties.keySet()) {
+ List<String> lst = properties.get(key);
+ if (A2Source.INCLUDE.equals(key)) {
+ includes = new ArrayList<>(lst);
+ } else if (A2Source.EXCLUDE.equals(key)) {
+ excludes = new ArrayList<>(lst);
+ } else {
+ if (lst.size() != 1)
+ throw new IllegalArgumentException("Invalid XOR definitions in " + uri);
+ xOr.put(key, lst.get(0));
+ }
+ }
+
+ if (SCHEME_A2.equals(u.getScheme()) || SCHEME_A2_REFERENCE.equals(u.getScheme())) {
+ if (u.getHost() == null || "".equals(u.getHost())) {
+ String baseStr = u.getPath();
+ if (File.separatorChar == '\\') {// MS Windows
+ baseStr = baseStr.substring(1).replace('/', File.separatorChar);
+ }
+ Path base = Paths.get(baseStr);
+ if (Files.exists(base)) {
+ FsA2Source source = new FsA2Source(base, xOr, SCHEME_A2_REFERENCE.equals(u.getScheme()),
+ includes, excludes);
+ source.load();
+ addSource(source);
+ logger.log(DEBUG, () -> "Registered " + uri + " as source");
+
+ // OS specific / native
+ String localRelPath = A2Contribution.localOsArchRelativePath();
+ Path localLibBase = base.resolve(A2Contribution.LIB).resolve(localRelPath);
+ if (Files.exists(localLibBase)) {
+ FsA2Source libSource = new FsA2Source(localLibBase, xOr,
+ SCHEME_A2_REFERENCE.equals(u.getScheme()), includes, excludes);
+ libSource.load();
+ addSource(libSource);
+ logger.log(DEBUG,
+ () -> "Registered OS-specific base " + localLibBase + " for source " + uri);
+ }
+ } else {
+ logger.log(TRACE, () -> "Source " + base + " does not exist, ignoring.");
+ }
+ } else {
+ throw new UnsupportedOperationException(
+ "Remote installation is not yet supported, cannot add source " + u);
+ }
+ } else {
+ throw new IllegalArgumentException("Unkown scheme: for source " + u);
+ }
+ } catch (Exception e) {
+ throw new A2Exception("Cannot add source " + uri, e);
+ }
+ }
+
+ public boolean registerDefaultSource() {
+ String frameworkLocation = bc.getProperty("osgi.framework");
+ try {
+ URI frameworkLocationUri = new URI(frameworkLocation);
+ if ("file".equals(frameworkLocationUri.getScheme())) {
+ Path frameworkPath = Paths.get(frameworkLocationUri);
+ if (frameworkPath.getParent().getFileName().toString().equals(A2Contribution.BOOT)) {
+ Path base = frameworkPath.getParent().getParent();
+ String baseStr = base.toString();
+ if (File.separatorChar == '\\')// MS Windows
+ baseStr = '/' + baseStr.replace(File.separatorChar, '/');
+ URI baseUri = new URI(A2Source.SCHEME_A2, null, null, 0, baseStr, null, null);
+ registerSource(baseUri.toString());
+ logger.log(TRACE, () -> "Default source from framework location " + frameworkLocation);
+ return true;
+ }
+ }
+ } catch (Exception e) {
+ logger.log(ERROR, "Cannot register default source based on framework location " + frameworkLocation, e);
+ }
+ return false;
+ }
+
+ public void install(String spec) {
+ if (spec == null) {
+ for (ProvisioningSource source : sources) {
+ installWholeSource(source);
+ }
+ }
+ }
+
+ /** @return the new/updated bundle, or null if nothing was done. */
+ protected Bundle installOrUpdate(A2Module module) {
+ try {
+ ProvisioningSource moduleSource = module.getBranch().getComponent().getContribution().getSource();
+ Version moduleVersion = module.getVersion();
+ A2Branch osgiBranch = osgiContext.findBranch(module.getBranch().getComponent().getId(), moduleVersion);
+ if (osgiBranch == null) {
+ Bundle bundle = moduleSource.install(bc, module);
+ // TODO make it more dynamic, based on OSGi APIs
+ osgiContext.registerBundle(bundle);
+// if (OsgiBootUtils.isDebug())
+// OsgiBootUtils.debug("Installed bundle " + bundle.getLocation() + " with version " + moduleVersion);
+ return bundle;
+ } else {
+ A2Module lastOsgiModule = osgiBranch.last();
+ int compare = moduleVersion.compareTo(lastOsgiModule.getVersion());
+ if (compare >= 0) {// update (also if same version)
+ Bundle bundle = (Bundle) lastOsgiModule.getLocator();
+ if (bundle.getBundleId() == 0)// ignore framework bundle
+ return null;
+ moduleSource.update(bundle, module);
+ // TODO make it more dynamic, based on OSGi APIs
+ // TODO remove old module? Or keep update history?
+ osgiContext.registerBundle(bundle);
+ if (compare == 0)
+ logger.log(TRACE,
+ () -> "Updated bundle " + bundle.getLocation() + " to same version " + moduleVersion);
+ else
+ logger.log(INFO, "Updated bundle " + bundle.getLocation() + " to version " + moduleVersion);
+ return bundle;
+ } else {
+ logger.log(TRACE,
+ () -> "Did not install as bundle module " + module + " since a module with higher version "
+ + lastOsgiModule.getVersion() + " is already installed for branch " + osgiBranch);
+ }
+ }
+ } catch (Exception e) {
+ logger.log(ERROR, "Could not install module " + module + ": " + e.getMessage(), e);
+ }
+ return null;
+ }
+
+ public Collection<Bundle> update() {
+ boolean fragmentsUpdated = false;
+ Set<Bundle> updatedBundles = new HashSet<>();
+ bundles: for (Bundle bundle : bc.getBundles()) {
+ for (ProvisioningSource source : sources) {
+ String componentId = bundle.getSymbolicName();
+ Version version = bundle.getVersion();
+ A2Branch branch = source.findBranch(componentId, version);
+ if (branch == null)
+ continue bundles;
+ A2Module module = branch.last();
+ Version moduleVersion = module.getVersion();
+ int compare = moduleVersion.compareTo(version);
+ if (compare > 0) {// update
+ try {
+ source.update(bundle, module);
+// bundle.update(in);
+ String fragmentHost = bundle.getHeaders().get(Constants.FRAGMENT_HOST);
+ if (fragmentHost != null)
+ fragmentsUpdated = true;
+ logger.log(INFO, "Updated bundle " + bundle.getLocation() + " to version " + moduleVersion);
+ updatedBundles.add(bundle);
+ } catch (Exception e) {
+ logger.log(ERROR, "Cannot update with module " + module, e);
+ }
+ }
+ }
+ }
+ FrameworkWiring frameworkWiring = bc.getBundle(0).adapt(FrameworkWiring.class);
+ if (fragmentsUpdated)// refresh all
+ frameworkWiring.refreshBundles(null);
+ else
+ frameworkWiring.refreshBundles(updatedBundles);
+ return updatedBundles;
+ }
+
+ private static Map<String, List<String>> queryToMap(URI uri) {
+ return queryToMap(uri.getQuery());
+ }
+
+ private static Map<String, List<String>> queryToMap(String queryPart) {
+ try {
+ final Map<String, List<String>> query_pairs = new LinkedHashMap<String, List<String>>();
+ if (queryPart == null)
+ return query_pairs;
+ final String[] pairs = queryPart.split("&");
+ for (String pair : pairs) {
+ final int idx = pair.indexOf("=");
+ final String key = idx > 0 ? URLDecoder.decode(pair.substring(0, idx), StandardCharsets.UTF_8.name())
+ : pair;
+ if (!query_pairs.containsKey(key)) {
+ query_pairs.put(key, new LinkedList<String>());
+ }
+ final String value = idx > 0 && pair.length() > idx + 1
+ ? URLDecoder.decode(pair.substring(idx + 1), StandardCharsets.UTF_8.name())
+ : null;
+ query_pairs.get(key).add(value);
+ }
+ return query_pairs;
+ } catch (UnsupportedEncodingException e) {
+ throw new IllegalArgumentException("Cannot convert " + queryPart + " to map", e);
+ }
+ }
+
+// public static void main(String[] args) {
+// if (args.length == 0)
+// throw new IllegalArgumentException("Usage: <path to A2 base>");
+// Map<String, String> configuration = new HashMap<>();
+// configuration.put("osgi.console", "2323");
+// configuration.put("org.osgi.framework.bootdelegation",
+// "com.sun.jndi.ldap,com.sun.jndi.ldap.sasl,com.sun.security.jgss,com.sun.jndi.dns,com.sun.nio.file,com.sun.nio.sctp,sun.nio.cs");
+// Framework framework = OsgiBootUtils.launch(configuration);
+// try {
+// ProvisioningManager pm = new ProvisioningManager(framework.getBundleContext());
+// Map<String, String> xOr = new HashMap<>();
+// xOr.put("osgi", "equinox");
+// xOr.put("swt", "rap");
+// FsA2Source context = new FsA2Source(Paths.get(args[0]), xOr);
+// context.load();
+// pm.addSource(context);
+// if (framework.getBundleContext().getBundles().length == 1) {// initial
+// pm.install(null);
+// } else {
+// pm.update();
+// }
+//
+// Thread.sleep(2000);
+//
+// Bundle[] bundles = framework.getBundleContext().getBundles();
+// Arrays.sort(bundles, (b1, b2) -> b1.getSymbolicName().compareTo(b2.getSymbolicName()));
+// for (Bundle b : bundles)
+// if (b.getState() == Bundle.RESOLVED || b.getState() == Bundle.STARTING || b.getState() == Bundle.ACTIVE)
+// System.out.println(b.getSymbolicName() + " " + b.getVersion());
+// else
+// System.err.println(b.getSymbolicName() + " " + b.getVersion() + " (" + b.getState() + ")");
+// } catch (Exception e) {
+// e.printStackTrace();
+// } finally {
+// try {
+// framework.stop();
+// } catch (Exception e) {
+// e.printStackTrace();
+// }
+// }
+// }
+
+}
--- /dev/null
+package org.argeo.api.a2;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Version;
+
+/** Where components are retrieved from. */
+public interface ProvisioningSource {
+ /** List all contributions of this source. */
+ Iterable<A2Contribution> listContributions(Object filter);
+
+ /** Install a module in the OSGi runtime. */
+ Bundle install(BundleContext bc, A2Module module);
+
+ /** Update a module in the OSGi runtime. */
+ void update(Bundle bundle, A2Module module);
+
+ /** Finds the {@link A2Branch} related to this component and version. */
+ A2Branch findBranch(String componentId, Version version);
+
+}
--- /dev/null
+/** A2 OSGi repository format. */
+package org.argeo.api.a2;
\ No newline at end of file
--- /dev/null
+package org.argeo.api.init;
+
+/** Supported init constants. */
+public interface InitConstants {
+
+ String PROP_ARGEO_OSGI_SOURCES = "argeo.osgi.sources";
+ String PROP_ARGEO_OSGI_START = "argeo.osgi.start";
+ String PROP_OSGI_INSTANCE_AREA = "osgi.instance.area";
+ String PROP_OSGI_CONFIGURATION_AREA = "osgi.configuration.area";
+ String PROP_OSGI_SHARED_CONFIGURATION_AREA = "osgi.sharedConfiguration.area";
+ String PROP_ARGEO_OSGI_MAX_START_LEVEL = "argeo.osgi.maxStartLevel";
+ /** UUID of the parent framework. Marks a nested runtime. */
+ String PROP_ARGEO_OSGI_PARENT_UUID = "argeo.osgi.parent.uuid";
+
+ // OSGi standard properties
+ String PROP_OSGI_BUNDLES_DEFAULTSTARTLEVEL = "osgi.bundles.defaultStartLevel";
+ String PROP_OSGI_STARTLEVEL = "osgi.startLevel";
+ String PROP_OSGI_USE_SYSTEM_PROPERTIES = "osgi.framework.useSystemProperties";
+
+ // Symbolic names
+ String SYMBOLIC_NAME_INIT = "org.argeo.init";
+ String SYMBOLIC_NAME_EQUINOX = "org.eclipse.osgi";
+
+}
--- /dev/null
+package org.argeo.api.init;
+
+/** A runtime context with a life cycle. */
+public interface RuntimeContext extends Runnable {
+ /** Wait until this runtime context has closed. */
+ void waitForStop(long timeout) throws InterruptedException;
+
+ /** Close this runtime context. */
+ void close() throws Exception;
+}
--- /dev/null
+package org.argeo.api.init;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UncheckedIOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Map;
+import java.util.Properties;
+import java.util.function.Consumer;
+
+/** Dynamically manages multiple runtimes within a single JVM. */
+public interface RuntimeManager {
+ String JVM_ARGS = "jvm.args";
+ String STATE = "state";
+ String DATA = "data";
+
+ public void startRuntime(String relPath, Consumer<Map<String, String>> configCallback);
+
+ public void closeRuntime(String relPath, boolean async);
+
+ /**
+ * Load configs recursively starting with the parent directories, until a
+ * jvm.args file is found.
+ */
+ static void loadConfig(Path dir, Map<String, String> config) {
+ try {
+ Path jvmArgsPath = dir.resolve(RuntimeManager.JVM_ARGS);
+ if (!Files.exists(jvmArgsPath)) {
+ // load from parent directory
+ loadConfig(dir.getParent(), config);
+ }
+
+ if (Files.exists(dir))
+ for (Path p : Files.newDirectoryStream(dir, "*.ini")) {
+ try (InputStream in = Files.newInputStream(p)) {
+ loadConfig(in, config);
+ }
+ }
+ } catch (IOException e) {
+ throw new UncheckedIOException("Cannot load configuration from " + dir, e);
+ }
+ }
+
+ /**
+ * Load config from a {@link Properties} formatted stream. If a property value
+ * starts with a '+' character, itis expected that the last character is a
+ * separator and it will be prepended to the existing value.
+ */
+ static void loadConfig(InputStream in, Map<String, String> config) throws IOException {
+ Properties props = new Properties();
+ props.load(in);
+ for (Object k : props.keySet()) {
+ String key = k.toString();
+ String value = props.getProperty(key);
+ if (value.length() > 1 && '+' == value.charAt(0)) {
+ String currentValue = config.get(key);
+ if (currentValue == null || "".equals(currentValue)) {
+ // remove the + and the trailing separator
+ value = value.substring(1, value.length() - 1);
+ config.put(key, value);
+ } else {
+ // remove the + but keep the trailing separator
+ value = value.substring(1);
+ config.put(key, value + currentValue);
+ }
+ } else {
+ config.put(key, value);
+ }
+ }
+ }
+}
+++ /dev/null
-package org.argeo.init;
-
-/** A runtime context with a life cycle. */
-public interface RuntimeContext extends Runnable {
- /** Wait until this runtime context has closed. */
- void waitForStop(long timeout) throws InterruptedException;
-
- /** Close this runtime context. */
- void close() throws Exception;
-}
--- /dev/null
+package org.argeo.init;
+
+import static org.argeo.api.init.InitConstants.SYMBOLIC_NAME_INIT;
+
+import java.lang.System.Logger;
+import java.lang.System.Logger.Level;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.function.Consumer;
+
+import org.argeo.api.init.InitConstants;
+import org.argeo.api.init.RuntimeContext;
+import org.argeo.api.init.RuntimeManager;
+import org.argeo.init.logging.ThinLoggerFinder;
+import org.argeo.init.osgi.OsgiBoot;
+import org.argeo.init.osgi.OsgiRuntimeContext;
+import org.argeo.internal.init.InternalState;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.FrameworkEvent;
+import org.osgi.framework.launch.Framework;
+
+/**
+ * Dynamically configures and launches multiple runtimes, coordinated by a main
+ * one.
+ */
+public class RuntimeManagerMain implements RuntimeManager {
+ private final static Logger logger = System.getLogger(RuntimeManagerMain.class.getName());
+
+ private final static String ENV_STATE_DIRECTORY = "STATE_DIRECTORY";
+// private final static String ENV_CONFIGURATION_DIRECTORY = "CONFIGURATION_DIRECTORY";
+// private final static String ENV_CACHE_DIRECTORY = "CACHE_DIRECTORY";
+
+ private final static long RUNTIME_SHUTDOWN_TIMEOUT = 60 * 1000;
+
+ private Path baseConfigArea;
+ private Path baseWritableArea;
+ private Map<String, String> configuration = new HashMap<>();
+
+ private Map<String, OsgiRuntimeContext> runtimeContexts = new TreeMap<>();
+
+ RuntimeManagerMain(Path configArea, Path stateArea) {
+ RuntimeManager.loadConfig(configArea, configuration);
+ configuration.put(InitConstants.PROP_OSGI_CONFIGURATION_AREA, stateArea.resolve(STATE).toUri().toString());
+ // use config area if instance area is not set
+ if (!configuration.containsKey(InitConstants.PROP_OSGI_INSTANCE_AREA))
+ configuration.put(InitConstants.PROP_OSGI_INSTANCE_AREA, stateArea.resolve(DATA).toUri().toString());
+ this.baseConfigArea = configArea.getParent();
+ this.baseWritableArea = stateArea.getParent();
+
+ logger.log(Level.TRACE, () -> "Runtime manager configuration: " + configuration);
+
+// System.out.println("java.library.path=" + System.getProperty("java.library.path"));
+ }
+
+ public void run() {
+ OsgiRuntimeContext managerRuntimeContext = new OsgiRuntimeContext(configuration);
+ try {
+ managerRuntimeContext.run();
+ InternalState.setMainRuntimeContext(managerRuntimeContext);
+
+ // shutdown on exit
+ Runtime.getRuntime().addShutdownHook(new Thread(() -> shutdown(), "Runtime shutdown"));
+
+ BundleContext bc = managerRuntimeContext.getFramework().getBundleContext();
+ // uninstall init as a bundle since it will be available via OSGi system
+ OsgiBoot.uninstallBundles(bc, SYMBOLIC_NAME_INIT);
+ bc.registerService(RuntimeManager.class, this, new Hashtable<>(configuration));
+ logger.log(Level.DEBUG, "Registered runtime manager");
+
+ managerRuntimeContext.waitForStop(0);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ System.exit(1);
+ }
+
+ }
+
+ protected void shutdown() {
+ // shutdowm runtimes
+ Map<String, RuntimeContext> shutdowning = new HashMap<>(runtimeContexts);
+ for (String id : new HashSet<>(runtimeContexts.keySet())) {
+ logger.log(Logger.Level.DEBUG, "Shutting down runtime " + id + " ...");
+ closeRuntime(id, true);
+ }
+ for (String id : shutdowning.keySet())
+ try {
+ RuntimeContext runtimeContext = shutdowning.get(id);
+ runtimeContext.waitForStop(RUNTIME_SHUTDOWN_TIMEOUT);
+ } catch (InterruptedException e) {
+ // silent
+ } catch (Exception e) {
+ logger.log(Logger.Level.DEBUG, "Cannot wait for " + id + " to shutdown", e);
+ }
+ // shutdown manager runtime
+ try {
+ InternalState.getMainRuntimeContext().close();
+ InternalState.getMainRuntimeContext().waitForStop(RUNTIME_SHUTDOWN_TIMEOUT);
+// logger.log(Logger.Level.INFO, "Argeo Init stopped with PID " + ProcessHandle.current().pid());
+ System.out.flush();
+ } catch (Exception e) {
+ e.printStackTrace();
+ Runtime.getRuntime().halt(1);
+ }
+ }
+
+ OsgiRuntimeContext loadRuntime(String relPath, Consumer<Map<String, String>> configCallback) {
+ closeRuntime(relPath, false);
+ Path writableArea = baseWritableArea.resolve(relPath);
+ Path configArea = baseConfigArea.resolve(relPath);
+ Map<String, String> config = new HashMap<>();
+ RuntimeManager.loadConfig(configArea, config);
+ config.put(InitConstants.PROP_OSGI_CONFIGURATION_AREA, writableArea.resolve(STATE).toUri().toString());
+
+ if (configCallback != null)
+ configCallback.accept(config);
+
+ // use config area if instance area is not set
+ if (!config.containsKey(InitConstants.PROP_OSGI_INSTANCE_AREA))
+ config.put(InitConstants.PROP_OSGI_INSTANCE_AREA, writableArea.resolve(DATA).toUri().toString());
+
+ OsgiRuntimeContext runtimeContext = new OsgiRuntimeContext(config);
+ runtimeContexts.put(relPath, runtimeContext);
+ return runtimeContext;
+ }
+
+ public void startRuntime(String relPath, Consumer<Map<String, String>> configCallback) {
+ OsgiRuntimeContext runtimeContext = loadRuntime(relPath, configCallback);
+ runtimeContext.run();
+ Framework framework = runtimeContext.getFramework();
+ if (framework != null) {// in case the framework has closed very quickly after run
+ framework.getBundleContext().addFrameworkListener((e) -> {
+ if (e.getType() >= FrameworkEvent.STOPPED) {
+ logger.log(Level.DEBUG, "Externally stopped runtime " + relPath + ". Unregistering...", e);
+ runtimeContexts.remove(relPath);
+ }
+ });
+ } else {
+ closeRuntime(relPath, false);
+ }
+ }
+
+ public void closeRuntime(String relPath, boolean async) {
+ if (!runtimeContexts.containsKey(relPath))
+ return;
+ RuntimeContext runtimeContext = runtimeContexts.get(relPath);
+ try {
+ runtimeContext.close();
+ if (!async) {
+ runtimeContext.waitForStop(RUNTIME_SHUTDOWN_TIMEOUT);
+ System.gc();
+ }
+ } catch (Exception e) {
+ logger.log(Level.ERROR, "Cannot close runtime context " + relPath, e);
+ } finally {
+ runtimeContexts.remove(relPath);
+ }
+
+ }
+
+ public static void main(String[] args) {
+ ThinLoggerFinder.reloadConfiguration();
+ logger.log(Logger.Level.DEBUG, () -> "Argeo Init starting with PID " + ProcessHandle.current().pid());
+ Map<String, String> env = System.getenv();
+// for (String envName : new TreeSet<>(env.keySet())) {
+// System.out.format("%s=%s%n", envName, env.get(envName));
+// }
+ if (args.length < 1)
+ throw new IllegalArgumentException("A relative configuration directory must be specified");
+ Path configArea = Paths.get(System.getProperty("user.dir"), args[0]);
+
+ // System.out.println("## Start with PID " + ProcessHandle.current().pid());
+ // System.out.println("user.dir=" + System.getProperty("user.dir"));
+
+ Path stateArea = Paths.get(env.get(ENV_STATE_DIRECTORY));
+
+ RuntimeManagerMain runtimeManager = new RuntimeManagerMain(configArea, stateArea);
+ runtimeManager.run();
+ }
+
+}
+++ /dev/null
-package org.argeo.init;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.lang.System.Logger;
-import java.lang.System.Logger.Level;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Properties;
-import java.util.TreeMap;
-
-import org.argeo.init.logging.ThinLoggerFinder;
-import org.argeo.init.osgi.OsgiBoot;
-import org.argeo.init.osgi.OsgiRuntimeContext;
-
-/** Configure and launch an Argeo service. */
-public class Service {
- private final static Logger logger = System.getLogger(Service.class.getName());
-
- final static String FILE_SYSTEM_PROPERTIES = "system.properties";
-
- public final static String PROP_ARGEO_INIT_MAIN = "argeo.init.main";
-
- private static RuntimeContext runtimeContext = null;
-
- private static List<Runnable> postStart = Collections.synchronizedList(new ArrayList<>());
-
- protected Service(String[] args) {
- }
-
- public static void main(String[] args) {
- final long pid = ProcessHandle.current().pid();
- logger.log(Logger.Level.DEBUG, () -> "Argeo Init starting with PID " + pid);
-
- // shutdown on exit
- Runtime.getRuntime().addShutdownHook(new Thread(() -> {
- try {
- if (Service.runtimeContext != null) {
-// System.out.println("Argeo Init stopping with PID " + pid);
- Service.runtimeContext.close();
- Service.runtimeContext.waitForStop(0);
- }
- } catch (Exception e) {
- e.printStackTrace();
- Runtime.getRuntime().halt(1);
- }
- }, "Runtime shutdown"));
-
- // TODO use args as well
- String dataArea = System.getProperty(OsgiBoot.PROP_OSGI_INSTANCE_AREA);
- String stateArea = System.getProperty(OsgiBoot.PROP_OSGI_CONFIGURATION_AREA);
- String configArea = System.getProperty(OsgiBoot.PROP_OSGI_SHARED_CONFIGURATION_AREA);
-
- if (configArea != null) {
- Path configAreaPath = Paths.get(configArea);
- Path additionalSystemPropertiesPath = configAreaPath.resolve(FILE_SYSTEM_PROPERTIES);
- if (Files.exists(additionalSystemPropertiesPath)) {
- Properties properties = new Properties();
- try (InputStream in = Files.newInputStream(additionalSystemPropertiesPath)) {
- properties.load(in);
- } catch (IOException e) {
- logger.log(Logger.Level.ERROR,
- "Cannot load additional system properties " + additionalSystemPropertiesPath, e);
- }
-
- for (Object key : properties.keySet()) {
- String currentValue = System.getProperty(key.toString());
- String value = properties.getProperty(key.toString());
- if (currentValue != null) {
- if (!Objects.equals(value, currentValue))
- logger.log(Logger.Level.WARNING, "System property " + key + " already set with value "
- + currentValue + " instead of " + value + ". Ignoring new value.");
- } else {
- System.setProperty(key.toString(), value);
- logger.log(Logger.Level.TRACE, () -> "Added " + key + "=" + value
- + " to system properties, from " + additionalSystemPropertiesPath.getFileName());
- }
- }
- ThinLoggerFinder.reloadConfiguration();
- }
- }
-
- Map<String, String> config = new HashMap<>();
- config.put(PROP_ARGEO_INIT_MAIN, "true");
-
- // add OSGi system properties to the configuration
- sysprops: for (Object key : new TreeMap<>(System.getProperties()).keySet()) {
- String keyStr = key.toString();
- switch (keyStr) {
- case OsgiBoot.PROP_OSGI_CONFIGURATION_AREA:
- case OsgiBoot.PROP_OSGI_SHARED_CONFIGURATION_AREA:
- case OsgiBoot.PROP_OSGI_INSTANCE_AREA:
- // we should already have dealt with those
- continue sysprops;
- default:
- }
-
- if (keyStr.startsWith("osgi.") || keyStr.startsWith("org.osgi.") || keyStr.startsWith("eclipse.")
- || keyStr.startsWith("org.eclipse.equinox.") || keyStr.startsWith("felix.")) {
- String value = System.getProperty(keyStr);
- config.put(keyStr, value);
- logger.log(Logger.Level.TRACE,
- () -> "Added " + key + "=" + value + " to configuration, from system properties");
- }
- }
-
- try {
- try {
- if (stateArea != null)
- config.put(OsgiBoot.PROP_OSGI_CONFIGURATION_AREA, stateArea);
- if (configArea != null)
- config.put(OsgiBoot.PROP_OSGI_SHARED_CONFIGURATION_AREA, configArea);
- if (dataArea != null)
- config.put(OsgiBoot.PROP_OSGI_INSTANCE_AREA, dataArea);
- // config.put(OsgiBoot.PROP_OSGI_USE_SYSTEM_PROPERTIES, "true");
-
- OsgiRuntimeContext osgiRuntimeContext = new OsgiRuntimeContext(config);
- osgiRuntimeContext.run();
- Service.runtimeContext = osgiRuntimeContext;
- for (Runnable run : postStart) {
- try {
- run.run();
- } catch (Exception e) {
- logger.log(Level.ERROR, "Cannot run post start callback " + run, e);
- }
- }
- Service.runtimeContext.waitForStop(0);
- } catch (NoClassDefFoundError noClassDefFoundE) {
- StaticRuntimeContext staticRuntimeContext = new StaticRuntimeContext((Map<String, String>) config);
- staticRuntimeContext.run();
- Service.runtimeContext = staticRuntimeContext;
- for (Runnable run : postStart) {
- try {
- run.run();
- } catch (Exception e) {
- logger.log(Level.ERROR, "Cannot run post start callback " + run, e);
- }
- }
- Service.runtimeContext.waitForStop(0);
- }
- } catch (Exception e) {
- e.printStackTrace();
- System.exit(1);
- }
- logger.log(Logger.Level.DEBUG, "Argeo Init stopped with PID " + pid);
- }
-
- /** The root runtime context in this JVM. */
- public static RuntimeContext getRuntimeContext() {
- return runtimeContext;
- }
-
- /** Add a post-start call back to be run after the runtime has been started. */
- public static void addPostStart(Runnable runnable) {
- postStart.add(runnable);
- }
-}
--- /dev/null
+package org.argeo.init;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.System.Logger;
+import java.lang.System.Logger.Level;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Properties;
+import java.util.TreeMap;
+
+import org.argeo.api.init.InitConstants;
+import org.argeo.init.logging.ThinLoggerFinder;
+import org.argeo.init.osgi.OsgiRuntimeContext;
+import org.argeo.internal.init.InternalState;
+
+/** Configures and launches a single runtime, typically as a systemd service. */
+public class ServiceMain {
+ private final static Logger logger = System.getLogger(ServiceMain.class.getName());
+
+ final static String FILE_SYSTEM_PROPERTIES = "system.properties";
+
+ public final static String PROP_ARGEO_INIT_MAIN = "argeo.init.main";
+
+// private static RuntimeContext runtimeContext = null;
+
+ private static List<Runnable> postStart = Collections.synchronizedList(new ArrayList<>());
+
+ protected ServiceMain(String[] args) {
+ }
+
+ public static void main(String[] args) {
+ final long pid = ProcessHandle.current().pid();
+ logger.log(Logger.Level.DEBUG, () -> "Argeo Init starting with PID " + pid);
+
+ // shutdown on exit
+ Runtime.getRuntime().addShutdownHook(new Thread(() -> {
+ try {
+ if (InternalState.getMainRuntimeContext() != null) {
+ InternalState.getMainRuntimeContext().close();
+ InternalState.getMainRuntimeContext().waitForStop(0);
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ Runtime.getRuntime().halt(1);
+ }
+ }, "Runtime shutdown"));
+
+ // TODO use args as well
+ String dataArea = System.getProperty(InitConstants.PROP_OSGI_INSTANCE_AREA);
+ String stateArea = System.getProperty(InitConstants.PROP_OSGI_CONFIGURATION_AREA);
+ String configArea = System.getProperty(InitConstants.PROP_OSGI_SHARED_CONFIGURATION_AREA);
+
+ if (configArea != null) {
+ Path configAreaPath = Paths.get(configArea);
+ Path additionalSystemPropertiesPath = configAreaPath.resolve(FILE_SYSTEM_PROPERTIES);
+ if (Files.exists(additionalSystemPropertiesPath)) {
+ Properties properties = new Properties();
+ try (InputStream in = Files.newInputStream(additionalSystemPropertiesPath)) {
+ properties.load(in);
+ } catch (IOException e) {
+ logger.log(Logger.Level.ERROR,
+ "Cannot load additional system properties " + additionalSystemPropertiesPath, e);
+ }
+
+ for (Object key : properties.keySet()) {
+ String currentValue = System.getProperty(key.toString());
+ String value = properties.getProperty(key.toString());
+ if (currentValue != null) {
+ if (!Objects.equals(value, currentValue))
+ logger.log(Logger.Level.WARNING, "System property " + key + " already set with value "
+ + currentValue + " instead of " + value + ". Ignoring new value.");
+ } else {
+ System.setProperty(key.toString(), value);
+ logger.log(Logger.Level.TRACE, () -> "Added " + key + "=" + value
+ + " to system properties, from " + additionalSystemPropertiesPath.getFileName());
+ }
+ }
+ ThinLoggerFinder.reloadConfiguration();
+ }
+ }
+
+ Map<String, String> config = new HashMap<>();
+ config.put(PROP_ARGEO_INIT_MAIN, "true");
+
+ // add OSGi system properties to the configuration
+ sysprops: for (Object key : new TreeMap<>(System.getProperties()).keySet()) {
+ String keyStr = key.toString();
+ switch (keyStr) {
+ case InitConstants.PROP_OSGI_CONFIGURATION_AREA:
+ case InitConstants.PROP_OSGI_SHARED_CONFIGURATION_AREA:
+ case InitConstants.PROP_OSGI_INSTANCE_AREA:
+ // we should already have dealt with those
+ continue sysprops;
+ default:
+ }
+
+ if (keyStr.startsWith("osgi.") || keyStr.startsWith("org.osgi.") || keyStr.startsWith("eclipse.")
+ || keyStr.startsWith("org.eclipse.equinox.") || keyStr.startsWith("felix.")) {
+ String value = System.getProperty(keyStr);
+ config.put(keyStr, value);
+ logger.log(Logger.Level.TRACE,
+ () -> "Added " + key + "=" + value + " to configuration, from system properties");
+ }
+ }
+
+ try {
+ try {
+ if (stateArea != null)
+ config.put(InitConstants.PROP_OSGI_CONFIGURATION_AREA, stateArea);
+ if (configArea != null)
+ config.put(InitConstants.PROP_OSGI_SHARED_CONFIGURATION_AREA, configArea);
+ if (dataArea != null)
+ config.put(InitConstants.PROP_OSGI_INSTANCE_AREA, dataArea);
+ // config.put(OsgiBoot.PROP_OSGI_USE_SYSTEM_PROPERTIES, "true");
+
+ OsgiRuntimeContext osgiRuntimeContext = new OsgiRuntimeContext(config);
+ osgiRuntimeContext.run();
+ InternalState.setMainRuntimeContext(osgiRuntimeContext);
+ for (Runnable run : postStart) {
+ try {
+ run.run();
+ } catch (Exception e) {
+ logger.log(Level.ERROR, "Cannot run post start callback " + run, e);
+ }
+ }
+ InternalState.getMainRuntimeContext().waitForStop(0);
+ } catch (NoClassDefFoundError noClassDefFoundE) {
+ StaticRuntimeContext staticRuntimeContext = new StaticRuntimeContext((Map<String, String>) config);
+ staticRuntimeContext.run();
+ InternalState.setMainRuntimeContext(staticRuntimeContext);
+ for (Runnable run : postStart) {
+ try {
+ run.run();
+ } catch (Exception e) {
+ logger.log(Level.ERROR, "Cannot run post start callback " + run, e);
+ }
+ }
+ InternalState.getMainRuntimeContext().waitForStop(0);
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ System.exit(1);
+ }
+ logger.log(Logger.Level.DEBUG, "Argeo Init stopped with PID " + pid);
+ }
+
+ /** Add a post-start call back to be run after the runtime has been started. */
+ public static void addPostStart(Runnable runnable) {
+ postStart.add(runnable);
+ }
+}
import java.util.Map;
+import org.argeo.api.init.RuntimeContext;
+
public class StaticRuntimeContext implements RuntimeContext {
private Map<String, String> config;
--- /dev/null
+package org.argeo.init;
+//#! /usr/bin/java --source 17 @/usr/local/etc/freed/pid1/jvm.args
+
+import static java.lang.System.Logger.Level.DEBUG;
+import static java.lang.System.Logger.Level.ERROR;
+import static java.lang.System.Logger.Level.INFO;
+import static java.lang.System.Logger.Level.WARNING;
+
+import java.io.Console;
+import java.io.IOException;
+import java.lang.System.Logger;
+import java.lang.management.ManagementFactory;
+import java.net.InetAddress;
+import java.net.InterfaceAddress;
+import java.net.NetworkInterface;
+import java.net.SocketException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.TreeMap;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import sun.misc.Signal;
+
+/** A minimalistic Linux init process. */
+class SysInitMain {
+ final static AtomicInteger runLevel = new AtomicInteger(-1);
+
+ private final static Logger logger = System.getLogger(SysInitMain.class.getName());
+
+ private final static List<String> initDServices = Collections.synchronizedList(new ArrayList<>());
+
+ public static void main(String... args) {
+ try {
+ final long pid = ProcessHandle.current().pid();
+ Signal.handle(new Signal("TERM"), (signal) -> {
+ System.out.println("SIGTERM caught");
+ System.exit(0);
+ });
+ Signal.handle(new Signal("INT"), (signal) -> {
+ System.out.println("SIGINT caught");
+ System.exit(0);
+ });
+ Signal.handle(new Signal("HUP"), (signal) -> {
+ System.out.println("SIGHUP caught");
+ System.exit(0);
+ });
+
+ boolean isSystemInit = pid == 1 || pid == 2;
+
+ if (isSystemInit && args.length > 0 && ("1".equals(args[0]) //
+ || "single".equals(args[0]) //
+ || "emergency".equals(args[0]))) {
+ runLevel.set(1);
+ for (Object key : new TreeMap<>(System.getProperties()).keySet()) {
+ System.out.println(key + "=" + System.getProperty(key.toString()));
+ }
+ System.out.println("Single user mode");
+ System.out.flush();
+ ProcessBuilder pb = new ProcessBuilder("/bin/bash");
+ pb.redirectError(ProcessBuilder.Redirect.INHERIT);
+ pb.redirectOutput(ProcessBuilder.Redirect.INHERIT);
+ pb.redirectInput(ProcessBuilder.Redirect.INHERIT);
+ Process singleUserShell = pb.start();
+ singleUserShell.waitFor();
+ } else {
+ if (args.length == 0)
+ runLevel.set(5);
+ else
+ runLevel.set(Integer.parseInt(args[0]));
+
+ if (runLevel.get() == 0) {// shutting down the whole system
+ if (!isSystemInit) {
+ logger.log(INFO, "Shutting down system...");
+ shutdown(false);
+ System.exit(0);
+ } else {
+ logger.log(ERROR, "Cannot start at run level " + runLevel.get());
+ System.exit(1);
+ }
+ } else if (runLevel.get() == 6) {// reboot the whole system
+ if (!isSystemInit) {
+ logger.log(INFO, "Rebooting the system...");
+ shutdown(true);
+ } else {
+ logger.log(ERROR, "Cannot start at run level " + runLevel.get());
+ System.exit(1);
+ }
+ }
+
+ logger.log(INFO, "FREEd Init daemon starting with pid " + pid + " after "
+ + ManagementFactory.getRuntimeMXBean().getUptime() + " ms");
+ // hostname
+ String hostname = Files.readString(Paths.get("/etc/hostname"));
+ new ProcessBuilder("/usr/bin/hostname", hostname).start();
+ logger.log(DEBUG, "Set hostname to " + hostname);
+ // networking
+ initSysctl();
+ startInitDService("networking", true);
+// Thread.sleep(3000);// leave some time for network to start up
+ if (!waitForNetwork(10 * 1000))
+ logger.log(ERROR, "No network available");
+
+ // OpenSSH
+ // TODO make it coherent with Java sshd
+ startInitDService("ssh", true);
+
+ // NSS services
+ startInitDService("nslcd", false);// Note: nslcd fails to stop
+
+ // login prompt
+ ServiceMain.addPostStart(() -> new LoginThread().start());
+
+ // init Argeo CMS
+ logger.log(INFO, "FREEd Init daemon starting Argeo Init after "
+ + ManagementFactory.getRuntimeMXBean().getUptime() + " ms");
+ ServiceMain.main(args);
+ }
+ } catch (Throwable e) {
+ logger.log(ERROR, "Unexpected exception in free-pid1 init, shutting down... ", e);
+ System.exit(1);
+ } finally {
+ stopInitDServices();
+ }
+ }
+
+ static void initSysctl() {
+ try {
+ Path sysctlD = Paths.get("/etc/sysctl.d/");
+ for (Path conf : Files.newDirectoryStream(sysctlD, "*.conf")) {
+ try {
+ new ProcessBuilder("/usr/sbin/sysctl", "-p", conf.toString()).start();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ static void startInitDService(String serviceName, boolean stopOnShutdown) {
+ Path serviceInit = Paths.get("/etc/init.d/", serviceName);
+ if (Files.exists(serviceInit))
+ try {
+ int exitCode = new ProcessBuilder(serviceInit.toString(), "start").start().waitFor();
+ if (exitCode != 0)
+ logger.log(ERROR, "Service " + serviceName + " dit not stop properly");
+ else
+ logger.log(DEBUG, "Service " + serviceName + " started");
+ if (stopOnShutdown)
+ initDServices.add(serviceName);
+// Runtime.getRuntime().addShutdownHook(new Thread(() -> {
+// try {
+// new ProcessBuilder(serviceInit.toString(), "stop").start().waitFor();
+// } catch (IOException | InterruptedException e) {
+// e.printStackTrace();
+// }
+// }, "FREEd stop service " + serviceName));
+ } catch (IOException | InterruptedException e) {
+ e.printStackTrace();
+ }
+ else
+ logger.log(WARNING, "Service " + serviceName + " not found and therefore not started");
+ }
+
+ static boolean waitForNetwork(long timeout) {
+ long begin = System.currentTimeMillis();
+ long duration = 0;
+ boolean networkAvailable = false;
+ try {
+ networkAvailable: while (!networkAvailable) {
+ duration = System.currentTimeMillis() - begin;
+ if (duration > timeout)
+ break networkAvailable;
+ Enumeration<NetworkInterface> netInterfaces = null;
+ try {
+ netInterfaces = NetworkInterface.getNetworkInterfaces();
+ } catch (SocketException e) {
+ throw new IllegalStateException("Cannot list network interfaces", e);
+ }
+ if (netInterfaces != null) {
+ while (netInterfaces.hasMoreElements()) {
+ NetworkInterface netInterface = netInterfaces.nextElement();
+ logger.log(DEBUG, "Interface:" + netInterface);
+ for (InterfaceAddress addr : netInterface.getInterfaceAddresses()) {
+ InetAddress inetAddr = addr.getAddress();
+ logger.log(DEBUG, " addr: " + inetAddr);
+ if (!inetAddr.isLoopbackAddress() && !inetAddr.isLinkLocalAddress()) {
+ try {
+ if (inetAddr.isReachable((int) timeout)) {
+ networkAvailable = true;
+ duration = System.currentTimeMillis() - begin;
+ logger.log(DEBUG,
+ "Network available after " + duration + " ms. IP: " + inetAddr);
+ break networkAvailable;
+ }
+ } catch (IOException e) {
+ logger.log(ERROR, "Cannot check whether " + inetAddr + " is reachable", e);
+ }
+ }
+ }
+ }
+ } else {
+ throw new IllegalStateException("No network interface has been found");
+ }
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ // silent
+ }
+ }
+ } catch (Exception e) {
+ logger.log(ERROR, "Cannot check whether network is available", e);
+ }
+ return networkAvailable;
+ }
+
+ static void shutdown(boolean reboot) {
+ try {
+ stopInitDServices();
+ Path sysrqP = Paths.get("/proc/sys/kernel/sysrq");
+ Files.writeString(sysrqP, "1");
+ Path sysrqTriggerP = Paths.get("/proc/sysrq-trigger");
+ Files.writeString(sysrqTriggerP, "e");// send SIGTERM to all processes
+ // Files.writeString(sysrqTriggerP, "i");// send SIGKILL to all processes
+ Files.writeString(sysrqTriggerP, "e");// flush data to disk
+ Files.writeString(sysrqTriggerP, "u");// unmount
+ if (reboot)
+ Files.writeString(sysrqTriggerP, "b");
+ else
+ Files.writeString(sysrqTriggerP, "o");
+ } catch (IOException e) {
+ logger.log(ERROR, "Cannot shut down system", e);
+ }
+ }
+
+ static void stopInitDServices() {
+ for (int i = initDServices.size() - 1; i >= 0; i--) {
+ String serviceName = initDServices.get(i);
+ Path serviceInit = Paths.get("/etc/init.d/", serviceName);
+ try {
+ int exitCode = new ProcessBuilder(serviceInit.toString(), "stop").start().waitFor();
+ if (exitCode != 0)
+ logger.log(ERROR, "Service " + serviceName + " dit not stop properly");
+ } catch (InterruptedException | IOException e) {
+ logger.log(ERROR, "Cannot stop service " + serviceName, e);
+ }
+ }
+ }
+
+ /** A thread watching the login prompt. */
+ static class LoginThread extends Thread {
+ private boolean systemShuttingDown = false;
+ private Process process = null;
+
+ public LoginThread() {
+ super("FREEd login prompt");
+ setDaemon(true);
+ Runtime.getRuntime().addShutdownHook(new Thread(() -> {
+ systemShuttingDown = true;
+ if (process != null)
+ process.destroy();
+ }));
+ }
+
+ @Override
+ public void run() {
+ boolean getty = true;
+ prompt: while (!systemShuttingDown) {
+ try {
+ if (getty) {
+ ProcessBuilder pb = new ProcessBuilder("/usr/sbin/getty", "38400", "tty2");
+ process = pb.start();
+ } else {
+ Console console = System.console();
+ console.readLine(); // type return once to activate login prompt
+ console.printf("login: ");
+ String username = console.readLine();
+ username = username.trim();
+ if ("".equals(username))
+ continue prompt;
+ ProcessBuilder pb = new ProcessBuilder("su", "--login", username);
+ pb.redirectError(ProcessBuilder.Redirect.INHERIT);
+ pb.redirectOutput(ProcessBuilder.Redirect.INHERIT);
+ pb.redirectInput(ProcessBuilder.Redirect.INHERIT);
+ process = pb.start();
+ }
+ Runtime.getRuntime().addShutdownHook(new Thread(() -> process.destroy()));
+ try {
+ process.waitFor();
+ } catch (InterruptedException e) {
+ process.destroy();
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ } finally {
+ process = null;
+ }
+ }
+ }
+
+ }
+}
+++ /dev/null
-package org.argeo.init.a2;
-
-import java.util.Collections;
-import java.util.SortedMap;
-import java.util.TreeMap;
-
-import org.argeo.init.osgi.OsgiBootUtils;
-import org.osgi.framework.Version;
-
-/**
- * A logical linear sequence of versions of a given {@link A2Component}. This is
- * typically a combination of major and minor version, indicating backward
- * compatibility.
- */
-public class A2Branch implements Comparable<A2Branch> {
- private final A2Component component;
- private final String id;
-
- final SortedMap<Version, A2Module> modules = Collections.synchronizedSortedMap(new TreeMap<>());
-
- public A2Branch(A2Component component, String id) {
- this.component = component;
- this.id = id;
- component.branches.put(id, this);
- }
-
- public Iterable<A2Module> listModules(Object filter) {
- return modules.values();
- }
-
- A2Module getOrAddModule(Version version, Object locator) {
- if (modules.containsKey(version)) {
- A2Module res = modules.get(version);
- if (OsgiBootUtils.isDebug() && !res.getLocator().equals(locator)) {
- OsgiBootUtils.debug("Inconsistent locator " + locator + " (registered: " + res.getLocator() + ")");
- }
- return res;
- } else
- return new A2Module(this, version, locator);
- }
-
- public A2Module last() {
- return modules.get(modules.lastKey());
- }
-
- public A2Module first() {
- return modules.get(modules.firstKey());
- }
-
- public A2Component getComponent() {
- return component;
- }
-
- public String getId() {
- return id;
- }
-
- @Override
- public int compareTo(A2Branch o) {
- return id.compareTo(id);
- }
-
- @Override
- public int hashCode() {
- return id.hashCode();
- }
-
- @Override
- public boolean equals(Object obj) {
- if (obj instanceof A2Branch) {
- A2Branch o = (A2Branch) obj;
- return component.equals(o.component) && id.equals(o.id);
- } else
- return false;
- }
-
- @Override
- public String toString() {
- return getCoordinates();
- }
-
- public String getCoordinates() {
- return component + ":" + id;
- }
-
- static String versionToBranchId(Version version) {
- return version.getMajor() + "." + version.getMinor();
- }
-}
+++ /dev/null
-package org.argeo.init.a2;
-
-import java.util.Collections;
-import java.util.SortedMap;
-import java.util.TreeMap;
-
-import org.osgi.framework.Version;
-
-/**
- * The logical name of a software package. In OSGi's case this is
- * <code>Bundle-SymbolicName</code>. This is the equivalent of Maven's artifact
- * id.
- */
-public class A2Component implements Comparable<A2Component> {
- private final A2Contribution contribution;
- private final String id;
-
- final SortedMap<String, A2Branch> branches = Collections.synchronizedSortedMap(new TreeMap<>());
-
- public A2Component(A2Contribution contribution, String id) {
- this.contribution = contribution;
- this.id = id;
- contribution.components.put(id, this);
- }
-
- public Iterable<A2Branch> listBranches(Object filter) {
- return branches.values();
- }
-
- A2Branch getOrAddBranch(String branchId) {
- if (!branches.containsKey(branchId)) {
- A2Branch a2Branch = new A2Branch(this, branchId);
- branches.put(branchId, a2Branch);
- }
- return branches.get(branchId);
- }
-
- A2Module getOrAddModule(Version version, Object locator) {
- A2Branch branch = getOrAddBranch(A2Branch.versionToBranchId(version));
- A2Module module = branch.getOrAddModule(version, locator);
- return module;
- }
-
- public A2Branch last() {
- return branches.get(branches.lastKey());
- }
-
- public A2Contribution getContribution() {
- return contribution;
- }
-
- public String getId() {
- return id;
- }
-
- @Override
- public int compareTo(A2Component o) {
- return id.compareTo(o.id);
- }
-
- @Override
- public int hashCode() {
- return id.hashCode();
- }
-
- @Override
- public boolean equals(Object obj) {
- if (obj instanceof A2Component) {
- A2Component o = (A2Component) obj;
- return contribution.equals(o.contribution) && id.equals(o.id);
- } else
- return false;
- }
-
- @Override
- public String toString() {
- return contribution.getId() + ":" + id;
- }
-
- void asTree(String prefix, StringBuffer buf) {
- if (prefix == null)
- prefix = "";
- A2Branch lastBranch = last();
- SortedMap<String, A2Branch> displayMap = new TreeMap<>(Collections.reverseOrder());
- displayMap.putAll(branches);
- for (String branchId : displayMap.keySet()) {
- A2Branch branch = displayMap.get(branchId);
- if (!lastBranch.equals(branch)) {
- buf.append('\n');
- buf.append(prefix);
- } else {
- buf.append(" -");
- }
- buf.append(prefix);
- buf.append(branchId);
- A2Module first = branch.first();
- A2Module last = branch.last();
- buf.append(" (").append(last.getVersion());
- if (!first.equals(last))
- buf.append(" ... ").append(first.getVersion());
- buf.append(')');
- }
- }
-
-}
+++ /dev/null
-package org.argeo.init.a2;
-
-import java.util.Collections;
-import java.util.Map;
-import java.util.TreeMap;
-
-/**
- * A category grouping a set of {@link A2Component}, typically based on the
- * provider of these components. This is the equivalent of Maven's group Id.
- */
-public class A2Contribution implements Comparable<A2Contribution> {
- final static String BOOT = "boot";
- final static String RUNTIME = "runtime";
- final static String CLASSPATH = "classpath";
-
- final static String DEFAULT = "default";
- final static String LIB = "lib";
-
- private final ProvisioningSource source;
- private final String id;
-
- final Map<String, A2Component> components = Collections.synchronizedSortedMap(new TreeMap<>());
-
- /**
- * The contribution must be added to the source. Rather use
- * {@link AbstractProvisioningSource#getOrAddContribution(String)} than this
- * contructor directly.
- */
- public A2Contribution(ProvisioningSource context, String id) {
- this.source = context;
- this.id = id;
-// if (context != null)
-// context.contributions.put(id, this);
- }
-
- public Iterable<A2Component> listComponents(Object filter) {
- return components.values();
- }
-
- A2Component getOrAddComponent(String componentId) {
- if (components.containsKey(componentId))
- return components.get(componentId);
- else
- return new A2Component(this, componentId);
- }
-
- public ProvisioningSource getSource() {
- return source;
- }
-
- public String getId() {
- return id;
- }
-
- @Override
- public int compareTo(A2Contribution o) {
- return id.compareTo(o.id);
- }
-
- @Override
- public int hashCode() {
- return id.hashCode();
- }
-
- @Override
- public boolean equals(Object obj) {
- if (obj instanceof A2Contribution) {
- A2Contribution o = (A2Contribution) obj;
- return id.equals(o.id);
- } else
- return false;
- }
-
- @Override
- public String toString() {
- return id;
- }
-
- void asTree(String prefix, StringBuffer buf) {
- if (prefix == null)
- prefix = "";
- for (String componentId : components.keySet()) {
- buf.append(prefix);
- buf.append(componentId);
- A2Component component = components.get(componentId);
- component.asTree(prefix, buf);
- buf.append('\n');
- }
- }
-
- static String localOsArchRelativePath() {
- return Os.local().toString() + "/" + Arch.local().toString();
- }
-
- static enum Os {
- LINUX, WIN32, MACOSX, UNKOWN;
-
- @Override
- public String toString() {
- return name().toLowerCase();
- }
-
- public static Os local() {
- String osStr = System.getProperty("os.name").toLowerCase();
- if (osStr.startsWith("linux"))
- return LINUX;
- if (osStr.startsWith("win"))
- return WIN32;
- if (osStr.startsWith("mac"))
- return MACOSX;
- return UNKOWN;
- }
-
- }
-
- static enum Arch {
- X86_64, AARCH64, X86, POWERPC, UNKOWN;
-
- @Override
- public String toString() {
- return name().toLowerCase();
- }
-
- public static Arch local() {
- String archStr = System.getProperty("os.arch").toLowerCase();
- return switch (archStr) {
- case "x86_64":
- case "amd64":
- case "x86-64": {
- yield X86_64;
- }
- case "aarch64":
- case "arm64": {
- yield AARCH64;
- }
- case "x86":
- case "i386":
- case "i686": {
- yield X86;
- }
- case "powerpc":
- case "ppc": {
- yield POWERPC;
- }
- default:
- yield UNKOWN;
- };
- }
- }
-
-}
+++ /dev/null
-package org.argeo.init.a2;
-
-/** Unchecked A2 provisioning exception. */
-public class A2Exception extends RuntimeException {
- private static final long serialVersionUID = 1927603558545397360L;
-
- public A2Exception(String message, Throwable e) {
- super(message, e);
- }
-
- public A2Exception(String message) {
- super(message);
- }
-
-}
+++ /dev/null
-package org.argeo.init.a2;
-
-import org.osgi.framework.Version;
-
-/**
- * An identified software package. In OSGi's case this is the combination of
- * <code>Bundle-SymbolicName</code> and <code>Bundle-version</code>. This is the
- * equivalent of the full coordinates of a Maven artifact version.
- */
-public class A2Module implements Comparable<A2Module> {
- private final A2Branch branch;
- private final Version version;
- private final Object locator;
-
- public A2Module(A2Branch branch, Version version, Object locator) {
- this.branch = branch;
- this.version = version;
- this.locator = locator;
- branch.modules.put(version, this);
- }
-
- public A2Branch getBranch() {
- return branch;
- }
-
- public Version getVersion() {
- return version;
- }
-
- Object getLocator() {
- return locator;
- }
-
- @Override
- public int compareTo(A2Module o) {
- return version.compareTo(o.version);
- }
-
- @Override
- public int hashCode() {
- return version.hashCode();
- }
-
- @Override
- public boolean equals(Object obj) {
- if (obj instanceof A2Module) {
- A2Module o = (A2Module) obj;
- return branch.equals(o.branch) && version.equals(o.version);
- } else
- return false;
- }
-
- @Override
- public String toString() {
- return getCoordinates();
- }
-
- public String getCoordinates() {
- return branch.getComponent() + ":" + version;
- }
-
-}
+++ /dev/null
-package org.argeo.init.a2;
-
-import java.net.URI;
-
-/** A provisioning source in A2 format. */
-public interface A2Source extends ProvisioningSource {
- /** Use standard a2 protocol, installing from source URL. */
- final static String SCHEME_A2 = "a2";
- /**
- * Use equinox-specific reference: installation, which does not copy the bundle
- * content.
- */
- final static String SCHEME_A2_REFERENCE = "a2+reference";
- final static String DEFAULT_A2_URI = SCHEME_A2 + ":///";
- final static String DEFAULT_A2_REFERENCE_URI = SCHEME_A2_REFERENCE + ":///";
-
- URI getUri();
-}
+++ /dev/null
-package org.argeo.init.a2;
-
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.URL;
-import java.nio.file.FileVisitResult;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.SimpleFileVisitor;
-import java.nio.file.attribute.BasicFileAttributes;
-import java.util.Collections;
-import java.util.Map;
-import java.util.SortedMap;
-import java.util.TreeMap;
-import java.util.jar.JarInputStream;
-import java.util.jar.JarOutputStream;
-import java.util.jar.Manifest;
-import java.util.zip.ZipEntry;
-
-import org.osgi.framework.Bundle;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.BundleException;
-import org.osgi.framework.Constants;
-import org.osgi.framework.Version;
-
-/** Where components are retrieved from. */
-public abstract class AbstractProvisioningSource implements ProvisioningSource {
- protected final Map<String, A2Contribution> contributions = Collections.synchronizedSortedMap(new TreeMap<>());
-
- private final boolean usingReference;
-
- public AbstractProvisioningSource(boolean usingReference) {
- this.usingReference = usingReference;
- }
-
- public Iterable<A2Contribution> listContributions(Object filter) {
- return contributions.values();
- }
-
- @Override
- public Bundle install(BundleContext bc, A2Module module) {
- try {
- Object locator = module.getLocator();
- if (usingReference && locator instanceof Path locatorPath) {
- String referenceUrl = "reference:file:" + locatorPath.toString();
- Bundle bundle = bc.installBundle(referenceUrl);
- return bundle;
- } else {
- Path locatorPath = (Path) locator;
- Path pathToUse;
- boolean isTemp = false;
- if (locator instanceof Path && Files.isDirectory(locatorPath)) {
- pathToUse = toTempJar(locatorPath);
- isTemp = true;
- } else {
- pathToUse = locatorPath;
- }
- Bundle bundle;
- try (InputStream in = newInputStream(pathToUse)) {
- bundle = bc.installBundle(locatorPath.toAbsolutePath().toString(), in);
- }
-
- if (isTemp && pathToUse != null)
- Files.deleteIfExists(pathToUse);
- return bundle;
- }
- } catch (BundleException | IOException e) {
- throw new A2Exception("Cannot install module " + module, e);
- }
- }
-
- @Override
- public void update(Bundle bundle, A2Module module) {
- try {
- Object locator = module.getLocator();
- if (usingReference && locator instanceof Path) {
- try (InputStream in = newInputStream(locator)) {
- bundle.update(in);
- }
- } else {
- Path locatorPath = (Path) locator;
- Path pathToUse;
- boolean isTemp = false;
- if (locator instanceof Path && Files.isDirectory(locatorPath)) {
- pathToUse = toTempJar(locatorPath);
- isTemp = true;
- } else {
- pathToUse = locatorPath;
- }
- try (InputStream in = newInputStream(pathToUse)) {
- bundle.update(in);
- }
- if (isTemp && pathToUse != null)
- Files.deleteIfExists(pathToUse);
- }
- } catch (BundleException | IOException e) {
- throw new A2Exception("Cannot update module " + module, e);
- }
- }
-
- @Override
- public A2Branch findBranch(String componentId, Version version) {
- A2Component component = findComponent(componentId);
- if (component == null)
- return null;
- String branchId = version.getMajor() + "." + version.getMinor();
- if (!component.branches.containsKey(branchId))
- return null;
- return component.branches.get(branchId);
- }
-
- protected A2Contribution getOrAddContribution(String contributionId) {
- if (contributions.containsKey(contributionId))
- return contributions.get(contributionId);
- else {
- A2Contribution contribution = new A2Contribution(this, contributionId);
- contributions.put(contributionId, contribution);
- return contribution;
- }
- }
-
- protected void asTree(String prefix, StringBuffer buf) {
- if (prefix == null)
- prefix = "";
- for (String contributionId : contributions.keySet()) {
- buf.append(prefix);
- buf.append(contributionId);
- buf.append('\n');
- A2Contribution contribution = contributions.get(contributionId);
- contribution.asTree(prefix + " ", buf);
- }
- }
-
- protected void asTree() {
- StringBuffer buf = new StringBuffer();
- asTree("", buf);
- System.out.println(buf);
- }
-
- protected A2Component findComponent(String componentId) {
- SortedMap<A2Contribution, A2Component> res = new TreeMap<>();
- for (A2Contribution contribution : contributions.values()) {
- components: for (String componentIdKey : contribution.components.keySet()) {
- if (componentId.equals(componentIdKey)) {
- res.put(contribution, contribution.components.get(componentIdKey));
- break components;
- }
- }
- }
- if (res.size() == 0)
- return null;
- // TODO explicit contribution priorities
- return res.get(res.lastKey());
-
- }
-
- protected String[] readNameVersionFromModule(Path modulePath) {
- Manifest manifest;
- if (Files.isDirectory(modulePath)) {
- manifest = findManifest(modulePath);
- } else {
- try (JarInputStream in = new JarInputStream(newInputStream(modulePath))) {
- manifest = in.getManifest();
- } catch (IOException e) {
- throw new A2Exception("Cannot read manifest from " + modulePath, e);
- }
- }
- String versionStr = manifest.getMainAttributes().getValue(Constants.BUNDLE_VERSION);
- String symbolicName = manifest.getMainAttributes().getValue(Constants.BUNDLE_SYMBOLICNAME);
- int semiColIndex = symbolicName.indexOf(';');
- if (semiColIndex >= 0)
- symbolicName = symbolicName.substring(0, semiColIndex);
- return new String[] { symbolicName, versionStr };
- }
-
- protected String readVersionFromModule(Path modulePath) {
- Manifest manifest;
- if (Files.isDirectory(modulePath)) {
- manifest = findManifest(modulePath);
- } else {
- try (JarInputStream in = new JarInputStream(newInputStream(modulePath))) {
- manifest = in.getManifest();
- } catch (IOException e) {
- throw new A2Exception("Cannot read manifest from " + modulePath, e);
- }
- }
- String versionStr = manifest.getMainAttributes().getValue(Constants.BUNDLE_VERSION);
- return versionStr;
- }
-
- protected String readSymbolicNameFromModule(Path modulePath) {
- Manifest manifest;
- if (Files.isDirectory(modulePath)) {
- manifest = findManifest(modulePath);
- } else {
- try (JarInputStream in = new JarInputStream(newInputStream(modulePath))) {
- manifest = in.getManifest();
- } catch (IOException e) {
- throw new A2Exception("Cannot read manifest from " + modulePath, e);
- }
- }
- String symbolicName = manifest.getMainAttributes().getValue(Constants.BUNDLE_SYMBOLICNAME);
- int semiColIndex = symbolicName.indexOf(';');
- if (semiColIndex >= 0)
- symbolicName = symbolicName.substring(0, semiColIndex);
- return symbolicName;
- }
-
- protected boolean isUsingReference() {
- return usingReference;
- }
-
- private InputStream newInputStream(Object locator) throws IOException {
- if (locator instanceof Path) {
- return Files.newInputStream((Path) locator);
- } else if (locator instanceof URL) {
- return ((URL) locator).openStream();
- } else {
- throw new IllegalArgumentException("Unsupported module locator type " + locator.getClass());
- }
- }
-
- private static Manifest findManifest(Path currentPath) {
- Path metaInfPath = currentPath.resolve("META-INF");
- if (Files.exists(metaInfPath) && Files.isDirectory(metaInfPath)) {
- Path manifestPath = metaInfPath.resolve("MANIFEST.MF");
- try {
- try (InputStream in = Files.newInputStream(manifestPath)) {
- Manifest manifest = new Manifest(in);
- return manifest;
- }
- } catch (IOException e) {
- throw new A2Exception("Cannot read manifest from " + manifestPath, e);
- }
- } else {
- Path parentPath = currentPath.getParent();
- if (parentPath == null)
- throw new A2Exception("MANIFEST.MF file not found.");
- return findManifest(currentPath.getParent());
- }
- }
-
- private static Path toTempJar(Path dir) {
- try {
- Manifest manifest = findManifest(dir);
- Path jarPath = Files.createTempFile("a2Source", ".jar");
- try (JarOutputStream zos = new JarOutputStream(new FileOutputStream(jarPath.toFile()), manifest)) {
- Files.walkFileTree(dir, new SimpleFileVisitor<Path>() {
- public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
- Path relPath = dir.relativize(file);
- // skip MANIFEST from folder
- if (relPath.toString().contentEquals("META-INF/MANIFEST.MF"))
- return FileVisitResult.CONTINUE;
- zos.putNextEntry(new ZipEntry(relPath.toString()));
- Files.copy(file, zos);
- zos.closeEntry();
- return FileVisitResult.CONTINUE;
- }
- });
- }
- return jarPath;
- } catch (IOException e) {
- throw new A2Exception("Cannot install OSGi bundle from " + dir, e);
- }
-
- }
-
-}
+++ /dev/null
-package org.argeo.init.a2;
-
-import java.io.File;
-import java.io.IOException;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.Arrays;
-import java.util.List;
-
-import org.argeo.init.osgi.OsgiBootUtils;
-import org.osgi.framework.Version;
-
-/**
- * A provisioning source based on the linear classpath with which the JVM has
- * been started.
- */
-public class ClasspathSource extends AbstractProvisioningSource {
-
- public ClasspathSource() {
- super(true);
- }
-
- void load() throws IOException {
- A2Contribution classpathContribution = getOrAddContribution( A2Contribution.CLASSPATH);
- List<String> classpath = Arrays.asList(System.getProperty("java.class.path").split(File.pathSeparator));
- parts: for (String part : classpath) {
- Path file = Paths.get(part);
- Version version;
- try {
- version = new Version(readVersionFromModule(file));
- } catch (Exception e) {
- // ignore non OSGi
- continue parts;
- }
- String moduleName = readSymbolicNameFromModule(file);
- A2Component component = classpathContribution.getOrAddComponent(moduleName);
- A2Module module = component.getOrAddModule(version, file);
- if (OsgiBootUtils.isDebug())
- OsgiBootUtils.debug("Registered " + module);
- }
-
- }
-}
+++ /dev/null
-package org.argeo.init.a2;
-
-import java.io.IOException;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.nio.file.DirectoryStream;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.SortedMap;
-import java.util.StringJoiner;
-import java.util.TreeMap;
-
-import org.argeo.init.osgi.OsgiBootUtils;
-import org.osgi.framework.Version;
-
-/** A file system {@link AbstractProvisioningSource} in A2 format. */
-public class FsA2Source extends AbstractProvisioningSource implements A2Source {
- private final Path base;
- private final Map<String, String> variantsXOr;
-
-// public FsA2Source(Path base) {
-// this(base, new HashMap<>());
-// }
-
- public FsA2Source(Path base, Map<String, String> variantsXOr, boolean usingReference) {
- super(usingReference);
- this.base = base;
- this.variantsXOr = new HashMap<>(variantsXOr);
- }
-
- void load() throws IOException {
- SortedMap<Path, A2Contribution> contributions = new TreeMap<>();
-
- DirectoryStream<Path> contributionPaths = Files.newDirectoryStream(base);
- contributions: for (Path contributionPath : contributionPaths) {
- if (Files.isDirectory(contributionPath)) {
- String contributionId = contributionPath.getFileName().toString();
- if (A2Contribution.BOOT.equals(contributionId))// skip boot
- continue contributions;
- if (contributionId.contains(".")) {
- A2Contribution contribution = getOrAddContribution(contributionId);
- contributions.put(contributionPath, contribution);
- } else {// variants
- Path variantPath = null;
- // is it an explicit variant?
- String variant = variantsXOr.get(contributionPath.getFileName().toString());
- if (variant != null) {
- variantPath = contributionPath.resolve(variant);
- }
-
- // is there a default variant?
- if (variantPath == null) {
- Path defaultPath = contributionPath.resolve(A2Contribution.DEFAULT);
- if (Files.exists(defaultPath)) {
- variantPath = defaultPath;
- }
- }
-
- if (variantPath == null)
- continue contributions;
-
- // a variant was found, let's collect its contributions (also common ones in its
- // parent)
- if (Files.exists(variantPath.getParent())) {
- for (Path variantContributionPath : Files.newDirectoryStream(variantPath.getParent())) {
- String variantContributionId = variantContributionPath.getFileName().toString();
- if (variantContributionId.contains(".")) {
- A2Contribution contribution = getOrAddContribution(variantContributionId);
- contributions.put(variantContributionPath, contribution);
- }
- }
- }
- if (Files.exists(variantPath)) {
- for (Path variantContributionPath : Files.newDirectoryStream(variantPath)) {
- String variantContributionId = variantContributionPath.getFileName().toString();
- if (variantContributionId.contains(".")) {
- A2Contribution contribution = getOrAddContribution(variantContributionId);
- contributions.put(variantContributionPath, contribution);
- }
- }
- }
- }
- }
- }
-
- for (Path contributionPath : contributions.keySet()) {
- String contributionId = contributionPath.getFileName().toString();
- A2Contribution contribution = getOrAddContribution(contributionId);
- DirectoryStream<Path> modulePaths = Files.newDirectoryStream(contributionPath);
- modules: for (Path modulePath : modulePaths) {
- if (!Files.isDirectory(modulePath)) {
- // OsgiBootUtils.debug("Registering " + modulePath);
- String moduleFileName = modulePath.getFileName().toString();
- int lastDot = moduleFileName.lastIndexOf('.');
- String ext = moduleFileName.substring(lastDot + 1);
- if (!"jar".equals(ext))
- continue modules;
- Version version;
- // TODO optimise? check attributes?
- String[] nameVersion = readNameVersionFromModule(modulePath);
- String componentName = nameVersion[0];
- String versionStr = nameVersion[1];
- if (versionStr != null) {
- version = new Version(versionStr);
- } else {
- OsgiBootUtils.debug("Ignore " + modulePath + " since version cannot be found");
- continue modules;
- }
-// }
- A2Component component = contribution.getOrAddComponent(componentName);
- A2Module module = component.getOrAddModule(version, modulePath);
- if (OsgiBootUtils.isDebug())
- OsgiBootUtils.debug("Registered " + module);
- }
- }
- }
-
- }
-
- @Override
- public URI getUri() {
- URI baseUri = base.toUri();
- try {
- if (baseUri.getScheme().equals("file")) {
- String queryPart = "";
- if (!getVariantsXOr().isEmpty()) {
- StringJoiner sj = new StringJoiner("&");
- for (String key : getVariantsXOr().keySet()) {
- sj.add(key + "=" + getVariantsXOr().get(key));
- }
- queryPart = sj.toString();
- }
- return new URI(isUsingReference() ? SCHEME_A2_REFERENCE : SCHEME_A2, null, base.toString(), queryPart,
- null);
- } else {
- throw new UnsupportedOperationException("Unsupported scheme " + baseUri.getScheme());
- }
- } catch (URISyntaxException e) {
- throw new IllegalStateException("Cannot build URI from " + baseUri, e);
- }
- }
-
- protected Map<String, String> getVariantsXOr() {
- return variantsXOr;
- }
-
-// public static void main(String[] args) {
-// if (args.length == 0)
-// throw new IllegalArgumentException("Usage: <path to A2 base>");
-// try {
-// Map<String, String> xOr = new HashMap<>();
-// xOr.put("osgi", "equinox");
-// xOr.put("swt", "rap");
-// FsA2Source context = new FsA2Source(Paths.get(args[0]), xOr);
-// context.load();
-// context.asTree();
-// } catch (Exception e) {
-// e.printStackTrace();
-// }
-// }
-
-}
+++ /dev/null
-package org.argeo.init.a2;
-
-import java.io.File;
-import java.io.IOException;
-import java.nio.file.FileVisitResult;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.nio.file.SimpleFileVisitor;
-import java.nio.file.attribute.BasicFileAttributes;
-
-import org.argeo.init.osgi.OsgiBootUtils;
-import org.osgi.framework.Version;
-
-/** A file system {@link AbstractProvisioningSource} in Maven 2 format. */
-public class FsM2Source extends AbstractProvisioningSource {
- private final Path base;
-
- public FsM2Source(Path base) {
- super(false);
- this.base = base;
- }
-
- void load() throws IOException {
- Files.walkFileTree(base, new ArtifactFileVisitor());
- }
-
- class ArtifactFileVisitor extends SimpleFileVisitor<Path> {
-
- @Override
- public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
- // OsgiBootUtils.debug("Processing " + file);
- if (file.toString().endsWith(".jar")) {
- Version version;
- try {
- version = new Version(readVersionFromModule(file));
- } catch (Exception e) {
- // ignore non OSGi
- return FileVisitResult.CONTINUE;
- }
- String moduleName = readSymbolicNameFromModule(file);
- Path groupPath = file.getParent().getParent().getParent();
- Path relGroupPath = base.relativize(groupPath);
- String contributionName = relGroupPath.toString().replace(File.separatorChar, '.');
- A2Contribution contribution = getOrAddContribution(contributionName);
- A2Component component = contribution.getOrAddComponent(moduleName);
- A2Module module = component.getOrAddModule(version, file);
- if (OsgiBootUtils.isDebug())
- OsgiBootUtils.debug("Registered " + module);
- }
- return super.visitFile(file, attrs);
- }
-
- }
-
- public static void main(String[] args) {
- try {
- FsM2Source context = new FsM2Source(Paths.get("/home/mbaudier/.m2/repository"));
- context.load();
- context.asTree();
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
-
-}
+++ /dev/null
-package org.argeo.init.a2;
-
-import org.argeo.init.osgi.OsgiBootUtils;
-import org.osgi.framework.Bundle;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.FrameworkUtil;
-import org.osgi.framework.Version;
-
-/**
- * A running OSGi bundle context seen as a {@link AbstractProvisioningSource}.
- */
-class OsgiContext extends AbstractProvisioningSource {
- private final BundleContext bc;
-
- private A2Contribution runtimeContribution;
-
- public OsgiContext(BundleContext bc) {
- super(false);
- this.bc = bc;
- runtimeContribution = getOrAddContribution(A2Contribution.RUNTIME);
- }
-
- public OsgiContext() {
- super(false);
- Bundle bundle = FrameworkUtil.getBundle(OsgiContext.class);
- if (bundle == null)
- throw new IllegalArgumentException(
- "OSGi Boot bundle must be started or a bundle context must be specified");
- this.bc = bundle.getBundleContext();
- }
-
- void load() {
- for (Bundle bundle : bc.getBundles()) {
- registerBundle(bundle);
- }
-
- }
-
- void registerBundle(Bundle bundle) {
- String componentId = bundle.getSymbolicName();
- Version version = bundle.getVersion();
- A2Component component = runtimeContribution.getOrAddComponent(componentId);
- A2Module module = component.getOrAddModule(version, bundle);
- if (OsgiBootUtils.isDebug())
- OsgiBootUtils.debug("Registered bundle module " + module + " (location id: " + bundle.getLocation() + ")");
-
- }
-}
+++ /dev/null
-package org.argeo.init.a2;
-
-import static org.argeo.init.a2.A2Source.SCHEME_A2;
-import static org.argeo.init.a2.A2Source.SCHEME_A2_REFERENCE;
-
-import java.io.File;
-import java.io.UnsupportedEncodingException;
-import java.net.URI;
-import java.net.URLDecoder;
-import java.nio.charset.StandardCharsets;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.LinkedHashMap;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-import org.argeo.init.osgi.OsgiBootUtils;
-import org.osgi.framework.Bundle;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.Constants;
-import org.osgi.framework.Version;
-import org.osgi.framework.wiring.FrameworkWiring;
-
-/** Loads provisioning sources into an OSGi context. */
-public class ProvisioningManager {
- BundleContext bc;
- OsgiContext osgiContext;
- List<ProvisioningSource> sources = Collections.synchronizedList(new ArrayList<>());
-
- public ProvisioningManager(BundleContext bc) {
- this.bc = bc;
- osgiContext = new OsgiContext(bc);
- osgiContext.load();
- }
-
- protected void addSource(ProvisioningSource source) {
- sources.add(source);
- }
-
- void installWholeSource(ProvisioningSource source) {
- Set<Bundle> updatedBundles = new HashSet<>();
- for (A2Contribution contribution : source.listContributions(null)) {
- for (A2Component component : contribution.components.values()) {
- A2Module module = component.last().last();
- Bundle bundle = installOrUpdate(module);
- if (bundle != null)
- updatedBundles.add(bundle);
- }
- }
-// FrameworkWiring frameworkWiring = bc.getBundle(0).adapt(FrameworkWiring.class);
-// frameworkWiring.refreshBundles(updatedBundles);
- }
-
- public void registerSource(String uri) {
- try {
- URI u = new URI(uri);
-
- // XOR
- Map<String, List<String>> properties = queryToMap(u);
- Map<String, String> xOr = new HashMap<>();
- for (String key : properties.keySet()) {
- List<String> lst = properties.get(key);
- if (lst.size() != 1)
- throw new IllegalArgumentException("Invalid XOR definitions in " + uri);
- xOr.put(key, lst.get(0));
- }
-
- if (SCHEME_A2.equals(u.getScheme()) || SCHEME_A2_REFERENCE.equals(u.getScheme())) {
- if (u.getHost() == null || "".equals(u.getHost())) {
- String baseStr = u.getPath();
- if (File.separatorChar == '\\') {// MS Windows
- baseStr = baseStr.substring(1).replace('/', File.separatorChar);
- }
- Path base = Paths.get(baseStr);
- if (Files.exists(base)) {
- FsA2Source source = new FsA2Source(base, xOr, SCHEME_A2_REFERENCE.equals(u.getScheme()));
- source.load();
- addSource(source);
- OsgiBootUtils.info("Registered " + uri + " as source");
-
- // OS specific / native
- String localRelPath = A2Contribution.localOsArchRelativePath();
- Path localLibBase = base.resolve(A2Contribution.LIB).resolve(localRelPath);
- if (Files.exists(localLibBase)) {
- FsA2Source libSource = new FsA2Source(localLibBase, xOr,
- SCHEME_A2_REFERENCE.equals(u.getScheme()));
- libSource.load();
- addSource(libSource);
- OsgiBootUtils.info("Registered OS-specific " + uri + " as source (" + localRelPath + ")");
- }
- } else {
- OsgiBootUtils.debug("Source " + base + " does not exist, ignoring.");
- }
- } else {
- throw new UnsupportedOperationException(
- "Remote installation is not yet supported, cannot add source " + u);
- }
- } else {
- throw new IllegalArgumentException("Unkown scheme: for source " + u);
- }
- } catch (Exception e) {
- throw new A2Exception("Cannot add source " + uri, e);
- }
- }
-
- public boolean registerDefaultSource() {
- String frameworkLocation = bc.getProperty("osgi.framework");
- try {
- URI frameworkLocationUri = new URI(frameworkLocation);
- if ("file".equals(frameworkLocationUri.getScheme())) {
- Path frameworkPath = Paths.get(frameworkLocationUri);
- if (frameworkPath.getParent().getFileName().toString().equals(A2Contribution.BOOT)) {
- Path base = frameworkPath.getParent().getParent();
- String baseStr = base.toString();
- if (File.separatorChar == '\\')// MS Windows
- baseStr = '/' + baseStr.replace(File.separatorChar, '/');
- URI baseUri = new URI(A2Source.SCHEME_A2, null, null, 0, baseStr, null, null);
- registerSource(baseUri.toString());
- OsgiBootUtils.debug("Default source from framework location " + frameworkLocation);
- return true;
- }
- }
- } catch (Exception e) {
- OsgiBootUtils.error("Cannot register default source based on framework location " + frameworkLocation, e);
- }
- return false;
- }
-
- public void install(String spec) {
- if (spec == null) {
- for (ProvisioningSource source : sources) {
- installWholeSource(source);
- }
- }
- }
-
- /** @return the new/updated bundle, or null if nothing was done. */
- protected Bundle installOrUpdate(A2Module module) {
- try {
- ProvisioningSource moduleSource = module.getBranch().getComponent().getContribution().getSource();
- Version moduleVersion = module.getVersion();
- A2Branch osgiBranch = osgiContext.findBranch(module.getBranch().getComponent().getId(), moduleVersion);
- if (osgiBranch == null) {
- Bundle bundle = moduleSource.install(bc, module);
- // TODO make it more dynamic, based on OSGi APIs
- osgiContext.registerBundle(bundle);
-// if (OsgiBootUtils.isDebug())
-// OsgiBootUtils.debug("Installed bundle " + bundle.getLocation() + " with version " + moduleVersion);
- return bundle;
- } else {
- A2Module lastOsgiModule = osgiBranch.last();
- int compare = moduleVersion.compareTo(lastOsgiModule.getVersion());
- if (compare >= 0) {// update (also if same version)
- Bundle bundle = (Bundle) lastOsgiModule.getLocator();
- if (bundle.getBundleId() == 0)// ignore framework bundle
- return null;
- moduleSource.update(bundle, module);
- // TODO make it more dynamic, based on OSGi APIs
- // TODO remove old module? Or keep update history?
- osgiContext.registerBundle(bundle);
- OsgiBootUtils.info("Updated bundle " + bundle.getLocation() + " to version " + moduleVersion);
- return bundle;
- } else {
- if (OsgiBootUtils.isDebug())
- OsgiBootUtils.debug("Did not install as bundle module " + module
- + " since a module with higher version " + lastOsgiModule.getVersion()
- + " is already installed for branch " + osgiBranch);
- }
- }
- } catch (Exception e) {
- OsgiBootUtils.error("Could not install module " + module + ": " + e.getMessage(), e);
- }
- return null;
- }
-
- public Collection<Bundle> update() {
- boolean fragmentsUpdated = false;
- Set<Bundle> updatedBundles = new HashSet<>();
- bundles: for (Bundle bundle : bc.getBundles()) {
- for (ProvisioningSource source : sources) {
- String componentId = bundle.getSymbolicName();
- Version version = bundle.getVersion();
- A2Branch branch = source.findBranch(componentId, version);
- if (branch == null)
- continue bundles;
- A2Module module = branch.last();
- Version moduleVersion = module.getVersion();
- int compare = moduleVersion.compareTo(version);
- if (compare > 0) {// update
- try {
- source.update(bundle, module);
-// bundle.update(in);
- String fragmentHost = bundle.getHeaders().get(Constants.FRAGMENT_HOST);
- if (fragmentHost != null)
- fragmentsUpdated = true;
- OsgiBootUtils.info("Updated bundle " + bundle.getLocation() + " to version " + moduleVersion);
- updatedBundles.add(bundle);
- } catch (Exception e) {
- OsgiBootUtils.error("Cannot update with module " + module, e);
- }
- }
- }
- }
- FrameworkWiring frameworkWiring = bc.getBundle(0).adapt(FrameworkWiring.class);
- if (fragmentsUpdated)// refresh all
- frameworkWiring.refreshBundles(null);
- else
- frameworkWiring.refreshBundles(updatedBundles);
- return updatedBundles;
- }
-
- private static Map<String, List<String>> queryToMap(URI uri) {
- return queryToMap(uri.getQuery());
- }
-
- private static Map<String, List<String>> queryToMap(String queryPart) {
- try {
- final Map<String, List<String>> query_pairs = new LinkedHashMap<String, List<String>>();
- if (queryPart == null)
- return query_pairs;
- final String[] pairs = queryPart.split("&");
- for (String pair : pairs) {
- final int idx = pair.indexOf("=");
- final String key = idx > 0 ? URLDecoder.decode(pair.substring(0, idx), StandardCharsets.UTF_8.name())
- : pair;
- if (!query_pairs.containsKey(key)) {
- query_pairs.put(key, new LinkedList<String>());
- }
- final String value = idx > 0 && pair.length() > idx + 1
- ? URLDecoder.decode(pair.substring(idx + 1), StandardCharsets.UTF_8.name())
- : null;
- query_pairs.get(key).add(value);
- }
- return query_pairs;
- } catch (UnsupportedEncodingException e) {
- throw new IllegalArgumentException("Cannot convert " + queryPart + " to map", e);
- }
- }
-
-// public static void main(String[] args) {
-// if (args.length == 0)
-// throw new IllegalArgumentException("Usage: <path to A2 base>");
-// Map<String, String> configuration = new HashMap<>();
-// configuration.put("osgi.console", "2323");
-// configuration.put("org.osgi.framework.bootdelegation",
-// "com.sun.jndi.ldap,com.sun.jndi.ldap.sasl,com.sun.security.jgss,com.sun.jndi.dns,com.sun.nio.file,com.sun.nio.sctp,sun.nio.cs");
-// Framework framework = OsgiBootUtils.launch(configuration);
-// try {
-// ProvisioningManager pm = new ProvisioningManager(framework.getBundleContext());
-// Map<String, String> xOr = new HashMap<>();
-// xOr.put("osgi", "equinox");
-// xOr.put("swt", "rap");
-// FsA2Source context = new FsA2Source(Paths.get(args[0]), xOr);
-// context.load();
-// pm.addSource(context);
-// if (framework.getBundleContext().getBundles().length == 1) {// initial
-// pm.install(null);
-// } else {
-// pm.update();
-// }
-//
-// Thread.sleep(2000);
-//
-// Bundle[] bundles = framework.getBundleContext().getBundles();
-// Arrays.sort(bundles, (b1, b2) -> b1.getSymbolicName().compareTo(b2.getSymbolicName()));
-// for (Bundle b : bundles)
-// if (b.getState() == Bundle.RESOLVED || b.getState() == Bundle.STARTING || b.getState() == Bundle.ACTIVE)
-// System.out.println(b.getSymbolicName() + " " + b.getVersion());
-// else
-// System.err.println(b.getSymbolicName() + " " + b.getVersion() + " (" + b.getState() + ")");
-// } catch (Exception e) {
-// e.printStackTrace();
-// } finally {
-// try {
-// framework.stop();
-// } catch (Exception e) {
-// e.printStackTrace();
-// }
-// }
-// }
-
-}
+++ /dev/null
-package org.argeo.init.a2;
-
-import org.osgi.framework.Bundle;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.Version;
-
-/** Where components are retrieved from. */
-public interface ProvisioningSource {
- /** List all contributions of this source. */
- Iterable<A2Contribution> listContributions(Object filter);
-
- /** Install a module in the OSGi runtime. */
- Bundle install(BundleContext bc, A2Module module);
-
- /** Update a module in the OSGi runtime. */
- void update(Bundle bundle, A2Module module);
-
- /** Finds the {@link A2Branch} related to this component and version. */
- A2Branch findBranch(String componentId, Version version);
-
-}
+++ /dev/null
-/** A2 OSGi repository format. */
-package org.argeo.init.a2;
\ No newline at end of file
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.Flow;
+import java.util.concurrent.Flow.Publisher;
import java.util.function.Consumer;
+import java.util.function.Supplier;
/**
* Factory for Java system logging. As it has to be a public class in order to
* be exposed as a service provider, it is also the main entry point for the
* thin logging system, via static methos.
*/
-public class ThinLoggerFinder extends LoggerFinder {
+public class ThinLoggerFinder extends LoggerFinder
+ implements Consumer<Map<String, Object>>, Supplier<Flow.Publisher<Map<String, Serializable>>> {
private static ThinLogging logging;
private static ThinJavaUtilLogging javaUtilLogging;
@Override
public Logger getLogger(String name, Module module) {
lazyInit();
- return logging.getLogger(name, module);
+ Objects.requireNonNull(name);
+ Logger logger = logging.getLogger(name, module);
+ Objects.requireNonNull(logger);
+ return logger;
}
private static void init() {
javaUtilLogging.readConfiguration(logging.getLevels());
}
- public static Consumer<Map<String, Object>> getConfigurationConsumer() {
+ static Consumer<Map<String, Object>> getConfigurationConsumer() {
Objects.requireNonNull(logging);
return logging;
}
- public static Flow.Publisher<Map<String, Serializable>> getLogEntryPublisher() {
+ static Flow.Publisher<Map<String, Serializable>> getLogEntryPublisher() {
Objects.requireNonNull(logging);
return logging.getLogEntryPublisher();
}
+ @Override
+ public Publisher<Map<String, Serializable>> get() {
+ return getLogEntryPublisher();
+ }
+
static void update(Map<String, Object> configuration) {
if (logging == null)
throw new IllegalStateException("Thin logging must be initialized first");
static Logger getLogger(String name) {
return logging.getLogger(name, null);
}
+
+ @Override
+ public void accept(Map<String, Object> t) {
+ if (logging != null) {
+ // delegate to thin logging
+ logging.accept(t);
+ } else {
+ // ignore
+ // TODO try to congure Java logging ?
+ }
+
+ }
+
}
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Consumer;
-import org.argeo.init.RuntimeContext;
-import org.argeo.init.Service;
+import org.argeo.api.init.RuntimeContext;
+import org.argeo.internal.init.InternalState;
/**
* A thin logging system based on the {@link Logger} framework. It is a
// we don't synchronize maps on purpose as it would be
// too expensive during normal operation
// updates to the config may be shortly inconsistent
- private SortedMap<String, ThinLogger> loggers = new TreeMap<>();
+ private SortedMap<String, ThinLogger> loggers = Collections.synchronizedSortedMap(new TreeMap<>());
private NavigableMap<String, Level> levels = new TreeMap<>();
private volatile boolean updatingConfiguration = false;
}
private void close() {
- RuntimeContext runtimeContext = Service.getRuntimeContext();
+ RuntimeContext runtimeContext = InternalState.getMainRuntimeContext();
if (runtimeContext != null) {
try {
runtimeContext.waitForStop(0);
}
public Logger getLogger(String name, Module module) {
+ Objects.requireNonNull(name, "logger name");
if (!loggers.containsKey(name)) {
ThinLogger logger = new ThinLogger(name, computeApplicableLevel(name));
loggers.put(name, logger);
+ return logger;
}
return loggers.get(name);
}
case "org.osgi.service.log.Logger":
case "org.eclipse.osgi.internal.log.LoggerImpl":
case "org.argeo.api.cms.CmsLog":
- case "org.argeo.init.osgi.OsgiBootUtils":
- case "org.slf4j.impl.ArgeoLogger":
- case "org.argeo.cms.internal.osgi.CmsOsgiLogger":
case "org.eclipse.jetty.util.log.Slf4jLog":
case "sun.util.logging.internal.LoggingProviderImpl$JULWrapper":
+ case "org.slf4j.impl.ArgeoLogger":
+ case "org.argeo.cms.internal.osgi.CmsOsgiLogger":
+ case "org.argeo.init.osgi.OsgiBootUtils":
lowestLoggerInterface = i;
continue stack;
default:
}
}
- public static void main(String args[]) {
- Logger logger = System.getLogger(ThinLogging.class.getName());
- logger.log(Logger.Level.ALL, "Log all");
- logger.log(Logger.Level.TRACE, "Multi\nline\ntrace");
- logger.log(Logger.Level.DEBUG, "Log debug");
- logger.log(Logger.Level.INFO, "Log info");
- logger.log(Logger.Level.WARNING, "Log warning");
- logger.log(Logger.Level.ERROR, "Log exception", new Throwable());
-
- try {
- // we ait a bit in order to make sure all messages are flushed
- // TODO synchronize more efficiently
- // executor.awaitTermination(300, TimeUnit.MILLISECONDS);
- ForkJoinPool.commonPool().awaitTermination(300, TimeUnit.MILLISECONDS);
- } catch (InterruptedException e) {
- // silent
- }
-
- }
+// public static void main(String args[]) {
+// Logger logger = System.getLogger(ThinLogging.class.getName());
+// logger.log(Logger.Level.ALL, "Log all");
+// logger.log(Logger.Level.TRACE, "Multi\nline\ntrace");
+// logger.log(Logger.Level.DEBUG, "Log debug");
+// logger.log(Logger.Level.INFO, "Log info");
+// logger.log(Logger.Level.WARNING, "Log warning");
+// logger.log(Logger.Level.ERROR, "Log exception", new Throwable());
+//
+// try {
+// // we wait a bit in order to make sure all messages are flushed
+// // TODO synchronize more efficiently
+// // executor.awaitTermination(300, TimeUnit.MILLISECONDS);
+// ForkJoinPool.commonPool().awaitTermination(300, TimeUnit.MILLISECONDS);
+// } catch (InterruptedException e) {
+// // silent
+// }
+//
+// }
}
import java.lang.System.Logger.Level;
import java.util.Objects;
-import org.argeo.init.Service;
+import org.argeo.init.ServiceMain;
import org.argeo.init.logging.ThinLoggerFinder;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
public void start(final BundleContext bundleContext) throws Exception {
// The OSGi runtime was created by us, and therefore already initialized
- argeoInit = Boolean.parseBoolean(bundleContext.getProperty(Service.PROP_ARGEO_INIT_MAIN));
+ argeoInit = Boolean.parseBoolean(bundleContext.getProperty(ServiceMain.PROP_ARGEO_INIT_MAIN));
if (!argeoInit) {
if (runtimeContext == null) {
runtimeContext = new OsgiRuntimeContext(bundleContext);
+++ /dev/null
-package org.argeo.init.osgi;
-
-import java.io.File;
-
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.launch.Framework;
-
-/** Monitors the runtime and can shut it down. */
-@Deprecated
-public class AdminThread extends Thread {
- public final static String PROP_ARGEO_OSGI_SHUTDOWN_FILE = "argeo.osgi.shutdownFile";
- private File shutdownFile;
- private final BundleContext bundleContext;
-
- public AdminThread(BundleContext bundleContext) {
- super("OSGi Boot Admin");
- this.bundleContext = bundleContext;
- if (System.getProperty(PROP_ARGEO_OSGI_SHUTDOWN_FILE) != null) {
- shutdownFile = new File(
- System.getProperty(PROP_ARGEO_OSGI_SHUTDOWN_FILE));
- if (!shutdownFile.exists()) {
- shutdownFile = null;
- OsgiBootUtils.warn("Shutdown file " + shutdownFile
- + " not found, feature deactivated");
- }
- }
- }
-
- public void run() {
- if (shutdownFile != null) {
- // wait for file to be removed
- while (shutdownFile.exists()) {
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
-
- Framework framework = (Framework) bundleContext.getBundle(0);
- try {
- // shutdown framework
- framework.stop();
- // wait 10 mins for shutdown
- framework.waitForStop(10 * 60 * 1000);
- // close VM
- System.exit(0);
- } catch (Exception e) {
- e.printStackTrace();
- System.exit(1);
- }
- }
- }
-}
package org.argeo.init.osgi;
+import static java.lang.System.Logger.Level.TRACE;
+
import java.io.File;
import java.io.IOException;
+import java.lang.System.Logger;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;
/** Intermediary structure used by path matching */
class BundlesSet {
+ private final static Logger logger = System.getLogger(BundlesSet.class.getName());
+
private String baseUrl = "reference:file";// not used yet
private final String dir;
private List<String> includes = new ArrayList<String>();
dirPath = dirPath.substring("file:".length());
dir = new File(dirPath.replace('/', File.separatorChar)).getCanonicalPath();
- if (OsgiBootUtils.isDebug())
- OsgiBootUtils.debug("Base dir: " + dir);
+ logger.log(TRACE, () -> "Base dir: " + dir);
} catch (IOException e) {
throw new RuntimeException("Cannot convert to absolute path", e);
}
package org.argeo.init.osgi;
+import static java.lang.System.Logger.Level.WARNING;
+
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
+import java.lang.System.Logger;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
* name of the URL and of the content of the index.
*/
public class DistributionBundle {
+ private final static Logger logger = System.getLogger(DistributionBundle.class.getName());
+
private final static String INDEX_FILE_NAME = "modularDistribution.csv";
private final String url;
public void processUrl() {
JarInputStream jarIn = null;
try {
- URL u = new URL(url);
+ URL u = new URI(url).toURL();
// local cache
URI localUri = new URI(localCache + relativeUrl);
try {
localUri = new URI(localCache + relativeUrl);
} catch (URISyntaxException e) {
- OsgiBootUtils.warn(e.getMessage());
+ logger.log(WARNING, e.getMessage());
localUri = null;
}
Version version = new Version(osgiArtifact.getVersion());
+++ /dev/null
-package org.argeo.init.osgi;
-
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.lang.reflect.Method;
-import java.util.ArrayList;
-import java.util.List;
-
-import org.osgi.framework.BundleContext;
-
-/** An OSGi launcher executing first another class in the system class path. */
-public class Launcher {
-
- public static void main(String[] args) {
- // Try to load system properties
- String systemPropertiesFilePath = getProperty(OsgiBoot.PROP_ARGEO_OSGI_BOOT_SYSTEM_PROPERTIES_FILE);
- if (systemPropertiesFilePath != null) {
- FileInputStream in;
- try {
- in = new FileInputStream(systemPropertiesFilePath);
- System.getProperties().load(in);
- } catch (IOException e1) {
- throw new RuntimeException("Cannot load system properties from " + systemPropertiesFilePath, e1);
- }
- if (in != null) {
- try {
- in.close();
- } catch (Exception e) {
- // silent
- }
- }
- }
-
- // Start main class
- startMainClass();
-
- // Start Equinox
- BundleContext bundleContext = null;
- try {
- bundleContext = OsgiBootUtils.launch(OsgiBootUtils.equinoxArgsToConfiguration(args)).getBundleContext();
- } catch (Exception e) {
- throw new RuntimeException("Cannot start Equinox.", e);
- }
-
- // OSGi bootstrap
- OsgiBoot osgiBoot = new OsgiBoot(bundleContext);
- osgiBoot.bootstrap();
- }
-
- protected static void startMainClass() {
- String className = getProperty(OsgiBoot.PROP_ARGEO_OSGI_BOOT_APPCLASS);
- if (className == null)
- return;
-
- String line = System.getProperty(OsgiBoot.PROP_ARGEO_OSGI_BOOT_APPARGS, "");
-
- String[] uiArgs = readArgumentsFromLine(line);
-
- try {
- // Launch main method using reflection
- Class<?> clss = Class.forName(className);
- Class<?>[] mainArgsClasses = new Class[] { uiArgs.getClass() };
- Object[] mainArgs = { uiArgs };
- Method mainMethod = clss.getMethod("main", mainArgsClasses);
- mainMethod.invoke(null, mainArgs);
- } catch (Exception e) {
- throw new RuntimeException("Cannot start main class.", e);
- }
-
- }
-
- /**
- * Transform a line into an array of arguments, taking "" as single arguments.
- * (nested \" are not supported)
- */
- private static String[] readArgumentsFromLine(String lineOrig) {
- String line = lineOrig.trim();// remove trailing spaces
- List<String> args = new ArrayList<String>();
- StringBuffer curr = new StringBuffer("");
- boolean inQuote = false;
- char[] arr = line.toCharArray();
- for (int i = 0; i < arr.length; i++) {
- char c = arr[i];
- switch (c) {
- case '\"':
- inQuote = !inQuote;
- break;
- case ' ':
- if (!inQuote) {// otherwise, no break: goes to default
- if (curr.length() > 0) {
- args.add(curr.toString());
- curr = new StringBuffer("");
- }
- break;
- }
- default:
- curr.append(c);
- break;
- }
- }
-
- // Add last arg
- if (curr.length() > 0) {
- args.add(curr.toString());
- curr = null;
- }
-
- String[] res = new String[args.size()];
- for (int i = 0; i < args.size(); i++) {
- res[i] = args.get(i).toString();
- }
- return res;
- }
-
- public static String getProperty(String name, String defaultValue) {
- final String value;
- if (defaultValue != null)
- value = System.getProperty(name, defaultValue);
- else
- value = System.getProperty(name);
-
- if (value == null || value.equals(""))
- return null;
- else
- return value;
- }
-
- public static String getProperty(String name) {
- return getProperty(name, null);
- }
-
-}
+++ /dev/null
-package org.argeo.init.osgi;
-
-import java.lang.management.ManagementFactory;
-
-public class Main {
-
- public static void main(String[] args) {
- String mainClass = System.getProperty(OsgiBoot.PROP_ARGEO_OSGI_BOOT_APPCLASS);
- if (mainClass == null) {
- throw new IllegalArgumentException(
- "System property " + OsgiBoot.PROP_ARGEO_OSGI_BOOT_APPCLASS + " must be specified");
- }
-
- OsgiBuilder osgi = new OsgiBuilder();
- String distributionUrl = System.getProperty(OsgiBoot.PROP_ARGEO_OSGI_DISTRIBUTION_URL);
- if (distributionUrl != null)
- osgi.install(distributionUrl);
- // osgi.conf("argeo.node.useradmin.uris", "os:///");
- // osgi.conf("osgi.clean", "true");
- // osgi.conf("osgi.console", "true");
- osgi.launch();
-
- if (OsgiBootUtils.isDebug()) {
- long jvmUptime = ManagementFactory.getRuntimeMXBean().getUptime();
- String jvmUptimeStr = (jvmUptime / 1000) + "." + (jvmUptime % 1000) + "s";
- OsgiBootUtils.debug("Ready to launch " + mainClass + " in " + jvmUptimeStr);
- }
-
- osgi.main(mainClass, args);
-
- osgi.shutdown();
-
- }
-
-}
+++ /dev/null
-package org.argeo.init.osgi;
-
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Properties;
-import java.util.ServiceLoader;
-
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.ServiceReference;
-import org.osgi.framework.launch.Framework;
-import org.osgi.framework.launch.FrameworkFactory;
-
-/** Launch an OSGi framework and deploy a CMS Node into it. */
-public class NodeRunner {
- private Long timeout = 30 * 1000l;
- private final Path baseDir;
- private final Path confDir;
- private final Path dataDir;
-
- private String baseUrl = "http://forge.argeo.org/data/java/argeo-2.1/";
- private String distributionUrl = null;
-
- private Framework framework = null;
-
- public NodeRunner(String distributionUrl, Path baseDir) {
- this.distributionUrl = distributionUrl;
- Path mavenBase = Paths.get(System.getProperty("user.home") + "/.m2/repository");
- Path osgiBase = Paths.get("/user/share/osgi");
- if (Files.exists(mavenBase)) {
- Path mavenPath = mavenBase.resolve(distributionUrl);
- if (Files.exists(mavenPath))
- baseUrl = mavenBase.toUri().toString();
- } else if (Files.exists(osgiBase)) {
- Path osgiPath = osgiBase.resolve(distributionUrl);
- if (Files.exists(osgiPath))
- baseUrl = osgiBase.toUri().toString();
- }
-
- this.baseDir = baseDir;
- this.confDir = this.baseDir.resolve("state");
- this.dataDir = this.baseDir.resolve("data");
-
- }
-
- public void start() {
- long begin = System.currentTimeMillis();
- // log4j
- Path log4jFile = confDir.resolve("log4j.properties");
- if (!Files.exists(log4jFile))
- copyResource("/org/argeo/osgi/boot/log4j.properties", log4jFile);
- System.setProperty("log4j.configuration", "file://" + log4jFile.toAbsolutePath());
-
- // Start Equinox
- try {
- ServiceLoader<FrameworkFactory> ff = ServiceLoader.load(FrameworkFactory.class);
- FrameworkFactory frameworkFactory = ff.iterator().next();
- Map<String, String> configuration = new HashMap<String, String>();
- configuration.put("osgi.configuration.area", confDir.toAbsolutePath().toString());
- configuration.put("osgi.instance.area", dataDir.toAbsolutePath().toString());
- defaultConfiguration(configuration);
-
- framework = frameworkFactory.newFramework(configuration);
- framework.start();
- info("## Date : " + new Date());
- info("## Data : " + dataDir.toAbsolutePath());
- } catch (Exception e) {
- throw new IllegalStateException("Cannot start OSGi framework", e);
- }
- BundleContext bundleContext = framework.getBundleContext();
- try {
-
- // Spring configs currently require System properties
- // System.getProperties().putAll(configuration);
-
- // expected by JAAS as System.property FIXME
- System.setProperty("osgi.instance.area", bundleContext.getProperty("osgi.instance.area"));
-
- // OSGi bootstrap
- OsgiBoot osgiBoot = new OsgiBoot(bundleContext);
-
- osgiBoot.installUrls(osgiBoot.getDistributionUrls(distributionUrl, baseUrl));
-
- // Start runtime
- Properties startProperties = new Properties();
- // TODO make it possible to override it
- startProperties.put("argeo.osgi.start.2.node",
- "org.eclipse.equinox.http.servlet,org.eclipse.equinox.http.jetty,"
- + "org.eclipse.equinox.metatype,org.eclipse.equinox.cm,org.eclipse.rap.rwt.osgi");
- startProperties.put("argeo.osgi.start.3.node", "org.argeo.cms");
- startProperties.put("argeo.osgi.start.4.node",
- "org.eclipse.gemini.blueprint.extender,org.eclipse.equinox.http.registry");
- osgiBoot.startBundles(startProperties);
-
- // Find node repository
- ServiceReference<?> sr = null;
- while (sr == null) {
- sr = bundleContext.getServiceReference("javax.jcr.Repository");
- if (System.currentTimeMillis() - begin > timeout)
- throw new RuntimeException("Could find node after " + timeout + "ms");
- Thread.sleep(100);
- }
- Object nodeDeployment = bundleContext.getService(sr);
- info("Node Deployment " + nodeDeployment);
-
- // Initialization completed
- long duration = System.currentTimeMillis() - begin;
- info("## CMS Launcher initialized in " + (duration / 1000) + "s " + (duration % 1000) + "ms");
- } catch (Exception e) {
- shutdown();
- throw new RuntimeException("Cannot start CMS", e);
- } finally {
-
- }
- }
-
- private void defaultConfiguration(Map<String, String> configuration) {
- // all permissions to OSGi security manager
- Path policyFile = confDir.resolve("node.policy");
- if (!Files.exists(policyFile))
- copyResource("/org/argeo/osgi/boot/node.policy", policyFile);
- configuration.put("java.security.policy", "file://" + policyFile.toAbsolutePath());
-
- configuration.put("org.eclipse.rap.workbenchAutostart", "false");
- configuration.put("org.eclipse.equinox.http.jetty.autostart", "false");
- configuration.put("org.osgi.framework.bootdelegation",
- "com.sun.jndi.ldap,com.sun.jndi.ldap.sasl,com.sun.security.jgss,com.sun.jndi.dns,"
- + "com.sun.nio.file,com.sun.nio.sctp");
-
- // Do clean
- // configuration.put("osgi.clean", "true");
- // if (args.length == 0) {
- // configuration.put("osgi.console", "");
- // }
- }
-
- public void shutdown() {
- try {
- framework.stop();
- framework.waitForStop(15 * 1000);
- } catch (Exception silent) {
- }
- }
-
- public Path getConfDir() {
- return confDir;
- }
-
- public Path getDataDir() {
- return dataDir;
- }
-
- public Framework getFramework() {
- return framework;
- }
-
- public static void main(String[] args) {
- try {
- String distributionUrl;
- Path executionDir;
- if (args.length == 2) {
- distributionUrl = args[0];
- executionDir = Paths.get(args[1]);
- } else if (args.length == 1) {
- executionDir = Paths.get(System.getProperty("user.dir"));
- distributionUrl = args[0];
- } else if (args.length == 0) {
- executionDir = Paths.get(System.getProperty("user.dir"));
- distributionUrl = "org/argeo/commons/org.argeo.dep.cms.sdk/2.1.70/org.argeo.dep.cms.sdk-2.1.70.jar";
- }else{
- printUsage();
- System.exit(1);
- return;
- }
-
- NodeRunner nodeRunner = new NodeRunner(distributionUrl, executionDir);
- nodeRunner.start();
-// if (args.length != 0)
-// System.exit(0);
- } catch (Exception e) {
- e.printStackTrace();
- System.exit(1);
- }
- }
-
- protected static void info(Object msg) {
- System.out.println(msg);
- }
-
- protected static void err(Object msg) {
- System.err.println(msg);
- }
-
- protected static void debug(Object msg) {
- System.out.println(msg);
- }
-
- protected static void copyResource(String resource, Path targetFile) {
- InputStream input = null;
- OutputStream output = null;
- try {
- input = NodeRunner.class.getResourceAsStream(resource);
- Files.createDirectories(targetFile.getParent());
- output = Files.newOutputStream(targetFile);
- byte[] buf = new byte[8192];
- while (true) {
- int length = input.read(buf);
- if (length < 0)
- break;
- output.write(buf, 0, length);
- }
- } catch (Exception e) {
- throw new RuntimeException("Cannot write " + resource + " file to " + targetFile, e);
- } finally {
- try {
- input.close();
- } catch (Exception ignore) {
- }
- try {
- output.close();
- } catch (Exception ignore) {
- }
- }
-
- }
-
- static void printUsage(){
- err("Usage: <distribution url> <base dir>");
- }
-}
package org.argeo.init.osgi;
-import static org.argeo.init.osgi.OsgiBootUtils.debug;
-import static org.argeo.init.osgi.OsgiBootUtils.warn;
+import static java.lang.System.Logger.Level.DEBUG;
+import static java.lang.System.Logger.Level.ERROR;
+import static java.lang.System.Logger.Level.TRACE;
+import static java.lang.System.Logger.Level.WARNING;
import java.io.File;
+import java.lang.System.Logger;
+import java.lang.System.Logger.Level;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.PathMatcher;
import java.nio.file.Paths;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
-import java.util.Properties;
+import java.util.Optional;
+import java.util.ServiceLoader;
import java.util.Set;
import java.util.SortedMap;
import java.util.StringTokenizer;
import java.util.TreeMap;
-import org.argeo.init.a2.A2Source;
-import org.argeo.init.a2.ProvisioningManager;
+import org.argeo.api.a2.A2Source;
+import org.argeo.api.a2.ProvisioningManager;
+import org.argeo.api.init.InitConstants;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleException;
import org.osgi.framework.FrameworkEvent;
import org.osgi.framework.Version;
+import org.osgi.framework.launch.Framework;
+import org.osgi.framework.launch.FrameworkFactory;
import org.osgi.framework.startlevel.BundleStartLevel;
import org.osgi.framework.startlevel.FrameworkStartLevel;
import org.osgi.framework.wiring.FrameworkWiring;
* properties. The approach is to generate list of URLs based on various
* methods, configured via properties.
*/
-public class OsgiBoot implements OsgiBootConstants {
- public final static String PROP_ARGEO_OSGI_START = "argeo.osgi.start";
- public final static String PROP_ARGEO_OSGI_MAX_START_LEVEL = "argeo.osgi.maxStartLevel";
- public final static String PROP_ARGEO_OSGI_SOURCES = "argeo.osgi.sources";
+public class OsgiBoot {
+ private final static Logger logger = System.getLogger(OsgiBoot.class.getName());
@Deprecated
final static String PROP_ARGEO_OSGI_BUNDLES = "argeo.osgi.bundles";
public final static String DEFAULT_BASE_URL = "reference:file:";
final static String DEFAULT_MAX_START_LEVEL = "32";
- // OSGi standard properties
- final static String PROP_OSGI_BUNDLES_DEFAULTSTARTLEVEL = "osgi.bundles.defaultStartLevel";
- final static String PROP_OSGI_STARTLEVEL = "osgi.startLevel";
- public final static String PROP_OSGI_INSTANCE_AREA = "osgi.instance.area";
- public final static String PROP_OSGI_CONFIGURATION_AREA = "osgi.configuration.area";
- public final static String PROP_OSGI_SHARED_CONFIGURATION_AREA = "osgi.sharedConfiguration.area";
- public final static String PROP_OSGI_USE_SYSTEM_PROPERTIES = "osgi.framework.useSystemProperties";
-
- // Symbolic names
- final static String SYMBOLIC_NAME_OSGI_BOOT = "org.argeo.init";
- final static String SYMBOLIC_NAME_EQUINOX = "org.eclipse.osgi";
-
private final BundleContext bundleContext;
private final String localCache;
private final ProvisioningManager provisioningManager;
localCache = getProperty(PROP_ARGEO_OSGI_LOCAL_CACHE, homeUri + ".m2/repository/");
provisioningManager = new ProvisioningManager(bundleContext);
- String sources = getProperty(PROP_ARGEO_OSGI_SOURCES);
+ String sources = getProperty(InitConstants.PROP_ARGEO_OSGI_SOURCES);
if (sources == null) {
provisioningManager.registerDefaultSource();
} else {
provisioningManager.registerSource(
A2Source.SCHEME_A2 + "://" + homePath.toString() + "/.local/share/a2" + queryPart);
provisioningManager.registerSource(A2Source.SCHEME_A2 + ":///usr/local/share/a2" + queryPart);
- provisioningManager.registerSource(A2Source.SCHEME_A2 + ":///usr/local/lib/a2" + queryPart);
+// provisioningManager.registerSource(A2Source.SCHEME_A2 + ":///usr/local/lib/a2" + queryPart);
provisioningManager.registerSource(A2Source.SCHEME_A2 + ":///usr/share/a2" + queryPart);
- provisioningManager.registerSource(A2Source.SCHEME_A2 + ":///usr/lib/a2" + queryPart);
+// provisioningManager.registerSource(A2Source.SCHEME_A2 + ":///usr/lib/a2" + queryPart);
} else if (source.trim().equals(A2Source.DEFAULT_A2_REFERENCE_URI)) {
if (Files.exists(homePath))
provisioningManager.registerSource(A2Source.SCHEME_A2_REFERENCE + "://" + homePath.toString()
+ "/.local/share/a2" + queryPart);
provisioningManager
.registerSource(A2Source.SCHEME_A2_REFERENCE + ":///usr/local/share/a2" + queryPart);
- provisioningManager
- .registerSource(A2Source.SCHEME_A2_REFERENCE + ":///usr/local/lib/a2" + queryPart);
+// provisioningManager
+// .registerSource(A2Source.SCHEME_A2_REFERENCE + ":///usr/local/lib/a2" + queryPart);
provisioningManager.registerSource(A2Source.SCHEME_A2_REFERENCE + ":///usr/share/a2" + queryPart);
- provisioningManager.registerSource(A2Source.SCHEME_A2_REFERENCE + ":///usr/lib/a2" + queryPart);
+// provisioningManager.registerSource(A2Source.SCHEME_A2_REFERENCE + ":///usr/lib/a2" + queryPart);
} else {
provisioningManager.registerSource(source + queryPart);
}
* with {@link BundleContext#getProperty(String)}. If these properties are
* <code>null</code>, system properties are used instead.
*/
- public void bootstrap(Map<String, String> properties) {
+ public void bootstrap() {
try {
long begin = System.currentTimeMillis();
-
- // notify start
- String osgiInstancePath = getProperty(PROP_OSGI_INSTANCE_AREA);
- String osgiConfigurationPath = getProperty(PROP_OSGI_CONFIGURATION_AREA);
- String osgiSharedConfigurationPath = getProperty(PROP_OSGI_CONFIGURATION_AREA);
- OsgiBootUtils.info("OSGi bootstrap starting" //
- + (osgiInstancePath != null ? " data: " + osgiInstancePath + "" : "") //
- + (osgiConfigurationPath != null ? " state: " + osgiConfigurationPath + "" : "") //
- + (osgiSharedConfigurationPath != null ? " config: " + osgiSharedConfigurationPath + "" : "") //
- );
-
- // legacy install bundles
- installUrls(getBundlesUrls());
- installUrls(getDistributionUrls());
-
- // A2 install bundles
- provisioningManager.install(null);
-
+ // Install bundles
+ install();
// Make sure fragments are properly considered by refreshing
- refreshFramework();
-
- // start bundles
-// if (properties != null && !Boolean.parseBoolean(properties.get(PROP_OSGI_USE_SYSTEM_PROPERTIES)))
- startBundles(properties);
-// else
-// startBundles();
-
- // complete
+ refresh();
+ // Start bundles
+ startBundles();
long duration = System.currentTimeMillis() - begin;
- OsgiBootUtils.info("OSGi bootstrap completed in " + Math.round(((double) duration) / 1000) + "s ("
+ logger.log(DEBUG, () -> "OSGi bootstrap completed in " + Math.round(((double) duration) / 1000) + "s ("
+ duration + "ms), " + bundleContext.getBundles().length + " bundles");
} catch (RuntimeException e) {
- OsgiBootUtils.error("OSGi bootstrap FAILED", e);
+ logger.log(ERROR, "OSGi bootstrap FAILED", e);
throw e;
}
// diagnostics
- if (OsgiBootUtils.isDebug()) {
+ if (logger.isLoggable(TRACE)) {
OsgiBootDiagnostics diagnostics = new OsgiBootDiagnostics(bundleContext);
diagnostics.checkUnresolved();
Map<String, Set<String>> duplicatePackages = diagnostics.findPackagesExportedTwice();
if (duplicatePackages.size() > 0) {
- OsgiBootUtils.info("Packages exported twice:");
+ logger.log(TRACE, "Packages exported twice:");
Iterator<String> it = duplicatePackages.keySet().iterator();
while (it.hasNext()) {
String pkgName = it.next();
- OsgiBootUtils.info(pkgName);
+ logger.log(TRACE, pkgName);
Set<String> bdles = duplicatePackages.get(pkgName);
Iterator<String> bdlesIt = bdles.iterator();
while (bdlesIt.hasNext())
- OsgiBootUtils.info(" " + bdlesIt.next());
+ logger.log(TRACE, " " + bdlesIt.next());
}
}
}
System.out.println();
}
- /**
- * Calls {@link #bootstrap(Map)} with <code>null</code>.
- *
- * @see #bootstrap(Map)
- */
- @Deprecated
- public void bootstrap() {
- bootstrap(null);
+ public void install() {
+ String osgiInstancePath = getProperty(InitConstants.PROP_OSGI_INSTANCE_AREA);
+ String osgiConfigurationPath = getProperty(InitConstants.PROP_OSGI_CONFIGURATION_AREA);
+ String osgiSharedConfigurationPath = getProperty(InitConstants.PROP_OSGI_CONFIGURATION_AREA);
+ logger.log(DEBUG, () -> "OSGi bootstrap starting" //
+ + (osgiInstancePath != null ? " data: " + osgiInstancePath + "" : "") //
+ + (osgiConfigurationPath != null ? " state: " + osgiConfigurationPath + "" : "") //
+ + (osgiSharedConfigurationPath != null ? " config: " + osgiSharedConfigurationPath + "" : "") //
+ );
+
+ // legacy install bundles
+ installUrls(getBundlesUrls());
+ installUrls(getDistributionUrls());
+
+ // A2 install bundles
+ provisioningManager.install(null);
+
}
public void update() {
try {
if (installedBundles.containsKey(url)) {
Bundle bundle = (Bundle) installedBundles.get(url);
- if (OsgiBootUtils.isDebug())
- debug("Bundle " + bundle.getSymbolicName() + " already installed from " + url);
- } else if (url.contains("/" + SYMBOLIC_NAME_EQUINOX + "/")
- || url.contains("/" + SYMBOLIC_NAME_OSGI_BOOT + "/")) {
- if (OsgiBootUtils.isDebug())
- warn("Skip " + url);
+ logger.log(TRACE, () -> "Bundle " + bundle.getSymbolicName() + " already installed from " + url);
+ } else if (url.contains("/" + InitConstants.SYMBOLIC_NAME_EQUINOX + "/")
+ || url.contains("/" + InitConstants.SYMBOLIC_NAME_INIT + "/")) {
+ if (logger.isLoggable(TRACE))
+ logger.log(WARNING, "Skip " + url);
return;
} else {
Bundle bundle = bundleContext.installBundle(url);
if (url.startsWith("http"))
- OsgiBootUtils
- .info("Installed " + bundle.getSymbolicName() + "-" + bundle.getVersion() + " from " + url);
- else if (OsgiBootUtils.isDebug())
- OsgiBootUtils.debug(
- "Installed " + bundle.getSymbolicName() + "-" + bundle.getVersion() + " from " + url);
+ logger.log(DEBUG,
+ () -> "Installed " + bundle.getSymbolicName() + "-" + bundle.getVersion() + " from " + url);
+ else
+ logger.log(TRACE,
+ () -> "Installed " + bundle.getSymbolicName() + "-" + bundle.getVersion() + " from " + url);
assert bundle.getSymbolicName() != null;
// uninstall previous versions
bundles: for (Bundle b : bundleContext.getBundles()) {
if (bundleV.getMicro() > bV.getMicro()) {
// uninstall older bundles
b.uninstall();
- OsgiBootUtils.debug("Uninstalled " + b);
+ logger.log(TRACE, () -> "Uninstalled " + b);
} else if (bundleV.getMicro() < bV.getMicro()) {
// uninstall just installed bundle if newer
bundle.uninstall();
- OsgiBootUtils.debug("Uninstalled " + bundle);
+ logger.log(TRACE, () -> "Uninstalled " + bundle);
break bundles;
} else {
// uninstall any other with same major/minor
if (!bundleV.getQualifier().equals(bV.getQualifier())) {
b.uninstall();
- OsgiBootUtils.debug("Uninstalled " + b);
+ logger.log(TRACE, () -> "Uninstalled " + b);
}
}
}
} catch (BundleException e) {
final String ALREADY_INSTALLED = "is already installed";
String message = e.getMessage();
- if ((message.contains("Bundle \"" + SYMBOLIC_NAME_OSGI_BOOT + "\"")
- || message.contains("Bundle \"" + SYMBOLIC_NAME_EQUINOX + "\""))
+ if ((message.contains("Bundle \"" + InitConstants.SYMBOLIC_NAME_INIT + "\"")
+ || message.contains("Bundle \"" + InitConstants.SYMBOLIC_NAME_EQUINOX + "\""))
&& message.contains(ALREADY_INSTALLED)) {
// silent, in order to avoid warnings: we know that both
// have already been installed...
} else {
if (message.contains(ALREADY_INSTALLED)) {
- if (OsgiBootUtils.isDebug())
- OsgiBootUtils.warn("Duplicate install from " + url + ": " + message);
+ if (logger.isLoggable(TRACE))
+ logger.log(WARNING, "Duplicate install from " + url + ": " + message);
} else
- OsgiBootUtils.warn("Could not install bundle from " + url + ": " + message);
+ logger.log(WARNING, "Could not install bundle from " + url + ": " + message);
}
- if (OsgiBootUtils.isDebug() && !message.contains(ALREADY_INSTALLED))
+ if (logger.isLoggable(TRACE) && !message.contains(ALREADY_INSTALLED))
e.printStackTrace();
}
}
*
* @see OsgiBoot#doStartBundles(Map)
*/
- public void startBundles(Map<String, String> properties) {
+ public void startBundles() {
Map<String, String> map = new TreeMap<>();
// first use properties
- if (properties != null) {
- for (String key : properties.keySet()) {
- String property = key;
- if (property.startsWith(PROP_ARGEO_OSGI_START)) {
- map.put(property, properties.get(property));
- }
- }
- }
+// if (properties != null) {
+// for (String key : properties.keySet()) {
+// String property = key;
+// if (property.startsWith(InitConstants.PROP_ARGEO_OSGI_START)) {
+// map.put(property, properties.get(property));
+// }
+// }
+// }
// then try all start level until a maximum
- int maxStartLevel = Integer.parseInt(getProperty(PROP_ARGEO_OSGI_MAX_START_LEVEL, DEFAULT_MAX_START_LEVEL));
+ int maxStartLevel = Integer
+ .parseInt(getProperty(InitConstants.PROP_ARGEO_OSGI_MAX_START_LEVEL, DEFAULT_MAX_START_LEVEL));
for (int i = 1; i <= maxStartLevel; i++) {
- String key = PROP_ARGEO_OSGI_START + "." + i;
+ String key = InitConstants.PROP_ARGEO_OSGI_START + "." + i;
String value = getProperty(key);
if (value != null)
map.put(key, value);
}
// finally, override with system properties
- for (Object key : System.getProperties().keySet()) {
- if (key.toString().startsWith(PROP_ARGEO_OSGI_START)) {
- map.put(key.toString(), System.getProperty(key.toString()));
- }
- }
+// for (Object key : System.getProperties().keySet()) {
+// if (key.toString().startsWith(InitConstants.PROP_ARGEO_OSGI_START)) {
+// map.put(key.toString(), System.getProperty(key.toString()));
+// }
+// }
// start
doStartBundles(map);
}
- @Deprecated
- public void startBundles(Properties properties) {
- Map<String, String> map = new TreeMap<>();
- // first use properties
- if (properties != null) {
- for (Object key : properties.keySet()) {
- String property = key.toString();
- if (property.startsWith(PROP_ARGEO_OSGI_START)) {
- map.put(property, properties.get(property).toString());
- }
- }
- }
- startBundles(map);
- }
+// void startBundles(Properties properties) {
+// Map<String, String> map = new TreeMap<>();
+// // first use properties
+// if (properties != null) {
+// for (Object key : properties.keySet()) {
+// String property = key.toString();
+// if (property.startsWith(InitConstants.PROP_ARGEO_OSGI_START)) {
+// map.put(property, properties.get(property).toString());
+// }
+// }
+// }
+// startBundles(map);
+// }
- /** Start bundle based on keys starting with {@link #PROP_ARGEO_OSGI_START}. */
+ /**
+ * Start bundle based on keys starting with
+ * {@link InitConstants#PROP_ARGEO_OSGI_START}.
+ */
protected void doStartBundles(Map<String, String> properties) {
FrameworkStartLevel frameworkStartLevel = bundleContext.getBundle(0).adapt(FrameworkStartLevel.class);
// default and active start levels from System properties
int initialStartLevel = frameworkStartLevel.getInitialBundleStartLevel();
- int defaultStartLevel = Integer.parseInt(getProperty(PROP_OSGI_BUNDLES_DEFAULTSTARTLEVEL, "4"));
- int activeStartLevel = Integer.parseInt(getProperty(PROP_OSGI_STARTLEVEL, "6"));
- if (OsgiBootUtils.isDebug()) {
- OsgiBootUtils.debug("OSGi default start level: "
- + getProperty(PROP_OSGI_BUNDLES_DEFAULTSTARTLEVEL, "<not set>") + ", using " + defaultStartLevel);
- OsgiBootUtils.debug("OSGi active start level: " + getProperty(PROP_OSGI_STARTLEVEL, "<not set>")
+ int defaultStartLevel = Integer.parseInt(getProperty(InitConstants.PROP_OSGI_BUNDLES_DEFAULTSTARTLEVEL, "4"));
+ int activeStartLevel = Integer.parseInt(getProperty(InitConstants.PROP_OSGI_STARTLEVEL, "6"));
+ if (logger.isLoggable(TRACE)) {
+ logger.log(TRACE,
+ "OSGi default start level: "
+ + getProperty(InitConstants.PROP_OSGI_BUNDLES_DEFAULTSTARTLEVEL, "<not set>") + ", using "
+ + defaultStartLevel);
+ logger.log(TRACE, "OSGi active start level: " + getProperty(InitConstants.PROP_OSGI_STARTLEVEL, "<not set>")
+ ", using " + activeStartLevel);
- OsgiBootUtils.debug("Framework start level: " + frameworkStartLevel.getStartLevel() + " (initial: "
+ logger.log(TRACE, "Framework start level: " + frameworkStartLevel.getStartLevel() + " (initial: "
+ initialStartLevel + ")");
}
try {
bundle.start();
} catch (BundleException e) {
- OsgiBootUtils.error("Cannot mark " + bsn + " as started", e);
+ logger.log(ERROR, "Cannot mark " + bsn + " as started", e);
}
- if (OsgiBootUtils.isDebug())
- OsgiBootUtils.debug(bsn + " v" + bundle.getVersion() + " starts at level " + level);
+ logger.log(TRACE, () -> bsn + " v" + bundle.getVersion() + " starts at level " + level);
}
}
}
- if (OsgiBootUtils.isDebug())
- OsgiBootUtils.debug("About to set framework start level to " + activeStartLevel + " ...");
+ logger.log(TRACE, () -> "About to set framework start level to " + activeStartLevel + " ...");
frameworkStartLevel.setStartLevel(activeStartLevel, (FrameworkEvent event) -> {
if (event.getType() == FrameworkEvent.ERROR) {
- OsgiBootUtils.error("Start sequence failed", event.getThrowable());
+ logger.log(ERROR, "Start sequence failed", event.getThrowable());
} else {
- if (OsgiBootUtils.isDebug())
- OsgiBootUtils.debug("Framework started at level " + frameworkStartLevel.getStartLevel());
+ logger.log(TRACE, () -> "Framework started at level " + frameworkStartLevel.getStartLevel());
}
});
Integer defaultStartLevel) {
// default (and previously, only behaviour)
- appendToStartLevels(startLevels, defaultStartLevel, properties.getOrDefault(PROP_ARGEO_OSGI_START, ""));
+ appendToStartLevels(startLevels, defaultStartLevel,
+ properties.getOrDefault(InitConstants.PROP_ARGEO_OSGI_START, ""));
// list argeo.osgi.start.* system properties
Iterator<String> keys = properties.keySet().iterator();
- final String prefix = PROP_ARGEO_OSGI_START + ".";
+ final String prefix = InitConstants.PROP_ARGEO_OSGI_START + ".";
while (keys.hasNext()) {
String key = keys.next();
if (key.startsWith(prefix)) {
return urls;
// bundlePatterns = SystemPropertyUtils.resolvePlaceholders(bundlePatterns);
- if (OsgiBootUtils.isDebug())
- debug(PROP_ARGEO_OSGI_BUNDLES + "=" + bundlePatterns);
+ logger.log(TRACE, () -> PROP_ARGEO_OSGI_BUNDLES + "=" + bundlePatterns);
StringTokenizer st = new StringTokenizer(bundlePatterns, ",");
List<BundlesSet> bundlesSets = new ArrayList<BundlesSet>();
File[] files = baseDir.listFiles();
if (files == null) {
- if (OsgiBootUtils.isDebug())
- OsgiBootUtils.warn("Base dir " + baseDir + " has no children, exists=" + baseDir.exists()
+ if (logger.isLoggable(TRACE))
+ logger.log(Level.WARNING, "Base dir " + baseDir + " has no children, exists=" + baseDir.exists()
+ ", isDirectory=" + baseDir.isDirectory());
return;
}
// }
} else {
boolean nonDirectoryOk = matcher.matches(Paths.get(newCurrentPath));
- if (OsgiBootUtils.isDebug())
- debug(currentPath + " " + (ok ? "" : " not ") + " matched with " + pattern);
+ logger.log(TRACE,
+ () -> currentPath + " " + (ok ? "" : " not ") + " matched with " + pattern);
if (nonDirectoryOk)
matched.add(relativeToFullPath(base, newCurrentPath));
}
return (basePath + '/' + relativePath).replace('/', File.separatorChar);
}
- private void refreshFramework() {
+ public void refresh() {
Bundle systemBundle = bundleContext.getBundle(0);
FrameworkWiring frameworkWiring = systemBundle.adapt(FrameworkWiring.class);
// TODO deal with refresh breaking native loading (e.g SWT)
return bundleContext;
}
+ /*
+ * PLAIN OSGI LAUNCHER
+ */
+ /** Launch an OSGi framework. OSGi Boot initialisation is NOT performed. */
+ public static Framework defaultOsgiLaunch(Map<String, String> configuration) {
+ Optional<FrameworkFactory> frameworkFactory = ServiceLoader.load(FrameworkFactory.class).findFirst();
+ if (frameworkFactory.isEmpty())
+ throw new IllegalStateException("No framework factory found");
+ return defaultOsgiLaunch(frameworkFactory.get(), configuration);
+ }
+
+ /** Launch an OSGi framework. OSGi Boot initialisation is NOT performed. */
+ public static Framework defaultOsgiLaunch(FrameworkFactory frameworkFactory, Map<String, String> configuration) {
+ // start OSGi
+ Framework framework = frameworkFactory.newFramework(configuration);
+ try {
+ framework.start();
+ } catch (BundleException e) {
+ throw new IllegalStateException("Cannot start OSGi framework", e);
+ }
+ return framework;
+ }
+
+ /*
+ * OSGI UTILITIES
+ */
+ /** Uninstall all bundles with these symbolic names */
+ public static void uninstallBundles(BundleContext bc, String... symbolicNames) {
+ List<String> lst = Arrays.asList(symbolicNames);
+ for (Bundle b : bc.getBundles()) {
+ String sn = b.getSymbolicName();
+ if (sn == null)
+ continue;
+ if (lst.contains(sn)) {
+ try {
+ b.uninstall();
+ } catch (BundleException e) {
+ logger.log(ERROR, "Cannot uninstall " + sn, e);
+ }
+ }
+ }
+
+ }
}
+++ /dev/null
-package org.argeo.init.osgi;
-
-public interface OsgiBootConstants {
-
-}
package org.argeo.init.osgi;
+import static java.lang.System.Logger.Level.WARNING;
+
+import java.lang.System.Logger;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.osgi.service.packageadmin.ExportedPackage;
import org.osgi.service.packageadmin.PackageAdmin;
-@SuppressWarnings("deprecation")
class OsgiBootDiagnostics {
+ private final static Logger logger = System.getLogger(OsgiBootDiagnostics.class.getName());
+
private final BundleContext bundleContext;
public OsgiBootDiagnostics(BundleContext bundleContext) {
}
if (unresolvedBundles.size() != 0) {
- OsgiBootUtils.warn("Unresolved bundles " + unresolvedBundles);
+ logger.log(WARNING, "Unresolved bundles " + unresolvedBundles);
}
// try to start unresolved bundles in order to trigger diagnostics
+++ /dev/null
-package org.argeo.init.osgi;
-
-import java.lang.System.Logger;
-import java.lang.System.Logger.Level;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Optional;
-import java.util.ServiceLoader;
-import java.util.StringTokenizer;
-
-import org.osgi.framework.Bundle;
-import org.osgi.framework.BundleException;
-import org.osgi.framework.launch.Framework;
-import org.osgi.framework.launch.FrameworkFactory;
-
-/** Utilities, mostly related to logging. */
-public class OsgiBootUtils {
- private final static Logger logger = System.getLogger(OsgiBootUtils.class.getName());
-
- public static void info(Object obj) {
- logger.log(Level.INFO, () -> Objects.toString(obj));
- }
-
- public static void debug(Object obj) {
- logger.log(Level.TRACE, () -> Objects.toString(obj));
- }
-
- public static void warn(Object obj) {
- logger.log(Level.WARNING, () -> Objects.toString(obj));
- }
-
- public static void error(Object obj, Throwable e) {
- logger.log(Level.ERROR, () -> Objects.toString(obj), e);
- }
-
- public static boolean isDebug() {
- return logger.isLoggable(Level.TRACE);
- }
-
- public static String stateAsString(int state) {
- switch (state) {
- case Bundle.UNINSTALLED:
- return "UNINSTALLED";
- case Bundle.INSTALLED:
- return "INSTALLED";
- case Bundle.RESOLVED:
- return "RESOLVED";
- case Bundle.STARTING:
- return "STARTING";
- case Bundle.ACTIVE:
- return "ACTIVE";
- case Bundle.STOPPING:
- return "STOPPING";
- default:
- return Integer.toString(state);
- }
- }
-
- /**
- * @return ==0: versions are identical, <0: tested version is newer, >0:
- * currentVersion is newer.
- */
- public static int compareVersions(String currentVersion, String testedVersion) {
- List<String> cToks = new ArrayList<String>();
- StringTokenizer cSt = new StringTokenizer(currentVersion, ".");
- while (cSt.hasMoreTokens())
- cToks.add(cSt.nextToken());
- List<String> tToks = new ArrayList<String>();
- StringTokenizer tSt = new StringTokenizer(currentVersion, ".");
- while (tSt.hasMoreTokens())
- tToks.add(tSt.nextToken());
-
- int comp = 0;
- comp: for (int i = 0; i < cToks.size(); i++) {
- if (tToks.size() <= i) {
- // equals until then, tested shorter
- comp = 1;
- break comp;
- }
-
- String c = (String) cToks.get(i);
- String t = (String) tToks.get(i);
-
- try {
- int cInt = Integer.parseInt(c);
- int tInt = Integer.parseInt(t);
- if (cInt == tInt)
- continue comp;
- else {
- comp = (cInt - tInt);
- break comp;
- }
- } catch (NumberFormatException e) {
- if (c.equals(t))
- continue comp;
- else {
- comp = c.compareTo(t);
- break comp;
- }
- }
- }
-
- if (comp == 0 && tToks.size() > cToks.size()) {
- // equals until then, current shorter
- comp = -1;
- }
-
- return comp;
- }
-
- public static Framework launch(Map<String, String> configuration) {
- Optional<FrameworkFactory> frameworkFactory = ServiceLoader.load(FrameworkFactory.class).findFirst();
- if (frameworkFactory.isEmpty())
- throw new IllegalStateException("No framework factory found");
- return launch(frameworkFactory.get(), configuration);
- }
-
- /** Launch an OSGi framework. */
- public static Framework launch(FrameworkFactory frameworkFactory, Map<String, String> configuration) {
- // start OSGi
- Framework framework = frameworkFactory.newFramework(configuration);
- try {
- framework.start();
- } catch (BundleException e) {
- throw new IllegalStateException("Cannot start OSGi framework", e);
- }
- return framework;
- }
-
- @Deprecated
- public static Map<String, String> equinoxArgsToConfiguration(String[] args) {
- // FIXME implement it
- return new HashMap<>();
- }
-
-}
package org.argeo.init.osgi;
+import static java.lang.System.Logger.Level.ERROR;
+import static java.lang.System.Logger.Level.TRACE;
+
+import java.lang.System.Logger;
import java.lang.reflect.Method;
import java.net.URI;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
-import java.util.Properties;
import java.util.Set;
import java.util.TreeMap;
+import org.argeo.api.init.InitConstants;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleEvent;
/** OSGi builder, focusing on ease of use for scripting. */
public class OsgiBuilder {
+ private final static Logger logger = System.getLogger(OsgiBuilder.class.getName());
+
private final static String PROP_HTTP_PORT = "org.osgi.service.http.port";
private final static String PROP_HTTPS_PORT = "org.osgi.service.https.port";
private final static String PROP_OSGI_CLEAN = "osgi.clean";
public OsgiBuilder() {
// configuration.put("osgi.clean", "true");
- configuration.put(OsgiBoot.PROP_OSGI_CONFIGURATION_AREA, System.getProperty(OsgiBoot.PROP_OSGI_CONFIGURATION_AREA));
- configuration.put(OsgiBoot.PROP_OSGI_INSTANCE_AREA, System.getProperty(OsgiBoot.PROP_OSGI_INSTANCE_AREA));
+ configuration.put(InitConstants.PROP_OSGI_CONFIGURATION_AREA,
+ System.getProperty(InitConstants.PROP_OSGI_CONFIGURATION_AREA));
+ configuration.put(InitConstants.PROP_OSGI_INSTANCE_AREA,
+ System.getProperty(InitConstants.PROP_OSGI_INSTANCE_AREA));
configuration.put(PROP_OSGI_CLEAN, System.getProperty(PROP_OSGI_CLEAN));
}
public Framework launch() {
+ configuration.putAll(startLevelsToProperties());
// start OSGi
- framework = OsgiBootUtils.launch(configuration);
+ framework = OsgiBoot.defaultOsgiLaunch(configuration);
BundleContext bc = framework.getBundleContext();
- String osgiData = bc.getProperty(OsgiBoot.PROP_OSGI_INSTANCE_AREA);
+ String osgiData = bc.getProperty(InitConstants.PROP_OSGI_INSTANCE_AREA);
// String osgiConf = bc.getProperty(OsgiBoot.CONFIGURATION_AREA_PROP);
String osgiConf = framework.getDataFile("").getAbsolutePath();
- if (OsgiBootUtils.isDebug())
- OsgiBootUtils.debug("OSGi starting - data: " + osgiData + " conf: " + osgiConf);
+ logger.log(TRACE, () -> "OSGi starting - data: " + osgiData + " conf: " + osgiConf);
OsgiBoot osgiBoot = new OsgiBoot(framework.getBundleContext());
if (distributionBundles.isEmpty()) {
}
}
// start bundles
- osgiBoot.startBundles(startLevelsToProperties());
+ osgiBoot.startBundles();
// if (OsgiBootUtils.isDebug())
// for (Bundle bundle : bc.getBundles()) {
try {
return st.waitForService(timeout);
} catch (InterruptedException e) {
- OsgiBootUtils.error("Interrupted", e);
+ logger.log(ERROR, "Interrupted", e);
return null;
} finally {
st.close();
//
// UTILITIES
//
- private Properties startLevelsToProperties() {
- Properties properties = new Properties();
+ private Map<String, String> startLevelsToProperties() {
+ Map<String, String> properties = new HashMap<>();
for (Integer startLevel : startLevels.keySet()) {
- String property = OsgiBoot.PROP_ARGEO_OSGI_START + "." + startLevel;
+ String property = InitConstants.PROP_ARGEO_OSGI_START + "." + startLevel;
StringBuilder value = new StringBuilder();
for (String bundle : startLevels.get(startLevel).getBundles()) {
value.append(bundle);
package org.argeo.init.osgi;
+import java.io.Serializable;
+import java.lang.System.Logger;
+import java.lang.System.Logger.Level;
import java.lang.System.LoggerFinder;
import java.util.Collections;
import java.util.Hashtable;
import java.util.Map;
-import java.util.Objects;
import java.util.Optional;
import java.util.ServiceLoader;
import java.util.concurrent.Flow;
import java.util.function.Consumer;
+import java.util.function.Supplier;
-import org.argeo.init.RuntimeContext;
-import org.argeo.init.logging.ThinLoggerFinder;
-import org.osgi.framework.Bundle;
+import org.argeo.api.init.RuntimeContext;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleException;
import org.osgi.framework.Constants;
/** An OSGi runtime context. */
public class OsgiRuntimeContext implements RuntimeContext, AutoCloseable {
+ private final static Logger logger = System.getLogger(OsgiRuntimeContext.class.getName());
+
+ private final static long STOP_FOR_UPDATE_TIMEOUT = 60 * 1000;
+ private final static long CLOSE_TIMEOUT = 60 * 1000;
+
+ // private final static String SYMBOLIC_NAME_FELIX_SCR = "org.apache.felix.scr";
+
private Map<String, String> config;
private Framework framework;
- private OsgiBoot osgiBoot;
+// private OsgiBoot osgiBoot;
/**
* Constructor to use when the runtime context will create the OSGi
@Override
public void run() {
- ServiceLoader<FrameworkFactory> sl = ServiceLoader.load(FrameworkFactory.class);
- Optional<FrameworkFactory> opt = sl.findFirst();
- if (opt.isEmpty())
- throw new IllegalStateException("Cannot find OSGi framework");
- framework = opt.get().newFramework(config);
+ if (framework != null && framework.getState() >= Framework.STARTING)
+ throw new IllegalStateException("OSGi framework is already started");
+
+ if (framework == null) {
+ ServiceLoader<FrameworkFactory> sl = ServiceLoader.load(FrameworkFactory.class);
+ Optional<FrameworkFactory> opt = sl.findFirst();
+ if (opt.isEmpty())
+ throw new IllegalStateException("Cannot find OSGi framework");
+ framework = opt.get().newFramework(config);
+ }
+
try {
framework.start();
BundleContext bundleContext = framework.getBundleContext();
}
}
- public void start(BundleContext bundleContext) {
+ protected void start(BundleContext bundleContext) {
// preferences
// SystemRootPreferences systemRootPreferences = ThinPreferencesFactory.getInstance().getSystemRootPreferences();
// bundleContext.registerService(AbstractPreferences.class, systemRootPreferences, new Hashtable<>());
// Make sure LoggerFinder has been searched for, since it is lazily loaded
- LoggerFinder.getLoggerFinder();
-
- // logging
- bundleContext.registerService(Consumer.class, ThinLoggerFinder.getConfigurationConsumer(),
- new Hashtable<>(Collections.singletonMap(Constants.SERVICE_PID, "argeo.logging.configuration")));
- bundleContext.registerService(Flow.Publisher.class, ThinLoggerFinder.getLogEntryPublisher(),
- new Hashtable<>(Collections.singletonMap(Constants.SERVICE_PID, "argeo.logging.publisher")));
+ LoggerFinder loggerFinder = LoggerFinder.getLoggerFinder();
+
+ if (loggerFinder instanceof Consumer<?> && loggerFinder instanceof Supplier<?>) {
+ @SuppressWarnings("unchecked")
+ Consumer<Map<String, Object>> consumer = (Consumer<Map<String, Object>>) loggerFinder;
+ // ThinLoggerFinder.getConfigurationConsumer()
+ // ThinLoggerFinder.getLogEntryPublisher()
+
+ @SuppressWarnings("unchecked")
+ Supplier<Flow.Publisher<Map<String, Serializable>>> supplier = (Supplier<Flow.Publisher<Map<String, Serializable>>>) loggerFinder;
+ // logging
+ bundleContext.registerService(Consumer.class, consumer,
+ new Hashtable<>(Collections.singletonMap(Constants.SERVICE_PID, "argeo.logging.configuration")));
+ bundleContext.registerService(Flow.Publisher.class, supplier.get(),
+ new Hashtable<>(Collections.singletonMap(Constants.SERVICE_PID, "argeo.logging.publisher")));
+ }
+ OsgiBoot osgiBoot = new OsgiBoot(bundleContext);
+ String frameworkUuuid = bundleContext.getProperty(Constants.FRAMEWORK_UUID);
+
+ // separate thread in order to improve logging
+ Thread osgiBootThread = new Thread("OSGi boot framework " + frameworkUuuid) {
+ @Override
+ public void run() {
+ osgiBoot.bootstrap();
+ }
+ };
+ osgiBootThread.start();
+ // TODO return a completable stage so that inits can run in parallel
+// try {
+// osgiBootThread.join(60 * 1000);
+// } catch (InterruptedException e) {
+// // silent
+// }
+ }
- osgiBoot = new OsgiBoot(bundleContext);
- osgiBoot.bootstrap(config);
+ public void update() {
+ stop();
+ try {
+ waitForStop(STOP_FOR_UPDATE_TIMEOUT);
+ } catch (InterruptedException e) {
+ logger.log(Level.TRACE, "Wait for stop interrupted", e);
+ }
+ run();
+ // TODO Optimise with OSGi mechanisms (e.g. framework.update())
+// if (osgiBoot != null) {
+// Objects.requireNonNull(osgiBoot);
+// osgiBoot.update();
+// }
}
- public void update() {
- Objects.requireNonNull(osgiBoot);
- osgiBoot.update();
+ protected void stop() {
+ if (framework == null)
+ return;
+ stop(framework.getBundleContext());
+ try {
+ framework.stop();
+ } catch (BundleException e) {
+ throw new IllegalStateException("Cannot stop OSGi framework", e);
+ }
}
- public void stop(BundleContext bundleContext) {
+ protected void stop(BundleContext bundleContext) {
// if (loggingConfigurationSr != null)
// try {
// loggingConfigurationSr.unregister();
@Override
public void waitForStop(long timeout) throws InterruptedException {
if (framework == null)
- throw new IllegalStateException("Framework is not initialised");
+ return;
framework.waitForStop(timeout);
}
public void close() throws Exception {
+ if (framework == null)
+ return;
// TODO make shutdown of dynamic service more robust
- Bundle scrBundle = osgiBoot.getBundlesBySymbolicName().get("org.apache.felix.scr");
- if (scrBundle != null) {
- scrBundle.stop();
- while (!(scrBundle.getState() <= Bundle.RESOLVED)) {
- Thread.sleep(500);
- }
- Thread.sleep(1000);
- }
-
- stop(framework.getBundleContext());
- if (framework != null)
- framework.stop();
+// for (Bundle scrBundle : framework.getBundleContext().getBundles()) {
+// if (scrBundle.getSymbolicName().equals(SYMBOLIC_NAME_FELIX_SCR)) {
+// if (scrBundle.getState() > Bundle.RESOLVED) {
+// scrBundle.stop();
+// while (!(scrBundle.getState() <= Bundle.RESOLVED)) {
+// Thread.sleep(100);
+// }
+// Thread.sleep(500);
+// }
+// }
+// }
+ stop();
+ waitForStop(CLOSE_TIMEOUT);
+ framework = null;
+// osgiBoot = null;
+ config.clear();
}
public Framework getFramework() {
--- /dev/null
+package org.argeo.init.osgi;
+
+import static java.lang.System.Logger.Level.INFO;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.System.Logger;
+import java.net.URL;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Collections;
+import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.UUID;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+import org.argeo.api.init.InitConstants;
+import org.argeo.api.init.RuntimeManager;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.Constants;
+import org.osgi.framework.FrameworkEvent;
+import org.osgi.framework.connect.ConnectContent;
+import org.osgi.framework.connect.ConnectFrameworkFactory;
+import org.osgi.framework.connect.ConnectModule;
+import org.osgi.framework.connect.ModuleConnector;
+import org.osgi.framework.launch.Framework;
+import org.osgi.framework.wiring.BundleWiring;
+
+public class SubFrameworkActivator implements BundleActivator {
+ private final static Logger logger = System.getLogger(SubFrameworkActivator.class.getName());
+
+// private final static String EQUINOX_FRAMEWORK_CLASS = "org.eclipse.osgi.launch.Equinox";
+ private final static String EQUINOX_FRAMEWORK_FACTORY_CLASS = "org.eclipse.osgi.launch.EquinoxFactory";
+
+// private ClassLoader bundleClassLoader;
+// private ClassLoader subFrameworkClassLoader;
+ private BundleContext foreignBundleContext;
+
+ private ConnectFrameworkFactory frameworkFactory;
+
+ private Map<UUID, Framework> subFrameworks = Collections.synchronizedMap(new HashMap<>());
+
+ private UUID foreignFrameworkUuid;
+
+ @Override
+ public void start(BundleContext context) throws Exception {
+ this.foreignBundleContext = context;
+ foreignFrameworkUuid = UUID.fromString(foreignBundleContext.getProperty(Constants.FRAMEWORK_UUID));
+
+ try {
+// Bundle bundle = context.getBundle();
+// ClassLoader bundleClassLoader = bundle.adapt(BundleWiring.class).getClassLoader();
+// subFrameworkClassLoader = new URLClassLoader(new URL[0], bundleClassLoader);
+
+ @SuppressWarnings("unchecked")
+ Class<? extends ConnectFrameworkFactory> frameworkFactoryClass = (Class<? extends ConnectFrameworkFactory>) Framework.class
+ .getClassLoader().loadClass(EQUINOX_FRAMEWORK_FACTORY_CLASS);
+ frameworkFactory = frameworkFactoryClass.getConstructor().newInstance();
+
+ boolean test = true;
+ if (test)
+ new Thread() {
+
+ @Override
+ public void run() {
+ for (int i = 0; i < 5; i++) {
+ Map<String, String> config = new HashMap<>();
+ Path basePase = Paths.get(System.getProperty("user.home"), ".config/argeo/test/",
+ "test" + i);
+ config.put(InitConstants.PROP_OSGI_CONFIGURATION_AREA,
+ basePase.resolve(RuntimeManager.STATE).toString());
+ config.put(InitConstants.PROP_OSGI_INSTANCE_AREA,
+ basePase.resolve(RuntimeManager.DATA).toString());
+ config.put("argeo.host", "host" + i);
+ config.put("osgi.console", "host" + i + ":2023");
+ createFramework(config);
+ }
+ }
+
+ }.start();
+ } catch (Exception e) {
+ e.printStackTrace();
+ throw e;
+ }
+ }
+
+ Framework createFramework(Map<String, String> config) {
+ try {
+ URL bundleConfigUrl = foreignBundleContext.getBundle().getEntry("config.ini");
+ try (InputStream in = bundleConfigUrl.openStream()) {
+ RuntimeManager.loadConfig(in, config);
+ }
+
+ // Equinox
+// config.put("osgi.frameworkParentClassloader", "current");
+// config.put("osgi.parentClassLoader", "app");
+// config.put("osgi.contextClassLoaderParent", "app");
+
+ ModuleConnector moduleConnector = new ParentBundleModuleConnector(foreignBundleContext);
+
+// URL frameworkUrl = URI.create(bundleContext.getProperty("osgi.framework")).toURL();
+// URLClassLoader frameworkClassLoader = new URLClassLoader(new URL[] { frameworkUrl, });
+// Class<? extends Framework> frameworkClass = (Class<? extends Framework>) frameworkClassLoader
+// .loadClass(EQUINOX_FRAMEWORK_CLASS);
+// Framework framework = frameworkClass.getConstructor(Map.class, ModuleConnector.class).newInstance(config,
+// moduleConnector);
+
+ config.put(InitConstants.PROP_ARGEO_OSGI_PARENT_UUID, foreignFrameworkUuid.toString());
+ Framework framework = frameworkFactory.newFramework(config, moduleConnector);
+
+ framework.init((e) -> {
+ UUID frameworkUuid = UUID
+ .fromString(framework.getBundleContext().getProperty(Constants.FRAMEWORK_UUID));
+ if (e.getType() == FrameworkEvent.STOPPED) {
+ subFrameworks.remove(frameworkUuid);
+ logger.log(INFO, "Removed subframework " + frameworkUuid + " in parent " + foreignFrameworkUuid);
+ }
+ });
+
+ for (Bundle b : foreignBundleContext.getBundles()) {
+ if (b.getBundleId() == 0)
+ continue;
+ String location = b.getLocation();
+ if (location.contains("/org.argeo.tp/") //
+ || location.contains("/org.argeo.tp.sys/") //
+ || location.contains("/org.argeo.tp.httpd/") //
+ || location.contains("/org.argeo.tp.sshd/") //
+ ) {
+ framework.getBundleContext().installBundle(b.getLocation());
+ }
+ }
+
+ OsgiBoot osgiBoot = new OsgiBoot(framework.getBundleContext());
+ osgiBoot.install();
+// OsgiBoot.uninstallBundles(osgiBoot.getBundleContext(), "org.argeo.api.cms");
+// OsgiBoot.uninstallBundles(osgiBoot.getBundleContext(), "org.osgi.service.useradmin");
+// osgiBoot.getBundleContext()
+// .installBundle("initial@reference:file:../../../../../argeo-commons/org.argeo.api.cms/");
+// osgiBoot.getBundleContext().installBundle(
+// "reference:file:/usr/local/share/a2/osgi/equinox/org.argeo.tp.osgi/org.osgi.service.useradmin.1.1.jar");
+ osgiBoot.refresh();
+ framework.start();
+ osgiBoot.startBundles();
+
+// for (Bundle b : framework.getBundleContext().getBundles()) {
+// BundleContext bc = b.getBundleContext();
+// if (bc == null)
+// System.err.println(b.getSymbolicName() + " BC null");
+// }
+
+ UUID frameworkUuid = UUID.fromString(framework.getBundleContext().getProperty(Constants.FRAMEWORK_UUID));
+ subFrameworks.put(frameworkUuid, framework);
+ logger.log(INFO, "Created subframework " + frameworkUuid + " in parent " + foreignFrameworkUuid);
+ return framework;
+ } catch (Exception e) {
+ throw new IllegalStateException("Cannot start framework", e);
+ }
+ }
+
+ @Override
+ public void stop(BundleContext context) throws Exception {
+ for (Iterator<Framework> it = subFrameworks.values().iterator(); it.hasNext();) {
+ Framework framework = it.next();
+ framework.stop();
+ it.remove();
+
+ }
+// for (Framework framework : subFrameworks.values()) {
+// framework.stop();
+// }
+ subFrameworks.clear();
+ foreignBundleContext = null;
+ frameworkFactory = null;
+ }
+
+ static class ParentBundleModuleConnector implements ModuleConnector {
+ private final BundleContext foreignBundleContext;
+ private BundleContext localBundleContext;
+
+ public ParentBundleModuleConnector(BundleContext foreignBundleContext) {
+ this.foreignBundleContext = foreignBundleContext;
+ }
+
+ @Override
+ public Optional<BundleActivator> newBundleActivator() {
+ return Optional.of(new BundleActivator() {
+ @Override
+ public void start(BundleContext context) throws Exception {
+ ParentBundleModuleConnector.this.localBundleContext = context;
+ }
+
+ @Override
+ public void stop(BundleContext context) throws Exception {
+ ParentBundleModuleConnector.this.localBundleContext = null;
+ }
+
+ });
+ }
+
+ @Override
+ public void initialize(File storage, Map<String, String> configuration) {
+ }
+
+ @Override
+ public Optional<ConnectModule> connect(String location) throws BundleException {
+ Bundle bundle = foreignBundleContext.getBundle(location);
+ if (bundle != null && bundle.getBundleId() != 0) {
+ // System.out.println("Foreign Bundle: " + bundle.getSymbolicName() + " " +
+ // location);
+ ConnectModule module = new ConnectModule() {
+
+ @Override
+ public ConnectContent getContent() throws IOException {
+ return new ForeignBundleConnectContent(localBundleContext, bundle);
+ }
+ };
+ return Optional.of(module);
+ }
+ return Optional.empty();
+ }
+ }
+
+ static class ForeignBundleClassLoader extends ClassLoader {// implements BundleReference {
+ private BundleContext localBundleContext;
+ private Bundle foreignBundle;
+
+ public ForeignBundleClassLoader(BundleContext localBundleContext, Bundle foreignBundle) {
+ super("Foreign bundle " + foreignBundle.toString(), Optional
+ .ofNullable(foreignBundle.adapt(BundleWiring.class)).map((bw) -> bw.getClassLoader()).orElse(null));
+ this.localBundleContext = localBundleContext;
+ this.foreignBundle = foreignBundle;
+ }
+
+// @Override
+ protected Bundle getBundle() {
+ return localBundleContext.getBundle(foreignBundle.getLocation());
+ }
+
+// @Override
+// public URL getResource(String resName) {
+// URL res = super.getResource(resName);
+// return res;
+// }
+//
+// @Override
+// protected URL findResource(String resName) {
+// Bundle localBundle = getBundle();
+// if (localBundle != null) {
+// URL res = localBundle.getEntry(resName);
+// if (res != null)
+// return res;
+// }
+// return null;
+// }
+
+ }
+
+ static class ForeignBundleConnectContent implements ConnectContent {
+ private final Bundle foreignBundle;
+ private final ClassLoader classLoader;
+
+ public ForeignBundleConnectContent(BundleContext localBundleContext, Bundle foreignBundle) {
+ this.foreignBundle = foreignBundle;
+ this.classLoader = new ForeignBundleClassLoader(localBundleContext, foreignBundle);
+ }
+
+ @Override
+ public Optional<Map<String, String>> getHeaders() {
+ Dictionary<String, String> dict = foreignBundle.getHeaders();
+ List<String> keys = Collections.list(dict.keys());
+ Map<String, String> dictCopy = keys.stream().collect(Collectors.toMap(Function.identity(), dict::get));
+ return Optional.of(dictCopy);
+ }
+
+ @Override
+ public Iterable<String> getEntries() throws IOException {
+ List<String> lst = Collections.list(foreignBundle.findEntries("", "*", true)).stream()
+ .map((u) -> u.getPath()).toList();
+ return lst;
+ }
+
+ @Override
+ public Optional<ConnectEntry> getEntry(String path) {
+ URL u = foreignBundle.getEntry(path);
+ if (u == null) {
+ u = foreignBundle.getEntry("bin/" + path);
+ // System.err.println(u2);
+ }
+ if (u == null) {
+ if ("plugin.xml".equals(path))
+ return Optional.empty();
+ if (path.startsWith("META-INF/versions/"))
+ return Optional.empty();
+ System.err.println(foreignBundle.getSymbolicName() + " " + path + " not found");
+ return Optional.empty();
+ }
+ URL url = u;
+ ConnectEntry urlConnectEntry = new ConnectEntry() {
+
+ @Override
+ public String getName() {
+ return path;
+ }
+
+ @Override
+ public long getLastModified() {
+ return foreignBundle.getLastModified();
+ }
+
+ @Override
+ public InputStream getInputStream() throws IOException {
+ return url.openStream();
+ }
+
+ @Override
+ public long getContentLength() {
+ return -1;
+ }
+ };
+ return Optional.of(urlConnectEntry);
+ }
+
+ @Override
+ public Optional<ClassLoader> getClassLoader() {
+ ClassLoader cl;
+ // cl = bundle.adapt(BundleWiring.class).getClassLoader();
+
+ // cl = subFrameworkClassLoader;
+ cl = classLoader;
+ return Optional.of(cl);
+// return Optional.empty();
+ }
+
+ @Override
+ public void open() throws IOException {
+ }
+
+ @Override
+ public void close() throws IOException {
+
+ }
+
+ }
+}
--- /dev/null
+package org.argeo.internal.init;
+
+import org.argeo.api.init.RuntimeContext;
+
+/**
+ * Keep track of the internal state mostly with static variables, typically in
+ * order to synchronise shutdown.
+ */
+public class InternalState {
+ private static RuntimeContext mainRuntimeContext;
+
+ /** The root runtime context in this JVM. */
+ public static RuntimeContext getMainRuntimeContext() {
+ return mainRuntimeContext;
+ }
+
+ public static void setMainRuntimeContext(RuntimeContext mainRuntimeContext) {
+ InternalState.mainRuntimeContext = mainRuntimeContext;
+ }
+
+}
-Subproject commit cabcc3462226b71849ca42301c21e05b63f150c2
+Subproject commit 979b11e352bda7d783c921a62e8cb5ed950a7564
major=2
minor=3
-micro=27
-qualifier=.next
+micro=28
+qualifier=
Bundle-Copyright= \
Copyright 2007-2023 Mathieu Baudier, \
<arguments>
</arguments>
</buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.ds.core.builder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.pde.PluginNature</nature>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" activate="start" deactivate="stop" name="org.argeo.swt.minidesktop">
+ <implementation class="org.argeo.minidesktop.MiniDesktopManager"/>
+</scr:component>
Import-Package: org.eclipse.swt,\
*
+
+Service-Component:\
+OSGI-INF/miniDesktop.xml,\
+bin.includes = META-INF/,\
+ .,\
+ OSGI-INF/miniDesktop.xml
source.. = src/
output.. = bin/
-bin.includes = META-INF/,\
- .
browser.addLocationListener(new LocationAdapter() {
@Override
public void changed(LocationEvent event) {
- System.out.println(event);
if (addressT != null)
addressT.setText(event.location);
}
-
});
MiniDesktopSpecific.getMiniDesktopSpecific().addBrowserTitleListener(this, browser);
}
public Browser openNewBrowserWindow() {
-
if (isFullScreen()) {
// TODO manage multiple tabs?
return browser;
this.stacking = stacking;
}
+ public MiniDesktopManager() {
+ this(false, false);
+ }
+
+ public void start() {
+ init();
+ }
+
+ public void stop() {
+ dispose();
+ if (display != null) {
+ display.dispose();
+ display = null;
+ }
+ }
+
public void init() {
Display.setAppName("Mini SWT Desktop");
display = Display.getCurrent();
if (display != null)
throw new IllegalStateException("Already a display " + display);
- display = new Display();
+ display = Display.getDefault();
- if (display.getTouchEnabled()) {
- System.out.println("Touch enabled.");
- }
-
- images = new MiniDesktopImages(display);
+ display.syncExec(() -> {
+ if (display.getTouchEnabled()) {
+ System.out.println("Touch enabled.");
+ }
- int toolBarSize = 48;
+ images = new MiniDesktopImages(display);
+
+ int toolBarSize = 48;
+
+ if (isFullscreen()) {
+ rootShell = new Shell(display, SWT.NO_TRIM);
+ rootShell.setFullScreen(true);
+ Rectangle bounds = display.getBounds();
+ rootShell.setLocation(0, 0);
+ rootShell.setSize(bounds.width, bounds.height);
+ } else {
+ rootShell = new Shell(display, SWT.CLOSE | SWT.RESIZE);
+ Rectangle shellArea = rootShell.computeTrim(200, 200, 800, 480);
+ rootShell.setSize(shellArea.width, shellArea.height);
+ rootShell.setText(Display.getAppName());
+ rootShell.setImage(images.terminalIcon);
+ }
- if (isFullscreen()) {
- rootShell = new Shell(display, SWT.NO_TRIM);
- rootShell.setFullScreen(true);
- Rectangle bounds = display.getBounds();
- rootShell.setLocation(0, 0);
- rootShell.setSize(bounds.width, bounds.height);
- } else {
- rootShell = new Shell(display, SWT.CLOSE | SWT.RESIZE);
- Rectangle shellArea = rootShell.computeTrim(200, 200, 800, 480);
- rootShell.setSize(shellArea.width, shellArea.height);
- rootShell.setText(Display.getAppName());
- rootShell.setImage(images.terminalIcon);
- }
+ rootShell.setLayout(noSpaceGridLayout(new GridLayout(2, false)));
+ Composite toolBarArea = new Composite(rootShell, SWT.NONE);
+ toolBarArea.setLayoutData(new GridData(toolBarSize, rootShell.getSize().y));
- rootShell.setLayout(noSpaceGridLayout(new GridLayout(2, false)));
- Composite toolBarArea = new Composite(rootShell, SWT.NONE);
- toolBarArea.setLayoutData(new GridData(toolBarSize, rootShell.getSize().y));
-
- ToolBar toolBar;
- if (isFullscreen()) {
- toolBarShell = new Shell(rootShell, SWT.NO_TRIM | SWT.ON_TOP);
- toolBar = new ToolBar(toolBarShell, SWT.VERTICAL | SWT.FLAT | SWT.BORDER);
- createDock(toolBar);
- toolBarShell.pack();
- toolBarArea.setLayoutData(new GridData(toolBar.getSize().x, toolBar.getSize().y));
- } else {
- toolBar = new ToolBar(toolBarArea, SWT.VERTICAL | SWT.FLAT | SWT.BORDER);
- createDock(toolBar);
- toolBarArea.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false));
- }
+ ToolBar toolBar;
+ if (isFullscreen()) {
+ toolBarShell = new Shell(rootShell, SWT.NO_TRIM | SWT.ON_TOP);
+ toolBar = new ToolBar(toolBarShell, SWT.VERTICAL | SWT.FLAT | SWT.BORDER);
+ createDock(toolBar);
+ toolBarShell.pack();
+ toolBarArea.setLayoutData(new GridData(toolBar.getSize().x, toolBar.getSize().y));
+ } else {
+ toolBar = new ToolBar(toolBarArea, SWT.VERTICAL | SWT.FLAT | SWT.BORDER);
+ createDock(toolBar);
+ toolBarArea.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false));
+ }
- if (isStacking()) {
- tabFolder = new CTabFolder(rootShell, SWT.MULTI | SWT.BORDER | SWT.BOTTOM);
- tabFolder.setLayout(noSpaceGridLayout(new GridLayout()));
- tabFolder.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
-
- Color selectionBackground = display.getSystemColor(SWT.COLOR_LIST_SELECTION);
- tabFolder.setSelectionBackground(selectionBackground);
-
- // background
- Control background = createBackground(tabFolder);
- CTabItem homeTabItem = new CTabItem(tabFolder, SWT.NONE);
- homeTabItem.setText("Home");
- homeTabItem.setImage(images.homeIcon);
- homeTabItem.setControl(background);
- tabFolder.setFocus();
- } else {
- createBackground(rootShell);
- }
+ if (isStacking()) {
+ tabFolder = new CTabFolder(rootShell, SWT.MULTI | SWT.BORDER | SWT.BOTTOM);
+ tabFolder.setLayout(noSpaceGridLayout(new GridLayout()));
+ tabFolder.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+
+ Color selectionBackground = display.getSystemColor(SWT.COLOR_LIST_SELECTION);
+ tabFolder.setSelectionBackground(selectionBackground);
+
+ // background
+ Control background = createBackground(tabFolder);
+ CTabItem homeTabItem = new CTabItem(tabFolder, SWT.NONE);
+ homeTabItem.setText("Home");
+ homeTabItem.setImage(images.homeIcon);
+ homeTabItem.setControl(background);
+ tabFolder.setFocus();
+ } else {
+ createBackground(rootShell);
+ }
- rootShell.open();
- // rootShell.layout(true, true);
+ rootShell.open();
+ // rootShell.layout(true, true);
- if (toolBarShell != null) {
- int toolBarShellY = (display.getBounds().height - toolBar.getSize().y) / 2;
- toolBarShell.setLocation(0, toolBarShellY);
- toolBarShell.open();
- }
+ if (toolBarShell != null) {
+ int toolBarShellY = (display.getBounds().height - toolBar.getSize().y) / 2;
+ toolBarShell.setLocation(0, toolBarShellY);
+ toolBarShell.open();
+ }
- long jvmUptime = ManagementFactory.getRuntimeMXBean().getUptime();
- System.out.println("SWT Mini Desktop Manager available in " + jvmUptime + " ms.");
+ long jvmUptime = ManagementFactory.getRuntimeMXBean().getUptime();
+ System.out.println("SWT Mini Desktop Manager available in " + jvmUptime + " ms.");
+ });
}
protected void createDock(ToolBar toolBar) {
return entryPoint;
}, properties);
if (log.isDebugEnabled())
- log.info("Added web entry point " + (contextName != null ? "/" + contextName : "") + entryPointName);
+ log.debug("Added web entry point " + (contextName != null ? "/" + contextName : "") + entryPointName);
}
// if (log.isDebugEnabled())
// log.debug("Published CMS web app /" + (contextName != null ? contextName : ""));
rwtAppReg.unregister();
if (bundleContext != null) {
rwtAppReg = bundleContext.registerService(ApplicationConfiguration.class, this, regProps);
- if (log.isDebugEnabled())
- log.debug("Publishing CMS web app /" + (contextName != null ? contextName : "") + " ...");
+ log.info("Publishing CMS web app /" + (contextName != null ? contextName : "") + " ...");
}
}
package org.argeo.eclipse.ui.specific;
-import org.eclipse.jface.viewers.AbstractTableViewer;
-import org.eclipse.jface.viewers.ColumnViewer;
-import org.eclipse.jface.viewers.ColumnViewerToolTipSupport;
-import org.eclipse.jface.viewers.Viewer;
import org.eclipse.rap.rwt.RWT;
import org.eclipse.swt.widgets.Widget;
widget.setData("org.eclipse.rap.rwt.markupValidationDisabled", Boolean.TRUE);
}
- /**
- * TootlTip support is supported only for {@link AbstractTableViewer} in RAP
- */
- public static void enableToolTipSupport(Viewer viewer) {
- if (viewer instanceof ColumnViewer)
- ColumnViewerToolTipSupport.enableFor((ColumnViewer) viewer);
- }
-
private EclipseUiSpecificUtils() {
}
}
// Styling
CmsTheme theme = CmsSwtUtils.getCmsTheme(parent);
if (theme != null) {
- cssEngine = new CSSSWTEngineImpl(display);
- for (String path : theme.getSwtCssPaths()) {
- try (InputStream in = theme.loadPath(path)) {
- cssEngine.parseStyleSheet(in);
- } catch (IOException e) {
- throw new IllegalStateException("Cannot load stylesheet " + path, e);
+ try {
+ cssEngine = new CSSSWTEngineImpl(display);
+ for (String path : theme.getSwtCssPaths()) {
+ try (InputStream in = theme.loadPath(path)) {
+ cssEngine.parseStyleSheet(in);
+ } catch (IOException e) {
+ throw new IllegalStateException("Cannot load stylesheet " + path, e);
+ }
}
+ cssEngine.setErrorHandler(new CSSErrorHandler() {
+ public void error(Exception e) {
+ log.error("SWT styling error: ", e);
+ }
+ });
+ applyStyles(shell);
+ } catch (Throwable e) {// could be a class not found error
+ log.error("Cannot initialise RCP theming", e);
}
- cssEngine.setErrorHandler(new CSSErrorHandler() {
- public void error(Exception e) {
- log.error("SWT styling error: ", e);
- }
- });
- applyStyles(shell);
}
shell.layout(true, true);
public void run() {
try {
display = Display.getDefault();
- display.setRuntimeExceptionHandler((e) -> e.printStackTrace());
- display.setErrorHandler((e) -> e.printStackTrace());
-
- while (!shutdown) {
- if (!display.readAndDispatch())
- display.sleep();
+ boolean displayOwner = display.getThread() == this;
+ if (displayOwner) {
+ display.setRuntimeExceptionHandler((e) -> e.printStackTrace());
+ display.setErrorHandler((e) -> e.printStackTrace());
+
+ while (!shutdown) {
+ if (!display.readAndDispatch())
+ display.sleep();
+ }
+ display.dispose();
+ display = null;
}
- display.dispose();
- display = null;
} catch (UnsatisfiedLinkError e) {
logger.log(Level.ERROR,
"Cannot load SWT, either because the SWT DLLs are no in the java.library.path,"
}
}
+ @Deprecated
public Display getDisplay() {
return display;
}
Import-Package: \
-!java.*,\
-org.apache.commons.io,\
-org.eclipse.core.commands,\
+org.eclipse.core.commands;resolution:=optional,\
!org.eclipse.core.runtime,\
!org.eclipse.ui.plugin,\
org.eclipse.swt,\
-javax.servlet.http;version="[3,5)",\
-javax.servlet;version="[3,5)",\
+javax.servlet.http;resolution:=optional,\
+javax.servlet;resolution:=optional,\
*
Export-Package: org.argeo.*,\
org.eclipse.rap.fileupload.*;version="3.10",\
org.eclipse.rap.rwt.*;version="3.10"
-# Was !org.eclipse.core.commands,\ why ?
+# Was !org.eclipse.core.commands, why ?
-#Bundle-Activator: org.argeo.eclipse.ui.ArgeoUiPlugin
+Bundle-Activator: org.argeo.internal.swt.specific.osgi.SwtSpecificRcpActivator
#Bundle-ActivationPolicy: lazy
#Ignore-Package: org.eclipse.core.commands
\ No newline at end of file
package org.argeo.eclipse.ui.rcp.internal.rwt;
import java.io.ByteArrayInputStream;
-import java.io.IOException;
import java.io.InputStream;
import java.util.Collections;
import java.util.Map;
import java.util.TreeMap;
-import org.argeo.cms.util.StreamUtils;
import org.eclipse.rap.rwt.service.ResourceManager;
public class RcpResourceManager implements ResourceManager {
@Override
public void register(String name, InputStream in) {
- try {
- register.put(name, StreamUtils.toByteArray(in));
- } catch (IOException e) {
- throw new RuntimeException("Cannot register " + name, e);
- }
+ // FIXME implement it
+ throw new UnsupportedOperationException();
+// try {
+// register.put(name, StreamUtils.toByteArray(in));
+// } catch (IOException e) {
+// throw new RuntimeException("Cannot register " + name, e);
+// }
}
@Override
package org.argeo.eclipse.ui.specific;
-import org.eclipse.jface.viewers.ColumnViewer;
-import org.eclipse.jface.viewers.ColumnViewerToolTipSupport;
-import org.eclipse.jface.viewers.Viewer;
import org.eclipse.swt.widgets.Widget;
/** Static utilities to bridge differences between RCP and RAP */
// does nothing
}
- /**
- * TootlTip support is supported for {@link ColumnViewer} in RCP
- *
- * @see ColumnViewerToolTipSupport#enableFor(Viewer)
- */
- public static void enableToolTipSupport(Viewer viewer) {
- if (viewer instanceof ColumnViewer)
- ColumnViewerToolTipSupport.enableFor((ColumnViewer) viewer);
- }
-
private EclipseUiSpecificUtils() {
}
}
--- /dev/null
+package org.argeo.internal.swt.specific.osgi;
+
+import org.eclipse.swt.SWTError;
+import org.eclipse.swt.layout.FillLayout;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+
+public class SwtSpecificRcpActivator implements BundleActivator {
+ private static Display display;
+ private static Shell rootShell;
+ private static UiThread uiThread;
+
+ private static boolean debug = true;
+
+ @Override
+ public void start(BundleContext context) throws Exception {
+ if (display != null)
+ throw new IllegalStateException("SWT display already exists");
+ uiThread = new UiThread();
+ uiThread.start();
+ }
+
+ @Override
+ public void stop(BundleContext context) throws Exception {
+ if (display == null)
+ return; // TODO log issue
+ display.asyncExec(() -> rootShell.dispose());
+ display.wake();
+ uiThread.join(60 * 1000);
+ uiThread = null;
+ display = null;
+ }
+
+ static class UiThread extends Thread {
+
+ @Override
+ public void run() {
+ boolean displayOwner = true;
+ Display d = Display.getDefault();
+ if (d.getThread() != UiThread.this) {
+ displayOwner = false;
+ // throw new IllegalStateException("There was already a default SWT display");
+ }
+
+ d.syncExec(() -> {
+ try {
+ rootShell = new Shell(d);
+ } catch (SWTError e) {
+ e.printStackTrace();
+ display.dispose();
+ return;
+ }
+
+ if (debug) {
+ rootShell.setLayout(new FillLayout());
+ new Label(rootShell, 0).setText("ROOT SHELL");
+ rootShell.pack();
+ rootShell.open();
+ }
+ });
+
+ display = d;
+
+ if (displayOwner) {
+ while (!rootShell.isDisposed())
+ if (!display.readAndDispatch())
+ display.sleep();
+ display.dispose();
+ }
+ }
+ }
+}