Project

General

Profile

Download (12.2 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
            if(!instance.bindJndiDataSource()){
102
                // a problem with the datasource occurred skip this webapp
103
//                cdmWebappContext = null;
104
                logger.error("a problem with the datasource occurred -> aboarding atartup of /" + instance.getConfiguration().getInstanceName());
105
                instance.setStatus(Status.error);
106
//                return cdmWebappContext;
107
            }
108
            if(logger.isDebugEnabled()) {
109
                logger.debug("starting " + instance.getConfiguration().getInstanceName());
110
            }
111
            instance.getWebAppContext().start();
112
        }
113
    }
114

    
115
    public void stop(CdmInstance instance) throws Exception{
116
        // TODO do we need to bootloader().removeCdmInstanceContext(existingInstance); always?
117
        //    see reLoadInstanceConfigurations()
118
        if(instance.getWebAppContext() != null){
119
            instance.getWebAppContext().stop();
120
        }
121
        instance.unbindJndiDataSource();
122
        instance.getProblems().clear();
123
        // explicitly set status stopped here to clear up prior error states
124
        instance.setStatus(Status.stopped);
125
    }
126

    
127
    /**
128
     * Sets the {@link SharedAttributes.ATTRIBUTE_FORCE_SCHEMA_UPDATE} attribute
129
     * to the application context and starts the instance
130
     *
131
     * @param instance
132
     * @throws Exception
133
     */
134
    public void updateToCurrentVersion(CdmInstance instance) throws Exception{
135
        if(instance.getWebAppContext() != null){
136
            instance.getWebAppContext().getAttributes().setAttribute(SharedAttributes.ATTRIBUTE_FORCE_SCHEMA_UPDATE, true);
137
        }
138
        start(instance);
139
    }
140

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

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

    
172
        ListOrderedMap currentInstances = instances;
173
        ListOrderedMap updatedInstances = new ListOrderedMap();
174

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

    
178
        for (Configuration config : configList) {
179
            String key = config.getInstanceName();
180
            if(currentInstances.containsKey(key)){
181
                CdmInstance existingInstance = (CdmInstance)currentInstances.get(key);
182
                if(!(existingInstance.getStatus().equals(Status.removed) && existingInstance.getWebAppContext() == null)){
183
                    // re-added instance if not already removed (removed instances will not be re-added if they have been stopped successfully)
184
                    updatedInstances.put(key, existingInstance);
185
                    if ( !(existingInstance).getConfiguration().equals(config)) {
186
                        // instance has changed: stop it, clear error states, set new configuration
187
                        try {
188
                            // TODO change problems into messages + severity (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("Error while stopping modified instance: " + e.getMessage());
195
                            logger.error(e, e);
196
                        }
197
                    }
198
                }
199
            } else {
200
                // create and add a new instance
201
                updatedInstances.put(key, new CdmInstance(config));
202
            }
203
        }
204

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

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

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

    
225
            }
226
        }
227

    
228
        instances = updatedInstances;
229

    
230
        verifyMemoryRequirements();
231

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

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

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

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

    
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
    *
289
    */
290
   private void verifyMemoryRequirements() {
291

    
292
       verifyMemoryRequirement("PermGenSpace", PERM_GEN_SPACE_CDMSERVER, PERM_GEN_SPACE_PER_INSTANCE, JvmManager.getPermGenSpaceUsage().getMax());
293
       verifyMemoryRequirement("HeapSpace", HEAP_CDMSERVER, HEAP_PER_INSTANCE, JvmManager.getHeapMemoryUsage().getMax());
294

    
295
   }
296
    private void verifyMemoryRequirement(String memoryName, long requiredSpaceServer, long requiredSpacePerInstance, long availableSpace) {
297

    
298

    
299
        long recommendedMinimumSpace = recommendedMinimumSpace(requiredSpaceServer, requiredSpacePerInstance, null);
300

    
301
        if(recommendedMinimumSpace > availableSpace){
302

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

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

    
325
    /**
326
     * @param requiredServerSpace
327
     * @param requiredSpacePerIntance
328
     * @param numOfInstances may be null, the total number of instances found in
329
     *  the 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

    
340

    
341
}
(4-4/6)