Refactor CMS UUID factory
authorMathieu Baudier <mbaudier@argeo.org>
Tue, 5 Mar 2024 17:13:36 +0000 (18:13 +0100)
committerMathieu Baudier <mbaudier@argeo.org>
Tue, 5 Mar 2024 17:13:36 +0000 (18:13 +0100)
18 files changed:
org.argeo.api.uuid/src/org/argeo/api/uuid/AbstractUuidFactory.java
org.argeo.api.uuid/src/org/argeo/api/uuid/ConcurrentUuidFactory.java
org.argeo.api.uuid/src/org/argeo/api/uuid/MacAddressUuidFactory.java
org.argeo.api.uuid/src/org/argeo/api/uuid/NodeIdSupplier.java
org.argeo.cms.lib.jetty/src/org/argeo/cms/jetty/JettyHttpServer.java
org.argeo.cms/OSGI-INF/cmsState.xml [deleted file]
org.argeo.cms/OSGI-INF/cmsUuidFactory.xml [new file with mode: 0644]
org.argeo.cms/OSGI-INF/uuidFactory.xml [deleted file]
org.argeo.cms/bnd.bnd
org.argeo.cms/src/org/argeo/cms/acr/CmsUuidFactory.java [deleted file]
org.argeo.cms/src/org/argeo/cms/internal/osgi/CmsActivator.java
org.argeo.cms/src/org/argeo/cms/internal/osgi/CmsOsgiLogger.java
org.argeo.cms/src/org/argeo/cms/internal/runtime/CmsStateImpl.java
org.argeo.cms/src/org/argeo/cms/internal/runtime/CmsUuidFactory.java [new file with mode: 0644]
org.argeo.cms/src/org/argeo/cms/internal/runtime/KernelUtils.java
org.argeo.cms/src/org/argeo/cms/runtime/StaticCms.java
org.argeo.init/src/org/argeo/api/a2/ProvisioningManager.java
org.argeo.init/src/org/argeo/init/osgi/OsgiRuntimeContext.java

index 4f2cf3765dcb69d6d912a7491511bee1350742f5..4486a9f8b4b907ccb347fa0f78c71b7e3ff30f7a 100644 (file)
@@ -110,30 +110,6 @@ public abstract class AbstractUuidFactory implements UuidFactory {
                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
         */
index 130a90a84bfb49be449539bd939fc2cc696bd742..d78be0c5d548a470c0d16b06e67095fafaf194cc 100644 (file)
@@ -28,7 +28,7 @@ public class ConcurrentUuidFactory extends AbstractAsyncUuidFactory implements T
                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);
        }
index 31fe3783112661e23f28074d31669870c0db0d42..51d68e3ef2aefb0420366ae212a17c108e229ceb 100644 (file)
@@ -52,7 +52,7 @@ public class MacAddressUuidFactory extends ConcurrentUuidFactory {
 
        }
 
-       public static byte[] hardwareAddressToNodeId(NetworkInterface nic) {
+       public static byte[] hardwareAddressToNodeId(NetworkInterface nic) throws IllegalStateException {
                try {
                        byte[] hardwareAddress = nic.getHardwareAddress();
                        final int length = 6;
index 81d368d2c8c651c37ea9ab48f7f048db98351f16..1a3cd5673e17d1504277c7c1428de4795ed30490 100644 (file)
@@ -25,4 +25,28 @@ public interface NodeIdSupplier extends Supplier<Long> {
                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;
+       }
 }
index 9d35dadb5d49eabe9464c433f88a8111387eff3e..4e91ea41c734a51f739a1d82d2d030000788fb38 100644 (file)
@@ -230,6 +230,7 @@ public class JettyHttpServer extends HttpsServer {
                        server.stop();
                        // TODO delete temp dir
                        started = false;
+                       log.debug(() -> "Stopped Jetty server");
                } catch (Exception e) {
                        log.error("Cannot stop Jetty HTTP server", e);
                }
diff --git a/org.argeo.cms/OSGI-INF/cmsState.xml b/org.argeo.cms/OSGI-INF/cmsState.xml
deleted file mode 100644 (file)
index 71dc6d4..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-<?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>
diff --git a/org.argeo.cms/OSGI-INF/cmsUuidFactory.xml b/org.argeo.cms/OSGI-INF/cmsUuidFactory.xml
new file mode 100644 (file)
index 0000000..d55970a
--- /dev/null
@@ -0,0 +1,8 @@
+<?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>
diff --git a/org.argeo.cms/OSGI-INF/uuidFactory.xml b/org.argeo.cms/OSGI-INF/uuidFactory.xml
deleted file mode 100644 (file)
index c1ad6f8..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-<?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>
index 01443b5e8150215f77435c4f4a8ce56850f3895e..3807af9d17226c0cf98a0a27d0075a246957507d 100644 (file)
@@ -6,9 +6,8 @@ org.osgi.*;version=0.0.0,\
 
 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,\
diff --git a/org.argeo.cms/src/org/argeo/cms/acr/CmsUuidFactory.java b/org.argeo.cms/src/org/argeo/cms/acr/CmsUuidFactory.java
deleted file mode 100644 (file)
index b61e35d..0000000
+++ /dev/null
@@ -1,89 +0,0 @@
-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.debug("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;
-       }
-
-}
index b09956203cc2eb4afa0bfa60b2a1c22cf1fe9f5c..fe7c80d3b0b5c0a91fc2686cf48eebe4a52707d5 100644 (file)
@@ -1,9 +1,10 @@
 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;
@@ -19,7 +20,7 @@ import org.osgi.service.permissionadmin.PermissionInfo;
  * 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);
 
        // TODO make it configurable
        private boolean hardened = false;
@@ -30,13 +31,6 @@ public class CmsActivator implements BundleActivator {
        }
 
        void destroy() {
-               try {
-                       bundleContext = null;
-//                     this.logReaderService = null;
-               } catch (Exception e) {
-                       log.error("CMS activator shutdown failed", e);
-               }
-
                new GogoShellKiller().start();
        }
 
@@ -76,14 +70,14 @@ public class CmsActivator implements BundleActivator {
 
        }
 
-       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 {
@@ -98,20 +92,32 @@ public class CmsActivator implements BundleActivator {
        @Override
        public void start(BundleContext bc) throws Exception {
                bundleContext = bc;
-
+               CmsStateImpl cmsState = new CmsStateImpl();
+               cmsState.start();
+               bundleContext.registerService(new String[] { CmsState.class.getName(), NodeIdSupplier.class.getName() },
+                               cmsState, null);
                init();
 
        }
 
        @Override
        public void stop(BundleContext bc) throws Exception {
-
-               destroy();
-               bundleContext = null;
+               try {
+                       destroy();
+                       CmsStateImpl cmsState = (CmsStateImpl) getService(CmsState.class);
+                       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);
+       }
 }
index 85f045bae660b6783d2aa4448e7fdce59af327e1..405fa46f4a92e27ae93d2422b3f0581ef87c6fb0 100644 (file)
@@ -19,38 +19,8 @@ import org.osgi.service.log.LogReaderService;
 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) {
@@ -58,51 +28,10 @@ public class CmsOsgiLogger implements LogListener {
                        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);
        }
 
@@ -216,296 +145,4 @@ public class CmsOsgiLogger implements LogListener {
                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());
-//                     }
-//             }
-//     }
 }
index ee7f06340d521a76642c0bd9424436c4244e3629..2758ba9b191bf08693c37d44ff457f0240622e84 100644 (file)
@@ -3,6 +3,7 @@ package org.argeo.cms.internal.runtime;
 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;
@@ -37,16 +38,18 @@ import javax.security.auth.login.Configuration;
 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
@@ -55,8 +58,9 @@ public class CmsStateImpl implements CmsState {
        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;
 
@@ -112,7 +116,7 @@ public class CmsStateImpl implements CmsState {
                                log.trace("CMS State started");
 
                        String frameworkUuid = KernelUtils.getFrameworkProp(KernelUtils.OSGI_FRAMEWORK_UUID);
-                       this.uuid = frameworkUuid != null ? UUID.fromString(frameworkUuid) : uuidFactory.timeUUID();
+                       this.uuid = frameworkUuid != null ? UUID.fromString(frameworkUuid) : UUID.randomUUID();
 
                        // hostname
                        this.hostname = getDeployProperty(CmsDeployProperty.HOST);
@@ -121,7 +125,8 @@ public class CmsStateImpl implements CmsState {
                                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);
@@ -133,6 +138,16 @@ public class CmsStateImpl implements CmsState {
                                        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();
@@ -155,7 +170,7 @@ public class CmsStateImpl implements CmsState {
                                                }
                                        }
                                }
-                               log.debug("## CMS starting... (" + uuid + ")\n" + sb + "\n");
+                               log.debug("## CMS starting on " + hostname + " ... (" + uuid + ")\n" + sb + "\n");
                        }
 
                        if (log.isTraceEnabled()) {
@@ -170,6 +185,7 @@ public class CmsStateImpl implements CmsState {
 
                } catch (RuntimeException | IOException e) {
                        log.error("## FATAL: CMS state failed", e);
+                       throw new IllegalStateException(e);
                }
        }
 
@@ -398,6 +414,77 @@ public class CmsStateImpl implements CmsState {
                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
         */
@@ -406,9 +493,9 @@ public class CmsStateImpl implements CmsState {
                return uuid;
        }
 
-       public void setUuidFactory(UuidFactory uuidFactory) {
-               this.uuidFactory = uuidFactory;
-       }
+//     public void setUuidFactory(UuidFactory uuidFactory) {
+//             this.uuidFactory = uuidFactory;
+//     }
 
        public String getHostname() {
                return hostname;
@@ -454,4 +541,5 @@ public class CmsStateImpl implements CmsState {
                // TODO make passphrase more configurable
                return new IdentClient(remoteAddr);
        }
+
 }
diff --git a/org.argeo.cms/src/org/argeo/cms/internal/runtime/CmsUuidFactory.java b/org.argeo.cms/src/org/argeo/cms/internal/runtime/CmsUuidFactory.java
new file mode 100644 (file)
index 0000000..4077838
--- /dev/null
@@ -0,0 +1,90 @@
+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;
+       }
+}
index 3797eb913ca05544d5f7c6042d89f2c7227c35a1..db33ff9d407bb2fcdbb0ae3cd470954ba6732753 100644 (file)
@@ -96,9 +96,8 @@ class KernelUtils implements KernelConstants {
 
        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;
index 76775fed8060f912bed6a725df629113fc8f211e..1b127c89dc210f53a373b200806c9438f735e905 100644 (file)
@@ -16,8 +16,9 @@ import org.argeo.api.cms.transaction.WorkTransaction;
 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;
@@ -36,19 +37,20 @@ public class StaticCms {
        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
index 7a17932df2a9ac8ddb0d10aaa16826d25d639578..af22787b177a6d0aac744452ecbaa9753083a9e8 100644 (file)
@@ -99,7 +99,7 @@ public class ProvisioningManager {
                                                                includes, excludes);
                                                source.load();
                                                addSource(source);
-                                               logger.log(INFO, () -> "Registered " + uri + " as source");
+                                               logger.log(DEBUG, () -> "Registered " + uri + " as source");
 
                                                // OS specific / native
                                                String localRelPath = A2Contribution.localOsArchRelativePath();
index b7079f60e87fa7611bbf936bd3603fd78a52e3d7..ecfeca74ee839a66b8b1df9dd9f9539752f79d9a 100644 (file)
@@ -95,7 +95,13 @@ public class OsgiRuntimeContext implements RuntimeContext, AutoCloseable {
                                        new Hashtable<>(Collections.singletonMap(Constants.SERVICE_PID, "argeo.logging.publisher")));
                }
                OsgiBoot osgiBoot = new OsgiBoot(bundleContext);
-               osgiBoot.bootstrap(config);
+               String frameworkUuuid = bundleContext.getProperty(Constants.FRAMEWORK_UUID);
+               new Thread("OSGi boot framework " + frameworkUuuid) {
+                       @Override
+                       public void run() {
+                               osgiBoot.bootstrap(config);
+                       }
+               }.start();
        }
 
        public void update() {
@@ -151,28 +157,19 @@ public class OsgiRuntimeContext implements RuntimeContext, AutoCloseable {
        public void close() throws Exception {
                if (framework == null)
                        return;
-//             Bundle scrBundle = osgiBoot.getBundlesBySymbolicName().get();
-//             if (scrBundle != null && scrBundle.getState() > Bundle.RESOLVED) {
-//                     scrBundle.stop();
-//                     while (!(scrBundle.getState() <= Bundle.RESOLVED)) {
-//                             Thread.sleep(500);
+               // TODO make shutdown of dynamic service more robust
+//             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);
+//                             }
 //                     }
-//                     Thread.sleep(1000);
 //             }
 
-               // TODO make shutdown of dynamic service more robust
-               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(100);
-                               }
-                       }
-               }
-
                stop();
                waitForStop(CLOSE_TIMEOUT);
                framework = null;