Giter VIP home page Giter VIP logo

ff4j's Introduction

✨✨✨ FF4J - Feature Flipping for Java ✨✨✨

Build Status Backers on Open Collective Sponsors on Open Collective Maven Central codecov

Codacy Badge chat License Apache2

FF4j, is an implementation of the Feature Toggle pattern.

🤘Features

  • Feature Toggle: Enable. and disable features at runtime - no deployments. In your code implement multiple paths protected by dynamic predicates (if/then/else).

  • Role-based Toggling: Enable features not only with flag values but also drive access with roles and groups (Canary Release). Different frameworks supported starting by Spring Security.

  • Strategy-based Toggling: Implement custom predicates (Strategy Pattern) to evaluate if a feature is enabled. Some are provided out of the box: White/Black lists ,Time based, Expression based. Connect external source like a Drools rule engine.

  • AOP-driven Toggling: Keep your code clean and readable: Avoid nested if statements but use annotations. Thanks to Spring AOP target implementation is pick at runtime, and thus driven by feature statuses.

  • Features Monitoring: For each features execution, ff4j evaluates the predicate therefore it's possible to collect and record events, metrics to compute nice dashboards or draw curves for features usage over time.

  • Audit Trail: Each action (create, update, delete, toggles) can be traced and saved in the audit trail for troubleshooting. With permissions management (AuthorizationManager) it's possible to identify users.

  • Web Console: Administrate FF4j (including features and properties) with the web UI. Packaged as a servlet in the library you will expose it in your backend applications. Almost 10 languages available.

  • Wide choice of Databases Our proud: we support 20+ databases technologies to store your features, properties and events. Same business model, multiple implementations. Thanks to extension points it's easy to build your own.

  • Spring Boot Starter Import ff4j-spring-boot-starter dependency in your microservices to get the web console and rest api working immediately. (To be used for the backend app. Now compliant with Spring Boot 2x: 👉 SAMPLES

  • REST Api Operate FF4j through a WEB API. This is the way to go to use ff4j with others languages, specially javascript frontends.(also: leverage on FeatureStoreHttp to avoid microservices to directly connect to the DB.

  • Properties (CMDB) Store not only feature statuses but any property value.. Create properties you can change at runtime . It is integrated with most used frameworks like Spring, Archaius, commons-config or Consul.

  • (Distributed) Cache Evaluating predicates may put pressure on DB (high hit ratio). ff4j provides local and distributed caches to help. (edit feature also evict cache). Leveraging JSR-107 it supports most of cache solutions.

  • Command Line Interface To automate things or because web ports may be blocked (you know, production...) you can work through SSH using our Command Line Interface (cli), our Shell #devOps. It will interact directly with storages.

  • JMX and MBeans Limited set of operations can be performed through JMX. ff4j exposes some Mbeans to read metrics or toggle features from external tools (Nagios...). Not all applications are web based.(batches, shell, standalone...)

More information can be found at ff4j.org or Reference Documentation in the wiki.

🔨 Getting Started

Check the Getting started here

👀 Screenshot

Home Page

Features

Monitoring

👤Contributors

This project exists thanks to all the people who contribute. [Contribute].

Backers

Thank you to all our backers! 🙏 [Become a backer]

Sponsors

Support this project by becoming a sponsor. Your logo will show up here with a link to your website. [Become a sponsor]

ff4j's People

Contributors

ablaszczyk avatar aclemons avatar alexandrenavarro avatar asood123 avatar balasubramanyamb avatar clun avatar danielwisky avatar dependabot[bot] avatar drizztguen77 avatar felipeadorno avatar georgekankava avatar harryemartland avatar honsq90 avatar ian-cit avatar jackpf avatar jayakumarc avatar jeromevdl avatar kutzi avatar leoaugust19 avatar mariusz-zawadzki avatar mjeanroy avatar mrgrew avatar nclro avatar paul58914080 avatar shoito avatar snyk-bot avatar tbcs avatar thyago avatar twillouer avatar yschimke avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

ff4j's Issues

Implement Audit trail for Changes to feature flags

We need to have a auditing capability so that we always can trace who did which change.

One way of doing it would be a write through hazelcast to a GIT-repo with the user as committer and/or author.

Maven central Artifact Java 1.6

Hi ,
The artifact in the Maven central are not java 1.6 compatible .

How we can use ff4j with java 1.6 project , without compiling ff4j-core in local ?

Thanks in Advance

ff4j leaves non-daemon threads running

If the eventpublisher self-initialises, there is no code which eventually stops the thread pool. You will then have something like:

"ff4j-monitoring-pool-1-thread-1" prio=10 tid=0x00007fbc10084000 nid=0x3c4d waiting on condition [0x00007fbb7e140000]
   java.lang.Thread.State: WAITING (parking)
        at sun.misc.Unsafe.park(Native Method)
        - parking to wait for  <0x0000000790456708> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
        at java.util.concurrent.locks.LockSupport.park(LockSupport.java:186)
        at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2043)
        at java.util.concurrent.ArrayBlockingQueue.take(ArrayBlockingQueue.java:374)
        at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1068)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1130)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
        at java.lang.Thread.run(Thread.java:745)

which prevents cleanly stopping the application

Improve website

There are some corrections to be made one the website:

  • Link the pdf from github instead of sourceforge
  • The images and links of the use-cases do not work correctly

Since it is a static site, I recommend using github to host the site.

Document extension points

Some interface allow to implements your own behavior as the FlippingStrategy and it should be more visible

Date format bug in ReleaseDateStrategy

Hi,
The date format specified in the ReleaseDateStrategy must be set to "yyyy-MM-dd-HH:mm" instead of "yyyy-MM-DD-HH:mm", since DD is day in year instead of day in month.

Thank you,
Arsen

ff4j tablig location is not correct ?

When using the ff4j taglib, I encountered the following error :

The absolute uri: http://www.ff4j.org/taglibs/ff4j cannot be resolved in either web.xml or the jar files deployed with this application

In documentation it says that the taglib should be discovered if the file is present in META-INF. I'm not sure whether the file should be present in the root of META-INF folder or not.

For information, I use jetty.

ff4j-console issue "FF4J has not been initialized"

Might just be a documentation issue, but have added ff4j-web as a dependency and included servlet AdministrationConsoleServlet as per https://github.com/clun/ff4j#administr-ation-servlet.

However, this gives the following response when /ff4j-console request is made:

2014-03-26 16:34:41.440:WARN:oejs.ServletHandler:qtp765511918-56: /ff4j-console
java.lang.IllegalArgumentException: FF4J has not been initialized.
at org.ff4j.web.FF4jWebContextHolder.getFf4j(FF4jWebContextHolder.java:57)

Is there something else that needs to be done to initialize FF4J, other than the Spring component scan?

why using text/plain for CONTENT_TYPE_JS ?

Hi,

In the embedded console, the js file is served with a content type "text/plain"
So if I deploy this servlet in an application with spring security, I get the following error when I get to the page :

Refused to execute script from 'https://localhost:8080/admin/ff4j?rsc=js' because its MIME type ('text/plain') is not executable, and strict MIME type checking is enabled.

Is there any reason to serve the file in "text/plain" instead of "application/javascript" ?

Missing remote client persistance options to allow for HA

It would be useful to be able to decorate any FeatureStore with caching and persistence.

I need to be able to use a remote client that continues working even if the central server is unavailable. Even if you start the application when the central store is down you should be able to fall back onto the previous value. I am thinking of someything like this:

  • If feature flag is in cache and not older than timeout specified - use that.
  • If cache expired, try remote.
  • If remote timeout (needs to be configurable), trip circuit breaker and try loading from local persistance store.
  • If remote answers, put into cache and persist to local storage
  • If all fails, use default value

FF4j.exportFeatures() method produces invalid XML for ExpressionFlipStrategy

I have the following org.ff4j.strategy.el.ExpressionFlipStrategy in ff4j.xml file which I use for initializing FF4j instance.

<feature uid="kernel-featrue1" enable="false" description="description" >
        <flipstrategy class="org.ff4j.strategy.el.ExpressionFlipStrategy">
                <param name="expression"><![CDATA[Feature1 & !Feature2]]></param>
        </flipstrategy>
</feature>

But when I use FF4j.exportFeatures() method, it produces the following XML element which is invalid.

<flipstrategy class="org.ff4j.strategy.el.ExpressionFlipStrategy" >
     <param name="expression" value="Feature1 & !Feature2" />
</flipstrategy>

Missing import org.ff4j.store.FeatureStoreMongoDB;

org.ff4j.test.store.FeatureStoreMongoDBCore1Test refers to org.ff4j.store.FeatureStoreMongoDB;

However org.ff4j.store.FeatureStoreMongoDB is not in the 1.2 distribution or in the source. Can you please address this?

Fix FeatureCacheProviderRedis

Currently, the constructor for FeatureCacheProviderRedis accepts 2 parameters but uses defaults while creating new Jedis object.

public FeatureCacheProviderRedis(String host, int port) {
    jedis = new Jedis(redisHost, redisport);
}

Kindly update the constructor to use the parameters.

The method below uses a prefix to search for cache keys.

@Override
public Set<String> listCachedFeatureNames() {
    return jedis.keys(PREFIX_KEY + "*");
}

I believe the presence of the prefix "PREFIX_KEY" may be an error. The "put" method does not use the prefix to cache the feature. Hence this method returns empty set unless it is backed by a Redis feature store (which is not correct, as in that case it return the actual feature rather than the cached feature)

Fix documentation - JDBC Store

The script to create the tables needed to store the features via JDBC has a problem. The column "FEAT_UID" has different sizes, but they should be the same:

-- Main Table to store Features CREATE TABLE FF4J_FEATURES ( "FEAT_UID" VARCHAR(100), ... );

-- Roles to store ACL, FK to main table
CREATE TABLE FF4J_ROLES (
"FEAT_UID" VARCHAR(50) REFERENCES FF4J_FEATURES("FEAT_UID"),
...
);

The column should VARCHAR(100) for both:

-- Main Table to store Features CREATE TABLE FF4J_FEATURES ( "FEAT_UID" VARCHAR(100), ... );

-- Roles to store ACL, FK to main table
CREATE TABLE FF4J_ROLES (
"FEAT_UID" VARCHAR(100) REFERENCES FF4J_FEATURES("FEAT_UID"),
...
);

Android usage

Hello,

I am interested to use feature toggles on an Android project. Do you have any experience on this? Is it possible?

Cheers.

Cannot map ff4j-1.4.0.xsd to Java classes using xjc

ff4j-1.4.0.xsd contains two attribute elements with name value. When running
xjc ff4j-1.4.0.xsd
xjc issues the following error:
Property "Value" is already defined

While it's possible using xml bindings file and map value to valueAttribute, it's a bad solution because further along the way when you unmarshal an ff4j xml with JAXB you'll get:
Multiple fields representing property "value"

I suggest changing the value attribute names in the xsd to something like valueAttribute

FF4J.disableGroup() enable the group instead

Need to change:

    public FF4j disableGroup(String groupName) {
        getFeatureStore().enableGroup(groupName);
        getEventPublisher().publish(groupName, EventType.DISABLE_GROUP);
        return this;

To

    public FF4j disableGroup(String groupName) {
        getFeatureStore().disableGroup(groupName);
        getEventPublisher().publish(groupName, EventType.DISABLE_GROUP);
        return this;

Having a default configuration when using FeatureStoreMongodb

Hi,

I'm using FeatureStoreMongodb and I'm wondering if if would be a good idea to be able to pass a filename to initialize with features configuration if features do not exist in database.

Currently, I use a workaround like this :

    @Bean
    public FeatureStore featureStore() {
        MongoCollection ff4j = jongo.getCollection("ff4j");
        FeatureStoreMongoDB storeMongoDB = new FeatureStoreMongoDB(ff4j.getDBCollection());

        InMemoryFeatureStore localStore = new InMemoryFeatureStore("ff4j.xml");
        Map<String, Feature> features = localStore.readAll();

        features.keySet()
                .stream()
                .filter(featureName -> !storeMongoDB.exist(featureName))
                .forEach(featureName -> storeMongoDB.create(features.get(featureName)));

        return storeMongoDB;
    }

I divert the original use of InMemoryFeatureStore just to use its parsers. Then I check if features exists in the mongodb store. And if not, I create the feature.
That's a solution to initialize the application with some features or to add features lately.

Maybe another way of writing that would be

    @Bean
    public FeatureStore featureStore() {
        MongoCollection ff4j = jongo.getCollection("ff4j");
        // all the previous logic managed by FeatureStoreMongoDB ?
        return new FeatureStoreMongoDB(ff4j.getDBCollection(), "ff4j.xml");
    }

ConsoleServlet

ConsoleServlet should response with Javascript content-type when provided JavaScript scripts

Javascript issue when editing feature in console

I am testing with ff4j version 1.3.5

When i want to edit a feature i click on the edit button and it looks like this:

2016-01-12_153536

When i select any Group, Strategy or Permission, then i come to this page.

2016-01-12_153629

When i click the back button, no change is stored.

My browser is Mozilla Firefox 43.0.4

Export problem

When I try to export the features, I get funny result.
I get the xml file and the html file concateneted in one file.

For example :

<?xml version="1.0" encoding="UTF-8" ?>
<head/><features xmlns="http://www.ff4j.org/schema/ff4j" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.ff4j.org/schema/ff4j http://ff4j.org/schema/ff4j-1.2.0.xsd">>
<feature uid="choose_lang" description="Capability to choose language" enable="true">
<flipstrategy class="com.hopwork.ff4j.strategy.AdminOrAccountFilterStrategy">
<param name="accounts" value="some values"/>
</flipstrategy>
</feature>
<feature uid="moderation" description="Enable moderation" enable="true">
</feature>
</features>
<!DOCTYPE config>
<html lang="en">
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="apple-mobile-web-app-capable" content="yes">
<title>FF4J - Embedded Administration Console</title>
<link href="/admin/ff4j?rsc=css" rel="stylesheet">
<script type="text/javascript" charset="utf-8" src="/admin/ff4j?rsc=js"></script>
</head>
....

If I extract the xml part it's ok, the file is valid.

FF4j disableGroup method enables instead of disable

See: getFeatureStore().enableGroup(groupName) in the following method. Issue in ff4j-core-1.3.1

/**
* Disable group.
*
* @param groupName
* target groupeName
* @return current instance
*/
public FF4j disableGroup(String groupName) {
getFeatureStore().enableGroup(groupName);
getEventPublisher().publish(groupName, EventType.DISABLE_GROUP);
return this;
}

ff4j renames my http threads

The thread which calls FF4j.check(String) get renamed to "FF4J Monitoring worker processing xyz". This is caused by the constructor for org.ff4j.audit.EventWorker. I am checking a flag in a servlet filter and my tomcat threads are being renamed.

Reference documentation online

Is there a way to read the reference documentation online without downloading a pdf?
I haven`t found a way, but i prefer to read it online instead of opening a pdf.

So maybe also add the documentation on http://ff4j.org/?

Cannot switch off auditing

I could like to completely disable the auditing in ff4j in certain scenarios. Unfortunately EventPublisher is not an interface, so I cannot implement a NoOpEventPublish, but with the new setter for EventPublisher, I could set the EventPublisher to something like:

        new EventPublisher() {
            @Override
            public void publish(final Event e) {
                // do nothing
            }
        };

It would be nice if org.ff4j.FF4j had a setAuditEnabled(boolean) or something similiar which would cause it to not call getEventPublisher().publish(..) in the first place.

What do you think?

Console crashes when strategy is initialized via Spring bean

ConsoleServlet generates exception on renderPage method when a custom strategy is configured as Spring bean.
Attached is the screenshot.
2014-10-09_14-33-28

Here is my config in Spring xml:

<bean id="ff4j" class="org.ff4j.FF4j" >
    <property name="store" ref="ff4j.store.inmemory" />
</bean>
<bean id="ff4j.store.inmemory" class="org.ff4j.store.InMemoryFeatureStore" >
    <constructor-arg type="java.util.Map">
        <map key-type="java.lang.String" value-type="org.ff4j.core.Feature">
            <entry key="myFeature" value-ref="myFeatureRef/>
        </map>
    </constructor-arg>
</bean>
<bean id="myFeatureRef class="org.ff4j.core.Feature">
    <constructor-arg name="uid" value="myFeature"/>
    <property name="enable" value="true"/>
    <property name="description" value="Feature description"/>
    <property name="flippingStrategy" ref="myReleaseStrategy"/>
    <property name="group" value="myFeatureGroup"/>
</bean>
<bean id="myReleaseStrategy" class="com.mycompany.feature.ReleaseDateFlipStrategy">
    <constructor-arg type="java.lang.String" value="${effectiveDate}"/>
</bean>

Editing feature in embedded console disables feature

If I edit a feature which is currently enabled through the embedded console, the feature status if flipped to false after the update. This is because the console servlet calls:

org.ff4j.web.embedded.ConsoleOperations.updateFeatureDescription(FF4j, HttpServletRequest)

which does not read the feature from the store but instead uses:

Feature fp = new Feature(featureId, false);

This means the object passed to the feature store for update always has its status set to false. Is this expected behaviour?

EventRepository codehale metrics

Today metrics relative to feature can go to JDBC or Inmemory databases with custom schema.

We wloud like to leverage on metrics and its numerous exporters. As a consequence will will publish the monitoring information into a metrics registry.

Question about FeatureTagEnable

Hi,

I'm running a benchmark on our application and I found a place that looks weird at the first place in FeatureTagEnable :

executionContext.putString("LOCALE", pageContext.getRequest().getLocalName());

This is the full stack trace :

http-nio-8087-exec-173@13802" daemon prio=5 tid=0x14e nid=NA runnable
  java.lang.Thread.State: RUNNABLE
      at java.net.Inet6AddressImpl.getHostByAddr(Inet6AddressImpl.java:-1)
      at java.net.InetAddress$2.getHostByAddr(InetAddress.java:932)
      at java.net.InetAddress.getHostFromNameService(InetAddress.java:617)
      at java.net.InetAddress.getHostName(InetAddress.java:559)
      at java.net.InetAddress.getHostName(InetAddress.java:531)
      at org.apache.coyote.http11.Http11NioProcessor.actionInternal(Http11NioProcessor.java:341)
      at org.apache.coyote.http11.AbstractHttp11Processor.action(AbstractHttp11Processor.java:919)
      at org.apache.coyote.Request.action(Request.java:379)
      at org.apache.catalina.connector.Request.getLocalName(Request.java:1277)
      at org.apache.catalina.connector.RequestFacade.getLocalName(RequestFacade.java:989)
      at javax.servlet.ServletRequestWrapper.getLocalName(ServletRequestWrapper.java:326)
      at javax.servlet.ServletRequestWrapper.getLocalName(ServletRequestWrapper.java:326)
      at javax.servlet.ServletRequestWrapper.getLocalName(ServletRequestWrapper.java:326)
      at javax.servlet.ServletRequestWrapper.getLocalName(ServletRequestWrapper.java:326)
      at javax.servlet.ServletRequestWrapper.getLocalName(ServletRequestWrapper.java:326)
      at javax.servlet.ServletRequestWrapper.getLocalName(ServletRequestWrapper.java:326)
      at javax.servlet.ServletRequestWrapper.getLocalName(ServletRequestWrapper.java:326)
      at javax.servlet.ServletRequestWrapper.getLocalName(ServletRequestWrapper.java:326)
      at org.ff4j.web.taglib.FeatureTagEnable.eval(FeatureTagEnable.java:52)
      at org.ff4j.web.taglib.AbstractFeatureTag.doStartTag(AbstractFeatureTag.java:86)

Each time this is called, there is a Inet6AddressImpl.getHostByAddr called and it's quite costly.

What is the point of having the host name ?

ClassCastException: org.bson.LazyBSONList cannot be cast to org.bson.types.BasicBSONList

When adding a feature using FeatureStoreMongodb, the produced json in database is :

> db.ff4j.find().pretty()
{
        "_id" : "choose_lang",
        "enable" : false,
        "description" : "Capability to choose language",
        "groupname" : null,
        "strategy" : "com.hopwork.ff4j.strategy.AdminOrAccountFilterStrategy",
        "expression" : "some values",
        "roles" : [ ]
}

However, it fails in FeatureDBObjectMapper on line 92 :

        if (dbObject.containsField(ROLES)) {
            for (Object role : (BasicBSONList) dbObject.get(ROLES)) {
                authorisation.add(role.toString());
            }
        }

=> 
ClassCastException: org.bson.LazyBSONList cannot be cast to org.bson.types.BasicBSONList

If I unset this variable in mongoshell, everything works correctly.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.