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-16 16:42:20 -0700 (Fri, 16 Sep 2011) $'
10
 * '$Revision: 6451 $'
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.CNReplicationTask;
51
import edu.ucsb.nceas.metacat.dataone.D1NodeService;
52
import edu.ucsb.nceas.metacat.properties.PropertyService;
53
import edu.ucsb.nceas.metacat.shared.BaseService;
54
import edu.ucsb.nceas.metacat.shared.ServiceException;
55
import edu.ucsb.nceas.utilities.PropertyNotFoundException;
56
/**
57
 * The Hazelcast service enables Metacat as a Hazelcast cluster member
58
 */
59
public class HazelcastService extends BaseService
60
  implements InstanceListener, EntryListener<Identifier, SystemMetadata> {
61
  
62
  /* The instance of the logging class */
63
  private static Logger logMetacat = Logger.getLogger(HazelcastService.class);
64
  
65
  /* The singleton instance of the hazelcast service */
66
  private static HazelcastService hzService = null;
67
  
68
  /* The Hazelcast configuration */
69
  private Config hzConfig;
70
  
71
  /* The instance of the Hazelcast client */
72
  private HazelcastClient hzClient;
73

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

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

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

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

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

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

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

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

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

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

    
331
}
(1-1/3)