Project

General

Profile

Download (11.7 KB) Statistics
| Branch: | Tag: | Revision:
1
/**
2
 * Copyright (C) 2013 EDIT
3
 * European Distributed Institute of Taxonomy
4
 * http://www.e-taxonomy.eu
5
 *
6
 * The contents of this file are subject to the Mozilla Public License Version 1.1
7
 * See LICENSE.TXT at the top of this package for the full license terms.
8
 */
9
package eu.etaxonomy.cdm.server.instance;
10

    
11
import static eu.etaxonomy.cdm.server.AssumedMemoryRequirements.HEAP_CDMSERVER;
12
import static eu.etaxonomy.cdm.server.AssumedMemoryRequirements.HEAP_PER_INSTANCE;
13
import static eu.etaxonomy.cdm.server.AssumedMemoryRequirements.MB;
14

    
15
import java.io.File;
16
import java.io.IOException;
17
import java.util.List;
18

    
19
import org.apache.commons.collections4.map.ListOrderedMap;
20
import org.apache.logging.log4j.LogManager;
21
import org.apache.logging.log4j.Logger;
22
import org.eclipse.jetty.util.component.LifeCycle;
23

    
24
import eu.etaxonomy.cdm.server.Bootloader;
25
import eu.etaxonomy.cdm.server.JvmManager;
26

    
27
/**
28
 * Manager to load / reload list of instances the instance manager holds the
29
 * list of {@link CdmInstance}, the list can be updated, the instances are
30
 * identified by the bean id map<String,CdmInstance>
31
 *
32
 * The instance list is initially empty, all instances are usually started after
33
 * loading the list, see {@#loadDataSources()}
34
 *
35
 * @author a.kohlbecker
36
 * @date May 10, 2013
37
 */
38
public class InstanceManager implements LifeCycle.Listener {
39

    
40
    private static final Logger logger = LogManager.getLogger();
41

    
42
    private ListOrderedMap<String,CdmInstance> instances = new ListOrderedMap<>();
43

    
44
    private final StartupQueue queue = new StartupQueue();
45

    
46
    private final boolean austostart = true;
47

    
48
    boolean serverIsRunning = false;
49

    
50
    private File datasourcesFile;
51

    
52
    /**
53
     * @return the datasourcesFile
54
     */
55
    public File getDatasourcesFile() {
56
        return datasourcesFile;
57
    }
58

    
59
    /**
60
     * @param datasourcesFile
61
     *            the datasourcesFile to set
62
     */
63
    public void setDatasourcesFile(File datasourcesFile) {
64
        this.datasourcesFile = datasourcesFile;
65
    }
66

    
67
    public InstanceManager(File configurationFile) {
68
        this.datasourcesFile = configurationFile;
69
        queue.setParallelStartUps(JvmManager.availableProcessors());
70
    }
71

    
72
    /**
73
     * @return the {@link Bootloader} singelton instance
74
     */
75
    private Bootloader bootloader() {
76
        return Bootloader.getBootloader();
77
    }
78

    
79
    /**
80
     * this list of instances may contain removed instances.
81
     * {@link #numOfConfiguredInstances()}
82
     *
83
     * @return the instances
84
     */
85
    public List<CdmInstance> getInstances() {
86
        return instances.valueList();
87
    }
88

    
89
    public CdmInstance getInstance(String instanceName) {
90
        return instances.get(instanceName);
91
    }
92

    
93
    /**
94
     * Starts the instance
95
     *
96
     * Re-binds the JndiDataSource and starts the given instance. The method
97
     * returns once the instance is fully started up.
98
     *
99
     * @param instance
100
     * @throws Exception
101
     */
102
    public void start(CdmInstance instance) {
103
        instance.clearProblems();
104
        if (instance.getWebAppContext() != null) {
105
            // instance.unbindJndiDataSource();
106
            // instance.bindJndiDataSource();
107
            if (!instance.bindJndiDataSource()) {
108
                // a problem with the datasource occurred skip this webapp
109
                logger.error("a problem with the datasource occurred -> aborting startup of /" + instance.getName());
110
                instance.setStatus(Status.error);
111
            } else {
112
                // ready for startup add to queue
113
                if (logger.isDebugEnabled()) {
114
                    logger.debug("starting " + instance.getName());
115
                }
116
                queue.add(instance);
117
            }
118
        }
119
    }
120

    
121
    public void stop(CdmInstance instance) throws Exception {
122
        // TODO do we need to
123
        // bootloader().removeCdmInstanceContext(existingInstance); always?
124
        // see reLoadInstanceConfigurations()
125
        if (instance.getWebAppContext() != null) {
126
            instance.getWebAppContext().stop();
127
        }
128
        instance.unbindJndiDataSource();
129
        instance.getProblems().clear();  //resets the UI
130
        // explicitly set status stopped here to clear up prior error states
131
        instance.setStatus(Status.stopped);
132
    }
133

    
134
    /**
135
     * @return the number of existing instances, former instances which have
136
     *         been removed are not counted
137
     */
138
    public int numOfConfiguredInstances() {
139
        int cnt = 0;
140
        for (CdmInstance instance : getInstances()) {
141
            if (instance.getStatus().equals(Status.removed)) {
142
                continue;
143
            }
144
            cnt++;
145
        }
146
        return cnt;
147
    }
148

    
149
    /**
150
     * loads and reloads the list of instances. After loading the configuration
151
     * the required memory is checked
152
     * <p>
153
     * reload behavior:
154
     * <ol>
155
     * <li>newly added instances are created but are not started automatically</li>
156
     * <li>removed instances are stopped, configuration and context are removed,
157
     * state is set to Status.removed to indicate removal, removed instances can
158
     * be re-added.</li>
159
     * <li>the order of instances as defined in the config file is always
160
     * retained
161
     * </ol>
162
     *
163
     */
164
    synchronized public void reLoadInstanceConfigurations() {
165

    
166
        ListOrderedMap<String,CdmInstance> currentInstances = instances;
167
        ListOrderedMap<String,CdmInstance> updatedInstances = new ListOrderedMap<>();
168

    
169
        List<Configuration> configList = DataSourcePropertyParser.parseDataSourceConfigs(datasourcesFile);
170
        logger.info("cdm server instance names loaded: " + configList.toString());
171

    
172
        for (Configuration config : configList) {
173
            String key = config.getInstanceName();
174
            if (currentInstances.containsKey(key)) {
175
                CdmInstance existingInstance = currentInstances.get(key);
176
                if (!(existingInstance.getStatus().equals(Status.removed) && existingInstance.getWebAppContext() == null)) {
177
                    // re-added instance if not already removed (removed
178
                    // instances will not be re-added if they have been stopped
179
                    // successfully)
180
                    updatedInstances.put(key, existingInstance);
181
                    if (!(existingInstance).getConfiguration().equals(config)) {
182
                        // instance has changed: stop it, clear error states,
183
                        // set new configuration
184
                        try {
185
                            // TODO change problems into messages + severity
186
                            // (notice, error)
187
                            stop(existingInstance);
188
                            bootloader().removeCdmInstanceContext(existingInstance);
189
                            existingInstance.setConfiguration(config);
190
                            existingInstance.getProblems().add("Reloaded with modified configuration");
191
                        } catch (Exception e) {
192
                            existingInstance.getProblems().add(
193
                                    "Error while stopping modified instance: " + e.getMessage());
194
                            logger.error(e, e);
195
                        }
196
                    }
197
                }
198
            } else {
199
                // create and add a new instance
200
                updatedInstances.put(key, new CdmInstance(config));
201
            }
202
        }
203

    
204
        // find removed instances
205
        for (String keyOfExisting : currentInstances.keyList()) {
206
            if (!updatedInstances.containsKey(keyOfExisting)) {
207
                CdmInstance removedInstance = currentInstances.get(keyOfExisting);
208

    
209
                if (removedInstance.getStatus().equals(Status.removed)) {
210
                    // instance already is removed, forget it now
211
                    continue;
212
                }
213

    
214
                // remove the instance but remember it until next config reload
215
                updatedInstances.put(keyOfExisting, removedInstance);
216
                removedInstance.setStatus(Status.removed);
217
                removedInstance.getProblems().add("Removed from configuration and thus stopped");
218
                try {
219
                    bootloader().removeCdmInstanceContext(removedInstance);
220
                } catch (Exception e) {
221
                    logger.error(e.getMessage(), e);
222
                }
223
            }
224
        }
225

    
226
        instances = updatedInstances;
227

    
228
        verifyMemoryRequirements();
229

    
230
        if (serverIsRunning) {
231
            addNewInstancesToServer(false);
232
        }
233
    }
234

    
235
    private void addNewInstancesToServer(boolean austostart) {
236
        for (CdmInstance instance : getInstances()) {
237
            if (instance.getStatus().equals(Status.uninitialized)) {
238
                try {
239
                    if (bootloader().addCdmInstanceContext(instance) != null && austostart) {
240
                        try {
241
                            start(instance);
242
                        } catch (Exception e) {
243
                            logger.error("Could not start " + instance.getWebAppContext().getContextPath(), e);
244
                            instance.getProblems().add(e.getMessage());
245
                            instance.setStatus(Status.error);
246
                        }
247
                    }
248
                } catch (IOException e) {
249
                    logger.error(e, e); // TODO better throw?
250
                }
251
            }
252
        }
253
    }
254

    
255
    @Override
256
    public void lifeCycleFailure(LifeCycle event, Throwable cause) {
257
        logger.error("Jetty LifeCycleFailure", cause);
258
    }
259

    
260
    @Override
261
    public void lifeCycleStarted(LifeCycle event) {
262
        serverIsRunning = true;
263
        logger.info("cdmserver has started, now adding CDM server contexts");
264
        addNewInstancesToServer(austostart);
265

    
266
    }
267

    
268
    @Override
269
    public void lifeCycleStarting(LifeCycle event) {
270
    }
271

    
272
    @Override
273
    public void lifeCycleStopped(LifeCycle event) {
274
        serverIsRunning = false;
275
    }
276

    
277
    @Override
278
    public void lifeCycleStopping(LifeCycle event) {
279
        serverIsRunning = false;
280
    }
281

    
282
    private void verifyMemoryRequirements() {
283

    
284
        verifyMemoryRequirement("HeapSpace", HEAP_CDMSERVER, HEAP_PER_INSTANCE, JvmManager.getHeapMemoryUsage()
285
                .getMax());
286
    }
287

    
288
    private void verifyMemoryRequirement(String memoryName, long requiredSpaceServer, long requiredSpacePerInstance,
289
            long availableSpace) {
290

    
291
        long recommendedMinimumSpace = recommendedMinimumSpace(requiredSpaceServer, requiredSpacePerInstance, null);
292

    
293
        if (recommendedMinimumSpace > availableSpace) {
294

    
295
            String message = memoryName + " (" + (availableSpace / MB) + "MB) insufficient for "
296
                    + numOfConfiguredInstances() + " instances. Increase " + memoryName + " to "
297
                    + (recommendedMinimumSpace / MB) + "MB";
298

    
299
            logger.error(message + " => disabling some instances!!!");
300

    
301
            // disabling some instances
302
            int i = 0;
303
            for (CdmInstance instance : getInstances()) {
304
                i++;
305
                if (recommendedMinimumSpace(requiredSpaceServer, requiredSpacePerInstance, i) > availableSpace) {
306
                    instance.setStatus(Status.disabled);
307
                    instance.getProblems().add("Disabled due to: " + message);
308
                }
309
            }
310
        }
311
    }
312

    
313
    /**
314
     * @param requiredServerSpace
315
     * @param requiredSpacePerIntance
316
     * @param numOfInstances
317
     *            may be null, the total number of instances found in the
318
     *            current configuration is used in this case.
319
     * @return
320
     */
321
    public long recommendedMinimumSpace(long requiredServerSpace, long requiredSpacePerIntance, Integer numOfInstances) {
322
        if (numOfInstances == null) {
323
            numOfInstances = numOfConfiguredInstances();
324
        }
325
        return (numOfInstances * requiredSpacePerIntance) + requiredServerSpace;
326
    }
327

    
328
}
(4-4/7)