summaryrefslogtreecommitdiffstats
path: root/standalone-container/src/main/java/com/yahoo/container/standalone/StandaloneSubscriberFactory.java
blob: e9d8dbd3642668e96a824f9e8527231640231c3a (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
// Copyright 2018 Yahoo Holdings. 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.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;

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

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

        @Override
        public void close() {
        }

        @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 (!Thread.interrupted()) {
                        Thread.sleep(10000);
                    }
                } 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) {
        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();
    }
}