at.ac.univie.isc.asio.nest.D2rqNestAssembler.java Source code

Java tutorial

Introduction

Here is the source code for at.ac.univie.isc.asio.nest.D2rqNestAssembler.java

Source

/*
 * #%L
 * asio server
 * %%
 * Copyright (C) 2013 - 2015 Research Group Scientific Computing, University of Vienna
 * %%
 * 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.
 * #L%
 */
package at.ac.univie.isc.asio.nest;

import at.ac.univie.isc.asio.Container;
import at.ac.univie.isc.asio.Id;
import at.ac.univie.isc.asio.Scope;
import at.ac.univie.isc.asio.brood.Assembler;
import at.ac.univie.isc.asio.d2rq.D2rqConfigModel;
import at.ac.univie.isc.asio.d2rq.D2rqJdbcModel;
import at.ac.univie.isc.asio.d2rq.LoadD2rqModel;
import at.ac.univie.isc.asio.database.Jdbc;
import at.ac.univie.isc.asio.spring.SpringContextFactory;
import com.google.common.io.ByteSource;
import com.hp.hpl.jena.rdf.model.Model;
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.event.ContextClosedEvent;
import org.springframework.stereotype.Component;

import javax.annotation.Nonnull;
import java.util.List;

import static org.slf4j.LoggerFactory.getLogger;

/**
 * Create container based on the {@link at.ac.univie.isc.asio.nest.NestBluePrint}.
 */
@Component
public /* final */ class D2rqNestAssembler implements Assembler {
    private static final Logger log = getLogger(D2rqNestAssembler.class);

    private final SpringContextFactory create;
    private final List<Configurer> configurers;
    private final List<OnClose> cleaners;

    /** Ensure there is always at least on Configurer and OnClose available. */
    @Bean
    public static ListenerDummy dummyConfigurer() {
        return new ListenerDummy();
    }

    @Autowired
    public D2rqNestAssembler(final SpringContextFactory create, final List<Configurer> configurers,
            final List<OnClose> cleaners) {
        this.create = create;
        this.configurers = configurers;
        this.cleaners = cleaners;
    }

    @Override
    public String toString() {
        return "D2rqNestAssembler{" + "create=" + create + ", configurers=" + configurers + '}';
    }

    @Override
    public Container assemble(final Id name, final ByteSource source) {
        log.debug(Scope.SYSTEM.marker(), "assemble <{}> from {}", name, source);
        final Model model = LoadD2rqModel.inferBaseUri().parse(source);
        final NestConfig initial = parse(model);
        initial.getDataset().setName(name);
        log.debug(Scope.SYSTEM.marker(), "initial config for {} : {}", name, initial);
        final NestConfig processed = postProcess(initial, configurers);
        log.debug(Scope.SYSTEM.marker(), "final config for {} : {}", name, initial);
        final AnnotationConfigApplicationContext context = create.named(name.asString());
        inject(context, processed);
        log.debug(Scope.SYSTEM.marker(), "assembled container <{}> with {} ({})", name, context.getId(), context);
        return NestContainer.wrap(context, processed);
    }

    // === assembly steps - package visible for testing ==============================================

    final NestConfig parse(final Model model) {
        final D2rqConfigModel d2rq = D2rqConfigModel.wrap(model);
        final Dataset dataset = new Dataset().setIdentifier(d2rq.getIdentifier()).setTimeout(d2rq.getTimeout())
                .setFederationEnabled(d2rq.isFederationEnabled());
        final D2rqJdbcModel jdbcConfig = d2rq.getJdbcConfig();
        final Jdbc jdbc = new Jdbc().setUrl(jdbcConfig.getUrl()).setSchema(jdbcConfig.getSchema())
                .setDriver(jdbcConfig.getDriver()).setUsername(jdbcConfig.getUsername())
                .setPassword(jdbcConfig.getPassword()).setProperties(jdbcConfig.getProperties());
        return NestConfig.create(dataset, jdbc, d2rq);
    }

    final NestConfig postProcess(final NestConfig initial, final List<Configurer> configurers) {
        assert initial.getDataset().getName() != null : "implementation error - dataset name not set";
        NestConfig processed = initial;
        for (Configurer configurer : configurers) {
            log.debug(Scope.SYSTEM.marker(), "applying {} to {}", configurer, processed);
            processed = configurer.apply(processed);
        }
        return processed;
    }

    final void inject(final AnnotationConfigApplicationContext context, final NestConfig config) {
        context.register(NestBluePrint.class);
        final ConfigurableListableBeanFactory beans = context.getBeanFactory();
        beans.registerSingleton("container-cleanup", new ContainerCleanUp(context, config, cleaners));
        beans.registerSingleton("dataset", config.getDataset());
        beans.registerSingleton("jdbc", config.getJdbc());
        beans.registerSingleton("d2rq", config.getD2rq());
    }

    /** attach OnClose listeners to a container */
    static final class ContainerCleanUp implements ApplicationListener<ContextClosedEvent> {
        private final ApplicationContext target;
        private final NestConfig config;
        private final List<OnClose> actions;

        ContainerCleanUp(final ApplicationContext target, final NestConfig config, final List<OnClose> actions) {
            this.target = target;
            this.config = config;
            this.actions = actions;
        }

        @Override
        public void onApplicationEvent(final ContextClosedEvent event) {
            if (event.getSource() == target) {
                log.info(Scope.SYSTEM.marker(), "cleaning up destroyed container using {}", actions);
                for (final OnClose listener : actions) {
                    try {
                        listener.cleanUp(config);
                    } catch (final RuntimeException e) {
                        log.info(Scope.SYSTEM.marker(), "error during container clean up", e);
                    }
                }
            } else {
                log.debug(Scope.SYSTEM.marker(), "ignoring destruction of {}", event.getSource());
            }
        }
    }

    /** used to prevent wiring failure if no Configurer or OnClose present */
    static final class ListenerDummy implements Configurer, OnClose {
        @Nonnull
        @Override
        public NestConfig apply(final NestConfig input) {
            return input;
        }

        @Override
        public void cleanUp(final NestConfig spec) throws RuntimeException {
        }

        @Override
        public String toString() {
            return "Dummy{}";
        }
    }
}