This is an automated email from the ASF dual-hosted git repository.
shuber pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/unomi.git
The following commit(s) were added to refs/heads/master by this push:
new d8749be UNOMI-542 : add consistent check to know if bundles are
correctly sta… (#381)
d8749be is described below
commit d8749bea228c2b586c5c28b91bdd25be90905544
Author: jsinovassin <[email protected]>
AuthorDate: Fri Jan 28 09:35:33 2022 +0100
UNOMI-542 : add consistent check to know if bundles are correctly sta…
(#381)
* UNOMI-542 : add consistent check to know if bundles are correctly started
* handle feddback
---
.../org/apache/unomi/lifecycle/BundleWatcher.java | 119 ++++++++++++++++-----
.../resources/OSGI-INF/blueprint/blueprint.xml | 38 ++++++-
.../main/resources/org.apache.unomi.lifecycle.cfg | 19 ++++
3 files changed, 149 insertions(+), 27 deletions(-)
diff --git
a/lifecycle-watcher/src/main/java/org/apache/unomi/lifecycle/BundleWatcher.java
b/lifecycle-watcher/src/main/java/org/apache/unomi/lifecycle/BundleWatcher.java
index 828a288..e72b6d7 100644
---
a/lifecycle-watcher/src/main/java/org/apache/unomi/lifecycle/BundleWatcher.java
+++
b/lifecycle-watcher/src/main/java/org/apache/unomi/lifecycle/BundleWatcher.java
@@ -16,7 +16,14 @@
*/
package org.apache.unomi.lifecycle;
-import org.osgi.framework.*;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleEvent;
+import org.osgi.framework.Filter;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceEvent;
+import org.osgi.framework.ServiceListener;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.SynchronousBundleListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -24,7 +31,17 @@ import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TimerTask;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
/**
* This class listens to the global Apache Unomi bundle lifecycle, to provide
statistics and state of the overall
@@ -35,9 +52,10 @@ public class BundleWatcher implements
SynchronousBundleListener, ServiceListener
private static final Logger logger =
LoggerFactory.getLogger(BundleWatcher.class.getName());
private long startupTime;
- private Map<String,Long> bundleStartupTimes = new LinkedHashMap<>();
- private long unomiStartedBundleCount = 0;
- private long requiredStartedBundleCount;
+ private Map<String, Boolean> requiredBundles;
+
+ private ScheduledExecutorService scheduler;
+ private ScheduledFuture<?> scheduledFuture;
private String requiredServices;
private Set<Filter> requiredServicesFilters = new LinkedHashSet<>();
@@ -48,8 +66,14 @@ public class BundleWatcher implements
SynchronousBundleListener, ServiceListener
private boolean shutdownMessageAlreadyDisplayed = false;
private List<String> logoLines = new ArrayList<>();
- public void setRequiredStartedBundleCount(long requiredStartedBundleCount)
{
- this.requiredStartedBundleCount = requiredStartedBundleCount;
+ private Integer checkStartupStateRefreshInterval = 60;
+
+ public void setRequiredBundles(Map<String, Boolean> requiredBundles) {
+ this.requiredBundles = requiredBundles;
+ }
+
+ public void setCheckStartupStateRefreshInterval(Integer
checkStartupStateRefreshInterval) {
+ this.checkStartupStateRefreshInterval =
checkStartupStateRefreshInterval;
}
public void setRequiredServices(String requiredServices) {
@@ -70,6 +94,7 @@ public class BundleWatcher implements
SynchronousBundleListener, ServiceListener
}
public void init() {
+ scheduler = Executors.newSingleThreadScheduledExecutor();
bundleContext.addBundleListener(this);
bundleContext.addServiceListener(this);
loadLogo();
@@ -78,9 +103,26 @@ public class BundleWatcher implements
SynchronousBundleListener, ServiceListener
logger.info("Bundle watcher initialized.");
}
+ private boolean allBundleStarted() {
+ return getInactiveBundles().isEmpty();
+ }
+
+ private void displayLogsForInactiveBundles() {
+ getInactiveBundles().forEach(inactiveBundle -> logger
+ .warn("The bundle {} is in not active, some errors could
happen when using the application", inactiveBundle));
+ }
+
+ private List<String> getInactiveBundles() {
+ return requiredBundles.entrySet().stream().filter(entry ->
!entry.getValue()).map(Map.Entry::getKey).collect(Collectors.toList());
+
+ }
+
public void destroy() {
bundleContext.removeServiceListener(this);
bundleContext.removeBundleListener(this);
+ if (scheduledFuture != null) {
+ scheduledFuture.cancel(true);
+ }
logger.info("Bundle watcher shutdown.");
}
@@ -91,7 +133,7 @@ public class BundleWatcher implements
SynchronousBundleListener, ServiceListener
break;
case BundleEvent.STARTED:
if
(event.getBundle().getSymbolicName().startsWith("org.apache.unomi")) {
- unomiStartedBundleCount++;
+ requiredBundles.put(event.getBundle().getSymbolicName(),
true);
checkStartupComplete();
}
break;
@@ -99,9 +141,11 @@ public class BundleWatcher implements
SynchronousBundleListener, ServiceListener
break;
case BundleEvent.STOPPED:
if
(event.getBundle().getSymbolicName().startsWith("org.apache.unomi")) {
- unomiStartedBundleCount--;
+ requiredBundles.put(event.getBundle().getSymbolicName(),
false);
}
break;
+ default:
+ break;
}
}
@@ -146,16 +190,45 @@ public class BundleWatcher implements
SynchronousBundleListener, ServiceListener
}
}
+ private void displayLogsForInactiveServices() {
+ requiredServicesFilters.forEach(requiredServicesFilters -> {
+ ServiceReference[] serviceReference = new ServiceReference[0];
+ String filterToString = requiredServicesFilters.toString();
+ try {
+ serviceReference = bundleContext.getServiceReferences((String)
null, filterToString);
+ } catch (InvalidSyntaxException e) {
+ logger.error("Failed to get the service reference for {}",
filterToString, e);
+ }
+ if (serviceReference == null) {
+ logger.warn("No service found for the filter {}, some errors
could happen when using the application", filterToString);
+ }
+ });
+ }
+
private void checkStartupComplete() {
if (!isStartupComplete()) {
+ if (scheduledFuture == null || scheduledFuture.isCancelled()) {
+ TimerTask task = new TimerTask() {
+ @Override
+ public void run() {
+ displayLogsForInactiveBundles();
+ displayLogsForInactiveServices();
+ checkStartupComplete();
+ }
+ };
+ scheduledFuture = scheduler
+ .scheduleWithFixedDelay(task,
checkStartupStateRefreshInterval, checkStartupStateRefreshInterval,
TimeUnit.SECONDS);
+ }
return;
}
+ if (scheduledFuture != null) {
+ scheduledFuture.cancel(true);
+ scheduledFuture = null;
+ }
if (!startupMessageAlreadyDisplayed) {
long totalStartupTime = System.currentTimeMillis() - startupTime;
- if (logoLines.size() > 0) {
- for (String logoLine : logoLines) {
- System.out.println(logoLine);
- }
+ if (!logoLines.isEmpty()) {
+ logoLines.forEach(System.out::println);
}
String buildNumber = "n/a";
if
(bundleContext.getBundle().getHeaders().get("Implementation-Build") != null) {
@@ -165,25 +238,23 @@ public class BundleWatcher implements
SynchronousBundleListener, ServiceListener
if
(bundleContext.getBundle().getHeaders().get("Implementation-TimeStamp") !=
null) {
timestamp =
bundleContext.getBundle().getHeaders().get("Implementation-TimeStamp");
}
- String versionMessage = " " +
bundleContext.getBundle().getVersion().toString() + " Build:" + buildNumber +
" Timestamp:" + timestamp;
+ String versionMessage =
+ " " + bundleContext.getBundle().getVersion().toString() +
" Build:" + buildNumber + " Timestamp:" + timestamp;
System.out.println(versionMessage);
System.out.println("--------------------------------------------------------------------------");
- System.out.println("Successfully started " +
unomiStartedBundleCount + " bundles and " + matchedRequiredServicesCount + "
required services in " + totalStartupTime + " ms");
- logger.info("Apache Unomi version: " + versionMessage);
- logger.info("Apache Unomi successfully started {} bundles and {}
required services in {} ms", unomiStartedBundleCount,
matchedRequiredServicesCount, totalStartupTime);
+ System.out.println(
+ "Successfully started " + requiredBundles.size() + "
bundles and " + requiredServicesFilters.size() + " " + "required "
+ + "services in " + totalStartupTime + " ms");
+ logger.info("Apache Unomi version: {}", versionMessage);
+ logger.info("Apache Unomi successfully started {} bundles and {}
required services in {} ms", requiredBundles.size(),
+ requiredServicesFilters.size(), totalStartupTime);
startupMessageAlreadyDisplayed = true;
shutdownMessageAlreadyDisplayed = false;
}
}
public boolean isStartupComplete() {
- if (unomiStartedBundleCount < requiredStartedBundleCount) {
- return false;
- }
- if (matchedRequiredServicesCount < requiredServicesFilters.size()) {
- return false;
- }
- return true;
+ return allBundleStarted() && matchedRequiredServicesCount ==
requiredServicesFilters.size();
}
private void loadLogo() {
diff --git
a/lifecycle-watcher/src/main/resources/OSGI-INF/blueprint/blueprint.xml
b/lifecycle-watcher/src/main/resources/OSGI-INF/blueprint/blueprint.xml
index 3fe88cf..bed16c8 100644
--- a/lifecycle-watcher/src/main/resources/OSGI-INF/blueprint/blueprint.xml
+++ b/lifecycle-watcher/src/main/resources/OSGI-INF/blueprint/blueprint.xml
@@ -25,15 +25,47 @@
<cm:property-placeholder persistent-id="org.apache.unomi.lifecycle"
update-strategy="reload"
placeholder-prefix="${lifecycle.">
<cm:default-properties>
- <cm:property name="requiredStartedBundleCount" value="16" />
- <cm:property name="requiredServices"
value="(urlPatterns=/eventcollector),(urlPatterns=/context.json)" />
+ <cm:property name="checkStartupState.refresh.interval" value="60"/>
+ <cm:property name="requiredServices"
value="(urlPatterns=/eventcollector),(urlPatterns=/context.json),(osgi.service.blueprint.compname=elasticSearchPersistenceServiceImpl)"
/>
</cm:default-properties>
</cm:property-placeholder>
<bean id="bundleWatcher" init-method="init" destroy-method="destroy"
class="org.apache.unomi.lifecycle.BundleWatcher">
<property name="bundleContext" ref="blueprintBundleContext"/>
<property name="requiredServices"
value="${lifecycle.requiredServices}" />
- <property name="requiredStartedBundleCount"
value="${lifecycle.requiredStartedBundleCount}" />
+ <property name="checkStartupStateRefreshInterval"
value="${lifecycle.checkStartupState.refresh.interval}"/>
+ <property name="requiredBundles">
+ <map>
+ <entry key="org.apache.unomi.api" value="false"/>
+ <entry key="org.apache.unomi.common" value="false"/>
+ <entry key="org.apache.unomi.scripting" value="false"/>
+ <entry key="org.apache.unomi.metrics" value="false"/>
+ <entry key="org.apache.unomi.persistence-spi" value="false"/>
+ <entry key="org.apache.unomi.persistence-elasticsearch-core"
value="false"/>
+ <entry key="org.apache.unomi.services" value="false"/>
+ <entry key="org.apache.unomi.cxs-lists-extension-services"
value="false"/>
+ <entry key="org.apache.unomi.cxs-lists-extension-rest"
value="false"/>
+ <entry key="org.apache.unomi.cxs-geonames-services"
value="false"/>
+ <entry key="org.apache.unomi.cxs-geonames-rest" value="false"/>
+ <entry key="org.apache.unomi.cxs-privacy-extension-services"
value="false"/>
+ <entry key="org.apache.unomi.cxs-privacy-extension-rest"
value="false"/>
+ <entry key="org.apache.unomi.rest" value="false"/>
+ <entry key="org.apache.unomi.wab" value="false"/>
+ <entry key="org.apache.unomi.plugins-base" value="false"/>
+ <entry key="org.apache.unomi.plugins-request" value="false"/>
+ <entry key="org.apache.unomi.plugins-mail" value="false"/>
+ <entry key="org.apache.unomi.plugins-optimization-test"
value="false"/>
+ <entry key="org.apache.unomi.cxs-lists-extension-actions"
value="false"/>
+ <entry key="org.apache.unomi.router-api" value="false"/>
+ <entry key="org.apache.unomi.router-core" value="false"/>
+ <entry key="org.apache.unomi.router-service" value="false"/>
+ <entry key="org.apache.unomi.router-rest" value="false"/>
+ <entry key="org.apache.unomi.shell-dev-commands"
value="false"/>
+ <entry key="org.apache.unomi.web-tracker-wab" value="false"/>
+ <entry key="org.apache.unomi.groovy-actions-services"
value="false"/>
+ <entry key="org.apache.unomi.groovy-actions-rest"
value="false"/>
+ </map>
+ </property>
</bean>
<service id="bundleWatcherService" ref="bundleWatcher">
diff --git
a/lifecycle-watcher/src/main/resources/org.apache.unomi.lifecycle.cfg
b/lifecycle-watcher/src/main/resources/org.apache.unomi.lifecycle.cfg
new file mode 100644
index 0000000..e2bd0f2
--- /dev/null
+++ b/lifecycle-watcher/src/main/resources/org.apache.unomi.lifecycle.cfg
@@ -0,0 +1,19 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# The interval in seconds to check again the bundles and services state
+checkStartupState.refresh.interval=${org.apache.unomi.lifecycle.checkStartupState.refresh.interval:-60}