blob: e016ae17f566aca796a7fef23d2ee89bb8d2c6e7 [file] [log] [blame]
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~ 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.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
package org.apache.sling.graphql.schema.aggregator.impl;
import java.io.IOException;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.osgi.annotation.bundle.Capability;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleEvent;
import org.osgi.framework.wiring.BundleRevision;
import org.osgi.framework.wiring.BundleWire;
import org.osgi.framework.wiring.BundleWiring;
import org.osgi.namespace.extender.ExtenderNamespace;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.util.tracker.BundleTracker;
import org.osgi.util.tracker.BundleTrackerCustomizer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/** Tracks bundles which provide partial schemas and collects the corresponding set of schemas.
*/
@Component(
service = {ProviderBundleTracker.class}
)
@Capability(
namespace = ExtenderNamespace.EXTENDER_NAMESPACE,
name = "sling.graphql-schema-aggregator",
version = "0.1"
)
public class ProviderBundleTracker implements BundleTrackerCustomizer<Object> {
public static final String SCHEMA_PATH_HEADER = "Sling-GraphQL-Schema";
private final Logger log = LoggerFactory.getLogger(getClass().getName());
private final Map<PartialInfo, BundleEntryPartial> schemaProviders = new ConcurrentHashMap<>();
private BundleContext bundleContext;
@Activate
public void activate(BundleContext bundleContext) {
this.bundleContext = bundleContext;
BundleTracker<?> bt = new BundleTracker<>(bundleContext, Bundle.ACTIVE, this);
bt.open();
}
@Override
public Object addingBundle(Bundle bundle, BundleEvent event) {
BundleWiring bundleWiring = bundle.adapt(BundleWiring.class);
Bundle us = bundleContext.getBundle();
if (bundleWiring.getRequiredWires(ExtenderNamespace.EXTENDER_NAMESPACE).stream().map(BundleWire::getProvider)
.map(BundleRevision::getBundle).anyMatch(us::equals)) {
final String providersPath = bundle.getHeaders().get(SCHEMA_PATH_HEADER);
if (providersPath == null) {
log.debug("Bundle {} has no {} header, ignored", bundle.getSymbolicName(), SCHEMA_PATH_HEADER);
} else {
// For now we only support file entries which are directly under providersPath
final Enumeration<String> paths = bundle.getEntryPaths(providersPath);
if (paths != null) {
while (paths.hasMoreElements()) {
final String path = paths.nextElement();
try {
addIfNotPresent(BundleEntryPartial.forBundle(bundle, path));
} catch (IOException ioe) {
// TODO save errors and refuse to work if any happended?
log.error("Error reading partial " + path, ioe);
}
}
}
}
}
return bundle;
}
private void addIfNotPresent(BundleEntryPartial a) {
if(a != null) {
if(schemaProviders.containsKey(a.getPartialInfo())) {
log.warn("Partial provider for partial {} already present, new one will be ignored", a.getPartialInfo());
} else {
log.info("Registering {}", a);
schemaProviders.put(a.getPartialInfo(), a);
}
}
}
@Override
public void removedBundle(Bundle bundle, BundleEvent event, Object object) {
final long id = bundle.getBundleId();
schemaProviders.forEach((key, value) -> {
if (id == value.getBundleId()) {
log.info("Removing {}", value);
schemaProviders.remove(key);
}
});
}
@Override
public void modifiedBundle(Bundle bundle, BundleEvent event, Object object) {
// do nothing
}
Map<PartialInfo, Partial> getSchemaProviders() {
return Collections.unmodifiableMap(schemaProviders);
}
}