/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hadoop.gateway.services.registry.impl;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.io.FileUtils;
import org.apache.hadoop.gateway.GatewayMessages;
import org.apache.hadoop.gateway.config.GatewayConfig;
import org.apache.hadoop.gateway.i18n.messages.MessagesFactory;
import org.apache.hadoop.gateway.services.Service;
import org.apache.hadoop.gateway.services.ServiceLifecycleException;
import org.apache.hadoop.gateway.services.registry.ServiceRegistry;
import org.apache.hadoop.gateway.services.security.CryptoService;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
public class DefaultServiceRegistryService implements ServiceRegistry, Service {
private static GatewayMessages LOG = MessagesFactory.get( GatewayMessages.class );
protected char[] chars = { 'a', 'b', 'c', 'd', 'e', 'f', 'g',
'h', 'j', 'k', 'm', 'n', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K',
'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
'2', '3', '4', '5', '6', '7', '8', '9',};
private CryptoService crypto;
private Registry registry = new Registry();
private String registryFileName;
public DefaultServiceRegistryService() {
}
public void setCryptoService(CryptoService crypto) {
this.crypto = crypto;
}
public String getRegistrationCode(String clusterName) {
String code = generateRegCode(16);
byte[] signature = crypto.sign("SHA256withRSA","gateway-identity",code);
String encodedSig = Base64.encodeBase64URLSafeString(signature);
return code + "::" + encodedSig;
}
private String generateRegCode(int length) {
StringBuffer sb = new StringBuffer();
Random r = new Random();
for (int i = 0; i < length; i++) {
sb.append(chars[r.nextInt(chars.length)]);
}
return sb.toString();
}
public void removeClusterServices(String clusterName) {
registry.remove(clusterName);
}
public boolean registerService(String regCode, String clusterName, String serviceName, String url) {
boolean rc = false;
// verify the signature of the regCode
if (regCode == null) {
throw new IllegalArgumentException("Registration Code must not be null.");
}
String[] parts = regCode.split("::");
// part one is the code and part two is the signature
boolean verified = crypto.verify("SHA256withRSA", "gateway-identity", parts[0], Base64.decodeBase64(parts[1]));
if (verified) {
HashMap<String,RegEntry> clusterServices = registry.get(clusterName);
if (clusterServices == null) {
synchronized(this) {
clusterServices = new HashMap<String,RegEntry>();
registry.put(clusterName, clusterServices);
}
}
RegEntry regEntry = new RegEntry();
regEntry.setClusterName(clusterName);
regEntry.setServiceName(serviceName);
regEntry.setUrl(url);
clusterServices.put(serviceName , regEntry);
String json = renderAsJsonString(registry);
try {
FileUtils.write(new File(registryFileName), json);
rc = true;
} catch (IOException e) {
// log appropriately
e.printStackTrace(); //TODO: I18N
}
}
return rc;
}
private String renderAsJsonString(HashMap<String,HashMap<String,RegEntry>> registry) {
String json = null;
ObjectMapper mapper = new ObjectMapper();
try {
// write JSON to a file
json = mapper.writeValueAsString((Object)registry);
} catch ( JsonProcessingException e ) {
e.printStackTrace(); //TODO: I18N
}
return json;
}
public String lookupServiceURL(String clusterName, String serviceName) {
String url = null;
RegEntry entry = null;
HashMap clusterServices = registry.get(clusterName);
if (clusterServices != null) {
entry = (RegEntry) clusterServices.get(serviceName);
if( entry != null ) {
url = entry.url;
}
}
return url;
}
private HashMap<String, HashMap<String,RegEntry>> getMapFromJsonString(String json) {
Registry map = null;
JsonFactory factory = new JsonFactory();
ObjectMapper mapper = new ObjectMapper(factory);
TypeReference<Registry> typeRef
= new TypeReference<Registry>() {};
try {
map = mapper.readValue(json, typeRef);
} catch (JsonParseException e) {
LOG.failedToGetMapFromJsonString( json, e );
} catch (JsonMappingException e) {
LOG.failedToGetMapFromJsonString( json, e );
} catch (IOException e) {
LOG.failedToGetMapFromJsonString( json, e );
}
return map;
}
@Override
public void init(GatewayConfig config, Map<String, String> options)
throws ServiceLifecycleException {
String securityDir = config.getGatewaySecurityDir();
String filename = "registry";
setupRegistryFile(securityDir, filename);
}
protected void setupRegistryFile(String securityDir, String filename) throws ServiceLifecycleException {
File registryFile = new File(securityDir, filename);
if (registryFile.exists()) {
try {
String json = FileUtils.readFileToString(registryFile);
registry = (Registry) getMapFromJsonString(json);
} catch (Exception e) {
throw new ServiceLifecycleException("Unable to load the persisted registry.", e);
}
}
registryFileName = registryFile.getAbsolutePath();
}
@Override
public void start() throws ServiceLifecycleException {
}
@Override
public void stop() throws ServiceLifecycleException {
}
}