/** * 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.feed.scanner; import static org.opencastproject.util.ReadinessIndicator.ARTIFACT; import org.opencastproject.feed.api.FeedGenerator; import org.opencastproject.search.api.SearchService; import org.opencastproject.series.api.SeriesService; import org.opencastproject.util.ReadinessIndicator; import org.apache.commons.io.IOUtils; import org.apache.felix.fileinstall.ArtifactInstaller; import org.osgi.framework.BundleContext; import org.osgi.framework.ServiceRegistration; import org.osgi.service.component.ComponentContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.File; import java.io.FileInputStream; import java.io.FilenameFilter; import java.util.Dictionary; import java.util.HashMap; import java.util.Hashtable; import java.util.Map; import java.util.Properties; /** * Installs feeds matching "*.properties" in the feeds watch directory. */ public class FeedRegistrationScanner implements ArtifactInstaller { public static final String FEED_CLASS = "feed.class"; public static final String FEED_URI = "feed.uri"; public static final String FEED_SELECTOR = "feed.selector"; public static final String FEED_ENTRY = "feed.entry"; private static final Logger logger = LoggerFactory.getLogger(FeedRegistrationScanner.class); /** A map to keep track of each feed registration file and feed generator it produces */ protected Map<File, ServiceRegistration<?>> generators = new HashMap<>(); /** The search service to use in each feed generator */ protected SearchService searchService; /** The series service to be used by the series feeds */ protected SeriesService seriesService; /** The bundle context for this osgi component */ protected BundleContext bundleContext; /** Sum of profiles files currently installed */ private int sumInstalledFiles = 0; /** Sets the search service */ public void setSearchService(SearchService searchService) { this.searchService = searchService; } /** Sets the series service */ public void setSeriesService(SeriesService seriesService) { this.seriesService = seriesService; } /** * Activates the component * * @param cc * the component's context */ protected void activate(ComponentContext cc) { this.bundleContext = cc.getBundleContext(); } /** * Deactivates the component */ protected void deactivate() { this.bundleContext = null; } /** * {@inheritDoc} * * @see org.apache.felix.fileinstall.ArtifactListener#canHandle(java.io.File) */ @Override public boolean canHandle(File artifact) { return "feeds".equals(artifact.getParentFile().getName()) && artifact.getName().endsWith(".properties"); } /** * {@inheritDoc} * * @see org.apache.felix.fileinstall.ArtifactInstaller#install(java.io.File) */ @Override public void install(File artifact) throws Exception { logger.info("Installing a feed from '{}'", artifact.getName()); Properties props = new Properties(); FileInputStream in = null; try { in = new FileInputStream(artifact); props.load(in); } finally { IOUtils.closeQuietly(in); } // Always include the server URL obtained from the bundle context props.put("org.opencastproject.server.url", bundleContext.getProperty("org.opencastproject.server.url")); Class<?> clazz = getClass().getClassLoader().loadClass(props.getProperty(FEED_CLASS)); FeedGenerator generator = (FeedGenerator) clazz.newInstance(); generator.setSearchService(searchService); generator.setSeriesService(seriesService); generator.initialize(props); ServiceRegistration<?> reg = bundleContext.registerService(FeedGenerator.class.getName(), generator, null); generators.put(artifact, reg); sumInstalledFiles++; // Determine the number of available profiles String[] filesInDirectory = artifact.getParentFile().list(new FilenameFilter() { @Override public boolean accept(File arg0, String name) { return name.endsWith(".properties"); } }); // Once all profiles have been loaded, announce readiness if (filesInDirectory.length == sumInstalledFiles) { Dictionary<String, String> properties = new Hashtable<>(); properties.put(ARTIFACT, "feed"); logger.debug("Indicating readiness of feed"); bundleContext.registerService(ReadinessIndicator.class.getName(), new ReadinessIndicator(), properties); logger.info("All {} feeds installed", filesInDirectory.length); } else { logger.debug("{} of {} feeds installed", sumInstalledFiles, filesInDirectory.length); } } /** * {@inheritDoc} * * @see org.apache.felix.fileinstall.ArtifactInstaller#uninstall(java.io.File) */ @Override public void uninstall(File artifact) throws Exception { ServiceRegistration reg = generators.get(artifact); if (reg != null) { reg.unregister(); generators.remove(artifact); } } /** * {@inheritDoc} * * @see org.apache.felix.fileinstall.ArtifactInstaller#update(java.io.File) */ @Override public void update(File artifact) throws Exception { uninstall(artifact); install(artifact); } }