/*
* Copyright 2011 GigaSpaces Technologies Ltd
*
* Licensed 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.openspaces.rest.utils;
import java.io.BufferedReader;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.type.TypeReference;
import org.openspaces.admin.Admin;
import org.openspaces.admin.AdminFactory;
import org.openspaces.admin.space.Space;
import org.openspaces.core.GigaSpace;
import org.openspaces.core.GigaSpaceConfigurer;
import org.openspaces.core.space.UrlSpaceConfigurer;
import org.openspaces.rest.exceptions.TypeNotFoundException;
import org.springframework.http.converter.HttpMessageNotReadableException;
import com.gigaspaces.document.SpaceDocument;
import com.gigaspaces.metadata.SpacePropertyDescriptor;
import com.gigaspaces.metadata.SpaceTypeDescriptor;
import com.j_spaces.core.UnknownTypeException;
/**
* some helper methods to the SpaceApiController class
* @author rafi
* @since 8.0
*/
public class ControllerUtils {
private static final Logger logger = Logger.getLogger(ControllerUtils.class.getName());
private static final TypeReference<HashMap<String, Object>[]> typeRef = new TypeReference<HashMap<String, Object>[]>() {};
private static final ObjectMapper mapper = new ObjectMapper();
public static final XapConnectionCache xapCache=new XapConnectionCache();
public static SpaceDocument[] createSpaceDocuments(String type, BufferedReader reader, GigaSpace gigaSpace)
throws TypeNotFoundException {
HashMap<String, Object>[] propertyMapArr;
try{
//get payload
StringBuilder sb = new StringBuilder();
String line = reader.readLine();
while (line != null) {
sb.append(line + "\n");
line = reader.readLine();
}
reader.close();
//if single json object convert it to array
String data = sb.toString();
if (!data.startsWith("[")){
sb.insert(0, "[");
sb.append("]");
}
//convert to json
propertyMapArr = mapper.readValue(sb.toString(), typeRef);
} catch(Exception e){
throw new HttpMessageNotReadableException(e.getMessage(), e.getCause());
}
SpaceDocument[] documents = new SpaceDocument[propertyMapArr.length];
for (int i = 0; i < propertyMapArr.length; i++) {
try {
Map<String, Object> typeBasedProperties = getTypeBasedProperties(type, propertyMapArr[i], gigaSpace);
documents[i] = new SpaceDocument(type, typeBasedProperties);
} catch (UnknownTypeException e) {
logger.log(Level.SEVERE,"could not convert properties based on type", e);
//cancel previous documents and return null ( do not write anything to space)
return null;
}
}
return documents;
}
public static Map<String, Object>[] createPropertiesResult(SpaceDocument[] docs) {
Map<String, Object>[] result = new HashMap[docs.length];
for (int i = 0; i < docs.length; i++) {
result[i] = new HashMap(docs[i].getProperties());
}
return result;
}
/**
* @param documentType
* @param propertyMap
* @param gigaSpace
* @return
* @throws UnknownTypeException
* @throws TypeNotFoundException
*/
private static Map<String, Object> getTypeBasedProperties(String documentType, Map<String, Object> propertyMap, GigaSpace gigaSpace) throws UnknownTypeException, TypeNotFoundException {
SpaceTypeDescriptor spaceTypeDescriptor = gigaSpace.getTypeManager().getTypeDescriptor(documentType);
if (spaceTypeDescriptor == null){
throw new TypeNotFoundException(documentType);
}else{
Map<String, Object> buildTypeBasedProperties = buildTypeBasedProperties(propertyMap, spaceTypeDescriptor, gigaSpace);
return buildTypeBasedProperties;
}
}
@SuppressWarnings("unchecked")
private static Map<String, Object> buildTypeBasedProperties(
Map<String, Object> propertyMap,
SpaceTypeDescriptor spaceTypeDescriptor, GigaSpace gigaSpace) throws UnknownTypeException, TypeNotFoundException {
HashMap<String, Object> newPropertyMap = new HashMap<String, Object>();
for(Entry<String, Object> entry : propertyMap.entrySet()){
String propKey = entry.getKey();
Object oldPropValue = entry.getValue();
SpacePropertyDescriptor propDesc = spaceTypeDescriptor.getFixedProperty(propKey);
if (propDesc == null){
if(logger.isLoggable(Level.WARNING))
logger.warning("could not find SpacePropertyDescriptor for " + propKey + ", using String as property type");
newPropertyMap.put(propKey, oldPropValue);
}/*else if(propDesc.getType().equals(Object.class)){
logger.warning("Existing Type of " + propKey + " is Object, using String as property type");
newPropertyMap.put(propKey, oldPropValue);
}*/
else{
Object convertedObj;
if (oldPropValue instanceof Map){
String typeName = propDesc.getType().getName();
Map<String, Object> nestedObjProps = getTypeBasedProperties(typeName, (Map<String, Object>) oldPropValue, gigaSpace) ;
convertedObj = new SpaceDocument(typeName, nestedObjProps);
}else{
convertedObj = convertPropertyToPrimitiveType((String)oldPropValue, propDesc.getType(), propKey);
}
newPropertyMap.put(propKey, convertedObj);
}
}
return newPropertyMap;
}
public static Object convertPropertyToPrimitiveType(String object, Class type, String propKey) throws UnknownTypeException {
if (type.equals(Long.class))
return Long.valueOf(object);
if (type.equals(Boolean.class))
return Boolean.valueOf(object);
if (type.equals(Integer.class))
return Integer.valueOf(object);
if (type.equals(Byte.class))
return Byte.valueOf(object);
if (type.equals(Short.class))
return Short.valueOf(object);
if (type.equals(Float.class))
return Float.valueOf(object);
if (type.equals(Double.class))
return Double.valueOf(object);
if (type.isEnum())
return Enum.valueOf(type, object);
if (type.equals(String.class) || type.equals(Object.class))
return String.valueOf(object);
//unknown type
throw new UnknownTypeException("non primitive type when converting property", type.getName());
}
/**
* Open ended thread safe cache for XAP connections
*
* @author DeWayne
*
*/
public static class XapConnectionCache{
private final Logger log=Logger.getLogger("XapConnectionCache");
private static Map<String,XapEndpoint> cache=new ConcurrentHashMap<String,XapEndpoint>();
public XapConnectionCache(){
}
public GigaSpace get(String spaceName,String locators){
if(spaceName==null || spaceName.length()==0)throw new IllegalArgumentException("invalid (null or 0 length) spacename");
if(locators==null || locators.length()==0)throw new IllegalArgumentException("invalid (null or 0 length) spacename");
synchronized(cache){
log.finest("getting space");
GigaSpace gs=get(spaceName);
if(gs!=null)return gs;
String url="jini://*/*/"+spaceName+"?locators="+locators;
log.finest(" connecting to "+url);
UrlSpaceConfigurer usc=new UrlSpaceConfigurer(url);
gs=new GigaSpaceConfigurer(usc.space()).gigaSpace();
log.finest(" got space. connecting admin");
Admin admin=new AdminFactory().addLocators(locators).discoverUnmanagedSpaces().useDaemonThreads(true).create();
for(Map.Entry<String,Space> entry:admin.getSpaces().getNames().entrySet()){
log.finest(" found space:"+entry.getKey());
}
log.finest(" admin created, waiting for space:"+spaceName);
Space space=admin.getSpaces().waitFor(spaceName);
cache.put(spaceName,new XapEndpoint(gs,usc,space.getNumberOfInstances()));
log.finest(" returning space");
return gs;
}
}
/**
* Gets a space in the cache. Doesn't open new connections.
* @param spaceName the name of the space to get
* @return GigaSpace if successful. Null otherwise.
*/
public GigaSpace get(String spaceName){
XapEndpoint ep=cache.get(spaceName);
if(ep==null)return null;
return ep.space;
}
public int getInstances(String spaceName){
if(spaceName==null || spaceName.length()==0)throw new IllegalArgumentException("null or zero length space name");
XapEndpoint ep=cache.get(spaceName);
if(ep==null)throw new IllegalArgumentException("space name '"+spaceName+"' unknown");
return ep.instanceCount;
}
}
private static class XapEndpoint{
public GigaSpace space=null;
public UrlSpaceConfigurer usc=null;
public int instanceCount=0;
public XapEndpoint(GigaSpace space,UrlSpaceConfigurer usc,int instances){
this.space=space;
this.usc=usc;
this.instanceCount=instances;
}
}
}