Giter Site home page Giter Site logo

adobe / bridgeservice Goto Github PK

View Code? Open in Web Editor NEW
3.0 6.0 3.0 600 KB

The BridgeService is a library that allows your project to access java code to which you do not have implicite and normal access. Use cases are JS/Python/C++ accessing Java objects or java objects that are protected, and that you do not want to distribute as a library.

License: MIT License

Shell 0.45% Java 99.55%
cross-compiler cross-platform jni jni-java omg integro

bridgeservice's Introduction

BridgeService

PhasedTesting

unit-tests codecov javadoc Quality Gate Status

This project allows you to expose your Java project/library as a REST service. It allows you to make calls to Java code from any language or framework you are in.

Table of Contents

Release Notes

The release notes can be found here.

Implementing The Bridge Service in Your Project

The bridge service can be used in two ways:

  • By adding it to the project you want to expose (recommended),
  • By including your project as a dependency to the Bridge cervice deployed.

Adding the Bridge Service to Your Project

We think it is simplest to the BridgeService dependency to your project. This allows you to only update your code when needed, and you do not need to create a new release/snapshot everytime your project changes.

BridgeService Injection Model

When starting the bridge service you need to run the following command line:

mvn compile exec:java -Dexec.mainClass=MainContainer -Dexec.args="test"
Installation

The following dependency needs to be added to your pom file:

 <dependency>
    <groupId>com.adobe.campaign.tests.bridge.service</groupId>
    <artifactId>integroBridgeService</artifactId>
    <version>2.11.14</version>
</dependency>

Considerations

Since the BridgeService uses Jetty and java Spark, it is quite possible that there maybe conflicts in the project when you add this library. Most importantly you will need to ensure that javax.servlet is set to "compile" in your maven scope.

We have found it simplest to simply add that library directly in the pom file with the scope "compile".

Including your project in the BridgeService

In this model you can simply add your project as a dependency to the BridgeProject.

BridgeService Aggregator Model

Starting the Bridge Service

When deploying this as a project we run this as an executable jar. This is usually done in a Docker image.

Running the Bridge Locally

You can also run the project locally to debug your project. To do this, you need to run the following command line:

from the root project: mvn -pl integroBridgeService exec:java -Dexec.args="test"

or directly from the module "integroBridgeService": mvn exec:java -Dexec.args="test"

This will make the service available under : http://localhost:8080

Running a DEMO

The bridge service can be launched in this project with a demo project (Included in an aggregator mode). When deployed in this mode, we include the module bridgeService-data which is part of this project. If you want to include this project in your deployment you need to set the property demo.project.mode to compile.

from the root project: mvn -pl integroBridgeService exec:java -Dexec.args="test" -Ddemo.project.mode=compile

or directly from the module "integroBridgeService": mvn exec:java -Dexec.args="test" -Ddemo.project.mode=compile

Setting Information About your Environment

The users accessing bridge service will encounter two different technologies:

  • The Bridge Service
  • The Host Project

In order to make provide context information the Bridge Service will always let you know which version it is. However, we need to let the users know about the version of the host project. Ths version number can be set by setting the environment property IBS.PRODUCT.USER.VERSION.

Testing That all is Working

All you need to do is to call : /test

If all is good you should get:

All systems up - in production
Version : 2.11.14
Product user version : 7.0

Testing That all External Dervices can be Accessed

One of the added values of this service is to create a single point of access for external dependencies. However, this needs to be checked, before using this service. In order to do this you need to the following POST call:

/service-check

The payload needs to have the following format:

{
    "<URL ID 1>": "<dns 1>:<Port>",
    "<URL ID 2>": "<dns 2>:<Port>"
}

The payload returns a JSON with the test results:

{
    "<URL ID 1>": true,
    "<URL ID 2>": false
}

In the example above "<URL ID 2>" is marked as false because it can not be accessed from the BridgeService.

Making a basic Java Call

The simplest java call is done in the following way: /call

{
    "callContent": {
        "<ID>": {
            "class": "<package name>.<class Name>",
            "method": "<method name>",
            "args": ["argument1","argument2"]
        }
    }
}

If the IntegroBridgeService can find the method it will execute it. The result is, when successful, is encapsulated in a 'returnValues' object.

{
    "returnValues": {
        "<ID>": "<result>"
    },
    "callDurations": {
        "<ID>": "<duration ms>"
    }
}

Instantiating Objects

If we do not specify a method, the bridge service assumes that we are instantiating the given class:

{
    "callContent": {
        "<ID>": {
            "class": "<package name>.<class Name>",
            "args": ["argument1","argument2"]
        }
    }
}

Note : You can also set the method name to the class name, but it may be easier to simply skip setting a method name in this case.

Managing Timeouts

As of version 2.11.6 we now introduce the notion of timeouts. This means that after a declared time a call will be interrupted. Setting this value can be done at two levels:

  • The deployment level
  • The Call session

Note : If set to 0, there is no timeout.

Setting Timeout Globally

You can set a default value when starting the service. This is done by setting the environment variable IBS.TIMEOUT.DEFAULT. If not set, the default value is 10000ms.

Setting a Timeout for the Call Session

We can also set the Timeout for a java call transaction. In that case the value you pass overrides the global value, but only for you session. If the timeout is not test in the payload at the next call, the global value will be used.

In the example below the method methodWithTimeOut waits for the provided, in this case 800ms, amount of time. In the example below the test will pass because we wait for 800ms, and the timeout is 1000s.

{
	"callContent": {
		"call1": {
			"class": "com.adobe.campaign.tests.bridge.testdata.one.SimpleStaticMethods",
			"method": "methodWithTimeOut",
			"args": [
				800
			]
		}
	},
	"timeout": 1000
}

If the payload above would have a timeout below 800ms, the call will fail.

Call Chaining a basic Java Call

We can chain a series of java calls in the same payload:

{
    "callContent": {
        "<ID-1>": {
            "class": "<package name 1>.<class name 1>",
            "method": "<method name 1>",
            "args": ["argument1","argument2"]
        },
        "<ID-2>": {
           "class": "<package name 2>.<class name 2>",
           "method": "<method name 2>",
           "args": ["argument1","argument2"]
        }
    }
}

In the example above the results will be stored in the following way:

{
    "returnValues": {
        "<ID-1>": "<result>",
        "<ID-2>": "<result>"
    },
    "callDurations": {
        "<ID-1>": "<duration ms>",
        "<ID-2>": "<duration ms>"
    }
}

Call Chaining and Call Dependencies

We now have the possibility of injecting call results from one call to the other:

{
    "callContent": {
        "<ID-1>": {
            "class": "<package name 1>.<class name 1>",
            "method": "<method name 1>",
            "args": ["argument1","argument2"]
        },
        "<ID-2>": {
           "class": "<package name 2>.<class name 2>",
           "method": "<method name 2>",
           "args": ["<ID-1>","argument2"]
        }
    }
}

In the example above "ID-2" will use the return value of the call "ID-1" as ts first argument.

NOTE : When passing a call result as an argument, it needs to be a String. In many languages such as JavaScript, the JSON keys need not be a string, however, for this to work you need to pass the ID as a string.

Call Chaining and Instance Methods

We now have the possibility of injecting call results from one call to the other. In the example below we instantiate an object, and in the following call we call a method of that object. This is done by passing the ID of the first call as the instance value for the following call.

{
    "callContent": {
        "<ID-1>": {
           "class": "<package name>.<class name>",
           "args": ["argument1","argument2"]
        },
        "<ID-2>": {
           "class": "<ID-1>",
           "method": "<method name>",
           "args": ["argument2"]
        }
    }
}

In the example above "ID-2" will use call the instance method of the object created in call "ID-1".

Creating a Call Context

We sometimes need to set environment variables when making calls. This is usually indirectly related to the call you are doing. These variable mirror the values of a property file, so they are always treated as strings.

{
    "callContent": {
        "<ID>": {
            "class": "<package name>.<class Name>",
            "method": "<method name>",
            "args": ["argument1","argument2"]
        }
    },
    "environmentVariables": {
        "<ENVIRONMENT PROPERTY 1>": "<value>"
    }
}

When making the call we first update the environment variables for the system.

This call will use your internal method for setting environment variables. This method can be set by setting the following environment values, when activating the Bridge on your project:

  • IBS.ENVVARS.SETTER.CLASS
  • IBS.ENVVARS.SETTER.METHOD

Static Variable Scopes

One of our main concerns has been the management of static variables. For the sake of conversation we need to identify scopes:

  • Session Scope : Access to the variables in the same call to the Bridge Service
  • Product Scope : Access to the variables between two different calls

Session Scopes

For now our approach is that two distinct calls to the same Bridge Service node, should share little or no variables. Another way of defining this is that two calls should not interfere with one another. In a Session Context we have two use cases:

We have covered all the use cases in the document Managing Contexts and Static Variables.

Product Scope

Although we do not, yet, provide tools for managing variables that are valid for all calls to the IBS, we can define a series or local environment variables are deployment of the service. This can be done in two ways:

  • Managing a properties for in your deployment
  • Injecting Runtime properties at the commandline

Error Management

Currently, whenever there is an error in the underlying java call we will include the orginal error message in the error response. For example, for the call:

{
    "callContent": {
        "call1": {
            "class": "com.adobe.campaign.tests.bridge.testdata.one.SimpleStaticMethods",
            "method": "methodThrowingException",
            "returnType": "java.lang.String",
            "args": [
                3,
                3
            ]
        }
    }
}

We would normally get the error:

java.lang.IllegalArgumentException: We do not allow numbers that are equal.

When using the bridge service, we also include additional info:

{
  "title": "Error during call of target Java Class and Method.",
  "code": 500,
  "detail": "We experienced an exception when calling the provided method com.adobe.campaign.tests.bridge.testdata.one.SimpleStaticMethods.methodThrowingException.\nProvided error message : java.lang.IllegalArgumentException: We do not allow numbers that are equal.",
  "bridgeServiceException": "com.adobe.campaign.tests.bridge.service.exceptions.TargetJavaMethodCallException",
  "originalException": "java.lang.IllegalArgumentException",
  "originalMessage": "We do not allow numbers that are equal."
}

The BridgeService exception is how the bridgeService manages underlying errors. However we also share the background, originating exception and message in order to help.

Contribution

There are two main docs for contributing:

Known Errors

Linked Error

When using the "manual" mode, this error can happen when you are chaining calls, and that the called classes call a third class in a static way. In such cases you will get an error like:

Problems with payload. Check the passed environment variables.
java.lang.LinkageError: loader  constraint violation: when resolving method "com.adobe.campaign.tests.bridge.testdata.issue34.pckg2.MiddleManClassFactory.getMarketingInstance()Lcom/adobe/campaign/tests/bridge/testdata/issue34/pckg1/MiddleMan;" the class loader com.adobe.campaign.tests.bridge.service.IntegroBridgeClassLoader @697a1a68 (instance of com.adobe.campaign.tests.bridge.service.IntegroBridgeClassLoader, child of 'app' jdk.internal.loader.ClassLoaders$AppClassLoader) of the current class, com/adobe/campaign/tests/bridge/testdata/issue34/pckg1/CalledClass2, and the class loader 'app' (instance of jdk.internal.loader.ClassLoaders$AppClassLoader) for the method's defining class, com/adobe/campaign/tests/bridge/testdata/issue34/pckg2/MiddleManClassFactory, have different Class objects for the type com/adobe/campaign/tests/bridge/testdata/issue34/pckg1/MiddleMan used in the signature

This usually means that some additional packages need to be included in the environment variable: IBS.CLASSLOADER.STATIC.INTEGRITY.PACKAGES.

Known Issues and Limitations

As this is a new project there are a few limitations to our solution:

Cannot call overloaded methods with the same number of arguments.

Today, in order to simply the call model, we have chosen not to specify the argument types in the call. The consequence of this is that in the case of overloaded methods, we only pick the method with the same number of arguments. If two such overloaded methods exist, we choose to throw an exception: We could not find a unique method for <method>.

Only simple arguments

Since this is a REST call we can only correctly manage simple arguments in the payload. One workaround is to use Call Dependencies in Call Chaining (see above). I.e. you can pass simple arguments to one method, and use the complex results of that method as an argument for the following java call.

Complex Non-Serialisable Return Objects

In many cases the object a method returns is not rerializable. If that is the case we mine the object, and extract all simple values from the object.

Calling Enum Methods

We are currently unable to call enums with the Bridge Service.

bridgeservice's People

Contributors

adobe-bot avatar baubakg avatar himanshu12131 avatar himanshu12133 avatar

Stargazers

 avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

Forkers

ghas-results

bridgeservice's Issues

Better management of method finding

Today, the IBS looks for methods with the same number of arguments as the ones passed.

It does not consider the types of the arguments.

This task has two sides:

  • Better identify types of the arguments
  • Targetting the correct overloaded method

Document the execution properties

We need a chapter on the execution properties. This means what a user can set when deploying the bridgeService and what their effects are.

Activate E2ERemoteTests

Currently the test class E2ERemoteTests is commented out because it is a real integration test, where you can test deployed instances.

However we have deactivated it because it is not part of the Continuous Integration process.

We should activate it as well as removing it from the standard CI steps.

We should also document it.

Include class instantiation

I would like to be able to instantiate a class.

class: a.b.C
method: C
args : []

If class and method are the same then we instantiate class with the constructor.

Include JavaDoc in IBS

We can imagine introducing javadoc.

This requires a preprocessing that generates a JSON of javadocs for the host classes.

Basically the IBS before deploying generates a JSON of the source code. One method -> one JAVADOC

We will have two modes:

  • /help?class=&method= ->returns the javadoc
  • on failue we return the javadocs attached to the method

Risks: overloaded methods

Research better formatting for errors

See if errors can be formatted into HTML or if there are best practices

Content type application/problem+json

Follow format

{
"title": "",
"bridgServiceException": "",
"originalException":"",
"code": XXX,
"detail",""
}

described here:
https://josipmisko.com/posts/rest-api-error-handling#3-rest-api-error-message

Example:

{
    "originalException": "com.adobe.campaign.tests.bridge.service.exceptions.ClassLoaderConflictException",
    "bridgServiceException": "com.adobe.campaign.tests.bridge.service.exceptions.IBSConfigurationException",
    "code": 500,
    "detail": "Linkage Error detected. This can be corrected by either setting the config value IBS.CLASSLOADER.INTEGRITY.MODE to 'automatic', or by enriching the IBS.CLASSLOADER.STATIC.INTEGRITY.PACKAGES property with the given path.",
    "title": "The provided class and method for setting environment variables is not valid."
}

automatically calculate IBS version

We cannot get the following code to work:

PRODUCT_VERSION("IBS.PRODUCT.VERSION","not found", false, "The version of the BridgeService, which is used to identify the version that is accessed."){
        @Override
        public String fetchValue() {

            return isSet() ? super.fetchValue() : this.getClass().getPackage().getImplementationVersion();
        }
    }

class jdk.internal.reflect.ConstructorAccessorImpl loaded by IntegroBridgeClassLoader cannot access jdk/internal/reflect superclass jdk.internal.reflect.MagicAccessorImpl

When using the 2.11.11 version in automatic mode we stumpled upon the error


{
	"title": "The provided class and method for setting environment variables is not valid.",
	"code": 500,
	"detail": "Linkage Error detected. This can be corrected by either setting the config value IBS.CLASSLOADER.INTEGRITY.MODE to 'automatic', or by enriching the IBS.CLASSLOADER.STATIC.INTEGRITY.PACKAGES property with the given path.",
	"bridgeServiceException": "com.adobe.campaign.tests.bridge.service.exceptions.IBSConfigurationException",
	"originalException": "java.lang.IllegalAccessError",
	"originalMessage": "class jdk.internal.reflect.ConstructorAccessorImpl loaded by com.adobe.campaign.tests.bridge.service.IntegroBridgeClassLoader @64989a8f cannot access jdk/internal/reflect superclass jdk.internal.reflect.MagicAccessorImpl"
}

Stack traces need to be relevant

The stack trace we return is almost always from IBS.
However often we need the stacktrace of the called element.

When in filure, IBS needs to return the stack trace of the original issue. If there is no original issue, it should return the IBS stack trace.

Adapt project to public github

We need to perform the following tasks:

  • github actions
  • status bars
  • sonar project
  • publish first official build
  • create release branch
  • add arefactory tokens

Implement a versioning system

A versioning system allows us to :

  • Highlight the version of IBS
  • Use it for the docker images
  • create an API version

java.lang.LinkageError: loader constraint violation: loader 'app' (instance of jdk.internal.loader.ClassLoaders

Getting error:
com.adobe.campaign.tests.bridge.service.exceptions.IBSRunTimeException: java.lang.LinkageError: loader constraint violation: loader 'app' (instance of jdk.internal.loader.ClassLoaders$AppClassLoader) wants to load class utils.Element. A different class with the same name was previously loaded by com.adobe.campaign.tests.bridge.service.IntegroBridgeClassLoader @2c30b1a7 (instance of com.adobe.campaign.tests.bridge.service.IntegroBridgeClassLoader, child of 'app' jdk.internal.loader.ClassLoaders$AppClassLoader).


0 = {StackTraceElement@6841} "com.adobe.campaign.tests.bridge.service.JavaCalls.call(JavaCalls.java:76)"
1 = {StackTraceElement@6842} "com.adobe.campaign.tests.bridge.service.JavaCalls.submitCalls(JavaCalls.java:101)"
2 = {StackTraceElement@6843} "com.adobe.campaign.tests.bridge.service.IntegroAPI.lambda$startServices$2(IntegroAPI.java:67)"
3 = {StackTraceElement@6844} "spark.RouteImpl$1.handle(RouteImpl.java:72)"
4 = {StackTraceElement@6845} "spark.http.matching.Routes.execute(Routes.java:61)"
5 = {StackTraceElement@6846} "spark.http.matching.MatcherFilter.doFilter(MatcherFilter.java:134)"
6 = {StackTraceElement@6847} "spark.embeddedserver.jetty.JettyHandler.doHandle(JettyHandler.java:50)"
7 = {StackTraceElement@6848} "org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:1598)"
8 = {StackTraceElement@6849} "org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:141)"
9 = {StackTraceElement@6850} "org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:127)"
10 = {StackTraceElement@6851} "org.eclipse.jetty.server.Server.handle(Server.java:516)"
11 = {StackTraceElement@6852} "org.eclipse.jetty.server.HttpChannel.lambda$handle$1(HttpChannel.java:487)"
12 = {StackTraceElement@6853} "org.eclipse.jetty.server.HttpChannel.dispatch(HttpChannel.java:732)"
13 = {StackTraceElement@6854} "org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:479)"
14 = {StackTraceElement@6855} "org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:277)"
15 = {StackTraceElement@6856} "org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:311)"
16 = {StackTraceElement@6857} "org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:105)"
17 = {StackTraceElement@6858} "org.eclipse.jetty.io.ChannelEndPoint$1.run(ChannelEndPoint.java:104)"
18 = {StackTraceElement@6859} "org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:883)"
19 = {StackTraceElement@6860} "org.eclipse.jetty.util.thread.QueuedThreadPool$Runner.run(QueuedThreadPool.java:1034)"
20 = {StackTraceElement@6861} "java.base/java.lang.Thread.run(Thread.java:834)"

We also have a bad message:
Problems with payload. Check the passed environment variables.

Allow for logs to be displayed as a service

With the bridgeService, we need to be able to also check the logs.

This feature proposes that we provide an API or method to access the logs.

Event better in the result load we can include the logs for the session.

Requirements:

  • Include Session ID as an input
  • If not set a random + unique session should be proposed.
  • Prefix all logs with the session ID
  • Be able to access the logs in real-time

Features (TBD):

  • Include logs in return payload of call
  • Include session ID in return payload
  • Include API to parse logs given a Session ID

Manage Map Objects

In some cases we return MAP as a value. It would be good to treat these objects.

Include an assert method

We would like to be able to perform assertions in the BridgeService

Following possibilities exist:

  • The results of two queries are equal (Can we use matchers)
  • The results of the results value is equal to a passed constant

Detect deprecated methods

If possible, we should create a warning about deprecated methods.

If so we should log this, and possibly add a warning payload

Add a test for checking that the IBS.ENVVARS.SETTER. properties actually work

The IBS.ENVVARS.SETTER. properties:

  • IBS.ENVVARS.SETTER.CLASS
  • IBS.ENVVARS.SETTER.METHOD

Are essential to the workings of the solutions. We need to provide a method to validate the effects of the set IBS.ENVVARS.SETTER. class and methods.

Use cases could be:

  • Testing this when running test.
  • Providing a warning at startup testing these.

Filter certain methods from the bridgeService

Idea from Agile Testing Days 2023: We should be good to filter out certain tests and classes from the bridgeService.

Candidates:

  • TestAPI/SDK which is not stable/flaky
  • Elements that are sensitive

Approaches:

  • Permissive - Opt out : Curreent methid, all public methods are exposed, we can introduce an annotation for opt-out.
  • Non-Permissive - Add in : By default nothing is added, we select the methods we add

I personally prefer the first. But we need to see what the consequences of opt-out are. Also we need to look at transitiveness.

The second mode is more like more traditional systems.

Better Manage internal errors

Whenever ther is a bug inside the bridgeService we get an internal error, but this needs to b better managed so we can debug this.
W should provide full error information in these cases.

Allow for value checking in ConfigValueHandlerIBS

We would like to check values in the ConfigValueHandlerIBS. Example in
ConfigValueHandlerIBS.INTEGRITY_PACKAGE_INJECTION_MODE

we should check if the given values are withing "automatic", "manual" or "semi-manual"

Dynamically add the loaded packages

As highlighted by @himmitta the packages that are loaded are static. An idea is to make this dynamic based on the called classes.

  1. Extract package
  2. Add to a set of loaded packages
  3. Loaded set of packages

Introduce automatic loading of classes

We need to introduce a new mode thta loads all used classes.

This loads all needed tests even if they are not in the context.

This requires that we refactor the Config values. We remove the config parameter IBS.CLASSLOADER.AUTOMATIC.INTEGRITY.INJECTION

Instead we will now use
IBS.CLASSLOADER.INTEGRITY.MODE, which accepts three modes:

  1. automatic
  2. maual
  3. semi-manual (replaces IBS.CLASSLOADER.AUTOMATIC.INTEGRITY.INJECTION)

Manage server side exceptions

Server side exceptions should be 50X

This involves for now:

  • TargetJavaMethodCallException
  • IBSRunTimeException
  • IBSConfigurationException

Include Call Chaining information on failures

If we have a set of calls
A, B, C

If there is a failure in A, we need to know the results of A, B at least that they were successfully executed. Ideally we should return the results so that we can reproduce whatever happened in C

example:

{
	"callContent": {
					"call1" : {
						"class": "com.adobe.campaign.tests.bridge.testdata.one.SimpleStaticMethods", 
            "method": "methodReturningMap", 
            "args": []
					},
          "addComment": {
            "class": "com.adobe.campaign.tests.bridge.testdata.one.SimpleStaticMethods", 
            "method": "methodThrowsException"
          }
	}
        
}

Issue with ReturnObjects with method called get

We have an error 500 whenever we get an internal error. Unfortunately this does not give us enough information.

We should:

  • manager the internal errors so that we see the root cause
  • Include stack trace in these cases

Add a SystemValueHandler

Add a method that manages the System values for the system using this tool. When used, it manages an intermediary cache for variables that are used.

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.