Cloud
I've developed multiple projects using Google Cloud Platform technology, making use of many of its features. My principal concern its always optimize the consumed resources and minimize the application response times.
Google App Engine
I started working on Google App Engine while I was working on Orcius, where after research about different cloud features and services as Microsoft Azure, Amazon and Google, I decided for the last one because the complete ecosystem.
Since then, I've been updating myself in the use of services that it has to offer, like building Virtual Machines, integrating API's like maps and geo-location, endpoints for mobile applications, etc. At same time, I've highlighted on design and building of Object Oriented Data Bases.
For work with Google Cloud Datastore, I've builded my own framework based on Objectify 5.0,, in this way, I can build and consume its services in a fastest way.
¿Objectify?
One Google's library for work with No Relational Data Bases, it's currently version 5. However, today, the effort to integrate inside a proyect generates lots of 'noise' code. For this reason, I've builded my own framework
Generalizing Objectify
First at all, Objectify doesn't works with types during the compilation process, so it's easy get confused, for solve this, I've generalizated the class and overwrited the mos common methods, so I can have it all easily called whit correct type. The most important methods of the class are the most elemental ones: add, delete, get, list, listByProperty and all they are lot overloaded with different args
public class ObjectifyDao<T> {
protected Class<T> clazz;
public ObjectifyDao(Class<T> clazz) {
this.clazz = clazz;
}
public Key<T> add(T entity) {
Result<Key<T>> result = ObjectifyService.ofy().save().entity(entity);
return result.now();
}
public Map<Key<T>, T> add(List<T> counts) {
Result<Map<Key<T>, T>> result = ObjectifyService.ofy().save().entities(counts);
return result.now();
}
public void delete(T entity) {
ObjectifyService.ofy().delete().entity(entity);
}
public void delete(Collection<Key<T>> entities) {
ObjectifyService.ofy().delete().entities(entities);
}
public void delete(Key<T> entityKey) {
ObjectifyService.ofy().delete().key(entityKey);
}
public T get(Key<T> key) throws EntityNotFoundException {
Result<T> result = ObjectifyService.ofy().load().key(key);
return result.now();
}
public Map<Key<T>, T> get(Collection<Key<T>> keys) {
return ObjectifyService.ofy().load().keys(keys);
}
public List<T> getAllList(Key<?> parentKey) {
Query<T> q = ObjectifyService.ofy().load().type(clazz).ancestor(parentKey);
return q.list();
}
public List<T> getAllList(String order, int offset, int limit) {
Query<T> q = ObjectifyService.ofy().load().type(clazz);
if (order != null && !order.trim().isEmpty()){
q = q.order(order);
}
q = q.offset(offset).limit(limit);
return q.list();
}
...
I've created one interface who any object manager can implements for work with datastore's entities
public interface EntityService<T>{
public void setObjectifyDao(ObjectifyDao<T> ofyDao);
public T load(Key<T> key);
public T load(String key);
public Map<Key<T>, T> load(Collection<Key<T>> keys);
public Key<T> save(T entity);
public Map<Key<T>, T> save(List<T> entity);
public void delete(Key<T> key);
public void deleteEntities(List<Keyable<T>> keys);
public void delete(List<Key<T>> keys);
public List<T> list(Key<?> parent);
public List<T> list(Key<?> parent, String order, int offset, int limit);
public List<T> list(Key<?> parent, int offset, int limit);
public List<T> list();
public List<T> list(int offset, int limit);
public List<T> list(String order, int offset, int limit);
public boolean isHardRequired();
public void setHardRequired(boolean hardRequired);
}
Lot of entities must implement this interface with equivalent procedures, so I created one class that can be heredated and it realizes some of the common process:
public abstract class EntityServiceImpl<T> implements EntityService<T> {
protected ObjectifyDao<T> dao;
protected boolean hardRequired = false;
public boolean isHardRequired() { return hardRequired; };
public void setHardRequired(boolean hardRequired) {
this.hardRequired = hardRequired;
}
public void setObjectifyDao(ObjectifyDao<T> objectifyDao) {
this.dao = objectifyDao;
}
@Override
public T load(Key<T> key) {
try {
return isHardRequired() ? dao.getHard(key) : dao.get(key);
} catch (Exception e) {
return null;
}
}
@Override
public T load(String keyString) {
if (keyString != null && !keyString.trim().isEmpty()){
Key<T> key = Key.create(keyString);
return load(key);
}
return null;
}
@Override
public Map<Key<T>, T> load(Collection<Key<T>> keys) {
return dao.get(keys);
}
@Override
public Key<T> save(T entity) {
return isHardRequired() ? dao.addHard(entity) : dao.add(entity);
}
@Override
public Map<Key<T>, T> save(List<T> entity) {
return dao.add(entity);
}
@Override
public void delete(Key<T> key) {
dao.delete(key);
}
public void deleteEntities(List<Keyable<T>> objects) {
List<Key<T>> keys = new ArrayList<>(objects.size());
for (Keyable<T> o : objects) {
keys.add(o.getKey());
}
dao.delete(keys);
}
public void delete(List<Key<T>> keys) {
dao.delete(keys);
}
@Override
public List<T> list(Key<?> parentKey) {
if (parentKey != null) {
return dao.getByParentId(parentKey);
} else {
return dao.getAllList();
}
}
@Override
public List<T> list(Key<?> parentKey, int offset, int limit) {
return list(parentKey, null, offset, limit);
}
@Override
public List<T> list(Key<?> parentKey, String order, int offset, int limit) {
if (parentKey != null) {
return dao.getList(parentKey, order, offset, limit);
} else {
return dao.getAllList(order, offset, limit);
}
}
@Override
public List<T> list() {
return dao.getAllList();
}
@Override
public List<T> list(int offset, int limit) {
return list("", offset, limit);
}
@Override
public List<T> list(String order, int offset, int limit) {
return dao.getAllList(order, offset, limit);
}
}
This classes and interfaces are defined inside my own library, so any project that wants implement them, just needs to create an Entity class the services with a few code lines
EntityClass, EntityService y EntityServiceImpl
Starting from a EntityExample class, build a process to access the Datastore its really simple
@Entity
public class EntidadEjemplo {
@Id private Long id;
private String dato;
@Index private Date fecha;
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public String getDato() { return dato; }
public void setDato(String dato) { this.dato = dato; }
public Date getFecha() { return fecha; }
public void setFecha(Date fecha) { this.fecha = fecha; }
}
I build an interface that allows me customize the behavior on the future, in this case, I just sólo extends from EntityService
public interface EntidadEjemploService extends EntityService<EntidadEjemplo> {}
Then I build a class which implements the interface recently created, inherit from EntityServiceImpl
public class EntidadEjemploServiceImpl implements EntidadEjemploService extends EntityServiceImpl<EntidadEjemplo> {}
With this, without any other code line, I can access the basic methods of Objectify which I had overloaded, with hard type defined, on any block I require
public class FuncionesEjemplo {
private EntidadEjemploService ees = ServiceFactory.getInstance().getEntidadEjemploService();
public String registrarEjemplo(Date hoy, String dato) {
EntidadEjemplo ee = ees.getByProperty("fecha", hoy);
if (ee == null) {
ee = new EntidadEjemplo();
}
ee.setDato(dato);
Key<EntidadEjemplo> key = ees.save(dato);
return key.getString();
}
}
Spring Configuration
As part of additional configurations required by Objectify, any entity must be initiated before we can use it to read/write, so I created a class OfyService who works in a singleton way, builded when the app starts with an Objectify instance and the register of working classes
Finally, like in the example, usually I mantain a singleton ServiceFactory that allows me access the services wherever I am on the project code
...
<bean id="OfyService" class="com.genexis.dao.OfyService" factory-method="getInstance">
<constructor-arg name="classes">
<array value-type="java.lang.Class">
<value>com.genexis.ccorona.entity.EntidadEjemplo</value>
...
</array>
</constructor-arg>
</bean>
...
<bean id="entidadEjemploDao" class="com.genexis.dao.ObjectifyDao">
<constructor-arg name="clazz" value="com.genexis.ccorona.entity.EntidadEjemplo" />
</bean>
<bean id="entidadEjemploService" class="com.genexis.ccorona.service.impl.EntidadEjemploServiceImpl">
<property name="objectifyDao" ref="entidadEjemploDao" />
</bean>
<bean id="ServiceFactory" class="com.genexis.ServiceFactory" factory-method="getInstance">
<property name="entidadEjemploService" ref="entidadEjemploService" />
...
</bean>