Project

General

Profile

Download (10.9 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
        if(instance.getWebAppContext() != null){
107
            instance.getWebAppContext().stop();
108
        }
109
        instance.getProblems().clear();
110
        // explicitly set status stopped here to clear up prior error states
111
        instance.setStatus(Status.stopped);
112
    }
113

    
114
    /**
115
     * @return the number of existing instances, former instances which have been
116
     * removed are not counted
117
     */
118
    public int numOfConfiguredInstances(){
119
        int cnt=0;
120
        for(CdmInstance instance : getInstances()){
121
            if(instance.getStatus().equals(Status.removed)){
122
                continue;
123
            }
124
            cnt++;
125
        }
126
        return cnt;
127
    }
128

    
129
    /**
130
     * loads and reloads the list of instances.
131
     * After loading the configuration the required memory is checked
132
     * <p>
133
     * reload behavior:
134
     * <ol>
135
     * <li>newly added instances are created but are not started automatically</li>
136
     * <li>removed instances are stopped, configuration and context are removed,
137
     * state is set to Status.removed to indicate removal, removed instances can
138
     * be re-added.</li>
139
     * <li>the order of instances as defined in the config file is always retained
140
     * </ol>
141
     *
142
     */
143
    synchronized public void reLoadInstanceConfigurations() {
144

    
145
        ListOrderedMap currentInstances = instances;
146
        ListOrderedMap updatedInstances = new ListOrderedMap();
147

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

    
151
        for (Configuration config : configList) {
152
            String key = config.getInstanceName();
153
            if(currentInstances.containsKey(key)){
154
                CdmInstance existingInstance = (CdmInstance)currentInstances.get(key);
155
                if(!(existingInstance.getStatus().equals(Status.removed) && existingInstance.getWebAppContext() == null)){
156
                    // re-added instance if not already removed (removed instances will not be re-added if they have been stopped successfully)
157
                    updatedInstances.put(key, existingInstance);
158
                    if ( !(existingInstance).getConfiguration().equals(config)) {
159
                        // instance has changed: stop it, clear error states, set new configuration
160
                        try {
161
                            // TODO change problems into messages + severity (notice, error)
162
                            stop(existingInstance);
163
                            bootloader().removeCdmInstanceContext(existingInstance);
164
                            existingInstance.setConfiguration(config);
165
                            existingInstance.getProblems().add("Reloaded with modified configuration");
166
                        } catch (Exception e) {
167
                            existingInstance.getProblems().add("Error while stopping modified instance: " + e.getMessage());
168
                            logger.error(e, e);
169
                        }
170
                    }
171
                }
172
            } else {
173
                // create and add a new instance
174
                updatedInstances.put(key, new CdmInstance(config));
175
            }
176
        }
177

    
178
        // find removed instances
179
        for(Object keyOfExisting : currentInstances.keyList()){
180
            if(!updatedInstances.containsKey(keyOfExisting)){
181
                CdmInstance removedInstance = (CdmInstance)currentInstances.get(keyOfExisting);
182

    
183
                if(removedInstance.getStatus().equals(Status.removed)){
184
                    // instance already is removed, forget it now
185
                    continue;
186
                }
187

    
188
                // remove the instance but remember it until next config reload
189
                updatedInstances.put(keyOfExisting, removedInstance);
190
                removedInstance.setStatus(Status.removed);
191
                removedInstance.getProblems().add("Removed from configuration and thus stopped");
192
                try {
193
                    bootloader().removeCdmInstanceContext(removedInstance);
194
                } catch (Exception e) {
195
                    logger.error(e.getMessage(), e);
196
                }
197

    
198
            }
199
        }
200

    
201
        instances = updatedInstances;
202

    
203
        verifyMemoryRequirements();
204

    
205
        if(serverIsRunning) {
206
            addNewInstancesToServer(false);
207
        }
208
    }
209

    
210
    private void addNewInstancesToServer(boolean austostart) {
211
        for (CdmInstance instance : getInstances()) {
212
            if (instance.getStatus().equals(Status.uninitialized)) {
213
                try {
214
                    if(bootloader().addCdmInstanceContext(instance) != null && austostart) {
215
                        try {
216
                            start(instance);
217
                        } catch (Exception e) {
218
                            logger.error("Could not start " + instance.getWebAppContext().getContextPath());
219
                            instance.getProblems().add(e.getMessage());
220
                            instance.setStatus(Status.error);
221
                        }
222
                    }
223
                } catch (IOException e) {
224
                    logger.error(e, e); // TODO better throw?
225
                }
226
            }
227
        }
228
    }
229

    
230
    @Override
231
    public void lifeCycleFailure(LifeCycle event, Throwable cause) {
232
        logger.error("Jetty LifeCycleFailure", cause);
233
    }
234

    
235
    @Override
236
    public void lifeCycleStarted(LifeCycle event) {
237
        serverIsRunning = true;
238
        logger.info("cdmserver has started, now adding CDM server contexts");
239
        addNewInstancesToServer(austostart);
240

    
241
    }
242

    
243

    
244
    @Override
245
    public void lifeCycleStarting(LifeCycle event) {
246
    }
247

    
248
    @Override
249
    public void lifeCycleStopped(LifeCycle event) {
250
        serverIsRunning = false;
251
    }
252

    
253
    @Override
254
    public void lifeCycleStopping(LifeCycle event) {
255
        serverIsRunning = false;
256
    }
257

    
258

    
259

    
260
    /**
261
    *
262
    */
263
   private void verifyMemoryRequirements() {
264

    
265
       verifyMemoryRequirement("PermGenSpace", PERM_GEN_SPACE_CDMSERVER, PERM_GEN_SPACE_PER_INSTANCE, JvmManager.getPermGenSpaceUsage().getMax());
266
       verifyMemoryRequirement("HeapSpace", HEAP_CDMSERVER, HEAP_PER_INSTANCE, JvmManager.getHeapMemoryUsage().getMax());
267

    
268
   }
269
    private void verifyMemoryRequirement(String memoryName, long requiredSpaceServer, long requiredSpacePerInstance, long availableSpace) {
270

    
271

    
272
        long recommendedMinimumSpace = recommendedMinimumSpace(requiredSpaceServer, requiredSpacePerInstance, null);
273

    
274
        if(recommendedMinimumSpace > availableSpace){
275

    
276
            String message = memoryName + " ("
277
                + (availableSpace / MB)
278
                + "MB) insufficient for "
279
                + numOfConfiguredInstances()
280
                + " instances. Increase " + memoryName + " to "
281
                + (recommendedMinimumSpace / MB)
282
                + "MB";
283
                ;
284
            logger.error(message + " => disabling some instances!!!");
285

    
286
            // disabling some instances
287
            int i=0;
288
            for(CdmInstance instance : getInstances()){
289
                i++;
290
                if(recommendedMinimumSpace(requiredSpaceServer, requiredSpacePerInstance, i)  > availableSpace){
291
                    instance.setStatus(Status.disabled);
292
                    instance.getProblems().add("Disabled due to: " + message);
293
                }
294
            }
295
        }
296
    }
297

    
298
    /**
299
     * @param requiredServerSpace
300
     * @param requiredSpacePerIntance
301
     * @param numOfInstances may be null, the total number of instances found in
302
     *  the current configuration is used in this case.
303
     * @return
304
     */
305
    public long recommendedMinimumSpace(long requiredServerSpace, long requiredSpacePerIntance, Integer numOfInstances) {
306
        if(numOfInstances == null){
307
            numOfInstances = numOfConfiguredInstances();
308
        }
309
        return (numOfInstances * requiredSpacePerIntance) + requiredServerSpace;
310
    }
311

    
312

    
313

    
314
}
(4-4/6)