Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save varocarbas/27fe96fc6c9c9073a43fa19a82d43b7b to your computer and use it in GitHub Desktop.
Save varocarbas/27fe96fc6c9c9073a43fa19a82d43b7b to your computer and use it in GitHub Desktop.
Context, conventions, structure, evolution -- accessory_java

Context, conventions, structure, evolution -- accessory_java

Introduction

In this article, I am analysing the main structure of the code and conventions used in accessory_java (main repository). Additional issues related to the development of this library, such as right context and evolution, are also being discussed. The code of another Java library based on similar ideas, ib (main repository), is also used as a supporting resource.

accessory_java

I started developing accessory_java while working on a personal project. It was meant to be a container of generic resources which I could eventually use in other applications. Basically, a library including not just useful methods and variables, but also a solid starting point to face virtually any project in Java. Or, in other words, a way to adapt Java to my programming approach, to help me easily overcome the language peculiarities. That initial code has kept growing and evolving until reaching the current stage, which I consider mature, comprehensive and reliable enough.

Main features of accessory_java:

  • Built from scratch, by relying on stable functionalities and with the minimum amount of external dependencies possible. At the moment, there is only one external dependency: the MySQL connector. And it can even be ignored in case of not using the database (DB) resources.
  • Including virtually no comments or documentation, but with an overall clear structure and quite rigid rules and conventions which are applied in a very systematic and consistent way. Actually, this article and potential future ones might be gradually compensating the aforementioned lacks.
  • Main priorities (ordered): friendliness, safety (e.g., default validity checks everywhere or no uncaught exceptions), efficiency/speed, scalability/adaptability.
  • Theoretically technology agnostic, although currently mostly focused on MySQL and Linux.

Before using any of the resources of this library, one of the start overloads in the accessory._ini class has to be called, as shown in the code below.

String app_name = samples.APP;
boolean includes_legacy = false; //When true, it looks for a package called "legacy" which might 
                                 //be missing anyway.

//------
//dbs_setup includes the setup to be used by default in all the DBs. 
//It can be populated by using one of the parent_ini_db.get_setup_vals methods or via adding valid
//types.CONFIG_DB_SETUP_[*]-value pairs. Any of those values can be overwritten by the specific setups 
//in the _ini_db class, which are included when adding a new DB through the populate_db method called 
//from populate_all_dbs.

String db_name = samples.PERFECT_NAME;
String username = samples.PERFECT_USERNAME;
String password = samples.PERFECT_PASSWORD;
        
//--- These two variables work together: if encrypt is true, user has to be non-null and the DB 
//credentials will be retrieved from the corresponding encrypted file.
boolean encrypt = true;
String user = samples.USER; //This is the crypto ID associated with the given encryption information, 
                            //which can be generated by calling the method db.encrypt_credentials.
//---
        
//If encrypt is true, a set of encrypted credentials located in a specific file is expected.
//These credentials can be generated by calling db.encrypt_credentials/db.update_credentials. 
//But that call or other related actions (e.g., changing the directory for credentials) can't 
//be performed before _ini.start is called. That is, if delay_encryption is set to false, 
//dbs_setup would be updated with previously-generated credentials. Alternatively, delay_encryption 
//can be true, dbs_setup would include a meaningless placeholder and the credentials would be updated 
//after _ini.start is called. 
boolean delay_encryption = true;
        
HashMap<String, Object> dbs_setup = null;
        
if (samples.USE_DB)
{
    String setup = null; //This variable can be null (i.e., default setup) or equal to the given DB
                         //(i.e., corresponding types.CONFIG_[*] constant). 
    String host = null; //A null value indicates that the default host will be used 
                        //(e.g., "localhost" in MySQL).
            
    if (encrypt && !delay_encryption) 
    {
        dbs_setup = parent_ini_db.get_setup_vals(db_name, setup, user, host, encrypt);
    }
    else dbs_setup = parent_ini_db.get_setup_vals(db_name, setup, username, password, host);
}
//------

_ini.start(app_name, includes_legacy, dbs_setup);
        
if (encrypt && delay_encryption)
{
    paths.update_dir(paths.DIR_CRYPTO, samples.PERFECT_LOCATION);
    paths.update_dir(paths.DIR_CREDENTIALS, samples.PERFECT_LOCATION);

    db.update_credentials(db_crypto.SOURCE, user, username, password);
}

permalink

public abstract class samples
{
    public static final String APP = "whatevs";
    public static final String ID = "whichevs";
    public static final String USER = "whoevs";
    public static final String PLACEHOLDER = "wherevs";
    
    //Set this variable to true only if there is a valid DB setup in place 
    //(i.e., MySQL connector, valid DB name and valid credentials).
    public static final boolean USE_DB = false;
    
    //--- I would input my own values where these constants are used if I were you.
    public static final String PERFECT_NAME = PLACEHOLDER;
    public static final String PERFECT_LOCATION = PLACEHOLDER;
    public static final String PERFECT_USERNAME = PLACEHOLDER;
    public static final String PERFECT_PASSWORD = PLACEHOLDER;
    //---

    public static void print_error(String sample_id_) { print_message(sample_id_, null, false); }
    
    public static void print_message(String sample_id_, String message_, boolean is_ok_)
    {
        String message = sample_id_ + misc.SEPARATOR_CONTENT;
        
        if (is_ok_) message += message_; 
        else message += "ERROR";
        
        generic.to_screen(message);
    }
    
    public static void print_messages(String sample_id_, String[] messages_)    
    {
        String message = sample_id_;

        for (String message2: messages_) { message += misc.SEPARATOR_CONTENT + message2; }
        
        generic.to_screen(message);
    }
    
    public static void print_end() { generic.to_screen("sample code completed successfully"); }
}

permalink

Previous articles about accessory_java:

ib

A Java library easing the automated communication with Interactive Brokers (IB). It relies on and emulates the defining ideas of accessory_java. An analysis of this library is beyond the scope of the present article and its code will be merely used to support the explanations about accessory_java.

Context

Since its inception, the development of accessory_java has relied on applying quite rigid rules, something understood as almost a necessity to achieve the intended clarity and friendliness despite the absence of more conventional documentation. Although my actual commitment to comply with those requirements, even in case of ignoring logical limitations of such an intention provoked by the normal evolution of a software development project of this sort, might be incompatible with what some people expect. Someone told me once that consistency is all that matters. And perhaps he was right. But, in my opinion, that person and many other people regularly fail to adequately understand the applicable context, the only place where consistency or anything else really make any sense. And that is why the current section seems required.

First of all, these libraries are meant to be understood as part of my personal work, almost as a peek on the inner workings of how I do the stuff I want in the way I want without arbitrary restrictions or constraints, without having to deliver something to meet external requirements or to convince anyone of anything. That essence is even transmitted in the names of the two main repositories via the "_RAW" addition to their names. That inclusion isn't just meant to indicate a lack of formal documentation or compliance with the most typical requirements. It also means that the main goal of these libraries is to fulfill my personal and private needs.

I am certainly trying to ensure the highest quality in the public versions. Although such an intention has to be understood together with the ideas in the previous paragraph, a situation which might provoke outcomes somehow different to what most people are used to expect from commits to a public code repository. Even logical side effects like promotion of my programming skills are probably not meeting the most common assumptions. On the other hand, I have released the contents of those repositories into the public domain and, consequently, anyone can use them as they please without having to even mention me. Also and regardless of the referred peculiarities, using code from a public repository more or less blindly is, well... let's just say that a person doing such a thing should better look into a mirror to find someone to blame for any problem.

My position on breaking changes is another important aspect to bear in mind. I do, of course, appreciate stability and being able to trust the code. Actually, this is pretty much the whole point of having created these libraries at all. I am also perfectly aware of the multiple problems which unexpected modifications might provoke, mainly when dealing with such low-levelish code. Conversely, all this is still a work in progress and it hasn't yet attracted much interest. Breaking changes are surely undesirable, but I personally prefer that, mainly under the current conditions, rather than having to keep maintaining parts which have become useless. Nevertheless, I do try to minimise the number of modifications with a relevant impact on the overall code structure, especially breaking changes. And, in case of deciding to go down that road, I would do my level best to facilitate the location and fixing of errors, for example by changing the method signatures. But performing preventive actions of this kind isn't always ideal or even possible.

Conventions

I fully support actions having a direct, positive impact on the pursued goals of clarity and friendliness, although always by bearing in mind the referred right context and their instrumental essence. That is, I am way much more concerned about making sure that the code works as expected than about applying certain rules, whose point is precisely increasing the chances of that result happening. Apparently, I ignore a big number of issues which some people consider relevant. Not even my natural tendency to rely on certain formats or expressions should be seen as truly important, unless they bring some additional meaning or usefulness. For example, it would be very weird if I suddenly started writing code by using a style different than (kind of) snake case, mainly in these libraries, but it wouldn't really matter. The resulting arbitrary lack of consistency would probably look ugly, although it wouldn't be really affecting my aforementioned main concerns. Similar ideas should be applied to other appearance-intensive aspects like spacing, brace location, meaningless wording, etc. I will probably not be making substantial changes on any of those fronts, although this shouldn't be taken as an excuse to misinterpret my intention. The code below is a descriptive sample of how my code tends to look nowadays.

public static Charset get_encoding() 
{
    Charset output = null;

    try { output = (Charset)config.get_strings(CONFIG_ENCODING); }
    catch (Exception e) { output = null; }

    return (output != null ? output : DEFAULT_ENCODING);
}

public static boolean is_ok(String string_) { return is_ok(string_, false); }

public static boolean are_ok(String[] strings_)
{
    if (!arrays.is_ok(strings_)) return false;

    for (String string: strings_)
    {
      if (!is_ok(string)) return false;
    }

    return true;
}

permalink

General ideas

  • The used wording tries to be as descriptive as possible. Shorter names tend to be preferred, but never at the expense of reducing the quality of the information.
private static HashMap<String, String> _sources_old = new HashMap<String, String>();
	
private static boolean _sources_old_populated = false;

permalink

  • Specific words and expressions are being systematically used for packages, classes, methods, variables/constants with certain functionalities. Examples: "is_ok" (generic validity check), "is_[*]" (boolean), "check" (categorised validity check), "parent" (parent of an inheriting class), "ini" (information retrieved at startup), "start" (methods called last at startup), "quick" (DB functionalities being faster and performing less checks/adaptations than the default configuration), "quicker" (DB functionalities being the fastest and performing virtually no checks/adaptations), etc.

  • All the classes, methods, variables/constants will get a higher accessibility/visibility (e.g., converting a private variable into one accessible within the package) than the corresponding default one, as defined below, only when strictly required.

  • By default, public classes, methods and variables are assumed to be directly usable, error-proof (e.g., no unexpected nulls or graceful management of invalid inputs).

public static String substring(String input_, int start_, int length_)
{
    String output = DEFAULT;

    int length0 = get_length(input_, false);
    if 
    (
        length_ < 0 || length0 < 1 || start_ < 0 || start_ + length_ > length0 || 
        (((long)start_ + (long)length_) > (long)numbers.MAX_INT)
    ) 
    { return output; }

    return (length_ > 0 ? input_.substring(start_, start_ + length_) : input_.substring(start_));
}

permalink

  • No code warnings of any kind are allowed.
public static boolean insert(String source_, Object vals_) 
{ 
    return insert(source_, vals_, is_quick(source_)); 
}

permalink

  • The names of the methods which are locked (i.e., they call accessory.parent_static.__lock()) have to start with either a double (“__”) or a single (“_”) underscore, in case that scenario happens always or just eventually, respectively. Those modifications are recursively transmitted to any other method calling them. That is, “__method1()“, only one calling accessory.parent_static.__lock(), transmits those underscores to “__method2()“, calling “__method1()“, and to “__method3()“, calling “__method2()“, and so on and so forth. Those rules also apply to non-static classes whose constructors call methods which are indirectly/directly, fully/partially locked, although a double underscore will only be used with classes where all their constructors are always locked. Note that calling a locked class constructor is equivalent to calling a locked method. As shown below these lines, ib._order is a good example of partially-locked class.
public _order(_order input_) { instantiate(input_); }
	
public _order(String type_place_, String symbol_, double quantity_, double stop_, double start_) 
{ 
    instantiate
    (
        type_place_, symbol_, quantity_, stop_, start_, common.WRONG_PRICE, __get_new_order_id()
    ); 
}
	
public _order
(
    String type_place_, String symbol_, double quantity_, 
    double stop_, double start_, double start2_
) 
{ instantiate(type_place_, symbol_, quantity_, stop_, start_, start2_, __get_new_order_id()); }
	
public _order
(
    String type_place_, String symbol_, double quantity_, double stop_, 
    double start_, double start2_, int id_main_
) 
{ instantiate(type_place_, symbol_, quantity_, stop_, start_, start2_, id_main_); }

permalink

Packages

  • The default package is never used.

  • In case of including a single package, the name should be descriptive of the given library/application (e.g., "accessory").

  • Packages can be used as an upper-level classification, as a clean way to group common subfunctionalities together, although only in case of it being advisable due to the number of classes. Even though some accessory_java parts (e.g., the DB classes: db and the other ones whose names start with "db_") could be considered to meet that requirement, the current single-package approach is unlikely to be changed in the future.

  • In case of relying on multiple packages, a descriptive keyword should be added when the names are generic enough to probably be used in other resources. All the classes named after the ones being part of accessory_java are expected to be included in a package called "accessory" (e.g., "accessory_ib").

Classes

  • By default, classes are only accessible at the package level (no access modifier).

  • Unless their configuration and expected usage advise otherwise, classes are abstract and static by default. The next sections can be helpful to understand that statement better.

  • It has to be possible to deduce the main functionality of the class from its name. The names of classes including a common functionality should make a clear reference to it, like what happens in accessory_java with the DB classes. Those requirements could be fulfilled at the package level (e.g., common, apps or basic are included in the db_ib package).

  • The names of the classes automatically loaded at the very beginning have to start with a single underscore.

Methods

  • By default, methods are private, unless being accessible within the package and included in a class with this same accessibility, in which case they would be public.

  • All the methods are static, except for the ones in non-static classes exclusively interacting with non-static resources. See next sections to understand better the static/non-static peculiarities.

  • The names of the methods which are private or exclusively used by others to perform some of their intermediate actions should include a reference to the calling method(s). For example: in accessory.strings, remove_escape_replace is being called from all the public remove, escape, unescape and replace overloads.

Variables/Constants

  • By default, variables are local (i.e., defined at the method level) and private. Constants (i.e., variables which aren't modified and which can include the final keyword or not) are global (i.e., defined at the class level) and private. Global variables/constants accessible within the given package and included in a class having this same status are public by default.

  • A value is assigned to all the variables/constants when they are defined.

public static final String ID = _types.CONFIG_CRYPTO_DB_FIELD_ID;
public static final String ALGO = _types.CONFIG_CRYPTO_DB_FIELD_ALGO;
public static final String IV = _types.CONFIG_CRYPTO_DB_FIELD_IV;
public static final String KEY = _types.CONFIG_CRYPTO_DB_FIELD_KEY;

public static final int MAX_SIZE_ID = db_common.MAX_SIZE_KEY;
public static final int MAX_SIZE_ALGO = db_common.MAX_SIZE_KEY;
public static final int MAX_SIZE_IV = 100;

public static final String WHAT_KEY = crypto.WHAT_KEY;
public static final String WHAT_IV = crypto.WHAT_IV;
public static final String WHAT_ALGO = crypto.WHAT_ALGO;

public static final String DEFAULT_SOURCE = _types.CONFIG_CRYPTO_DB_SOURCE;

private static String _source = DEFAULT_SOURCE;

permalink

  • The variable and constant names are written in lowercase and uppercase, respectively.

  • The global variable names start with a single underscore. At the local level, the method parameters end with a single underscore and the names of all the other variables neither start nor end with an underscore.

Structure

The code of accessory_java is formed by five main parts: startup, setup, parents, internal classes and public classes.

Startup

More detailed explanations about this part will probably be included in a future article. For the time being, it is enough to know that most of the global collections (or "arrays", as generically referred in accessory_java) aren't populated when being defined (i.e., automatically, when the given class is loaded, either directly or via a specific method), but from generic methods which are called at startup in a certain order. And this is precisely the main reason justifying this startup part and its complexity.

Basically, all the accessory_java classes whose names start with a single underscore have to be loaded at the very beginning, before using any of the library's functionalities. These methods also assign values to single variables, but they are much less important. For example, the setup described in the next subsection, where all the DB information is also included, is populated here.

Setup

This part is mainly managed by the accessory.config class, whose defining keys are stored as "CONFIG_" constants in accessory.types. A deeper discussion of all this will be included in another article, although the main idea is easy to understand: a global collection storing the library's main setup which is automatically loaded as part of the aforementioned startup.

There is no clear definition of what should or shouldn't be included here. There aren't even truly functional differences between these properties and the contents of any other collection, loaded at startup or not. The main goal is to store all the relevant information in a common place, with a common structure and common modification/storage rules. What to include here? The (categorised) bits of information which are more important and which, although potentially modifiable, are likely to always use the same values (stored as "DEFAULT_" constants).

Parents

The classes whose names start with "parent_" include the common features which other classes inherit. Except for accessory.parent_static, all these classes are non-static, although they can include static elements. A descriptive example is shown below these lines (accessory.parent_ini_config).

public abstract class parent_ini_config 
{
    protected boolean _populated = false;

    protected abstract void populate_all_internal();

    @SuppressWarnings("unchecked")
    public static HashMap<String, Object> get_config_default_generic
    (
        String type_, HashMap<String, Object> vals_
    )
    {
    
        HashMap<String, Object> output = (HashMap<String, Object>)arrays.get_new(vals_);
        
        for (String subtype: _types.get_subtypes(type_))
        {
            if (output.containsKey(subtype)) continue;
            
            output.put(subtype, strings.DEFAULT);
        }
        
        return output;
    }
    
    protected void populate_all()
    {
        if (_populated) return;
        
        populate_all_internal();
        
        _populated = true;
    }
    
    protected boolean populate
    (
        String type_store_, String type_root_, HashMap<String, Object> vals_
    )
    {
        String type_store = config.check_type(type_store_);
        if (!strings.is_ok(type_store) || !arrays.is_ok(vals_)) return false;
        
        String type_root = config.check_type(type_root_);
        if (!strings.is_ok(type_root)) type_root = type_store;
        
        return arrays.value_exists
        (
            config.update_ini
            (
                type_store_, get_config_default_generic(type_root, vals_)
            ), 
            false
        );
    }
}

permalink

In the current stage of development, non-static classes and inheritance in general have become a secondary priority. And this part of the code must be seen as one of the noticeable outcomes of the peculiarities associated with the evolution of this project, as highlighted in the next section.

The ideas in the previous paragraph are also applicable to the accessory.parent_static class with one important caveat: the __lock/__unlock methods.

private static volatile boolean _locked = false;
private static volatile boolean _locked2 = false;

public static void __lock()
{
    long elapsed = dates.start_elapsed();

    boolean locked2 = false;

    while (true)
    {
        if (!locked2)
        {
            if (!_locked2 || (dates.get_elapsed(elapsed) >= MAX_LOCK_ELAPSED))
            {
                _locked2 = true;
                locked2 = true;
            }
        }
        else
        {
            if (!_locked || (dates.get_elapsed(elapsed) >= MAX_LOCK_ELAPSED))
            {
                _locked = true;
                _locked2 = false;

                return;
            }
        }
    }
}

public static void __unlock() { _locked = false; }

public static boolean is_ok() 
{
    boolean output = _is_ok;

    _is_ok = true;

    return output; 
}

permalink

There are no plans to modify the implementation of these two methods (e.g., converting them into public static methods in a class like accessory.generic, for example). Although they surely represent a useful resource when dealing with asynchronous scenarios, as proven in different places of the ib package (sample below).

private static int __get_new_id(String symbol_)
{
    __lock();

    String[] symbols = async_data_apps_quicker.get_symbols();

    int max_id = async_data_apps_quicker.get_max_id();

    int first_id = WRONG_ID;
    int last_id = async_data_apps_quicker.get_last_id();

    int id = last_id;

    while (true)
    {
        id++;
        if (id > max_id) id = MIN_ID;

        boolean is_first = false;

        if (first_id == WRONG_ID) first_id = id;
        else if (id == first_id) is_first = true;

        int i = get_i(id, true);

        if (is_first || symbols[i] == null) 
        {
            if (is_first && symbols[i] != null) 
            {
                calls.cancelMktData(id);

                async_data_apps_quicker.stop(id, symbols[i], true, false);
            }

            break;
        }
    }

    if (id_is_ok(id)) async_data_apps_quicker.start(symbol_, id);

    __unlock();

    return id;
}

permalink

Internal classes

These are the classes which are exclusively accessible within the accessory package and, consequently, used internally by other classes. Their sole purpose is to make sure that the library's structure is as clear, consistent and coherent as possible.

Public classes

These are the classes fulfilling the final goal of accessory_java, the ones about which the final user should be eminently concerned. By default, these classes are abstract and their public methods are static.

Non-static classes can be the best choice under certain conditions. That is, in situations involving somewhat complex, self-contained operations, ideally when they aren't being used too often or in scenarios where speed/efficiency is an important factor. Good examples are accessory.crypto or some of the non-static DB classes (e.g., db_field, db_where or db_order).

Evolution

My intention, the scope of these libraries and even my expertise in some aspects of Java programming were quite different months ago, when I started this development. They were originally meant to somehow complement the rebuilding of certain system, almost just a migration from a different programming language, but everything has grown way beyond those original plans. The development of this new system, much better than the old one, has been representing a constant motivation and source of inspiration to keep improving these libraries. This systematic growth, always striving for the best version, the one facilitating my work as much as possible, with little concern for maintaining what isn't useful anymore, systematically improving and redefining approaches and conventions, is the main responsible for the current structure of accessory_java: pretty homogeneous, but including quite a few heterogeneities.

Virtually any software development project goes through an evolution of this sort, where it is very difficult to maintain an overall coherent format, even when being just one person applying quite rigid rules. Rules that, on the other hand, I have been defining and tuning for the most part precisely while working on this development. If I restarted the project now, blank slate, many parts would be quite different and the resulting code would surely be much more homogeneous and formally consistent, probably punctually better and more efficient. But such an eventuality is currently out of picture. In any case, heterogeneity isn't something necessarily negative, mainly when it allows outcomes which might be peculiar but surely worthy, and probably unattainable in any other way. Some times, scars are beautiful.

Non-static classes

I am not exactly a fervent fan of non-static classes, and the main reason for that is efficiency. Writing fast code and using as little resources as possible (without sacrificing code friendliness) is a natural tendency of mine. Relying on a class rather than on a simple collection, for example, is evidently cleaner, more elegant, but also much less efficient, to not mention the additional work required to compensate for the inbuilt functionalities of the collection. In case of relying on inheritance, for example, the scalability would be better, but more resources would be used. It seems intuitively clear that having to deal with information of, at least, two classes is less efficient than focusing on just one.

When starting my work on accessory_java, my overall intention was to build something "cleaner" than usual. Also, I was firstly focusing on parts where non-static classes were indeed the best option (e.g., accessory.db_field). Additionally, my ideas about the exact requirements of certain parts weren't too clear. Long story short, most of the startup classes are non-static, a circumstance that perhaps isn't ideal or, at least, isn't what I would be doing now. Either way, everything works fine and relevant changes aren't expected. Similar ideas should apply to accessory.tests/accessory.parent_tests, another part which works pretty well (e.g., the tests are automatically updated to include any new methods added to certain classes), but which I would probably develop in a different way now.

Static inheritance

Additionally to the already mentioned __lock/__unlock methods, accessory.parent_static includes other resources which were originally created with the intention of being used more widely.

public abstract class parent_static 
{
    static boolean _is_ok = true;
    
    protected static HashMap<String, Object> _temp = new HashMap<String, Object>();
    
    private static final long MAX_LOCK_ELAPSED = 1;
    
    private static boolean _ignore_errors = false;
    private static boolean _ignore_errors_persistent = false;
    private static boolean _error_triggered = false;
    
    private static volatile boolean _locked = false;
    private static volatile boolean _locked2 = false;
    
    public static void __lock()
    {
        long elapsed = dates.start_elapsed();
        
        boolean locked2 = false;
        
        while (true)
        {
            if (!locked2)
            {
                if (!_locked2 || (dates.get_elapsed(elapsed) >= MAX_LOCK_ELAPSED))
                {
                    _locked2 = true;
                    locked2 = true;
                }
            }
            else
            {
                if (!_locked || (dates.get_elapsed(elapsed) >= MAX_LOCK_ELAPSED))
                {
                    _locked = true;
                    _locked2 = false;
                    
                    return;
                }
            }
        }
    }
    
    public static void __unlock() { _locked = false; }
    
    public static boolean is_ok() 
    {
        boolean output = _is_ok;
        
        _is_ok = true;
        
        return output;
    }
    
    public static void ignore_errors() { ignore_errors(false); }
    
    public static void ignore_errors(boolean persistent_) 
    {
        _ignore_errors = true; 
        _ignore_errors_persistent = persistent_;
    }

	public static void ignore_errors_persistent_end() 
	{ 
		_ignore_errors_persistent = false; 
		_ignore_errors = false;
	}
	
    public static boolean update_temp(String key_, Object val_)
    {
        if (!strings.is_ok(key_) || val_ == null) return false;
        
        if (!arrays.is_ok(_temp)) _temp = new HashMap<String, Object>();
        _temp.put(key_, val_);
        
        return true;
    }
    
    protected static boolean ignore_errors_internal() 
    { 
        boolean output = _ignore_errors;

        if (!_ignore_errors_persistent) _ignore_errors = false;

        return output;
    }

    protected static void manage_error(String type_, Exception e_, HashMap<String, Object> info_)
    {
        if (!ignore_errors_internal()) 
        {
            errors.manage(type_, e_, info_); 

            _error_triggered = true;
        }
    }

    protected static void method_start()
    {
        _is_ok = false;
        _error_triggered = false;
    }

    protected static void method_end()
    {
        if (_error_triggered) 
        {
            _is_ok = false;
            _error_triggered = false;
        }
        else _is_ok = true;
    }
}

permalink

In fact, those resources are only being extensively used in one class: accessory.io. It is an important class which works pretty well and, in principle, I don't see any problem with that implementation. For example, its systematic reliance on method_start, method_end and manage_error seems clean and efficient, even a good approach to be implemented in a big number of static methods. But it simply didn't happen. And, at the moment, I have no plans to change this situation and to start using these resources everywhere.

DB classes

The DB classes are undoubtedly the part of accessory_java where the referred "scars" are more evident. This is perhaps where the reliance on non-static classes was more forced and less ideal, because efficiency is certainly very important here.

I will go into the specifics of all these classes in a future article. For now, it is enough to know that there is a main class (accessory.db), which relies on various internal classes, the one of the specific DB system (currently only MySQL is supported) among them. These classes represent the original version of this whole part of the code, the one relying on non-static classes and systematically checking (and adapting) everything. Despite reaching a pretty good level of reliability and actually delivering the kind of fully-managed, worry-free approach which accessory_java is precisely meant to offer, it soon became clear that, under these conditions, the best attainable performance was way below my expectations.

And this is when the "is_quick" addition was born. Namely, a simple boolean parameter added to all the querying methods and enabling a less-checks/-adaptations scenario. It still seemed improvable and that's why I created the "quicker" alternative: a fully static approach performing virtually no checks/adaptations.

After these three DB modes came into picture, I kept improving their coordination and easing their management, by creating new classes, algorithms and common ways to use their resources (firstly in ib and in my private code, and later in accessory_java). A reasonably mature version allowing a friendly and scalable management of this peculiar setup is very recent, and probably still incomplete. This whole part is likely to continue evolving in the upcoming weeks.

Out of all the referred peculiarities, this is undoubtedly my favourite one. The one which tells the most, the living story of how this development has been happening. And the one whose final outcomes are the least likely to have been achieved in any other way.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment