Object Database Design & Implementation - Part IV
OODB and Java
4
White

Joe viết ngày 07/08/2018

Chao cac ban,

Today I lead you into the Client world. A partial world of the myterious Client-Server castle. We have completed the Server part with a runnable ODBServer. What we have to do is to design and to implement an API for the client. And we do it now...

Note: End of this series I'll provide you a zip file odb.zip for downloading. This zip file contains all the API sources mentioned in this series, some useful tools (ODBUsers, InputPane) , one DEMO example, AND the compiled class of 144-bits EnDecrypt algorithm plus 2 userlists (userlist144 goes with EnDecrypt144).

CLIENT API

In Part III we have dicussed about Design and Implementation of ODB server. The minimum requirement bases on 6 following APIs:

  • ODMS. The lowest IO level between DB implementation and Operation System & Subsystems (disk driver)
  • ODManager. The multiuser Data Management System that coordinates, administers and synchronizes all activities between users and the low-level data. Moreover, it keeps track of data integrity (LOCK & UNLOCK) and consistency (e.g. panic shutdown)
  • ODBWorker. The Client interface. ODBWorker "translates" Client's requests into DB-requests so that ODManager could handle the transaction, and delivers the results back to its client partner
  • ODBResult. The outcomes of ODMS/ODBManager is wrapped in ODBResult by ODBWorker and sent it back to its client where one of partial results is selected as the returned value (boolean, ArrayList, Object, byte[] and errorMessage)
  • ODBService. This is the framework of ODB server. With ODBService you can develop your own "fancy" SWING or JFX ODB Server
  • UserList. To protect data and to keep track of users who work with your ODB this API is for this specific purpose. Again, you can build a SWING or JFX app that does the user-maintenance job. To relieve I have included a simple SWING app ./example/ODBUsers.java in odb.zip

The template EnDecrypt API should be your own encrypting/decrypting development. However, for your (temporary) convenience I provide the class of 144-bits EnDecrypt. If you want to use it just rename it back to EnDecrpyt.class and jar it together with your classes. Or use this following simple EnDecrypt algorithm for both sides: client and server.

Simple EnDecrypt.java.

package odb;
/**
a simple Encrypt/Decrypt algorithm
@autor Joe Nartca (C)
*/
public class EnDecrypt {
  /**
    decrypt an encrypted String
    @param inp encrypted String
    @return decrypted String (null if invalid inp)
  */
  public static String decrypt(String inp) {
    if (inp == null || inp.trim().length() == 0 || (inp.length() % 5) > 0) return null;
    int mx = inp.length();
    String en = inp.substring(2, mx-3);
    StringBuilder sb = new StringBuilder(mx);
    int f = Integer.parseInt(inp.substring(mx-3)+inp.substring(0, 2));
    for (int i=0,x=en.length();i< x; i+=5) sb.append(""+(char)(Integer.parseInt(en.substring(i,i+5))/f));
    return sb.toString();
  }
  /**
    encrypt() a String
    @param inp to be encrypted String
    @return encrypted String (null if invalid inp)
  */
  public static String encrypt(String inp) {
    if (inp == null || inp.trim().length() == 0) return null;
    int mx = inp.length(), f = 0;
    while (f == 0 || f > 255) f = (int)(Math.random()*255);
    String F = String.format("%05d", f);
    StringBuilder sb = new StringBuilder(5+mx*5);
    sb.append(F.substring(3));
    for (int i = 0; i < mx; ++i) sb.append(String.format("%05d",((int)inp.charAt(i)) * f));
    sb.append(F.substring(0, 3));
    return sb.toString();
  }
}

ALT

Fig.6 gives you an idea how a JAVA Client accesses an ODB server. As said in Part III ODBWorker represents only ONE (remote) client and works exclusively on behalf of it. ODBConnect API is the direct reflection of ODBWorker. Meaning: all ODBConnect methods are reflected inside of ODBWorker as its internal "commands". The basic methods of ODBConnect API for a workable ODB Client access are:

  • ODBConnect(String dbHost, int port). Constructor. An instantiation requires HostName and a port of ODB server.
  • connect(String dbName, String pw, String uID). SignIn or Connection of dbName as user with password and ID. This method must be firstly invoked before any other action can be used.
  • disconnect( ). SignOff and disconnection of communication.
  • open(String dbName). Open another dbName
  • close(String dbName). Close this dbName
  • getKeys(String dbName). Retrieve all available keys (or objectNames) of this dbName
  • add(String dbName, String key, Object obj). Append (add) an object associated with key to dbName
  • add(String dbName, String key, byte[] obj). Append (add) a byte-array associated with key to dbName
  • replace(String dbName, String key, Object obj). Replace an object of dbName
  • replace(String dbName, String key, byte[] obj). Replace a byte-array of dbName
  • delete(String dbName, String key). Remove object associated with key from dbName
  • read(String dbName, String key). Retrieve an object associated with key from dbName
  • getByteArray(String dbName, String key). Retrieve a byte-array associated with key from dbName
  • isExisted(String dbName, String key). Query the existence of an object of dbName associated with key
  • isLocked(String dbName, String key). Query the lock status of an object of dbName associated with key
  • lock(String dbName, String key). Lock an object of dbName associated with key
  • unlock(String dbName, String key). Unlock (and commit) an object of dbName associated with key
  • rollback(String dbName, String key). Rollback an object of dbName associated with key
  • rollback(String dbName). Rollback ALL objects of dbName

You may note that ODBConnect API contains a private "panicShutdown" method, too. It's an intended prevention for the case that YOUR client runs into troubles and has to be "rudely forced" to terminate (e.g. with CRTL-C or via TaskManager). ODBConnect is designed and implemented to handle several dbFiles at a time.

First: ODBConnect constructor establishes a SocketChannel connection to the specified ODBServer (HostName, port and network latency. Default 50 microSec.),

Then a client app must send its encrypted string PW:UID using connect() to its agent ODBWorker on the server side. And ODBWorker authenticates the enrollment. If the authentication succeeds it represents this client.

Now the client can work simultaneously with several dbFiles (using open() and close()).

Note that close() just shuts down the specified dbFile, but disconnect() disconnects the communication connection AND closes all pending dbFiles.

Design simplicity AND Implementation compactness (only the needed APIs) between client app and ODB server are the key of an optimal Client-Server architectural environment. NO fancy personalized Exception APIs, NO bundle of packages with tiny APIs. Just 8 average APIs. On the client side ODBConnect API is standing between client app and the net. On the server side only three active APIs are involved between dbFiles and the net (ODBWorker - ODManager - ODMS). ODBResult is just the "communicative package" of Server for Clients.

ODBConnect.java. Command String formats:

  • dbName+x01+command+x01
  • dbName+x01+command+x01+Key+x01
  • dbName+x01+command+x01+Key+x01+byte_array
  • dbName+x01+command+x01+Key+x01+Serialized_Object

Where x01 is Hex 01 (1 byte) and is used as separator.

package odb;

import java.io.*;
import java.net.*;
import java.util.*;
//
import java.nio.*;
import java.nio.channels.*;
import java.util.concurrent.*;
/**
Object Data Connect
@author Joe Nartca (c)
*/
public class ODBConnect {
  /**
  contructor
  @param dbHost String, OODB Server hostname
  @param port int, OODB server post
  @exception Exception thrown by JAVA
  */
  public ODBConnect(String dbHost, int port) throws Exception {
    init(dbHost, port, 100L);
  }
  /**
  contructor
  @param dbHost String, OODB Server hostname
  @param port int, OODB server post
  @param LATENT long, latent time of your WEB connection (in microseconds)
  @exception Exception thrown by JAVA
  */
  public ODBConnect(String dbHost, int port, int LATENT) throws Exception {
    init(dbHost, port, LATENT);
  }
  //
  private void init(String dbHost, int port, long LATENT) throws Exception {
    dbc = SocketChannel.open(new InetSocketAddress(dbHost, port));
    dbc.socket().setReceiveBufferSize(MAX_BUF); // 64KB
    dbc.socket().setSendBufferSize(MAX_BUF);
    bbuf =  ByteBuffer.allocate(MAX_BUF);
    this.LATENT = LATENT;
    con = false;
    panicShutdown();
  }
  /**
  connect(String pw, String uID) to odb
  @param dbName String, the DB name (abs.path, without any suffix). If suffix is given it will be cut off
  @param pw  String, the Password
  @param uID String, User ID
  @exception Exception thrown by JAVA
  */
  public void connect(String dbName, String pw, String uID) throws Exception {
    if (dbName == null || dbName.length() == 0) throw new Exception("DB name cannot be null or 0");
    ByteArrayOutputStream bao = new ByteArrayOutputStream(BLOCK);
    bao.write(("connect"+X01+dbName+X01+EnDecrypt.encrypt(pw+":"+uID)+"@"+X01).getBytes());
    ODBResult res = getResult(dbName, bao);
    con = res.bool;
  }
  /**
  disconnect() odb
  @exception Exception thrown by JAVA
  */
  public void disconnect( ) throws Exception {
    send("X", "disconnect");
    Thread.sleep(500); // wait for 0.5 sec
    dbc.close();
    con = false;
  }
  /**
  open(dbName) an oodb
  @param dbName String, the DB name (abs.path, without any suffix). If suffix is given it will be cut off
  @exception Exception thrown by JAVA
  */
  public void open(String dbName) throws Exception {
    send(dbName, "open");
  }
  /**
  close() an oodb
  @param dbName String, the DB name (abs.path, without any suffix). If suffix is given it will be cut off
  @exception Exception thrown by JAVA
  */
  public void close(String dbName) throws Exception {
    send(dbName, "close");
  }
  /**
  getKeys() returns all keys of dbName
  @param dbName String, the DB name (abs.path, without any suffix). If suffix is given it will be cut off
  @return ArrayList of String
  */
  public ArrayList<String> getKeys(String dbName) {
    try {
      ODBResult res = send(dbName, "getKeys");
      return res != null? res.list : null;
    } catch (Exception ex) { }
    return null;
  }
  /**
  add() object
  @param dbName String, the DB name (abs.path, without any suffix). If suffix is given it will be cut off
  @param key String, key name
  @param obj serialized object to be added
  @exception Exception thrown by JAVA
  */
  public void add(String dbName, String key, Object obj) throws Exception {
    send(dbName, "add", key, obj);
  }
  /**
  add() object
  @param dbName String, the DB name (abs.path, without any suffix). If suffix is given it will be cut off
  @param key String, key name
  @param obj byte array to be added
  @exception Exception thrown by JAVA
  */
  public void add(String dbName, String key, byte[] obj) throws Exception {
    send(dbName, "add", key, obj);
  }
  /**
  replace() object
  @param dbName String, the DB name (abs.path, without any suffix). If suffix is given it will be cut off
  @param key String, key name
  @param obj serialized object to be replaced
  @exception Exception thrown by JAVA
  */
  public void replace(String dbName, String key, Object obj) throws Exception {
    send(dbName, "replace", key, obj);
  }
  /**
  replace() object
  @param dbName String, the DB name (abs.path, without any suffix). If suffix is given it will be cut off
  @param key String, key name
  @param obj byte array to be replaced
  @exception Exception thrown by JAVA
  */
  public void replace(String dbName, String key, byte[] obj) throws Exception {
    send(dbName, "replace", key, obj);
  }
  /**
  delete() object
  @param dbName String, the DB name (abs.path, without any suffix). If suffix is given it will be cut off
  @param key String, key name
  @exception Exception thrown by JAVA
  */
  public void delete(String dbName, String key) throws Exception {
    send(dbName, "delete", key);
  }
  /**
  read() object
  @param dbName String, the DB name (abs.path, without any suffix). If suffix is given it will be cut off
  @param key String, key name
  @return serialized object or a byte array if data aren't serialized. See getBytes().
  @exception Exception thrown by JAVA
  */
  public Object read(String dbName, String key) throws Exception {
    ODBResult res = send(dbName, "read", key);
    if (res.obj != null) {
      try (ObjectInputStream oi = new ObjectInputStream(new ByteArrayInputStream(res.obj))) {
          Object obj = oi.readObject();
          oi.close();
          return obj;
      } catch (Exception ex) { }
      return res.obj;
    }
    throw new Exception("Exception due to unknown reason at KEY:"+key);
  }
  /**
  getBytes() byte array (of object)
  @param dbName String, the DB name (abs.path, without any suffix). If suffix is given it will be cut off
  @param key String, key name
  @return byte array of (serialized) object
  @exception Exception thrown by JAVA
  */
  public byte[] getByteArray(String dbName, String key) throws Exception {
    ODBResult res = send(dbName, "read", key);
    if (res.obj != null) return res.obj;
    throw new Exception("Exception due to unknown reason at KEY:"+key);
  }
  /**
  isExisted()
  @param dbName String, the DB name (abs.path, without any suffix). If suffix is given it will be cut off
  @param key String, key name
  @return boolean true: key exists, false if key is unknown
  */
  public boolean isExisted(String dbName, String key) {
    try {
      ODBResult res = send(dbName, "isExisted", key);
      return res != null? res.bool : false;
    } catch (Exception ex) { }
    return false;
  }
  /**
  isLocked()
  @param dbName String, the DB name (abs.path, without any suffix). If suffix is given it will be cut off
  @param key String, key name
  @return boolean true: key is locked, false if key is unknown or unlocked
  */
  public boolean isLocked(String dbName, String key) {
    try {
      ODBResult res = send(dbName, "isLocked", key);
      return res != null? res.bool : false;
    } catch (Exception ex) { }
    return false;
  }
  /**
  lock() lock key. Must be invoked before delete or replace.
  <br>By delete: locked key is released if delete was successful 
  @param dbName String, the DB name (abs.path, without any suffix). If suffix is given it will be cut off
  @param key String, key name
  @return boolean true: key is locked, false if unknown key or key was locked
  */
  public boolean lock(String dbName, String key) {
    try {
      ODBResult res = send(dbName, "lock", key);
      return res != null? res.bool : false;
    } catch (Exception ex) { }
    return false;
  }
  /**
  unlock() unlock key
  @param dbName String, the DB name (abs.path, without any suffix). If suffix is given it will be cut off
  @param key String, key name
  @return boolean true: key is unlocked, false if unknown key or key wasn't locked
  */
  public boolean unlock(String dbName, String key) {
    try {
      ODBResult res = send(dbName, "unlock", key);
      return res != null? res.bool : false;
    } catch (Exception ex) { }
    return false;
  }
  /**
  rollback()
  @param dbName String, the DB name (abs.path, without any suffix). If suffix is given it will be cut off
  @param key String, key of object to be rolled back
  @return boolean true if commit is successful, false: unknown key, no rollback
  @exception Exception thrown by JAVA
  */
  public boolean rollback(String dbName, String key) throws Exception {
    ODBResult res = send(dbName, "rollback", key);
    return res != null? res.bool : false;
  }
  /**
  rollback() ALL modified objects
  @param dbName String, the DB name (abs.path, without any suffix). If suffix is given it will be cut off
  @return boolean true if commit is successful, false: nothing to rollback
  @exception Exception thrown by JAVA
  */
  public boolean rollback(String dbName) throws Exception {
    ODBResult res = send(dbName, "rollback");
    return res != null? res.bool : false;
  } 
  //--------------------------------------------------------------------------
  private ODBResult send(String dbName, String cmd, String key, Object obj) throws Exception {
    if (!con) throw new Exception("There's NO connection.");
    if (dbName == null || dbName.length() == 0) throw new Exception("DB name cannot be null or 0");
    ByteArrayOutputStream bao = new ByteArrayOutputStream(BLOCK);
    bao.write((cmd+X01+dbName+X01+key+X01).getBytes());
    if (obj instanceof byte[]) bao.write((byte[]) obj);
    else {
      ObjectOutputStream oos = new ObjectOutputStream(bao);
      oos.writeObject(obj);
    }
    return getResult(dbName, bao);
  }
  //
  private ODBResult send(String dbName, String cmd, String key) throws Exception {
    if (!con) throw new Exception("There's NO connection.");
    if (dbName == null || dbName.length() == 0) throw new Exception("DB name cannot be null or 0");
    ByteArrayOutputStream bao = new ByteArrayOutputStream(BLOCK);
    bao.write((cmd+X01+dbName+X01+key+X01).getBytes());
    return getResult(dbName, bao);
  }
  //
  ODBResult send(String dbName, String cmd) throws Exception {
    if (!con) throw new Exception("There's NO connection.");
    if (dbName == null || dbName.length() == 0) throw new Exception("DB name cannot be null or 0");
    ByteArrayOutputStream bao = new ByteArrayOutputStream(BLOCK);
    bao.write((cmd+X01+dbName+X01).getBytes());
    return getResult(dbName, bao);
  }
  // send to OODB server and wait for ODBResult
  private ODBResult getResult(String dbName, ByteArrayOutputStream bao) throws Exception {
    byte[] bb = bao.toByteArray();
    if (bb.length <= MAX_BUF) {
      dbc.write(ByteBuffer.wrap(bb));
    } else {
      int p = 0, le = MAX_BUF;
      while (true) {
        dbc.write(ByteBuffer.wrap(bb, p, MAX_BUF));
        le += MAX_BUF;
        p  += MAX_BUF;
        if (le >= bb.length) {
          dbc.write(ByteBuffer.wrap(bb, p, bb.length-p));
          break;
        }
      }
    }       
    bao.reset();
    while (true) {
      bbuf.clear();
      int n = dbc.read(bbuf);
      // Synchronize the reading...
      TimeUnit.MICROSECONDS.sleep(LATENT);
      bao.write(bbuf.array(), 0, n);
      bao.flush();
      if (n < MAX_BUF) break;
    }
    ODBResult res = null;
    byte[] buf = bao.toByteArray();
    if (buf.length > 0) {
      TimeUnit.MICROSECONDS.sleep(LATENT);
      ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(buf));
      res = (ODBResult) ois.readObject(); // retrieve the result.
      if (res == null) throw new Exception("Unable to access OODB:"+dbName);
      if  (res.err.length() > 0) throw new Exception(res.err);
    }
    return res;
  }
  // hook the panicShutdown watchdog.
  private void panicShutdown(){
    Runtime.getRuntime().addShutdownHook(new Thread() {
      public void run() {
        try {
          disconnect();
          con = false;
        } catch (Exception ex) { }
      }
    });
  }
  // data area
  private long LATENT;
  private ByteBuffer bbuf;
  private SocketChannel dbc;
  private boolean con = false;
  private char X01 = (char)0X01;
  private int MAX_BUF = 65536, BLOCK = 1024;
}

With the basic ODBConnect API you're able to build any ODB SWING or JFX client that directly works with your ODBServer (see Part III).

HOW TO BUILD AN ODB JAR file

  • download all API sources given in Part II, III and IV: ODBService, ODBWorker, ODBResult, ODBConnect, ODManager, ODMS, UserList and your own EnDecrypt (or the obove-mentioned EnDecrypt example) in a directory, says: C:\odb. Or unzip the delivered odb.zip to this directory.
  • create a subdirectory classes inside C:\odb
  • open a CMD-WINDOW (or Terminal - Console on LINUX) and create the root directory odb and 4 subdirectories: classes, example, odb and doc as following:

CMD-WINDOWS (example)

C:\WINDOWS\system32>cd C:\
C:\>mkdir odb
C:\>cd odb
C:\odb>
C:\odb>mkdir odb
C:\odb>mkdir doc
C:\odb>mkdir example
C:\odb>mkdir classes
C:\odb>javadoc -d ./doc *.java
C:\odb>javac -g:none -d ./classes *.java
  • unzip the file \classes\odb\EnDecrypt.class into C:\odb\classes\odb then back to CMD-WINDOWS (it's the 144-bits version) and rename it back to EnDecrypt.class. Or you can use the simple EnDecrypt.java
C:\odb>cd classes
C:\odb\classes>jar -cvf ../odb.jar ./odb/*.class > ../o.txt
C:\odb\classes>cd ..
C:\odb>
  • if nothing happened you get your own odb.jar in your directory C:\odb. You can verify the output o.txt to see the "jarred" classes. And the subdirectory C:\odb\doc contains JAVA-Styled API-Documents.
  • unzip odb.zip and extract the file userlist to C:\odb\odb. Note: if you work with the 144-bits EnDecrypt algorithm you have to save the simple userlist and rename userlist144 back to userlist. Both contains ONE user: password is joey and userID is joe.

TESTING

To assure the functionality of our new ODB we create an ODB Client that works with our ODB server. Again, let's start a CMD-WINDOW (or Terminal Console on LINUX) and move to subdirectory example. At first we create a little file config.txt as following in C:\odb\example:

#
# this is an example
#
ODB_PATH=C:/odb/odb
#

then we create a batch-file setpath.bat (similar with a shell file on LINUX) and save it in C:\odb\example. The setpath.bat includes our odb.jar and the current working subdirectory ".\classes" in the global CLASSPATH

set CLASSPATH=C:\odb\odb.jar;.\classes;%CLASSPATH%

Back to CMD-WINDOW and move to C:\odb\example and run

CDM-WINDOWS

C:\odb\example>mkdir classes
C:\odb\example>setpath
C:\odb\example>javac -g:none -d ./classes ODBServer.java

Now you have a "runnable" ODBServer. Let's start to create an ODB Client. We keep this CMD-WINDOW open for later use. Back to our simple ODB Client.

1) we have to create an empty ODB named "people" with the people of people.txt (from odb.zip. Note: to save space we don't download and store the images of the "people" in our ODB, but let them where they are: on the WEB and use their url as "the data")

2) we design a serializable Object called People.java for our ODB and save it in C:\odb\example

import java.awt.*;
import java.net.URL;
import javax.swing.*;
// Joe Nartca (c)
public class People implements java.io.Serializable {
  private static final long serialVersionUID = 1234L;
  public People(String name, int age, String address,
                String profession, double income, String url) {
    this.age = age;
    this.url = url;
    this.name = name;
    this.income = income;
    this.address = address;
    this.profession = profession;
  }
  public String toString() {
    return "Name: "+name+", Age: "+age+", Addr.: "+address+", Income: "+income+
           ", Profession: "+profession;
  }
  public void print() {
    System.out.println("Name:\t\t"+name+
         "\r\nAge:\t\t"+age+
         "\r\nAddr.:\t\t"+address+
         "\r\nIncome:\t\t"+income+
         "\r\nProfession:\t"+profession);
  }
  public void picture(JFrame jf) {
    class Picture extends JDialog {
      public Picture(JFrame jf) {
        super(jf, true);           
        setImage(jf);
      }
      private void setImage(JFrame jf) {
        setTitle("People:"+name);
        setDefaultCloseOperation(DISPOSE_ON_CLOSE);
        JButton but;
        try {
          ImageIcon img;
          if (url.indexOf("://") > 0) img = new ImageIcon(new URL(url));
          else img = new ImageIcon(url);
          but = new JButton(img);
          setSize(img.getIconWidth(), img.getIconHeight());
        } catch (Exception e) {
           but = new JButton("No Image");
           pack();
        }
        but.setContentAreaFilled(false);
        but.setBorderPainted(false);
        add(but, BorderLayout.CENTER);     
        setLocationRelativeTo(jf);
      }
    }
    (new Picture(jf)).setVisible(true);
  }
  // private data
  private int age;
  private double income;
  private String name, url, address, profession;
}

3) we develop a simple SWING client ODBpeople.java with our personalized JOptionPane called InputPane.java. More about "personalized" JOptionPane read Simple GUI dialog for a Simple App. Extract InputPane.java from odb.zip to C:\odb\example

4) Extract ODBpeople.java to C:\odb\example

5) Back to our CMD-WINDOW and run

C:\odb\example>javac -g:none -d ./classes *.java
C:\odb\example>java ODBServer

Sixth: while ODBServer waits for clients open another CMD-WINDOW and move to directory C:\odb\example and run

C:\odb\example>setpath
C:\odb\example>java ODBpeople

You get a prompt. Just follow the prompt to create your first ODB called "people" as following:

  • click CONNECT and answer the prompts: dbName = people, password = "joey", userID = "joe"
  • click ADD and fill the prompt with the "people" from the list people.txt (Joe/../url, ..., Putin/../url, ..., Trump/../url)
  • click LIST NAMES to verify the list of your ODB people in directory C:\odb\odb
  • click DISCONNECT & EXIT to save (or commit) the objects and exit.

Then restart ODBpeople, click LIST NAMES to see whether all objects were correctly saved (i.e. committed) or not. Fig.7 shows you how your ODB client app looks like and works.
ALT
Fig.7 read(people, "Obama");

Next: Object Database Design & Implementation - ODB Mining Part V

Bình luận


White
{{ comment.user.name }}
Bỏ hay Hay
{{comment.like_count}}
Male avatar
{{ comment_error }}
Hủy
   

Hiển thị thử

Chỉnh sửa

White

Joe

28 bài viết.
203 người follow
Kipalog
{{userFollowed ? 'Following' : 'Follow'}}
Cùng một tác giả
White
27 11
Fuzzy Logic and Machine Learning Hi First of all: I apologize everyone for my writing in English. I come to this site because someone of Daynhauh...
Joe viết 11 tháng trước
27 11
White
25 11
You're a fresh graduate and work for more than 12 months in an IT company with some boring coding tasks... The tasks are unchallenging. Day in, day...
Joe viết 1 tháng trước
25 11
White
23 14
Chao cac ban To the Admins: if you think that this posting breaks some rules of your site please just delete it. NO need to send me a feedback. Th...
Joe viết 19 ngày trước
23 14
Bài viết liên quan
White
6 1
Chao cac ban, Design & Implementation of Data Integrity and Data Consistency In (Link) we design and implement our own file system with the follo...
Joe viết 15 ngày trước
6 1
White
11 9
Chao cac ban, Weeks ago I wrote a blog Object Data Management System (ODMS) The foundation of OO Database (OODB)](https://kipalog.com/posts/Object...
Joe viết 16 ngày trước
11 9
White
3 2
Chao cac ban, This is the last part of the series "Object Database Design & Implementation". The basic ODMSfunctions (READ, WRITE, DELETE, REPLACE...
Joe viết 9 ngày trước
3 2
{{like_count}}

kipalog

{{ comment_count }}

bình luận

{{liked ? "Đã kipalog" : "Kipalog"}}


White
{{userFollowed ? 'Following' : 'Follow'}}
28 bài viết.
203 người follow

 Đầu mục bài viết

Vẫn còn nữa! x

Kipalog vẫn còn rất nhiều bài viết hay và chủ đề thú vị chờ bạn khám phá!