CatalogUIAdapterFactory.java

/*
 * Licensed to The Apereo Foundation under one or more contributor license
 * agreements. See the NOTICE file distributed with this work for additional
 * information regarding copyright ownership.
 *
 *
 * The Apereo Foundation licenses this file to you under the Educational
 * Community 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://opensource.org/licenses/ecl2.txt
 *
 * 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.
 *
 */

package org.opencastproject.index.service.catalog.adapter;

import static java.lang.String.format;
import static org.opencastproject.util.OsgiUtil.getCfg;
import static org.opencastproject.util.OsgiUtil.getOptCfg;

import org.opencastproject.index.service.catalog.adapter.events.CommonEventCatalogUIAdapter;
import org.opencastproject.index.service.catalog.adapter.events.ConfigurableEventDCCatalogUIAdapter;
import org.opencastproject.index.service.catalog.adapter.series.CommonSeriesCatalogUIAdapter;
import org.opencastproject.index.service.catalog.adapter.series.ConfigurableSeriesDCCatalogUIAdapter;
import org.opencastproject.list.api.ListProvidersService;
import org.opencastproject.metadata.dublincore.EventCatalogUIAdapter;
import org.opencastproject.metadata.dublincore.SeriesCatalogUIAdapter;
import org.opencastproject.series.api.SeriesService;
import org.opencastproject.util.data.Option;
import org.opencastproject.workspace.api.Workspace;

import com.entwinemedia.fn.Prelude;

import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceRegistration;
import org.osgi.service.cm.ConfigurationException;
import org.osgi.service.cm.ManagedServiceFactory;
import org.osgi.service.component.ComponentContext;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Deactivate;
import org.osgi.service.component.annotations.Reference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Dictionary;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Based on the given service configuration this service factory will create new instances of
 * {@link SeriesCatalogUIAdapter} services and register them in the OSGi service registry.
 */
@Component(
    immediate = true,
    service = ManagedServiceFactory.class,
    property = {
        "service.pid=org.opencastproject.ui.metadata.CatalogUIAdapterFactory",
        "service.description=Admin UI - Catalog UI Adapter Factory"
    }
)
public class CatalogUIAdapterFactory implements ManagedServiceFactory {

  /** The logging facility. */
  private static final Logger logger = LoggerFactory.getLogger(CatalogUIAdapterFactory.class);

  /* The collection of keys to read from the OSGI configuration file */
  public static final String CONF_TYPE_KEY = "type";
  public static final String CONF_ORGANIZATION_KEY = "organization";
  public static final String CONF_FLAVOR_KEY = "flavor";
  public static final String CONF_TITLE_KEY = "title";
  public static final String CONF_COMMON_METADATA_KEY = "common-metadata";

  private static final String CATALOG_TYPE_EVENTS = "events";
  private static final String CATALOG_TYPE_SERIES = "series";

  /** Map with service registrations of registered {@link SeriesCatalogUIAdapter} instances. */
  private final Map<String, ServiceRegistration<?>> adapterServiceRegistrations = new ConcurrentHashMap<>();

  /** Reference to a {@link ListProvidersService} instance. */
  private ListProvidersService listProvidersService;

  /** Reference to a {@link SeriesService} instance. */
  private SeriesService seriesService;

  /** Reference to a {@link Workspace} instance. */
  private Workspace workspace;

  /** The OSGi bundle context. */
  private BundleContext bundleContext;

  /** OSGi callback. */
  @Activate
  void activate(ComponentContext cc) {
    bundleContext = cc.getBundleContext();
  }

  /** OSGi callback for deactivating component. */
  @Deactivate
  void deactivate() {
    for (String pid : adapterServiceRegistrations.keySet()) {
      deleted(pid);
    }
  }

  @Override
  public String getName() {
    return "Catalog UI Adapter Factory";
  }

  @Override
  public void updated(String pid, Dictionary<String, ?> properties) throws ConfigurationException {
    final String type = getCfg(properties, CONF_TYPE_KEY);
    Option<String> optCommonMetadata = getOptCfg(properties, CONF_COMMON_METADATA_KEY);
    final boolean isCommonMetadata = optCommonMetadata.isSome() ? Boolean.parseBoolean(optCommonMetadata.get()) : false;
    Dictionary serviceProperties = new Properties();
    serviceProperties.put(CONF_COMMON_METADATA_KEY, isCommonMetadata);

    // Check for valid configuration values
    if (!(CATALOG_TYPE_EVENTS.equalsIgnoreCase(type) || CATALOG_TYPE_SERIES.equalsIgnoreCase(type))) {
      throw new ConfigurationException(CONF_TYPE_KEY,
              format("The type must either be '%s' or '%s'", CATALOG_TYPE_EVENTS, CATALOG_TYPE_SERIES));
    }

    switch (type) {
      case CATALOG_TYPE_EVENTS: {
        if (adapterServiceRegistrations.containsKey(pid)) {
          ServiceRegistration<?> serviceRegistration = adapterServiceRegistrations.get(pid);
          ConfigurableEventDCCatalogUIAdapter adapter = (ConfigurableEventDCCatalogUIAdapter) bundleContext
                  .getService(serviceRegistration.getReference());
          adapter.updated(properties);
        } else {
          ConfigurableEventDCCatalogUIAdapter adapter;
          String[] adapterClassesNames;
          if (isCommonMetadata) {
            adapter = new CommonEventCatalogUIAdapter();
            adapterClassesNames = new String[] { CommonEventCatalogUIAdapter.class.getName(),
                    EventCatalogUIAdapter.class.getName() };
          } else {
            adapter = new ConfigurableEventDCCatalogUIAdapter();
            adapterClassesNames = new String[] { EventCatalogUIAdapter.class.getName() };
          }

          adapter.setListProvidersService(listProvidersService);
          adapter.setWorkspace(workspace);
          adapter.updated(properties);

          ServiceRegistration<?> configurationRegistration = bundleContext.registerService(adapterClassesNames, adapter,
                  serviceProperties);
          adapterServiceRegistrations.put(pid, configurationRegistration);
        }
        break;
      }
      case CATALOG_TYPE_SERIES: {
        if (adapterServiceRegistrations.containsKey(pid)) {
          ServiceRegistration<?> serviceRegistration = adapterServiceRegistrations.get(pid);
          ConfigurableSeriesDCCatalogUIAdapter adapter = (ConfigurableSeriesDCCatalogUIAdapter) bundleContext
                  .getService(serviceRegistration.getReference());
          adapter.updated(properties);
        } else {
          ConfigurableSeriesDCCatalogUIAdapter adapter;
          String[] adapterClassesNames;
          if (isCommonMetadata) {
            adapter = new CommonSeriesCatalogUIAdapter();
            adapterClassesNames = new String[] { CommonSeriesCatalogUIAdapter.class.getName(),
                    SeriesCatalogUIAdapter.class.getName() };
          } else {
            adapter = new ConfigurableSeriesDCCatalogUIAdapter();
            adapterClassesNames = new String[] { SeriesCatalogUIAdapter.class.getName() };
          }
          adapter.setListProvidersService(listProvidersService);
          adapter.setSeriesService(seriesService);
          adapter.updated(properties);

          ServiceRegistration<?> adapterServiceRegistration = bundleContext.registerService(adapterClassesNames,
                  adapter, serviceProperties);
          adapterServiceRegistrations.put(pid, adapterServiceRegistration);
        }
        break;
      }
      default:
        Prelude.unexhaustiveMatch(type);
    }

  }

  @Override
  public void deleted(String pid) {
    if (adapterServiceRegistrations.containsKey(pid)) {
      ServiceRegistration<?> serviceRegistration = adapterServiceRegistrations.remove(pid);
      serviceRegistration.unregister();
      logger.info("Service registration for PID {} removed", pid);
    }
  }

  /** OSGi callback to bind {@link ListProvidersService} instance. */
  @Reference
  void setListProvidersService(ListProvidersService listProvidersService) {
    this.listProvidersService = listProvidersService;
  }

  /** OSGi callback to bind {@link SeriesService} instance. */
  @Reference
  void setSeriesService(SeriesService seriesService) {
    this.seriesService = seriesService;
  }

  /** OSGi callback to bind {@link Workspace} instance. */
  @Reference
  void setWorkspace(Workspace workspace) {
    this.workspace = workspace;
  }

}