All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.yetamine.osgi.jdbc.internal.DriverWeaving Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2016 Yetamine
 *
 * Licensed 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 net.yetamine.osgi.jdbc.internal;

import java.util.ArrayDeque;
import java.util.Collection;
import java.util.Collections;
import java.util.Deque;
import java.util.HashSet;
import java.util.Objects;
import java.util.OptionalLong;
import java.util.Set;
import java.util.function.Function;

import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceRegistration;
import org.osgi.framework.hooks.weaving.WeavingHook;
import org.osgi.framework.wiring.BundleRevision;
import org.osgi.framework.wiring.BundleWiring;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import net.yetamine.osgi.jdbc.tweak.WeavingFilter;

/**
 * Encapsulates all components that handle class weaving.
 */
final class DriverWeaving implements AutoCloseable {

    private static final Logger LOGGER = LoggerFactory.getLogger(DriverWeaving.class);

    /** Bundle context for registering the service and tracker. */
    private final BundleContext serviceContext;
    /** Filter tracker to provide a unified filter. */
    private final WeavingFilterTracker filterTracker;
    /** Registration of the weaving hook. */
    private ServiceRegistration service;

    /**
     * Creates a new instance.
     *
     * @param bundleContext
     *            the bundle context for which this service should run. It must
     *            not be {@code null}.
     */
    public DriverWeaving(BundleContext bundleContext) {
        serviceContext = Objects.requireNonNull(bundleContext);
        filterTracker = new WeavingFilterTracker(serviceContext);
    }

    /**
     * Makes this instance operational if not operational yet.
     */
    public synchronized void open() {
        if (service != null) {
            return;
        }

        final Set exclusions = requirementClosure(Collections.singleton(serviceContext.getBundle()));
        LOGGER.debug("Opening weaving service with following bundles excluded from weaving: {}", exclusions);
        filterTracker.open(); // Start the tracking before the service starts weaving!
        final Function resolver = WeavingHookService::defaultCallerResolver;
        final WeavingFilter protection = (bundle, className) -> !exclusions.contains(bundle);
        final WeavingHook hook = new WeavingHookService(serviceContext, protection, filterTracker, resolver);
        service = serviceContext.registerService(WeavingHook.class, hook, null);
    }

    /**
     * Closes all operations related to this instance, putting it out of
     * service.
     *
     * @see java.lang.AutoCloseable#close()
     */
    public synchronized void close() {
        if (service == null) {
            return;
        }

        LOGGER.debug("Closing weaving service.");
        service.unregister();
        service = null;

        filterTracker.close(); // Stop the tracking after unregistering the weaving hook!
    }

    /**
     * Computes the requirement closure of the given seed bundles.
     *
     * @param bundles
     *            the seed bundles to exclude. It must not be {@code null}.
     */
    private static Set requirementClosure(Collection bundles) {
        // Compute dependencies of the seed bundle tos prevent weaving anything from them
        final Deque resolving = new ArrayDeque<>();
        bundles.forEach(bundle -> resolving.push(Objects.requireNonNull(bundle)));
        final Set result = new HashSet<>();

        // Depth-first search for all required bundles to exclude them for sure
        for (Bundle pending; ((pending = resolving.poll()) != null);) {
            if (result.add(pending)) {
                final BundleWiring wiring = pending.adapt(BundleWiring.class);
                if (wiring == null) {
                    continue;
                }

                wiring.getRequiredWires(BundleRevision.PACKAGE_NAMESPACE).forEach(wire -> {
                    resolving.push(wire.getProviderWiring().getBundle());
                });
            }
        }

        return Collections.unmodifiableSet(result);
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy