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.statistics.provider.matomo;
23  
24  import org.opencastproject.statistics.api.StatisticsCoordinator;
25  import org.opencastproject.statistics.api.StatisticsProvider;
26  import org.opencastproject.statistics.provider.matomo.provider.BatchMatomoRequest;
27  import org.opencastproject.statistics.provider.matomo.provider.MatomoProviderConfiguration;
28  import org.opencastproject.statistics.provider.matomo.provider.MatomoTimeSeriesStatisticsProvider;
29  import org.opencastproject.util.ConfigurationException;
30  
31  import org.apache.felix.fileinstall.ArtifactInstaller;
32  import org.osgi.service.component.annotations.Activate;
33  import org.osgi.service.component.annotations.Component;
34  import org.osgi.service.component.annotations.Deactivate;
35  import org.osgi.service.component.annotations.Modified;
36  import org.osgi.service.component.annotations.Reference;
37  import org.slf4j.Logger;
38  import org.slf4j.LoggerFactory;
39  
40  import java.io.File;
41  import java.nio.charset.Charset;
42  import java.nio.file.Files;
43  import java.util.Map;
44  import java.util.concurrent.ConcurrentHashMap;
45  
46  /**
47   * Implements statistics providers using Matomo for data retrieval.
48   */
49  @Component(
50      immediate = true,
51      service = { ArtifactInstaller.class },
52      property = {
53          "service.description=Statistics Provider Matomo Service"
54      }
55  )
56  public class StatisticsProviderMatomoService implements ArtifactInstaller {
57  
58    /** Logging utility */
59    private static final Logger logger = LoggerFactory.getLogger(StatisticsProviderMatomoService.class);
60  
61    private static final String KEY_MATOMO_API_URL = "matomo.api.url";
62    private static final String KEY_MATOMO_API_TOKEN = "matomo.api.token";
63  
64    private String matomoApiUrl;
65    private String matomoApiToken;
66  
67    private StatisticsCoordinator statisticsCoordinator;
68    private Map<String, StatisticsProvider> fileNameToProvider = new ConcurrentHashMap<>();
69    private Map<String, BatchMatomoRequest> methodToBatchRequest = new ConcurrentHashMap<>();
70  
71    /**
72     * Get an existing batch request for a specific ResourceType and Matomo API method
73     * @param resourceTypeMethod The ResourceType concatenated with the Matomo API method
74     * @return The existing batch request or null if none exists
75     */
76    public BatchMatomoRequest getBatchRequest(String resourceTypeMethod) {
77      return methodToBatchRequest.get(resourceTypeMethod);
78    }
79  
80    /**
81     * Register a new batch request for a specific ResourceType and Matomo API method
82     * @param resourceTypeMethod The ResourceType concatenated with the Matomo API method
83     * @param batchRequest The batch request to register
84     */
85    public void registerBatchRequest(String resourceTypeMethod, BatchMatomoRequest batchRequest) {
86      methodToBatchRequest.put(resourceTypeMethod, batchRequest);
87    }
88  
89    @Reference
90    public void setStatisticsCoordinator(StatisticsCoordinator service) {
91      this.statisticsCoordinator = service;
92    }
93  
94    @Activate
95    public void activate(Map<String, Object> properties) {
96      logger.info("Activating Statistics Provider Matomo Service");
97      modified(properties);
98    }
99  
100   @Deactivate
101   public void deactivate() {
102     logger.info("Deactivating Statistics Provider Matomo Service");
103   }
104 
105   @Override
106   public void install(File file) throws Exception {
107     final String json = new String(Files.readAllBytes(file.toPath()), Charset.forName("utf-8"));
108     final MatomoProviderConfiguration providerCfg = MatomoProviderConfiguration.fromJson(json);
109     StatisticsProvider provider;
110     switch (providerCfg.getType().toLowerCase()) {
111       case "timeseries": {
112         provider = new MatomoTimeSeriesStatisticsProvider(
113                 this,
114                 providerCfg.getId(),
115                 providerCfg.getResourceType(),
116                 providerCfg.getTitle(),
117                 providerCfg.getDescription(),
118                 providerCfg.getSources());
119         logger.info("Installed Matomo time series statistics provider '{}'", providerCfg.getId());
120       }
121       break;
122       default:
123         throw new ConfigurationException("Unknown Matomo statistics type: " + providerCfg.getType());
124     }
125     fileNameToProvider.put(file.getName(), provider);
126     statisticsCoordinator.addProvider(provider);
127   }
128 
129   @Override
130   public void uninstall(File file) {
131     if (fileNameToProvider.containsKey(file.getName())) {
132       statisticsCoordinator.removeProvider(fileNameToProvider.get(file.getName()));
133       fileNameToProvider.remove(file.getName());
134     }
135   }
136 
137   @Override
138   public boolean canHandle(File file) {
139     return "statistics".equals(file.getParentFile().getName())
140         && file.getName().endsWith(".json")
141         && file.getName().toUpperCase().startsWith("matomo.".toUpperCase());
142   }
143 
144   @Override
145   public void update(File file) throws Exception {
146     uninstall(file);
147     install(file);
148   }
149 
150   @Modified
151   public void modified(Map<String, Object> properties) {
152     if (properties == null) {
153       logger.info("Configuration file not found. Not connecting to Matomo API.");
154     } else if (!(properties.containsKey(KEY_MATOMO_API_TOKEN) || properties.containsKey(KEY_MATOMO_API_URL))) {
155       logger.info("No configuration available. Not connecting to Matomo API.");
156     } else {
157       final Object matomoApiUrlValue = properties.get(KEY_MATOMO_API_URL);
158       if (matomoApiUrlValue != null) {
159         matomoApiUrl = matomoApiUrlValue.toString();
160       } else {
161         throw new ConfigurationException("Matomo API URL is missing in config file.");
162       }
163       final Object matomoApiTokenValue = properties.get(KEY_MATOMO_API_TOKEN);
164       if (matomoApiTokenValue != null) {
165         matomoApiToken = matomoApiTokenValue.toString();
166       } else {
167         throw new ConfigurationException("Matomo API access token is missing in config file.");
168       }
169       logger.info("Updated Matomo API URL to '{}'", matomoApiUrl);
170     }
171   }
172 
173   public String getMatomoApiUrl() {
174     return matomoApiUrl;
175   }
176 
177   public String getMatomoApiToken() {
178     return matomoApiToken;
179   }
180 }