aboutsummaryrefslogtreecommitdiffstats
path: root/standalone-container/src/main/java/com/yahoo/container/standalone/StandaloneSubscriberFactory.java
blob: bcb8347fd32ce9beb88d2368507de379877d8aaf (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
// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.container.standalone;

import com.yahoo.config.ConfigBuilder;
import com.yahoo.config.ConfigInstance;
import com.yahoo.config.subscription.ConfigInterruptedException;
import com.yahoo.config.subscription.SubscriberClosedException;
import com.yahoo.container.di.config.Subscriber;
import com.yahoo.container.di.config.SubscriberFactory;
import com.yahoo.vespa.config.ConfigKey;
import com.yahoo.vespa.model.VespaModel;

import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
 * @author Tony Vaagenes
 * @author gjoranv
 * @author ollivir
 */
public class StandaloneSubscriberFactory implements SubscriberFactory {

    private final VespaModel root;

    public StandaloneSubscriberFactory(VespaModel root) {
        this.root = root;
    }

    private class StandaloneSubscriber implements Subscriber {

        private final Set<ConfigKey<ConfigInstance>> configKeys;
        private long generation = -1L;
        private volatile boolean shutdown = false;

        StandaloneSubscriber(Set<ConfigKey<ConfigInstance>> configKeys) {
            this.configKeys = configKeys;
        }

        @Override
        public boolean configChanged() {
            return generation == 0;
        }

        @Override public void close() { shutdown = true; }

        @Override
        public Map<ConfigKey<ConfigInstance>, ConfigInstance> config() {
            Map<ConfigKey<ConfigInstance>, ConfigInstance> ret = new HashMap<>();
            for (ConfigKey<ConfigInstance> key : configKeys) {
                ConfigInstance.Builder builder = root.getConfig(newBuilderInstance(key), key.getConfigId());
                if (builder == null)
                    throw new RuntimeException("Invalid config id " + key.getConfigId());
                ret.put(key, newConfigInstance(builder));
            }
            return ret;
        }

        @Override
        public long waitNextGeneration(boolean isInitializing) {
            generation++;

            if (generation != 0) {
                try {
                    while (!shutdown && !Thread.interrupted()) {
                        Thread.sleep(100);
                    }
                    if (shutdown) throw new SubscriberClosedException();
                } catch (InterruptedException e) {
                    throw new ConfigInterruptedException(e);
                }
            }

            return generation;
        }

        // if waitNextGeneration has not yet been called, -1 should be returned
        @Override
        public long generation() {
            return generation;
        }
    }

    @Override
    @SuppressWarnings("unchecked")
    public Subscriber getSubscriber(Set<? extends ConfigKey<?>> configKeys, String name) {
        return new StandaloneSubscriber((Set<ConfigKey<ConfigInstance>>) configKeys);
    }

    public void reloadActiveSubscribers(long generation) {
        throw new RuntimeException("unsupported");
    }

    private static ConfigInstance.Builder newBuilderInstance(ConfigKey<ConfigInstance> key) {
        try {
            return builderClass(key).getDeclaredConstructor().newInstance();
        } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
            throw new RuntimeException("ConfigInstance builder cannot be instantiated", e);
        }
    }

    @SuppressWarnings("unchecked")
    private static Class<ConfigInstance.Builder> builderClass(ConfigKey<ConfigInstance> key) {
        Class<?> configClass = key.getConfigClass();
        if (configClass != null) {
            Class<?>[] nestedClasses = configClass.getClasses();
            for (Class<?> clazz : nestedClasses) {
                if (clazz.getName().equals(key.getConfigClass().getName() + "$Builder")) {
                    return (Class<ConfigInstance.Builder>) clazz;
                }
            }
        }
        throw new RuntimeException("Builder class for " + (configClass == null ? null : configClass.getName()) + " could not be located");
    }

    private static ConfigInstance newConfigInstance(ConfigBuilder builder) {
        try {
            return configClass(builder).getConstructor(builder.getClass()).newInstance(builder);
        } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
            throw new RuntimeException("ConfigInstance cannot be instantiated", e);
        }
    }

    @SuppressWarnings("unchecked")
    private static Class<ConfigInstance> configClass(ConfigBuilder builder) {
        return (Class<ConfigInstance>) builder.getClass().getEnclosingClass();
    }
    @Override public void close() {}
}