class AbstractBackendStore<Session extends BackendSession>
        implements BackendStore {

    // TODO: move SystemSchemaStore into backend like MetaStore
    private final SystemSchemaStore systemSchemaStore;
    private final MetaDispatcher<Session> dispatcher;

    public AbstractBackendStore() {
        this.systemSchemaStore = new SystemSchemaStore();
        this.dispatcher = new MetaDispatcher<>();
    }

    protected MetaDispatcher<Session> metaDispatcher() {
        return this.dispatcher;
    }

    public void registerMetaHandler(String name, MetaHandler<Session> handler) {
        this.dispatcher.registerMetaHandler(name, handler);
    }

    @Override
    public String storedVersion() {
        throw new UnsupportedOperationException(
                "AbstractBackendStore.storedVersion()");
    }

    @Override
    public SystemSchemaStore systemSchemaStore() {
        return this.systemSchemaStore;
    }

    // Get metadata by key
    @Override
    public <R> R metadata(HugeType type, String meta, Object[] args) {<FILL_FUNCTION_BODY>}

    protected void checkOpened() throws ConnectionException {
        if (!this.opened()) {
            throw new ConnectionException(
                    "The '%s' store of %s has not been opened",
                    this.database(), this.provider().type());
        }
    }

    @Override
    public String toString() {
        return String.format("%s/%s", this.database(), this.store());
    }

    protected abstract BackendTable<Session, ?> table(HugeType type);

    // NOTE: Need to support passing null
    protected abstract Session session(HugeType type);
}

        Session session = this.session(type);
        MetaDispatcher<Session> dispatcher;
        if (type == null) {
            dispatcher = this.metaDispatcher();
        } else {
            BackendTable<Session, ?> table = this.table(type);
            dispatcher = table.metaDispatcher();
        }
        return dispatcher.dispatchMetaHandler(session, meta, args);
    
<no_super_class>