View Javadoc
1   /*
2    * Licensed to The Apereo Foundation under one or more contributor license
3    * agreements. See the NOTICE file distributed with this work for additional
4    * information regarding copyright ownership.
5    *
6    *
7    * The Apereo Foundation licenses this file to you under the Educational
8    * Community License, Version 2.0 (the "License"); you may not use this file
9    * except in compliance with the License. You may obtain a copy of the License
10   * at:
11   *
12   *   http://opensource.org/licenses/ecl2.txt
13   *
14   * Unless required by applicable law or agreed to in writing, software
15   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
16   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
17   * License for the specific language governing permissions and limitations under
18   * the License.
19   *
20   */
21  
22  package org.opencastproject.kernel.bundleinfo;
23  
24  import static org.opencastproject.kernel.bundleinfo.BundleInfoImpl.bundleInfo;
25  import static org.opencastproject.kernel.bundleinfo.BundleInfos.getBuildNumber;
26  import static org.opencastproject.util.OsgiUtil.getContextProperty;
27  import static org.opencastproject.util.data.Option.none;
28  import static org.opencastproject.util.data.Option.option;
29  import static org.opencastproject.util.data.Option.some;
30  
31  import org.opencastproject.systems.OpencastConstants;
32  import org.opencastproject.util.UrlSupport;
33  import org.opencastproject.util.data.Option;
34  import org.opencastproject.util.data.functions.Strings;
35  
36  import org.osgi.framework.Bundle;
37  import org.osgi.framework.BundleEvent;
38  import org.osgi.framework.BundleListener;
39  import org.osgi.service.component.ComponentContext;
40  import org.osgi.service.component.annotations.Activate;
41  import org.osgi.service.component.annotations.Component;
42  import org.osgi.service.component.annotations.Deactivate;
43  import org.osgi.service.component.annotations.Reference;
44  import org.slf4j.Logger;
45  import org.slf4j.LoggerFactory;
46  
47  /**
48   * Log information about bundle build versions. The bundle needs to have the manifest header "Build-Number" set.
49   */
50  @Component(
51      immediate = true
52  )
53  public class BundleInfoLogger implements BundleListener {
54  
55    private static final Logger logger = LoggerFactory.getLogger(BundleInfoLogger.class);
56  
57    // Wrap db into an option.
58    // This strategy prevents potential exceptions caused by an already closed connection pool or
59    // entity manager in deactivate(). This happens when the logger is deactivated because of a db shutdown.
60    //
61    // However in a concurrent situation there may still occur exceptions when db methods are called _after_
62    // the pool has been closed but _before_ BundleInfoLogger's deactivate method has been called.
63    private Option<BundleInfoDb> db;
64    private String host;
65  
66    /** OSGi DI */
67    @Reference(unbind = "unsetDb")
68    public void setDb(BundleInfoDb db) {
69      this.db = some(db);
70    }
71  
72    /** OSGi DI */
73    public void unsetDb(BundleInfoDb db) {
74      this.db = none();
75    }
76  
77    /** OSGi callback */
78    @Activate
79    public void activate(ComponentContext cc) {
80      host = option(getContextProperty(cc, OpencastConstants.SERVER_URL_PROPERTY)).bind(Strings.trimToNone).getOrElse(
81              UrlSupport.DEFAULT_BASE_URL);
82      for (BundleInfoDb a : db)
83        a.clear(host);
84      cc.getBundleContext().addBundleListener(this);
85      for (Bundle b : cc.getBundleContext().getBundles()) {
86        logBundle(b);
87      }
88    }
89  
90    /** OSGi callback */
91    @Deactivate
92    public void deactivate() {
93      for (BundleInfoDb a : db) {
94        logger.info("Clearing versions");
95        a.clear(host);
96      }
97    }
98  
99    @Override
100   public void bundleChanged(BundleEvent event) {
101     switch (event.getType()) {
102       case BundleEvent.INSTALLED:
103         logBundle(event.getBundle());
104         break;
105       case BundleEvent.STOPPED:
106       case BundleEvent.UNINSTALLED:
107         for (BundleInfoDb a : db)
108           a.delete(host, event.getBundle().getBundleId());
109         break;
110       default:
111         // do nothing
112     }
113   }
114 
115   private void logBundle(final Bundle bundle) {
116     final BundleInfo info = bundleInfo(host, bundle.getSymbolicName(), bundle.getBundleId(), bundle.getVersion()
117             .toString(), getBuildNumber(bundle));
118     final String log = String.format("Bundle %s, id %d, version %s, build number %s", info.getBundleSymbolicName(),
119             info.getBundleId(), info.getBundleVersion(), info.getBuildNumber().getOrElse("n/a"));
120     logger.info(log);
121     for (BundleInfoDb a : db)
122       a.store(info);
123   }
124 }