/*
* Copyright (C) 2014 Divide.io
*
* 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 io.divide.dao.appengine;
import com.googlecode.objectify.Key;
import com.googlecode.objectify.cmd.LoadType;
import io.divide.dao.ServerDAO;
import io.divide.shared.util.Crypto;
import io.divide.shared.util.ObjectUtils;
import io.divide.shared.transitory.TransientObject;
import io.divide.shared.transitory.query.*;
import sun.reflect.generics.reflectiveObjects.NotImplementedException;
import javax.ws.rs.core.Response;
import java.security.KeyPair;
import java.util.*;
import java.util.logging.Logger;
import static io.divide.dao.appengine.OfyService.ofy;
public class ObjectifyDAO implements ServerDAO {
Logger logger = Logger.getLogger(String.valueOf(ObjectifyDAO.class));
Random RANDOM = new Random();
@Override
public List<TransientObject> query(Query query) throws DAOException{
logger.info("query: " + query);
LoadType<?> filter = ofy().load().type(OfyObject.class);
com.googlecode.objectify.cmd.Query<?> oFilter =
filter.filter(TransientObject.META_DATA+"."+ TransientObject.OBJECT_TYPE_KEY.KEY + " =",query.getFrom());
for(Clause c : query.getWhere().values()){
oFilter = oFilter.filter(
c.getBefore() + " " +
(c.getOperand().equals(OPERAND.CONTAINS.toString())?OPERAND.EQ.toString():c.getOperand()), // replace CONTAINS with ==
c.getAfter());
}
if(query.getOffset()!=null){
oFilter = oFilter.offset(query.getOffset());
}
if(query.getLimit()!=null){
oFilter = oFilter.limit(query.getLimit());
}
if(query.getRandom()!=null){
int count = count(query.getFrom());
if(count < 1) return new ArrayList<TransientObject>();
int random = RANDOM.nextInt(count);
oFilter = oFilter.offset(random);
oFilter = oFilter.limit(query.getLimit());
}
List list = null;
switch (query.getAction()){
case SELECT:{
if(query.getSelect() == null){
list = oFilter.list();
List<TransientObject> toReturn = new ArrayList<TransientObject>(list.size());
try{
for (OfyObject oo : (List<OfyObject>)list){
logger.info("Got: " + oo);
toReturn.add(BackendToOfy.getBack(oo));
}
} catch (Exception e) {
throw new DAOException(Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(),e);
}
list.clear();
list = toReturn;
} else
if(query.getSelect().equals(SelectOperation.COUNT)){
int count = count(query.getFrom());
return Arrays.asList((TransientObject)new Count(count,query.getFrom()));
}
}break;
case DELETE:{
list = oFilter.keys().list();
ofy().delete().keys(list);
int count = list.size();
list.clear();
EmptyTO o = new EmptyTO();
o.put("count",count);
list.add(o);
}break;
case UPDATE:{
throw new NotImplementedException();
}
}
logger.info("Query Complete: " + list);
return (List<TransientObject>) list;
}
@Override
public Collection<TransientObject> get(String objectType, final String... keys) throws DAOException {
logger.info("get: " + ObjectUtils.v2c(keys));
List<Key<OfyObject>> ofyKeys = new ArrayList<Key<OfyObject>>(keys.length);
for (String to : keys){
ofyKeys.add(Key.create(OfyObject.class, to));
}
Map<Key<OfyObject>, OfyObject> ofyObjets = ofy().load().keys(ofyKeys);
List<TransientObject> tos = new ArrayList<TransientObject>(ofyObjets.size());
try{
for (OfyObject oo : ofyObjets.values()){
if(objectType.equals( oo.meta_data.get("object_type") ))
tos.add(BackendToOfy.getBack(oo));
}
} catch (Exception e) {
throw new DAOException(Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(),e);
}
logger.info("Get Complete: " + tos);
return tos;
}
@Override
public void save(TransientObject... objects) throws DAOException{
logger.info("save(): " + ObjectUtils.v2c(objects));
try{
for(TransientObject bo : objects){
ofy().save().entities(BackendToOfy.getOfy(bo)).now();
}
} catch (Exception e) {
throw new DAOException(Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(),e);
}
logger.info("Save Complete.");
}
@Override
public void delete(TransientObject... objects) throws DAOException {
logger.info("delete: " + ObjectUtils.v2c(objects));
try{
for(TransientObject bo : objects){
ofy().delete().key(Key.create(OfyObject.class, bo.getObjectKey())).now();
}
} catch (Exception e) {
throw new DAOException(Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(),e);
}
logger.info("Delete Complete.");
}
@Override
public boolean exists(TransientObject... objects) {
logger.info("exists: " + ObjectUtils.v2c(objects));
boolean exists = true;
for(TransientObject bo : objects){
if(!exists(bo.getObjectKey())) exists = false;
}
return exists;
}
@Override
public int count(String objectType) {
com.googlecode.objectify.cmd.Query<?> query =
ofy().
load().
type(OfyObject.class).
filter(TransientObject.META_DATA + "." + TransientObject.OBJECT_TYPE_KEY + " =", objectType);
int count = query.count();
logger.info(TransientObject.META_DATA + "." + TransientObject.OBJECT_TYPE_KEY + " =" + objectType + ": " + count);
return query.count();
}
@Override
public KeyPair keys(KeyPair keys) {
if(keys!=null){
KeyObject keyObject = new KeyObject();
keyObject.setKeys(keys.getPublic().getEncoded(), keys.getPrivate().getEncoded());
ofy().save().entities(keyObject);
return keys;
} else {
List<KeyObject> keyObject = ofy().load().type(KeyObject.class).limit(1).list();
KeyObject key = ObjectUtils.get1stOrNull(keyObject);
if(key!=null){
return Crypto.createKeyPair(key.getPublicKey(),key.getPrivateKey());
}else
return null;
}
}
private boolean exists(String key){
return (OfyService.ofy().load().filterKey(Key.create(OfyObject.class,key)).count() == 1);
}
private static class EmptyTO extends TransientObject{
protected EmptyTO() {
this.meta_data.clear();
this.user_data.clear();
}
@Override
public void put(String key, Object value){
super.put(key,value);
meta_data.clear();
}
}
}