Project

General

Profile

Download (11.6 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
    boolean serverIsRunning = false;
44

    
45
    private File datasourcesFile;
46

    
47
    /**
48
     * @return the datasourcesFile
49
     */
50
    public File getDatasourcesFile() {
51
        return datasourcesFile;
52
    }
53

    
54
    /**
55
     * @param datasourcesFile the datasourcesFile to set
56
     */
57
    public void setDatasourcesFile(File datasourcesFile) {
58
        this.datasourcesFile = datasourcesFile;
59
    }
60

    
61
    private ListOrderedMap instances = new ListOrderedMap();
62

    
63
    private final boolean austostart = true;
64

    
65
    public InstanceManager(File configurationFile) {
66
        this.datasourcesFile = configurationFile;
67
    }
68

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

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

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

    
91
    /**
92
     * Starts the given instance. Rebinds the JndiDataSource prior starting.
93
     *
94
     * @param instance
95
     * @throws Exception
96
     */
97
    public void start(CdmInstance instance) throws Exception{
98
        if(instance.getWebAppContext() != null){
99
            instance.unbindJndiDataSource();
100
            instance.bindJndiDataSource();
101
            instance.getWebAppContext().start();
102
        }
103
    }
104

    
105
    public void stop(CdmInstance instance) throws Exception{
106
        // TODO do we need to bootloader().removeCdmInstanceContext(existingInstance); always?
107
        //    see reLoadInstanceConfigurations()
108
        if(instance.getWebAppContext() != null){
109
            instance.getWebAppContext().stop();
110
        }
111
        instance.unbindJndiDataSource();
112
        instance.getProblems().clear();
113
        // explicitly set status stopped here to clear up prior error states
114
        instance.setStatus(Status.stopped);
115
    }
116

    
117
    /**
118
     * Sets the {@link SharedAttributes.ATTRIBUTE_FORCE_SCHEMA_UPDATE} attribute
119
     * to the application context and starts the instance
120
     *
121
     * @param instance
122
     * @throws Exception
123
     */
124
    public void updateToCurrentVersion(CdmInstance instance) throws Exception{
125
        if(instance.getWebAppContext() != null){
126
            instance.getWebAppContext().getAttributes().setAttribute(SharedAttributes.ATTRIBUTE_FORCE_SCHEMA_UPDATE, true);
127
        }
128
        start(instance);
129
    }
130

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

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

    
162
        ListOrderedMap currentInstances = instances;
163
        ListOrderedMap updatedInstances = new ListOrderedMap();
164

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

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

    
195
        // find removed instances
196
        for(Object keyOfExisting : currentInstances.keyList()){
197
            if(!updatedInstances.containsKey(keyOfExisting)){
198
                CdmInstance removedInstance = (CdmInstance)currentInstances.get(keyOfExisting);
199

    
200
                if(removedInstance.getStatus().equals(Status.removed)){
201
                    // instance already is removed, forget it now
202
                    continue;
203
                }
204

    
205
                // remove the instance but remember it until next config reload
206
                updatedInstances.put(keyOfExisting, removedInstance);
207
                removedInstance.setStatus(Status.removed);
208
                removedInstance.getProblems().add("Removed from configuration and thus stopped");
209
                try {
210
                    bootloader().removeCdmInstanceContext(removedInstance);
211
                } catch (Exception e) {
212
                    logger.error(e.getMessage(), e);
213
                }
214

    
215
            }
216
        }
217

    
218
        instances = updatedInstances;
219

    
220
        verifyMemoryRequirements();
221

    
222
        if(serverIsRunning) {
223
            addNewInstancesToServer(false);
224
        }
225
    }
226

    
227
    private void addNewInstancesToServer(boolean austostart) {
228
        for (CdmInstance instance : getInstances()) {
229
            if (instance.getStatus().equals(Status.uninitialized)) {
230
                try {
231
                    if(bootloader().addCdmInstanceContext(instance) != null && austostart) {
232
                        try {
233
                            start(instance);
234
                        } catch (Exception e) {
235
                            logger.error("Could not start " + instance.getWebAppContext().getContextPath());
236
                            instance.getProblems().add(e.getMessage());
237
                            instance.setStatus(Status.error);
238
                        }
239
                    }
240
                } catch (IOException e) {
241
                    logger.error(e, e); // TODO better throw?
242
                }
243
            }
244
        }
245
    }
246

    
247
    @Override
248
    public void lifeCycleFailure(LifeCycle event, Throwable cause) {
249
        logger.error("Jetty LifeCycleFailure", cause);
250
    }
251

    
252
    @Override
253
    public void lifeCycleStarted(LifeCycle event) {
254
        serverIsRunning = true;
255
        logger.info("cdmserver has started, now adding CDM server contexts");
256
        addNewInstancesToServer(austostart);
257

    
258
    }
259

    
260

    
261
    @Override
262
    public void lifeCycleStarting(LifeCycle event) {
263
    }
264

    
265
    @Override
266
    public void lifeCycleStopped(LifeCycle event) {
267
        serverIsRunning = false;
268
    }
269

    
270
    @Override
271
    public void lifeCycleStopping(LifeCycle event) {
272
        serverIsRunning = false;
273
    }
274

    
275

    
276

    
277
    /**
278
    *
279
    */
280
   private void verifyMemoryRequirements() {
281

    
282
       verifyMemoryRequirement("PermGenSpace", PERM_GEN_SPACE_CDMSERVER, PERM_GEN_SPACE_PER_INSTANCE, JvmManager.getPermGenSpaceUsage().getMax());
283
       verifyMemoryRequirement("HeapSpace", HEAP_CDMSERVER, HEAP_PER_INSTANCE, JvmManager.getHeapMemoryUsage().getMax());
284

    
285
   }
286
    private void verifyMemoryRequirement(String memoryName, long requiredSpaceServer, long requiredSpacePerInstance, long availableSpace) {
287

    
288

    
289
        long recommendedMinimumSpace = recommendedMinimumSpace(requiredSpaceServer, requiredSpacePerInstance, null);
290

    
291
        if(recommendedMinimumSpace > availableSpace){
292

    
293
            String message = memoryName + " ("
294
                + (availableSpace / MB)
295
                + "MB) insufficient for "
296
                + numOfConfiguredInstances()
297
                + " instances. Increase " + memoryName + " to "
298
                + (recommendedMinimumSpace / MB)
299
                + "MB";
300
                ;
301
            logger.error(message + " => disabling some instances!!!");
302

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

    
315
    /**
316
     * @param requiredServerSpace
317
     * @param requiredSpacePerIntance
318
     * @param numOfInstances may be null, the total number of instances found in
319
     *  the 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

    
330

    
331
}
(4-4/6)