Project

General

Profile

Download (12.1 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
import static eu.etaxonomy.cdm.server.AssumedMemoryRequirements.PERM_GEN_SPACE_CDMSERVER;
15
import static eu.etaxonomy.cdm.server.AssumedMemoryRequirements.PERM_GEN_SPACE_PER_INSTANCE;
16

    
17
import java.io.File;
18
import java.io.IOException;
19
import java.util.List;
20

    
21
import org.apache.commons.collections.map.ListOrderedMap;
22
import org.apache.log4j.Logger;
23
import org.eclipse.jetty.util.component.LifeCycle;
24

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

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

    
41
    private static final Logger logger = Logger.getLogger(InstanceManager.class);
42

    
43
    private ListOrderedMap instances = new ListOrderedMap();
44

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

    
47
    private final boolean austostart = true;
48

    
49
    boolean serverIsRunning = false;
50

    
51
    private File datasourcesFile;
52

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

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

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

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

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

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

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

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

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

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

    
168
        ListOrderedMap currentInstances = instances;
169
        ListOrderedMap updatedInstances = new ListOrderedMap();
170

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

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

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

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

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

    
226
            }
227
        }
228

    
229
        instances = updatedInstances;
230

    
231
        verifyMemoryRequirements();
232

    
233
        if (serverIsRunning) {
234
            addNewInstancesToServer(false);
235
        }
236
    }
237

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

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

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

    
269
    }
270

    
271
    @Override
272
    public void lifeCycleStarting(LifeCycle event) {
273
    }
274

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

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

    
285
    /**
286
    *
287
    */
288
    private void verifyMemoryRequirements() {
289

    
290
        verifyMemoryRequirement("HeapSpace", HEAP_CDMSERVER, HEAP_PER_INSTANCE, JvmManager.getHeapMemoryUsage()
291
                .getMax());
292
        if (JvmManager.getJvmVersion() == 7) {
293
            verifyMemoryRequirement("PermGenSpace", PERM_GEN_SPACE_CDMSERVER, PERM_GEN_SPACE_PER_INSTANCE, JvmManager
294
                    .getPermGenSpaceUsage().getMax());
295
        }
296

    
297
    }
298

    
299
    private void verifyMemoryRequirement(String memoryName, long requiredSpaceServer, long requiredSpacePerInstance,
300
            long availableSpace) {
301

    
302
        long recommendedMinimumSpace = recommendedMinimumSpace(requiredSpaceServer, requiredSpacePerInstance, null);
303

    
304
        if (recommendedMinimumSpace > availableSpace) {
305

    
306
            String message = memoryName + " (" + (availableSpace / MB) + "MB) insufficient for "
307
                    + numOfConfiguredInstances() + " instances. Increase " + memoryName + " to "
308
                    + (recommendedMinimumSpace / MB) + "MB";
309
            ;
310
            logger.error(message + " => disabling some instances!!!");
311

    
312
            // disabling some instances
313
            int i = 0;
314
            for (CdmInstance instance : getInstances()) {
315
                i++;
316
                if (recommendedMinimumSpace(requiredSpaceServer, requiredSpacePerInstance, i) > availableSpace) {
317
                    instance.setStatus(Status.disabled);
318
                    instance.getProblems().add("Disabled due to: " + message);
319
                }
320
            }
321
        }
322
    }
323

    
324
    /**
325
     * @param requiredServerSpace
326
     * @param requiredSpacePerIntance
327
     * @param numOfInstances
328
     *            may be null, the total number of instances found in the
329
     *            current configuration is used in this case.
330
     * @return
331
     */
332
    public long recommendedMinimumSpace(long requiredServerSpace, long requiredSpacePerIntance, Integer numOfInstances) {
333
        if (numOfInstances == null) {
334
            numOfInstances = numOfConfiguredInstances();
335
        }
336
        return (numOfInstances * requiredSpacePerIntance) + requiredServerSpace;
337
    }
338

    
339
}
(4-4/7)