summaryrefslogtreecommitdiffstats
path: root/jdisc_core/src/main/java/com/yahoo/jdisc/core/ActiveContainer.java
blob: 907d2a050fb5cd7e1c5cb2f06ae63babae62771b (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.jdisc.core;

import com.google.inject.AbstractModule;
import com.google.inject.Injector;
import com.yahoo.jdisc.AbstractResource;
import com.yahoo.jdisc.SharedResource;
import com.yahoo.jdisc.application.BindingSet;
import com.yahoo.jdisc.application.BindingSetSelector;
import com.yahoo.jdisc.application.ContainerBuilder;
import com.yahoo.jdisc.application.ResourcePool;
import com.yahoo.jdisc.handler.RequestHandler;
import com.yahoo.jdisc.service.BindingSetNotFoundException;
import com.yahoo.jdisc.service.CurrentContainer;
import com.yahoo.jdisc.service.NoBindingSetSelectedException;
import com.yahoo.jdisc.service.ServerProvider;

import java.net.URI;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * @author Simon Thoresen
 */
public class ActiveContainer extends AbstractResource implements CurrentContainer {

    private final ContainerTermination termination;
    private final Injector guiceInjector;
    private final Iterable<ServerProvider> serverProviders;
    private final ResourcePool resourceReferences = new ResourcePool();
    private final Map<String, BindingSet<RequestHandler>> serverBindings;
    private final Map<String, BindingSet<RequestHandler>> clientBindings;
    private final BindingSetSelector bindingSetSelector;
    private final TimeoutManagerImpl timeoutMgr;
    final Destructor destructor;

    public ActiveContainer(ContainerBuilder builder) {
        serverProviders = builder.serverProviders().activate();
        serverProviders.forEach(resourceReferences::retain);
        serverBindings = builder.activateServerBindings();
        serverBindings.forEach(
                (ignoredName, bindingSet) -> bindingSet.forEach(
                        binding -> resourceReferences.retain(binding.getValue())));
        clientBindings = builder.activateClientBindings();
        clientBindings.forEach(
                (ignoredName, bindingSet) -> bindingSet.forEach(
                        binding -> resourceReferences.retain(binding.getValue())));
        bindingSetSelector = builder.getInstance(BindingSetSelector.class);
        timeoutMgr = builder.getInstance(TimeoutManagerImpl.class);
        timeoutMgr.start();
        builder.guiceModules().install(new AbstractModule() {

            @Override
            protected void configure() {
                bind(TimeoutManagerImpl.class).toInstance(timeoutMgr);
            }
        });
        guiceInjector = builder.guiceModules().activate();
        termination = new ContainerTermination(builder.appContext());
        destructor = new Destructor(resourceReferences, timeoutMgr, termination);
    }

    @Override
    protected void destroy() {
        boolean alreadyDestructed = destructor.destruct();
        if (alreadyDestructed) {
            throw new IllegalStateException(
                    "Already destructed! This should not occur unless destroy have been called directly!");
        }
    }

    /**
     * Make this instance retain a reference to the resource until it is destroyed.
     */
    void retainReference(SharedResource resource) {
        resourceReferences.retain(resource);
    }

    public ContainerTermination shutdown() {
        return termination;
    }

    public Injector guiceInjector() {
        return guiceInjector;
    }

    public Iterable<ServerProvider> serverProviders() {
        return serverProviders;
    }

    public Map<String, BindingSet<RequestHandler>> serverBindings() {
        return serverBindings;
    }

    public BindingSet<RequestHandler> serverBindings(String setName) {
        return serverBindings.get(setName);
    }

    public Map<String, BindingSet<RequestHandler>> clientBindings() {
        return clientBindings;
    }

    public BindingSet<RequestHandler> clientBindings(String setName) {
        return clientBindings.get(setName);
    }

    TimeoutManagerImpl timeoutManager() {
        return timeoutMgr;
    }

    @Override
    public ContainerSnapshot newReference(URI uri) {
        String name = bindingSetSelector.select(uri);
        if (name == null) {
            throw new NoBindingSetSelectedException(uri);
        }
        BindingSet<RequestHandler> serverBindings = serverBindings(name);
        BindingSet<RequestHandler> clientBindings = clientBindings(name);
        if (serverBindings == null || clientBindings == null) {
            throw new BindingSetNotFoundException(name);
        }
        return new ContainerSnapshot(this, serverBindings, clientBindings);
    }

    // NOTE: An instance of this class must never contain a reference to the outer class (ActiveContainer).
    static class Destructor {
        private final ResourcePool resourceReferences;
        private final TimeoutManagerImpl timeoutMgr;
        private final ContainerTermination termination;
        private final AtomicBoolean done = new AtomicBoolean();

        private Destructor(ResourcePool resourceReferences,
                           TimeoutManagerImpl timeoutMgr,
                           ContainerTermination termination) {
            this.resourceReferences = resourceReferences;
            this.timeoutMgr = timeoutMgr;
            this.termination = termination;
        }

        boolean destruct() {
            boolean alreadyDestructed = this.done.getAndSet(true);
            if (!alreadyDestructed) {
                resourceReferences.release();
                timeoutMgr.shutdown();
                termination.run();
            }
            return alreadyDestructed;
        }
    }
}