Project

General

Profile

1
/**
2
 *  '$RCSfile$'
3
 *    Purpose: Implements a service for managing a Hazelcast cluster member
4
 *  Copyright: 2011 Regents of the University of California and the
5
 *             National Center for Ecological Analysis and Synthesis
6
 *    Authors: Christopher Jones
7
 * 
8
 *   '$Author: leinfelder $'
9
 *     '$Date: 2011-09-19 10:18:14 -0700 (Mon, 19 Sep 2011) $'
10
 * '$Revision: 6455 $'
11
 *
12
 * This program is free software; you can redistribute it and/or modify
13
 * it under the terms of the GNU General Public License as published by
14
 * the Free Software Foundation; either version 2 of the License, or
15
 * (at your option) any later version.
16
 *
17
 * This program is distributed in the hope that it will be useful,
18
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20
 * GNU General Public License for more details.
21
 *
22
 * You should have received a copy of the GNU General Public License
23
 * along with this program; if not, write to the Free Software
24
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
25
 */
26

    
27
package edu.ucsb.nceas.metacat.dataone.hazelcast;
28

    
29
import org.apache.log4j.Logger;
30
import org.dataone.service.types.v1.Identifier;
31
import org.dataone.service.types.v1.Node;
32
import org.dataone.service.types.v1.NodeReference;
33
import org.dataone.service.types.v1.SystemMetadata;
34

    
35
import com.hazelcast.client.HazelcastClient;
36
import com.hazelcast.config.Config;
37
import com.hazelcast.config.FileSystemXmlConfig;
38
import com.hazelcast.core.EntryEvent;
39
import com.hazelcast.core.EntryListener;
40
import com.hazelcast.core.Hazelcast;
41
import com.hazelcast.core.IMap;
42
import com.hazelcast.core.InstanceEvent;
43
import com.hazelcast.core.InstanceListener;
44
import com.hazelcast.core.Member;
45
import com.hazelcast.partition.Partition;
46
import com.hazelcast.partition.PartitionService;
47

    
48
import edu.ucsb.nceas.metacat.IdentifierManager;
49
import edu.ucsb.nceas.metacat.McdbDocNotFoundException;
50
import edu.ucsb.nceas.metacat.dataone.D1NodeService;
51
import edu.ucsb.nceas.metacat.properties.PropertyService;
52
import edu.ucsb.nceas.metacat.shared.BaseService;
53
import edu.ucsb.nceas.metacat.shared.ServiceException;
54
import edu.ucsb.nceas.utilities.PropertyNotFoundException;
55
/**
56
 * The Hazelcast service enables Metacat as a Hazelcast cluster member
57
 */
58
public class HazelcastService extends BaseService
59
  implements InstanceListener, EntryListener<Identifier, SystemMetadata> {
60
  
61
  /* The instance of the logging class */
62
  private static Logger logMetacat = Logger.getLogger(HazelcastService.class);
63
  
64
  /* The singleton instance of the hazelcast service */
65
  private static HazelcastService hzService = null;
66
  
67
  /* The Hazelcast configuration */
68
  private Config hzConfig;
69
  
70
  /* The instance of the Hazelcast client */
71
  private HazelcastClient hzClient;
72

    
73
  /* The name of the DataONE Hazelcast cluster group */
74
  private String groupName;
75

    
76
  /* The name of the DataONE Hazelcast cluster password */
77
  private String groupPassword;
78
  
79
  /* The name of the DataONE Hazelcast cluster IP addresses */
80
  private String addressList;
81
  
82
  /* The name of the node map */
83
  private String nodeMap;
84

    
85
  /* The name of the system metadata map */
86
  private String systemMetadataMap;
87
  
88
  /* The Hazelcast distributed task id generator namespace */
89
  private String taskIds;
90
  
91
  /* The Hazelcast distributed system metadata map */
92
  private IMap<NodeReference, Node> nodes;
93

    
94
  /* The Hazelcast distributed system metadata map */
95
  private IMap<Identifier, SystemMetadata> systemMetadata;
96
  
97
  /* The Hazelcast distributed pending replication tasks map*/
98
  private IMap<String, CNReplicationTask> pendingReplicationTasks;
99
  
100
  
101
  /*
102
   * Constructor: Creates an instance of the hazelcast service. Since
103
   * this uses a singleton pattern, use getInstance() to gain the instance.
104
   */
105
  private HazelcastService() {
106
    
107
    super();
108
    _serviceName="HazelcastService";
109
    
110
    try {
111
      init();
112
      
113
    } catch (ServiceException se) {
114
      logMetacat.error("There was a problem creating the HazelcastService. " +
115
                       "The error message was: " + se.getMessage());
116
      
117
    }
118
    
119
  }
120
  
121
  /**
122
   *  Get the instance of the HazelcastService that has been instantiated,
123
   *  or instantiate one if it has not been already.
124
   *
125
   * @return hazelcastService - The instance of the hazelcast service
126
   */
127
  public static HazelcastService getInstance(){
128
    
129
    if ( hzService == null ) {
130
      
131
      hzService = new HazelcastService();
132
      
133
    }
134
    return hzService;
135
  }
136
  
137
  /**
138
   * Initializes the Hazelcast service
139
   */
140
  public void init() throws ServiceException {
141
    
142
    logMetacat.debug("HazelcastService.doRefresh() called.");
143
    
144
    try {
145
    	String configFileName = PropertyService.getProperty("dataone.hazelcast.configFilePath");
146
//    	System.setProperty("hazelcast.config", configFileName);
147
		Config config = new FileSystemXmlConfig(configFileName);
148
		Hazelcast.init(config);
149
    } catch (Exception e) {
150
      String msg = e.getMessage();
151
      logMetacat.error(msg);
152
      throw new ServiceException(msg);
153
    }
154
    
155
    // Get configuration properties on instantiation
156
    try {
157
      groupName = 
158
        PropertyService.getProperty("dataone.hazelcast.processCluster.groupName");
159
      groupPassword = 
160
        PropertyService.getProperty("dataone.hazelcast.processCluster.password");
161
      addressList = 
162
        PropertyService.getProperty("dataone.hazelcast.processCluster.instances");
163
      nodeMap = 
164
        PropertyService.getProperty("dataone.hazelcast.processCluster.nodesMap");
165
      systemMetadataMap = 
166
        PropertyService.getProperty("dataone.hazelcast.storageCluster.systemMetadataMap");
167

    
168
      // Become a DataONE-process cluster client
169
      //TODO: where should this be?
170
//      String[] addresses = addressList.split(",");
171
//      hzClient = 
172
//        HazelcastClient.newHazelcastClient(this.groupName, this.groupPassword, addresses);
173
//      nodes = hzClient.getMap(nodeMap);
174
//      pendingReplicationTasks = hzClient.getMap(pendingTasksQueue);
175
      
176
      // Get a reference to the shared system metadata map as a cluster member
177
      systemMetadata = Hazelcast.getMap(systemMetadataMap);
178
      
179
      // Listen for changes to the system metadata map
180
      systemMetadata.addEntryListener(this, true);
181
      
182
    } catch (PropertyNotFoundException e) {
183

    
184
      String msg = "Couldn't find Hazelcast properties for the DataONE clusters. " +
185
        "The error message was: " + e.getMessage();
186
      logMetacat.error(msg);
187
      
188
    }
189
        
190
  }
191
  
192
  public IMap<Identifier,SystemMetadata> getSystemMetadataMap() {
193
	  return systemMetadata;
194
  }
195
  
196
  public IMap<String,CNReplicationTask> getPendingReplicationTasks() {
197
	  return pendingReplicationTasks;
198
  }
199
  
200
  /**
201
   * Indicate whether or not this service is refreshable.
202
   *
203
   * @return refreshable - the boolean refreshable status
204
   */
205
  public boolean refreshable() {
206
    // TODO: Determine the consequences of restarting the Hazelcast instance
207
    // Set this to true if it's okay to drop from the cluster, lose the maps,
208
    // and start back up again
209
    return false;
210
    
211
  }
212
  
213
  /**
214
   * Stop the HazelcastService. When stopped, the service will no longer
215
   * respond to requests.
216
   */
217
  public void stop() throws ServiceException {
218
    
219
    Hazelcast.getLifecycleService().shutdown();
220
    
221
  }
222

    
223
  /**
224
   * Listen for new Hazelcast member events
225
   */
226
  @Override
227
  public void instanceCreated(InstanceEvent event) {
228
    logMetacat.info("New Hazelcast instance created: " +
229
      event.getInstance().getId() + ", " +
230
      event.getInstance().getInstanceType());
231
    
232
  }
233

    
234
  @Override
235
  public void instanceDestroyed(InstanceEvent event) {
236
    logMetacat.info("Hazelcast instance removed: " +
237
        event.getInstance().getId() + ", " +
238
        event.getInstance().getInstanceType());
239
    
240
  }
241
  
242
  /**
243
   * Refresh the Hazelcast service by restarting it
244
   */
245
  @Override
246
  protected void doRefresh() throws ServiceException {
247

    
248
    // TODO: verify that the correct config file is still used
249
    Hazelcast.getLifecycleService().restart();
250
    
251
  }
252
  
253
  /**
254
	 * Implement the EntryListener interface for Hazelcast, reponding to entry
255
	 * added events in the hzSystemMetadata map. Evaluate the entry and create
256
	 * CNReplicationTasks as appropriate (for DATA, METADATA, RESOURCE)
257
	 * 
258
	 * @param event - The EntryEvent that occurred
259
	 */
260
	@Override
261
	public void entryAdded(EntryEvent<Identifier, SystemMetadata> event) {
262
		// handle as update - that method will create if necessary
263
		entryUpdated(event);
264
	}
265

    
266
	/**
267
	 * Implement the EntryListener interface for Hazelcast, reponding to entry
268
	 * evicted events in the hzSystemMetadata map.  Evaluate the entry and create
269
	 * CNReplicationTasks as appropriate (for DATA, METADATA, RESOURCE)
270
	 * 
271
	 * @param event - The EntryEvent that occurred
272
	 */
273
	@Override
274
	public void entryEvicted(EntryEvent<Identifier, SystemMetadata> event) {
275
	  // nothing to do, entries are still in the backing store
276
	  
277
	}
278
	
279
	/**
280
	 * Implement the EntryListener interface for Hazelcast, reponding to entry
281
	 * removed events in the hzSystemMetadata map.  Evaluate the entry and create
282
	 * CNReplicationTasks as appropriate (for DATA, METADATA, RESOURCE)
283
	 * 
284
	 * @param event - The EntryEvent that occurred
285
	 */
286
	@Override
287
	public void entryRemoved(EntryEvent<Identifier, SystemMetadata> event) {
288
	  // we don't remove objects
289
	  
290
	}
291
	
292
	/**
293
	 * Implement the EntryListener interface for Hazelcast, reponding to entry
294
	 * updated events in the hzSystemMetadata map.  Evaluate the entry and create
295
	 * CNReplicationTasks as appropriate (for DATA, METADATA, RESOURCE)
296
	 * 
297
	 * @param event - The EntryEvent that occurred
298
	 */
299
	@Override
300
	public void entryUpdated(EntryEvent<Identifier, SystemMetadata> event) {
301
	
302
			logMetacat.debug("Entry added/updated to System Metadata map: " + event.getKey().getValue());
303
			PartitionService partitionService = Hazelcast.getPartitionService();
304
			Partition partition = partitionService.getPartition(event.getKey());
305
			Member ownerMember = partition.getOwner();
306
			if (!ownerMember.localMember()) {
307
				// need to pull the entry into the local store
308
				logMetacat.debug("Saving entry locally: " + event.getKey().getValue());
309
				try {
310
					if (!IdentifierManager.getInstance().systemMetadataExists(event.getKey().getValue())) {
311
						IdentifierManager.getInstance().createSystemMetadata(event.getValue());
312
					} else {
313
						IdentifierManager.getInstance().updateSystemMetadata(event.getValue());
314
					}
315
				} catch (McdbDocNotFoundException e) {
316
					logMetacat.error(
317
							"Could not save System Metadata to local store.", e);
318
				}
319
			}
320
	
321
			// TODO evaluate the type of system metadata change, decide if it
322
			// warrants a replication event, what type (DATA, METADATA, RESOURCE),
323
			// iteratively lock the PID, create and submit the tasks, and expect a
324
			// result back. Deal with exceptions.
325
			boolean isMetadata = D1NodeService.isScienceMetadata(event.getValue());
326
			// TODO: do we need to do anything explicit here?
327
	  
328
	}
329

    
330
}
(2-2/4)