-
Notifications
You must be signed in to change notification settings - Fork 0
Customize_en
It's useful to use customized classes in Sofof. But be careful when using these classes with a remote database server, that server should have the same class in its classpath.
If you want to create your own commands you should know how the server works before and this depends primarily on your view of the database and how it works. Sofof isn't a set of ready commands that the server will execute it, but a framework for executing the commands regardless of its source and how it works which allow anyone to write his own commands that he will use it in the field he wants (big data analysis, writing logs, sharing data, etc). The server depends primarily on the two interfaces ListInputStream and ListOutputStream. The interfaces role is very like the role of the two commands Bind and Select. The server will pass its implementation to both interfaces to the execute method on the executable command which allows the command to read and write to the database and will pass the ListInputStream to the read method on the query which allows the query to just read from the database. The ListOutputStream has a write method to write the lists to the database by passing the list object, the binding name and the Class object of the objects you want to write (in the list). When you call that method it will write the new objects on the old ones that have the same binding name and Class. The ListInputStream can read the stored objects using the read method by passing the binding name and the Class that it will read its instances. What happens when there is more than command sent to the default sofof server is important to you to be more careful when you build your own commands. The server will run the commands just after receiving them. If there are two queries that read from the same Class and the same binding name the server will allow them to read. If there is an executable command the server will not run any other command (query or executable command) until the current one finishes. This is because the executable command can read and write to more than one Class instances that are binding to a specific binding name (this is the smallest unit of the database) and that requires the database to be stable until the executable command finishes executing. Because of that, you shouldn't write a long executing command. This following example will show you a simple executable command that will remove all the passed classes objects that are bound to the passed binding name:
import org.sofof.command.Executable;
import org.sofof.ListInputStream;
import org.sofof.ListOutputStream;
public class Format implements Executable {
private String bind;
private Class[] classes;
public Example(String bind, Class... classes) {
this.bind = bind;
this.classes = classes;
}
@Override
public int execute(ListInputStream in, ListOutputStream out) throws SofofException {
int affected = 0;
for (Class clazz : classes) {
affected +=in.read(bind, clazz).size();
out.write(new LinkedList(), bind, clazz);
}
return affected;
}
}In the previous example, it's preferred to return the number of affected objects in the execute method. For the following example you will see a query that calculates the average of each student and returns it in a list of hashmaps:
import org.sofof.command.Operation;
import example.Student;
import example.Marks;
import org.sofof.command.Query;
import org.sofof.command.Select;
import org.sofof.command.condition.ObjectCondition;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
class AVG implements Query{
@Override
public List query(ListInputStream in) throws SofofException {
List students = in.read("students", Student.class);
LinkedList maps = new LinkedList();
for(Student student : students){
HashMap studentAverage = new HashMap();
studentAverage.put("name", student.getName());
List<Integer> marks = new Select(Mark.class, "#getMark()").from("marks").where(new ObjectCondition("#getName()", Operation.Equal, student.getName())).query(in);
float sum = 0;
for(int mark : marks){
sum += mark;
}
studentAverage.put("avg", sum/marks.size());
maps.add(studentAverage);
}
return maps;
}
}Notic that I used another query in my query by passing the ListInputStream to the query method and the query will do what it supposted to do. What remains now is how to execute the executable expression in your command. This is done using the static method execute from the ExpressionExecuter class. This method will execute the expression on the object that you will pass to it and return the result of that. ListOutputStream offers some methods to control a transaction. The startTransaction method will start the transaction and all changes you are doing after that are reversible. If an exception is thrown in your command you can call the method rollback which will rollback all the changes you have done, but if there is no exception and your command finished executing successfully you should call the method commit to commit all the changes you have done else nothing will be saved from you changes.
This process is from the easiest ones that you can do. It's true that the existing conditions are enough for the normal use but writing your own conditions is important if you are building a complex program. The condition depends on the check method which receives the object that the condition is applied to it. This method should return true if the condition applied to that object else should return false. If you are building your own command you can add the ability to add conditions to your command. When you want to apply the changes that your command should do to objects you can execute the check method on the condition after passing the object that the condition should apply on it in an if statement. You can do this process on many objects that you command affect them e.x: You want to read objects that depend on a condition. In that case, the condition should apply on all the objects to know which one the user wants it to be affected.
You can write a sorter by implementing the Sorter interface which has only the sort method. This method receives a list that contains objects that should be sorted. You just have to sort that list in the way that you want. You shouldn't delete or add anything to the list from here.