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.collections.map.ListOrderedMap;
20
import org.apache.log4j.Logger;
21
import org.eclipse.jetty.util.component.LifeCycle;
22

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

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

    
39
    private static final Logger logger = Logger.getLogger(InstanceManager.class);
40

    
41
    private ListOrderedMap instances = new ListOrderedMap();
42

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

    
45
    private final boolean austostart = true;
46

    
47
    boolean serverIsRunning = false;
48

    
49
    private File datasourcesFile;
50

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

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

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

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

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

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

    
93
    /**
94
     * Starts the instance
95
     *
96
     * Rebinds 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
        if (instance.getWebAppContext() != null) {
104
            // instance.unbindJndiDataSource();
105
            // instance.bindJndiDataSource();
106
            if (!instance.bindJndiDataSource()) {
107
                // a problem with the datasource occurred skip this webapp
108
                // cdmWebappContext = null;
109
                logger.error("a problem with the datasource occurred -> aboarding startup of /" + instance.getName());
110
                instance.setStatus(Status.error);
111
                // return cdmWebappContext;
112
            }
113
            if (logger.isDebugEnabled()) {
114
                logger.debug("starting " + instance.getName());
115
            }
116
            // instance.getWebAppContext().start();
117
            queue.add(instance);
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();
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 currentInstances = instances;
167
        ListOrderedMap 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 = (CdmInstance) 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 (Object keyOfExisting : currentInstances.keyList()) {
206
            if (!updatedInstances.containsKey(keyOfExisting)) {
207
                CdmInstance removedInstance = (CdmInstance) 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

    
227
        instances = updatedInstances;
228

    
229
        verifyMemoryRequirements();
230

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

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

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

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

    
267
    }
268

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

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

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

    
283
    private void verifyMemoryRequirements() {
284

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

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

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

    
294
        if (recommendedMinimumSpace > availableSpace) {
295

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

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

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

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

    
329
}
(4-4/7)