Eclipse Photon Nears Release

by Kesha Williams at April 23, 2018 05:30 AM

Eclipse Photon, the seventeenth annual release of the Eclipse Project, will be released in June, but we’re keeping an eye on all the new and noteworthy features in each pre-release milestone. Milestone 6 (M6) offers noteworthy features for the Eclipse Platform, Java Development Tools (JDT), Plug-in Development Environment (PDE), Equinox sub-project, and for JDT and Eclipse Platform developers.

By Kesha Williams

by Kesha Williams at April 23, 2018 05:30 AM

EC by Example: FlatCollect

by Donald Raab at April 23, 2018 05:08 AM

Learn how to flatten a collection of collections into a single collection using the flatCollect method in Eclipse Collections.

Organize a collection of collections into a single collection

What is FlatCollect?

The method flatCollect is a special form of collect, where the output of the Function provided to the method must always be some Iterable type. The purpose of flatCollect is to provide a transformation by flattening a collection of collections. This method is similar in function to flatMap in Java Streams. The primary difference is that the Function for flatCollect must return an Iterable, while the Function for flatMap must return a Stream.

Creating Intervals from Integers and flattening them to a List

FlatCollecting a List (Java 8)

@Test
public void flatCollectingAListJava8()
{
MutableList<Integer> list = mList(5, 4, 3, 2, 1);
MutableList<Integer> result = list.flatCollect(Interval::oneTo);

MutableList<Integer> expected = mList(
1, 2, 3, 4, 5,
1, 2, 3, 4,
1, 2, 3,
1, 2,
1);
Assert.assertEquals(expected, result);
}

Collection Pipelines

Martin Fowler describes the Collection Pipeline pattern here. Here is an example of flatCollect used in a collection pipeline to find all of the methods that contain “flat” in their name for a List of classes. Here I used a overloaded form of flatCollect which takes a target collection as an argument.

@Test
public void flatCollectingAListOfMethodsToASetJava8()
{
MutableList<Class<?>> list = mList(
ListIterable.class,
MutableList.class,
ImmutableList.class);
MutableSet<String> result = list
.collect(Class::getMethods)
.flatCollect(Lists.fixedSize::with, mSet())
.collect(Method::getName)
.select(each -> each.toLowerCase().contains("flat"));

MutableSet<String> expected =
Sets.mutable.with("flatCollect");
Assert.assertEquals(expected, result);
}

The method getMethods on class returns an array, so in the Function I pass to flatCollect, I convert the array to a List. If getMethods had returned a List or some other Iterable type, I could have simply used flatCollect passing Class::getMethods.

Here’s the same example using Java 10 with Local-variable Type Inference.

@Test
public void flatCollectingAListOfMethodsToASetJava10()
{
var list = mList(
ListIterable.class,
MutableList.class,
ImmutableList.class);
var resultSet = list
.collect(Class::getMethods)
.flatCollect(Lists.fixedSize::with, mSet())
.collect(Method::getName)
.select(each -> each.toLowerCase().contains("flat"));

var expected =
Sets.mutable.with("flatCollect");
Assert.assertEquals(expected, resultSet);
}

Symmetric Sympathy Strikes Again

While there exists a method named collectWith which is a form of collect that takes a Function2, there currently is no method named flatCollectWith which also takes a Function2. I discovered the lack of flatCollectWith (again) this week. I have submitted an issue for this feature and began working on it over the weekend. I expect to have the flatCollectWith implementation tested and completed over the next week or two.

APIs covered in the examples

  1. flatCollect— transforms elements of a source collection to a new collection by flattening collections in the source collection to a single collection
  2. mList — creates a MutableList
  3. mSet — creates a MutableSet
  4. Interval.oneTo(int) — creates an Interval starting from 1 to the specified value
  5. var — Local variable Type Inference included in Java 10 (JEP 286)

Refer to my previous blogs in the EC by Example series for examples of collect and select.

Check out this presentation to learn more about the origins, design and evolution of the Eclipse Collections API. There is also a video covering the slides that was recorded at an Eclipse Community Virtual Meet-up.

Eclipse Collections is open for contributions. If you like the library, you can let us know by starring it on GitHub.


by Donald Raab at April 23, 2018 05:08 AM

India Java User Group Tour 2018

by Nikhil Nanivadekar at April 20, 2018 12:13 AM

After 27+ hours of travel I just reached Pune, my home town. I am excited for my India Java User Group Tour 2018. I will be presenting on Java 10, Java 9, Eclipse Collections, Spark and more!

It is a quick pit stop in Pune before I head over to Chennai for my first JUG meet-up in #INDJUG tour. Abstracts for all my talks are available at the end of this blog. Join me at one of these cities:

Chennai JUG: Saturday, April 21

Delhi-NCR JUG: Sunday, April 22

Bengaluru JUG: Wednesday, April 25

Hyderabad JUG: Sunday, April 29

Kerala JUG (Thiruvananthapuram): Saturday, May 5

Thank you to @MadrasJUG, @DelhiJUG, @bangalorejug, @JUGHYD, and @KeralaJUG for hosting me in each city.

I’ll be tweeting using the #INDJUG so if you can’t make it, you can still follow my adventures through India.

Hope to see you at one of the stops!

https://medium.com/media/1965d4579fd3e295b79002ad7b50a1f5/href

Abstracts

How to make your project Java-10 compatible:

Java 10 was recently released and was the first release with the new Java release cadence. However, one can’t simply upgrade their projects from Java 8 to Java 10. It first should be upgraded to Java 9, due to the numerous changes that might potentially break existing applications. This session is a case study of making a third-party Java Collections library (Eclipse Collections) first Java 9 compatible and then with relative ease Java 10 compatible. The audience will see an overview of all the steps taken and the evolution of the final product which is Java 10 compatible. Attending this session will put you in the driver’s seat when you will be required to upgrade your application to use JDK 10.

Getting Started with Spark:

Data analytics and machine learning have become mainstream in recent years. With the amount of data available, distributed computing has become a necessity. Apache Spark is one of the forerunners in distributed computing domain. In this hands-on session, the audience will learn about the background and basic concepts of Apache Spark. The speaker will build a reference implementation live and introduce new concepts along the way.

Collections.compare(JDK, Apache, Eclipse, Guava…):

Collections are a staple in any programming language: the need to collect, sort, or iterate over values is needed by nearly all developers. The Java language introduced the Collections framework long ago. It has plenty to offer, but many find it lacking: the number of collection libraries as active open source projects demonstrates the need for something else. This session does a holistic comparison of the most-common collections (pun intended!) frameworks, what they have to offer, and what you should consider for your next project. It also shows common programmer use cases; how each library handles them; and the impact on memory, processing power, and ease of use/coding. Come and let us help you choose the right bag for your tricks!

API Deep Dive: Designing Eclipse Collections

When designing an API, its authors have to consider many aspects: style, naming, scope, and implementation details are among these aspects. They have a direct impact on the resulting code, and its implementation can go in many different directions. How do you choose the best route to go? How do you maintain symmetry? How do you guarantee consistency and performance across the framework? Last but not the least, what is the complexity associated with adding a new API? Come take a look behind the curtains of a widely used API that has many years of development and that you can contribute to.


by Nikhil Nanivadekar at April 20, 2018 12:13 AM

ECF Photon supports Bndtools

by Scott Lewis (noreply@blogger.com) at April 19, 2018 09:39 PM

A second major enhancement for ECF Photon is adding support for using Bndtools to develop and test OSGi Remote Services.   Bndtools is increasingly popular for developing OSGi-based applications and frameworks, and we've added support for Bndtools Workspace, Project, and Run Descriptor templates for developing and testing remote services.

Initial documentation is available at Bndtools Support for Remote Services Development.

Note that these templates and the RSA impl may change slightly before ECF Photon, and new/additional templates will be added to (e.g.) support other distribution and discovery providers.



by Scott Lewis (noreply@blogger.com) at April 19, 2018 09:39 PM

What’s Coming in Sirius 6.0?

by Pierre-Charles David (pierre-charles.david@obeo.fr) at April 19, 2018 12:59 PM

The next major version of Sirius , version 6.0, will be released on June 27, 2018 as part of the Eclipse Photon Simultaneous Release, with a corresponding version of Obeo Designer Community Edition soon after. Many of the new features for this release are already available for testing in milestone versions, and now is the right time to test them and give us feedback: there’s still time to make adj...

by Pierre-Charles David (pierre-charles.david@obeo.fr) at April 19, 2018 12:59 PM

Who, what, when, where at EclipseCon France?

by Anonymous at April 19, 2018 06:19 AM

The schedule is now published, so you can work on your plan for the week! Thank you again for all the great submissions that make up this fantastic program.

There are even more choices this year, since Wednesday has five concurrent talks all day with a special focus on Jakarta EE and Eclipse MicroProfile.

Stay tuned for more about the Unconference on June 12. We'll see you soon in Toulouse!


by Anonymous at April 19, 2018 06:19 AM

EclipseCon France 2018 – Sessions

by tevirselrahc at April 18, 2018 03:05 PM

The sessions lineup for EclipseCon France 2018 have been published! If you plan on attending, I have a couple of suggestions for you!

First, a session about me (of course) and more love for my toolsmiths:

Papyrus as a Platform

by Philip Langer (EclipseSource Services GmbH).

Model-based engineering tools are most successful, if they are as domain-specific as possible, reflecting the specific needs of the domain and its users. Thus, not only a domain-specific modeling language, but also a specialized modeling environment is required that takes the domain users’ background, their roles, and currently used infrastructure into account. Often, the domain-specific modeling languages have a considerable overlap with UML though.

The second session is not directly focused on me, but it is very relevant to using me as a platform for domain-specific tools (and again, more love for my toolsmiths):

Comparison and merge use-cases from practice with EMF Compare

by Laurent Delaigue (Obeo) and Philip Langer (EclipseSource Services GmbH)

Have you ever needed to compare and merge heterogeneous domain-specific models (with both textual and graphical syntaxes)? Or maybe you needed to review changes on graphical models? We did.

 

Did I miss a presentation? If so, let me know!


by tevirselrahc at April 18, 2018 03:05 PM

IoT Developer Survey 2018 | Results are in!

April 17, 2018 02:00 PM

Results from the IoT Developer Survey are in! Read about the key findings about IoT cloud platforms, databases, security, and more.

April 17, 2018 02:00 PM

Key Trends from the IoT Developer Survey 2018

by Benjamin Cabé at April 17, 2018 12:44 PM

Executive Summary

The IoT Developer Survey 2018 collected feedback from 502 individuals between January and March 2018.

The key findings in this year’s edition of the survey include the following:

  • Amazon AWS and Microsoft Azure are the top 2 cloud services for IoT. Google Cloud Platform is failing to get traction.
  • MQTT remains the standard of choice for IoT messaging, while AMQP is becoming more and more popular as companies scale their IoT deployments and backend systems.
  • 93% of the databases and data stores used for IoT are open source software. Data collected and used in IoT applications is incredibly diverse, from time series sensor data to device information to logs.

Introduction

For the past four years, the IoT Developer Survey has been a great way to look at the IoT landscape, from understanding the key challenges for people building IoT solutions, to identifying relevant open source technology or standards.

Just like in previous years (see results from 2017, 2016 and 2015 survey), the Eclipse IoT Working Group has collaborated with a number of organizations to promote the survey to different IoT developer communities: Agile-IoT H2020 Project, IEEE, and the Open Mobile Alliance (now OMA SpecWorks).

We had a total of 502 individual responses. You will find a link to the complete report at the end of this blog post, as well as pointers to download the raw survey data.

Here are the key trends that we identified this year:

Amazon and Azure get traction, Google slips behind

For the past few years, we’ve asked people what cloud platform they use or plan on using for building their IoT solution.

IoT Developer Survey 2018: IoT Cloud Platforms Adoption – Amazon vs. Microsoft vs. Google

Since 2016, Amazon AWS has always come up as the platform of choice for the respondents, followed by Microsoft Azure and Google Cloud Platform.

📎 The use of AWS for building IoT solutions increased by 21% since 2017. 

Looking at this year’s results, there is a clear upward trend in terms of adoption for Amazon AWS (51.8%, a 21% increase from last year) and Microsoft Azure (31.21%, a 17% increase from 2017). In the meantime, Google Cloud Platform is struggling to get adoption from IoT developers (18.8%, an 8% year-to-year decrease).

📎 Google Cloud Platform struggles, with an 8% decrease in market share for IoT deployments since 2017. 

Seeing AWS ahead of the pack is no surprise. It seems to be the public cloud platform of choice for developers, according to the recent Stack Overflow Developer Survey, and one of the most loved platforms for development in general. And looking at the same survey, it seems Google is not really doing great with their Cloud Platform (it is used by 8.0% of the respondents vs. 24.1% for AWS).

IoT Developer Survey 2018: IoT Cloud Platforms Adoption – Trends

It will be interesting to see how, and if, Google catches up in the IoT cloud race, and whether we will see more acquisitions similar to Xively’s in February to help beef up their IoT offering in 2018. Since Microsoft is planning to invest $5 billion in IoT over the next four years, the IoT cloud competition will definitely be interesting to follow…

IoT Data is finally getting attention

While IoT has been around for a while now, it looks like developers are starting to realize that beyond the “cool” factor of building connected devices, the real motivation and business opportunity for IoT is in collecting data and making sense out of it.

📎 Collecting and analyzing data becomes #2 concern for #IoT developers. 

This year, 18% of the respondents identified Data Collection & Analytics as one of their top concerns for developing IoT solutions. This is a 50% increase from last year, and puts this topic as #2 concern—Security remains #1, and Connectivity is sharing the third place with Integration with Hardware.

IoT Developer Survey 2018: Key IoT Concerns

Unsurprisingly, industries such as Industrial Automation or Smart Cities tend to care about IoT data collection and analytics even more—23% of the respondents working in those industries consider data collection & analytics to be a key concern.

IoT Developer Survey 2018: Key IoT Concerns - Trends

On a side note, it is great to get the confirmation of a trend we identified last year, with Interoperability clearing becoming less of a concern for IoT developers. It’s been ranking #2 since we started doing the survey in 2015, and is now relegated to the 5th place.

As someone working with IoT open source communities on a day-to-day basis, I can’t help but think about the crucial role open standards and open source IoT platforms have had in making IoT interoperability a reality.

Consolidation in IoT messaging protocols

📎 MQTT is used in 62% of IoT solutions and remains the IoT messaging protocol of choice. 

An area I particularly like to observe year-over-year is the evolution of IoT messaging protocols. For many years now, MQTT has established itself as a protocol of choice for IoT, and this year’s survey is just confirming this: MQTT is used by over 62% of our respondents, followed by HTTP (54.1%).

Six years after IBM and Eurotech open sourced their implementations of the MQTT protocol (see the Eclipse Paho project), and with the ever-increasing popularity of the Eclipse Mosquitto project (and many other open MQTT-based projects out there of course), this is once again a demonstration that open wins. With MQTT 5 around the corner and several of the identified “limitations” of the protocol gone, MQTT will possibly become even more clearly THE IoT messaging standard in the future.

IoT Developer Survey 2018: Consolidation in IoT Messaging Protocols

It would appear that the use of HTTP is declining (54.1%), perhaps to the benefit of the more lightweight and versatile HTTP/2 (24.9% vs. 16.8% last year). XMPP (4.3%) is one of the protocols that seems to be losing the protocol consolidation battle, with a continued decline since 2016.

📎 Adoption of AMQP increased by over 30% since 2017 as people scale their IoT deployments. 

Since more and more people start scaling their IoT deployments, it is likely a reason for the significant increase in AMQP’s adoption (18.2%, from 13.9% last year), which is a core element of many IoT backends.

The use of proprietary vendor protocols and in-house protocols is steadily decreasing, confirming that the industry at large tends to favor open standards over closed solutions.

It will be interesting to watch how the adoption of DDS (4.9%) evolves over time. It already seems to be getting some traction in domains such as Automotive, where 10% of the respondents said they are using it.

IoT Developer Survey 2018: IoT Messaging Protocols – Trends

Focus on security increases

It is always interesting to watch how developers approach security in the context of IoT, and it has always been mentioned as the #1 concern for IoT developers since we started doing the survey in 2015.

However, it is no secret that security is hard, and there is unfortunately still only a limited set of security-related practices that are on the front burner of IoT developers. Communication-layer security (e.g the use of TLS or DTLS) and data encryption remain the two most popular practices, used by respectively 57.3% and 45.1% of the respondents.

IoT Developer Survey 2018: IoT Security Technologies

For the first time in the history of this survey, we explicitly asked respondents if they were using blockchain or distributed ledger technology (DLT) in their IoT solutions. I was frankly surprised to see that it would appear to be the case for 11% of the respondents. As the technology matures, and as some of the barriers making it sometimes impractical for constrained/embedded devices slowly disappear, I am expecting blockchain & DLT to be used more and more for securing IoT solutions (and probably in combination with data monetization use cases).

📎 Adoption of over-the-air updates to keep IoT applications up-to-date and secure increased by almost 50% since last year. 

To end on a positive note, it is pretty clear that developers are starting to bake security into their IoT products, as an increasing number of developers indicated they implement security techniques compared to 2017. Over-the-air updates appear to be used more and more (27.3%, a 47% increase from 2017). Open device management standards such as LWM2M, together with open source implementations such as Eclipse Wakaama and Eclipse Leshan, are certainly making it easier for developers to implement OTA in their solutions.

IoT Data is multifaceted and open source databases dominate the market

This year we added a few questions to the survey aimed at understanding better the kind of IoT data being collected, and how it is being stored.

It is interesting to see that across all industries, IoT data is equally multifaceted, and a wide variety of data is being collected by today’s IoT applications. 61.9% of the data collected is time series data (e.g sensor data), but almost equally important are device information (60.4%) and log data (54.1%). This is not really surprising as collecting sensor data is only half of the IoT operational equation: one also needs to be able to track and manage their fleet of devices.

IoT Developer Survey 2018: Types of IoT Data

Keeping that in mind, it is interesting to look at the landscape of databases and data stores used for IoT applications. While time series data is the most common form of data that IoT applications collect, traditional relational databases (namely, MySQL, with a clear leading position at 44.6%) are still widely used. It is likely reflecting the importance of storing all kinds of device metadata or legacy enterprise data in addition to sensor data.

IoT Developer Survey 2018: IoT Databases

With regards to NoSQL and time series databases, MongoDB (29.8%) and InfluxDB (15.7%) seem to be the two platforms of choice for storing non-relational IoT data (e.g time series).

📎 93% of databases used in IoT are open source. 

It is worth highlighting that an astounding majority (93%) of the databases used for IoT are open source, with Amazon DynamoDB (6.9%) being the only notable exception. With something as critical and sensitive as IoT data, it seems that solution developers tend to favor technology that is not only easy and free to access, but more importantly that allows them to really “own” their data.

Linux remains the undisputed IoT operating system

Once again, Linux (71.8%) remains the leading operating system across IoT devices, gateways, and cloud backends.

IoT Developer Survey 2018: Top IoT Operating Systems & Distros

Although Amazon’s acquisition of FreeRTOS occurred just a few months before the survey opened, it might partially explain the significant increase in its reported adoption. Going from 13% in 2016 to 20% this year, it becomes the leading embedded IoT operating system, followed by Arm Mbed (9%) and Contiki (7%).

📎 FreeRTOS becomes the leading embedded #IoT operating system, followed by Arm Mbed and Contiki OS. 

In terms of Linux distributions, and as Raspberry Pi stays a very popular platform for IoT prototyping, Raspbian (43.3%) remains the top Linux distribution followed by Ubuntu (40.2%).

IoT Developer Survey 2018: IoT Linux Distributions – Trends


You can find the complete report on Slideshare.

Should you want to play with the raw data yourself, we made it available as a Google Spreadsheet here – feel free to export it as whatever format suits you best.

Mike Milinkovich and I will be doing a webinar on Thursday, April 19, to go through the results and discuss our findings. Don’t forget to RSVP!

Thanks to everyone who took the time to fill out this survey, and thanks again to IEEE, OMA SpecWorks and the Agile-IoT project for their help with the promotion.

I am very interested in hearing your thoughts and feedback about this year’s findings in the comments of this post. And, of course, we are always open to suggestions on how to improve the survey in the future!

L’article Key Trends from the IoT Developer Survey 2018 est apparu en premier sur Benjamin Cabé.


by Benjamin Cabé at April 17, 2018 12:44 PM

ECF Photon supports OSGI R7

by Scott Lewis (noreply@blogger.com) at April 16, 2018 05:09 PM

ECF Photon has several major enhancements.   I'll blog about these enhancements individually over the coming weeks, starting with

Support for OSGI R7 Remote Services

In the R7 final draft specification (chapter 100) detail was added about the use of Remote Service Intents.   RS Intents describe a distribution provider's abstract capabilities.    By way of example, several new standard intents have been defined, including osgi.basic and osgi.async.   

The osgi.basic intent requires that a distribution provider support a remote service-specific timeout, as well as serialization of remote service arguments and return values include DTOs (Data Type Objects), java primitives, maps, collections, lists, OSGI Version, etc.   

The osgi.async intent requires that remote service method signatures using CompletableFuture, Future, and OSGI's Promise be supported directly by the distribution provider.   This allows non-blocking asynchronous remote services to be easily declared in the service interface, and implemented by the distribution provider.  Here is an example remote service declaration that demonstrates how the osgi.async intent can be used.   In a forthcoming tutorial, I'll show how such a small service can be easily defined, implemented, and remoted using ECF Remote Services.

ECF's remote services impl has multiple distribution providers, and open APIs for creating custom or extension providers.   Most of the existing ECF distribution providers already available have been updated to implement the R7-standardized intents.   Others will be updated prior to and after Photon release.



by Scott Lewis (noreply@blogger.com) at April 16, 2018 05:09 PM

WTP 3.9.5 Released!

April 15, 2018 10:00 AM

Web Tools Platform 3.9.5 has been released! Installation and update can be performed using the Oxygen Update Site or through the Eclipse Marketplace. Release 3.9.5 fixes issues that occur in prior releases or have been reported since 3.9's release. WTP 3.9.5 is NOT included in Oxygen.3a Eclipse IDE for Java EE Developers, with selected portions also included in other packages. Adopters can download the R3.9.5 build itself directly.

More news


April 15, 2018 10:00 AM

New community channels

by tsegismont at April 13, 2018 12:00 AM

If you visited the Eclipse Vert.x website recently, you might have noticed a couple of novelties on the community page:

  • the Stack Overflow vert.x tag for user questions
  • the vertx-users Gitter channel to chat with other users and module maintainers

Wondering why?

Answering questions wherever they are asked

In order to help us support the community, we (the core team and module maintainers) have tried so far to stick to a few rules:

  • user group for questions
  • IRC to chat
  • bugs and issues reported on GitHub

These rules were documented on the website, but admittedly they are hard (if possible at all) to enforce. And let’s face it: many new users like to ask questions on Stackoverflow and GitHub.

So we now answer questions wherever they are asked. We keep the user group as the main channel though: some conversations just don’t fit the Stack Overflow model, and not everyone in the community watches the GitHub repositories.

Moving from IRC to Gitter

The #vertx channel is recorded on echelog but it’s not convenient to search or browse. And sharing code, logs, issues is simply awful compared to modern alternatives like Gitter or Slack.

Gitter got our preference because:

  • it’s free, without limits
  • it’s not resource hungry (on a browser at least)
  • it’s well integrated with GitHub
  • it supports advanced formatting

Since the channel was opened, new people are joining every day. At the time of writing, there are 74 people registered already. We hope you will be the next one!

See you soon online.


by tsegismont at April 13, 2018 12:00 AM

Eclipse Oxygen 3A has been released!

April 12, 2018 02:00 PM

Download the Oxygen 3A packages today.

April 12, 2018 02:00 PM

Eclipse Oxygen.3a IDE Improvements: Java 10

by howlger at April 12, 2018 10:15 AM

Three weeks after the Oxygen.3 and the Java 10 release, Oxygen.3a now adds official support for Java 10 which was previously offered only as a pre-release via the Eclipse Marketplace. Because Oxygen.3a also contains some bug fixes, an upgrade is also recommended for Java developers who do not (yet) want to use Java 10.

The main innovation and the only language change of Java 10 is the so-called Local-Variable Type Inference: within code blocks, in variable declaration, you can use var instead of the type. The specific type is determined implicitly from the assigned value. With var ArrayList<String> list = new ArrayList<>(); can be shortened to var list = new ArrayList<String>();. This is often handy (e. g. in try-with-resources statements like try (var reader = new BufferedReader(new FileReader(path))) { /*...*/ }), but in some cases it can also lead to less readable code (in my opinion e. g. in enhanced for-loops like for (var item : x.getItems()) { /*...*/ }).

var is not a keyword, just a reserved type name. This means that var can still be used as a variable name but not as a type name: int int = 1; and var int = 1; are invalid, but int var = 1; and var var = 1; are both valid Java code. Hopefully I will never have to explain to anyone why a module can be named “module” and a var can be named “var”, but an int cannot be named “int” and a class cannot be named “class” and why “module” is a valid class name whereas “var” is not. And because var is just a reserved type name, final var invar = 1; may look weird but is valid.

Nonetheless, the Eclipse developers did a great job enhancing the Eclipse Java IDE to support var. The inferred type will be shown on mouse over and in the Javadoc view, the content assist is aware of the inferred type and offers the corresponding proposals and in Mark Occurences var is handled as a substitute for the inferred type:

Furthermore, the incremental Eclipse Java compiler had to be adapted for Java 10. Unlike Oracle’s Java compiler javac the Eclipse compiler, however, does not stumble over var s=List.of("a",1).

For Java and other developers, several Eclipse Oxygen.3a IDE packages are available for download. Older versions of Oxygen will prompt you to upgrade to Oxygen.3a unless the built-in automatic update is disabled. Oxygen.3a is the last update for Eclipse Oxygen. Numerous major changes are currently being finalized for the next major release, Eclipse Photon, on June 27. If you want you can already test a pre-release of Eclipse Photon.

Happy coding!


by howlger at April 12, 2018 10:15 AM

WTP 3.9.4 Released!

April 11, 2018 10:00 AM

Web Tools Platform 3.9.4 has been released! Installation and update can be performed using the Oxygen Update Site or through the Eclipse Marketplace. Release 3.9.4 fixes issues that occur in prior releases or have been reported since 3.9's release. WTP 3.9.4 is included in Oxygen.3a Eclipse IDE for Java EE Developers, with selected portions also included in other packages. Adopters can download the R3.9.4 build itself directly.

More news


April 11, 2018 10:00 AM

A New Logo for the Eclipse Foundation

by Mike Milinkovich at April 10, 2018 12:00 PM

Since the beginning of the Eclipse Foundation in 2004 we have had two corporate logos, both of which happened to be the same logo as used by the Eclipse IDE. For a long time that was a good idea, as the Eclipse IDE was definitely our flagship project.

However, as the role of our Foundation has changed, that has made less and less sense. With exciting new technologies such as Jakarta EE, IoT, machine learning, quantum computing, etc., tying our identity to just one of our 350 projects makes it a lot more difficult to explain.  Our visual identity needs to line up with our role as a platform and environment for developers and organizations to collaborate.

To that end, we are going to soon be rolling out a new logo and website look-and-feel for the Eclipse Foundation. Our web team lead Christopher Guindon has blogged about what the new website look-and-feel will be like. It includes both a refresh in the look and feel, while still making it simple for developers to get to their favorite projects and downloads. 

But first, I get the exciting job of sharing our new logo with the world. We really like it, and hope you do too!

eclipsefoundationlogo_4


by Mike Milinkovich at April 10, 2018 12:00 PM

Quicksilver: eclipse.org new look and feel

April 10, 2018 12:00 AM

Eclipse.org new home page

I am pleased to announce a new look and feel for www.eclipse.org, codenamed Quicksilver, and a new logo for the Eclipse Foundation which Mike Milinkovich, the Executive Director of the Eclipse Foundation, has blogged about.

The main goal of the redesign is to highlight the Eclipse Foundation as more than just the IDE it started with. We want the site to reflect the fact that the Foundation now represents a collection of working groups, open source projects, vendors and individual committers all collaborating in the same space to create innovative technologies.

We hope we have the balance right, making sure it remains easy for the millions of developers who visit our site to get to their desired downloads easily and quickly.

A Quicksilver prototype is available now via our public staging server. This server is protected with basic access authentication. The username is testuser and plaintext is the password.

The main focus of this redesign was on the following pages:

You can participate and contribute to this project by submitting feedback on Bug 533394 - Quicksilver: eclipse.org new look and feel.

Quicksilver is going to be available on April 19th, 2018 for www.eclipse.org only. We will continue the rollout across our broader web properties over the coming weeks.

Technology-wise, Quicksilver is an extension of Solstice. Since this is not a complete overhaul of our existing codebase, we’re expecting the transition from Solstice to Quicksilver to be fairly straightforward. Website maintainers for Eclipse projects and working groups should know that the colors are going to change but we don’t expect to break any existing Eclipse project websites.

Future plans

We plan on changing our look and feel gradually. The following tasks are going to get done after the initial release of Quicksilver:

A lot of work needs to get done before this becomes available but I am very proud of the work we did so far for this project.

A huge thank you to Eric Poirier, Stephanie Swart, Thabang Mashologu, and the entire Eclipse Foundation staff!


April 10, 2018 12:00 AM

EC by Example: Short-circuiting methods

by Donald Raab at April 09, 2018 05:54 AM

Learn the methods in Eclipse Collections that short-circuit and return a result as soon as a condition is met.

Short-circuiting methods won’t leave you stuck in a loop for longer than necessary

What are the short-circuiting methods?

The short-circuiting methods in Eclipse Collections all take a Predicate or Predicate2 as a parameter. We refer to them as short-circuiting methods because as soon as the condition is met for a particular algorithm, the loop is broken out of and a result is immediately returned.

“the”::equals is the method reference equivalent of string -> “the”.equals(string)

The short-circuiting methods in Eclipse Collections are:

The methods that include the suffix “With” all take Predicate2, while the other methods take a Predicate.

All of the examples in this blog are written using the Java 10 Local-Variable Type Inference feature (aka var).

A few forms of detect

@Test
public void detect()
{
var mutableList =
mList("the", "quick", "brown", "fox", "jumps");
var immutableList =
iList("over", "the", "lazy", "dog");

var the1 =
mutableList.detect("the"::equals);
var the2 =
immutableList.detectIfNone("fox"::equals, () -> "the");

Assert.assertEquals(the1, the2);

var the3 =
mutableList.detectWith(Object::equals, "the");
var the4 =
immutableList.detectWithIfNone(Object::equals, "fox",
() -> "the");

Assert.assertEquals(the3, the4);

var the5 =
mutableList.detectOptional(s -> s.endsWith("e"));
var the6 =
immutableList.detectWithOptional(String::startsWith, "t");

Assert.assertEquals(the5.get(), the6.get());
}

Detecting the first and last index

@Test
public void detectIndex()
{
var mutableList =
mList("the", "quick", "brown", "fox", "jumps");
var immutableList =
iList("over", "the", "lazy", "dog");

var the1 = mutableList.detectIndex("the"::equals);
var the2 = immutableList.detectIndex("the"::equals);
Assert.assertEquals(0, the1);
Assert.assertEquals(1, the2);

var fox = mutableList.detectLastIndex("fox"::equals);
var dog = immutableList.detectLastIndex("dog"::equals);
Assert.assertEquals(3, fox);
Assert.assertEquals(3, dog);
}

Examples of Any/All/NoneSatisfy

@Test
public void anyAllNoneSatisfy()
{
var list = mList("the", "quick", "brown", "fox");

Assert.assertTrue(list.anySatisfy(s -> s.endsWith("e")));
Assert.assertFalse(list.anySatisfyWith(String::endsWith, "z"));
    Assert.assertFalse(list.allSatisfyWith(String::endsWith, "e"));
Assert.assertTrue(list.allSatisfy(s -> !s.isEmpty()));
    Assert.assertFalse(list.noneSatisfyWith(String::endsWith, "x"));
Assert.assertTrue(list.noneSatisfy(String::isEmpty));
}

Try out short-circuiting methods in the EC Katas

eclipse/eclipse-collections-kata

Here’s a few solutions demonstrating short-circuiting methods in Exercise #2 of the Eclipse Collections Pet Kata.

Examples of anySatisfyWith, allSatisfy and detectWith from the Pet Kata

Check out this presentation to learn more about the origins, design and evolution of the Eclipse Collections API. There is also a video covering the slides that was recorded at an Eclipse Community Virtual Meet-up.

Eclipse Collections is open for contributions. If you like the library, you can let us know by starring it on GitHub.


by Donald Raab at April 09, 2018 05:54 AM

Eclipse Vert.x metrics now with Micrometer.io

by jotak at April 09, 2018 12:00 AM

Vert.x has already been providing metrics for some time, through the vertx-dropwizard-metrics and vertx-hawkular-metrics modules. Both of them implement a service provider interface (SPI) to collect the Vert.x metrics and make them available to their respective backends.

A new module, vertx-micrometer-metrics, is now added to the family. It implements the same SPI, which means that it is able to provide the same metrics. Micrometer.io is a pretty new metrics library, quite comparable to dropwizard metrics in that it collects metrics in a local, in-memory registry and is able to store them in various backends such as Graphite or InfluxDB. It has several advantages as we will see below.

Tell me more about Micrometer

Micrometer.io describes itself as a a vendor-neutral application metrics facade. It provides a well designed API, in Java, to define gauges, counters, timers and distribution summaries.

Among the available backends, Micrometer natively supports Graphite, InfluxDB, JMX, Prometheus and several others. Prometheus is very popular in the Kubernetes and microservices ecosystems, so its support by Micrometer was a strong motivation for implementing it in Vert.x.

For the the moment, our implementation in Vert.x supports Prometheus, InfluxDB and JMX. More should quickly come in the near future.

Dimensionality

Another interesting aspect in Micrometer is that it handles metrics dimensionality: metrics can be associated with a set of key/value pairs (sometimes refered as tags, sometimes as labels). Every value brings a new dimension to the metric, so that in Prometheus or any other backend that supports dimensionality, we can query for datapoints of one or several dimensions, or query for datapoints aggregated over several dimensions.

Example: our metric vertx_http_server_connections accepts labels local and remote, that are used to store addresses on HTTP connections

Prometheus is used in the following examples, but equivalent queries can be performed with InfluxDB.

In Prometheus, the query vertx_http_server_connections will return as many timeseries as there are combinations of local and remote values. Example:

vertx_http_server_connections{local="0.0.0.0:8080",remote="1.1.1.1"}
vertx_http_server_connections{local="0.0.0.0:8080",remote="2.2.2.2"}
vertx_http_server_connections{local="0.0.0.0:8080",remote="3.3.3.3"}

To query on a single dimension, we must provide the labels:

vertx_http_server_connections{local="0.0.0.0:8080",remote="1.1.1.1"}. It will return a single timeseries.

To get an aggregate, Prometheus (PromQL) provides several aggregation operators:

sum(vertx_http_server_connections) will return the sum across all dimensions.

So what are the Vert.x metrics?

People already familiar with the existing metrics modules (dropwizard or hawkular) will not be too disoriented. They are roughly the same. The main difference is where previously, some metric names could have a variable part within - such as vertx.eventbus.handlers.myaddress - here we take advantage of dimensionality and we will have vertx_eventbus_handlers{address="myaddress"}.

Some other metrics are no longer useful, for instance the dropwizard’s vertx.eventbus.messages.pending, vertx.eventbus.messages.pending-local and vertx.eventbus.messages.pending-remote are now just vertx_eventbus_pending{side=local} and vertx_eventbus_pending{side=remote} in micrometer. The sum of them can easily be computed at query time.

The metrics provided by Vert.x are dispatched into eight big families:

  • Net client: distribution summaries of bytes sent and received, number of connections, etc.
  • Net server: distribution summaries of bytes sent and received, number of connections, etc.
  • HTTP client: counter of requests, response times, etc.
  • HTTP server: counter of requests, processing times, etc.
  • Event bus: counter of handlers, messages sent and received, etc.
  • Pool: for worker pools and some datasource pools, queue size and waiting time, processing time, etc.
  • Verticles: number of verticles deployed.

The full list of collected metrics is available here.

Getting started

This section will guide you through a quick setup to run a Vert.x application with Micrometer. The code examples used here are taken from the micrometer-metrics-example in vertx-examples repository, in Java, using maven. But the same could be done with other Vert.x supported languages, as well as gradle instead of maven.

Maven configuration

The configuration and the maven imports will vary according to the backend storage that will be used. For maven, the common part is always:

<dependency>
  <groupId>io.vertxgroupId>
  <artifactId>vertx-micrometer-metricsartifactId>
  <version>3.5.1version>
dependency>
  • Then, to report to InfluxDB:
<dependency>
  <groupId>io.micrometergroupId>
  <artifactId>micrometer-registry-influxartifactId>
  <version>1.0.0version>
dependency>
  • Or Prometheus:
<dependency>
  <groupId>io.micrometergroupId>
  <artifactId>micrometer-registry-prometheusartifactId>
  <version>1.0.0version>
dependency>
<dependency>
  <groupId>io.vertxgroupId>
  <artifactId>vertx-webartifactId>
  <version>3.5.1version>
dependency>

Remark that, since Prometheus pulls metrics from their source, they must be exposed on an HTTP endpoint. That’s why vertx-web is imported here. It is not absolutely necessary (it’s possible to get the metrics registry content and expose it in any other way) but it’s probably the easiest way to do.

  • Or JMX:
<dependency>
  <groupId>io.micrometergroupId>
  <artifactId>micrometer-registry-jmxartifactId>
  <version>1.0.0version>
dependency>

Note
At the moment, it is not possible to report metrics to several backends at the same time. It might be soon implemented
.

Setting up Vert.x options

A MicrometerMetricsOptions object must be created and passed to VertxOptions, with one backend configured (though having no backend is possible: you would get metrics sent to a default Micrometer registry, but without any persistent storage).

  • InfluxDB example:
// Default InfluxDB options will push metrics to localhost:8086, db "default"
MicrometerMetricsOptions options = new MicrometerMetricsOptions()
  .setInfluxDbOptions(new VertxInfluxDbOptions().setEnabled(true))
  .setEnabled(true);
Vertx vertx = Vertx.vertx(new VertxOptions().setMetricsOptions(options));
// Then deploy verticles with this vertx instance
  • Prometheus example:
// Deploy with embedded server: prometheus metrics will be automatically exposed,
// independently from any other HTTP server defined
MicrometerMetricsOptions options = new MicrometerMetricsOptions()
  .setPrometheusOptions(new VertxPrometheusOptions()
    .setStartEmbeddedServer(true)
    .setEmbeddedServerOptions(new HttpServerOptions().setPort(8081))
    .setEnabled(true))
  .setEnabled(true);
Vertx vertx = Vertx.vertx(new VertxOptions().setMetricsOptions(options));
// Then deploy verticles with this vertx instance
  • Or Prometheus with the /metrics endpoint bound to an existing HTTP server:
// Deploy without embedded server: we need to "manually" expose the prometheus metrics
MicrometerMetricsOptions options = new MicrometerMetricsOptions()
  .setPrometheusOptions(new VertxPrometheusOptions().setEnabled(true))
  .setEnabled(true);
Vertx vertx = Vertx.vertx(new VertxOptions().setMetricsOptions(options));

Router router = Router.router(vertx);
PrometheusMeterRegistry registry = (PrometheusMeterRegistry) BackendRegistries.getDefaultNow();
// Setup a route for metrics
router.route("/metrics").handler(ctx -> {
  String response = registry.scrape();
  ctx.response().end(response);
});
vertx.createHttpServer().requestHandler(router::accept).listen(8080);
  • JMX example:
// Default JMX options will publish MBeans under domain "metrics"
MicrometerMetricsOptions options = new MicrometerMetricsOptions()
  .setJmxMetricsOptions(new VertxJmxMetricsOptions().setEnabled(true))
  .setEnabled(true);
Vertx vertx = Vertx.vertx(new VertxOptions().setMetricsOptions(options));
// Then deploy verticles with this vertx instance

Setup the backend server

  • InfluxDB, by default, is expected to run on localhost:8086 without authentication, database “default”. It is configurable in VertxInfluxDbOptions. If you don’t have a running instance of InfluxDB, the shortest way to start is certainly with docker, just run:
docker run -p 8086:8086 influxdb
  • Prometheus needs some configuration since it pulls metrics from the sources. Once it is installed, configure the scrape endpoints in prometheus.yml:
- job_name: 'vertx-8081'
  static_configs:
    - targets: ['localhost:8081']

or, when using /metrics endpoint bound to an existing HTTP server:

- job_name: 'vertx-8080'
  static_configs:
    - targets: ['localhost:8080']
  • For JMX there is nothing special to configure.

Collecting Vert.x metrics

From now on, all Vert.x metrics will be collected and sent to the configured backend. In our Vert.x example, we setup an HTTP server metrics:

Router router = Router.router(vertx);
router.get("/").handler(ctx -> {
  ctx.response().end("Hello Micrometer from HTTP!");
});
vertx.createHttpServer().requestHandler(router::accept).listen(8080);

And some event bus ping-pong:

// Producer side
vertx.setPeriodic(1000, x -> {
  vertx.eventBus().send("greeting", "Hello Micrometer from event bus!");
});
// Consumer side
vertx.eventBus().consumer("greeting", message -> {
  String greeting = message.body();
  System.out.println("Received: " + greeting);
  message.reply("Hello back!");
});

To trigger some activity on the HTTP server, we can write a small bash script:

while true
do curl http://localhost:8080/
    sleep .8
done

Viewing the results

Grafana can be used to display the InfluxDB and Prometheus metrics. The vertx-examples repository contains two dashboards for that: for InfluxDB and for Prometheus.

HTTP server metrics

HTTP server metrics

Event bus metrics

Event bus metrics

Going further

We’ve seen the basic setup. There is a good bunch of options available, detailed in the documentation: how to disable some metrics domains, how to filter or rearrange labels, how to export metrics snapshots to Json objects, how to add JVM or processor instrumentation, etc.

Before we finish, there is one important point that we can cover here: defining custom metrics. Because the module gives you access to its Micrometer registry, you can add your custom metrics there.

Getting the default registry is straightforward:

MeterRegistry registry = BackendRegistries.getDefaultNow();

Then you have plain access to the Micrometer API.

For instance, here is how you can track the execution time of a piece of code that is regularly called:

MeterRegistry registry = BackendRegistries.getDefaultNow();
Timer timer = Timer
  .builder("my.timer")
  .description("Time tracker for my extremely sophisticated algorithm")
  .register(registry);

vertx.setPeriodic(1000, l -> {
  timer.record(() -> myExtremelySophisticatedAlgorithm());
});

Since it is using the same registry, there is no extra backend configuration to do.

What’s next?

The vertx-micrometer-metrics module will continue to be improved, with already two planned enhancements:

Would you miss any feature, please ask on GitHub. Contributions and bug fixes are also welcome!

Now is time to enter the Metrics.


by jotak at April 09, 2018 12:00 AM

EC by Example: Collect

by Donald Raab at April 05, 2018 08:05 AM

Learn how to transform a collection using the collect method in Eclipse Collections.

The Golden Gate Bridge from Fort Point

What is Collect?

The method named collect in Eclipse Collections takes a Function as a parameter, applies that function to each element in a collection, and returns a the resulting collection. This method is the equivalent of the method named map on the java.util.Stream class in the JDK.

Calling collect on a List with a Function

The method collect in Eclipse Collections derives its name from the Smalltalk method named collect. The method collect will return the same collection type as the source collection (where feasible), but the value type contained in the collection may be different. The method collect in Eclipse Collections should not be confused with the method collect on java.util.Stream which takes a Collector. The method collect on Stream is a more general mutable reduction which may return any type. The method collect on java.util.Stream does not appear on java.util.Collection or any of its extensions. So while there is a conceptual naming collision, there is no overloading of the methods to cause extra confusion.

Collecting from a List (Java 8)

@Test
public void collectingFromAListJava8()
{
ExecutorService executor = Executors.newWorkStealingPool();

MutableList<Integer> mList = mList(1, 2, 3, 4, 5);
ImmutableList<Integer> iList = iList(1, 2, 3, 4, 5);

Function<Integer, Integer> squared = i -> i * i;

MutableList<Integer> mutable = mList.collect(squared);

ImmutableList<Integer> immutable = iList.collect(squared);

LazyIterable<Integer> lazy = mList.asLazy().collect(squared);

ParallelListIterable<Integer> parallel =
mList.asParallel(executor, 2).collect(squared);

ImmutableList<Integer> expected = iList(1, 4, 9, 16, 25);

Assert.assertEquals(expected, mutable);
Assert.assertEquals(expected, immutable);
Assert.assertEquals(expected, lazy.toList());
Assert.assertEquals(expected, parallel.toList());
}

Collecting from a List (Java 10)

@Test
public void collectingFromAListJava10()
{
var executor = Executors.newWorkStealingPool();

var mutableList = mList(1, 2, 3, 4, 5);
var immutableList = iList(1, 2, 3, 4, 5);

Function<Integer, Integer> squared = i -> i * i;

var mutable = mutableList.collect(squared);

var immutable = immutableList.collect(squared);

var lazy = mutableList.asLazy().collect(squared);

var parallel =
mutableList.asParallel(executor, 2).collect(squared);

var expected = iList(1, 4, 9, 16, 25);

Assert.assertEquals(expected, mutable);
Assert.assertEquals(expected, immutable);
Assert.assertEquals(expected, lazy.toList());
Assert.assertEquals(expected, parallel.toList());
}

Notice that Function cannot be replaced with var. This is because a lambda must be assigned to some type.

Collecting from a primitive List (Java 8)

@Test
public void collectingFromAPrimitiveListJava8()
{
MutableIntList mList = IntLists.mutable.with(1, 2, 3, 4, 5);
ImmutableIntList iList = IntLists.immutable.with(1, 2, 3, 4, 5);

IntToIntFunction squared = i -> i * i;

MutableIntList mutable =
mList.collectInt(squared, new IntArrayList());

ImmutableIntList immutable =
iList.collectInt(squared, new IntArrayList())
.toImmutable();

LazyIntIterable lazy =
mList.asLazy().collectInt(squared);

MutableIntList expected =
IntLists.mutable.with(1, 4, 9, 16, 25);

Assert.assertEquals(expected, mutable);
Assert.assertEquals(expected, immutable);
Assert.assertEquals(expected, lazy.toList());
}

Collecting from a primitive List (Java 10)

@Test
public void collectingFromAPrimitiveListJava10()
{
var mutableIntList = IntLists.mutable.with(1, 2, 3, 4, 5);
var immutableIntList = IntLists.immutable.with(1, 2, 3, 4, 5);

IntToIntFunction squared = i -> i * i;

var mutable =
mutableIntList.collectInt(squared, new IntArrayList());

var immutable =
immutableIntList.collectInt(squared, new IntArrayList())
.toImmutable();

var lazy = mutableIntList.asLazy().collectInt(squared);

var expected = IntLists.mutable.with(1, 4, 9, 16, 25);

Assert.assertEquals(expected, mutable);
Assert.assertEquals(expected, immutable);
Assert.assertEquals(expected, lazy.toList());
}

Notice that IntToIntFunction cannot be replaced with var. This is because a lambda must be assigned to some type.

Is it possible to collect selectively?

There is a method named collectIf which applies a Predicate first to each element, and then applies a Function to each element where the Predicate returned true. This method is a fused form of select and collect. It is also possible to just call select followed by collect either eagerly, lazily or in parallel.

@Test
public void collectingSelectively()
{
var executor = Executors.newWorkStealingPool();

var mutableList = mList(1, 2, 3, 4, 5);
var immutableList = iList(1, 2, 3, 4, 5);

Function<Integer, Integer> squared = i -> i * i;
Predicate<Integer> evens = i -> i % 2 ==0;

var mutable = mutableList.collectIf(evens, squared);

var immutable = immutableList.collectIf(evens, squared);

var lazy1 =
mutableList.asLazy().collectIf(evens, squared);
var lazy2 =
mutableList.asLazy().select(evens).collect(squared);

var parallel1 =
mutableList.asParallel(executor, 2)
.collectIf(evens, squared);

var parallel2 =
mutableList.asParallel(executor, 2)
.select(evens).collect(squared);

var expected = iList(4, 16);

Assert.assertEquals(expected, mutable);
Assert.assertEquals(expected, immutable);
Assert.assertEquals(expected, lazy1.toList());
Assert.assertEquals(expected, lazy2.toList());
Assert.assertEquals(expected, parallel1.toList());
Assert.assertEquals(expected, parallel2.toList());
}

Notice that Function and Predicate cannot be replaced with var. This is because a lambda must be assigned to some type.

APIs covered in the examples

  1. Collect (Eager, Lazy and Parallel) — transforms elements of a source collection to a new collection
  2. CollectIf (Eager, Lazy and Parallel) — transforms the elements of a source collection which satisfy a given predicate to a new collection
  3. mList — Creates a MutableList
  4. iList — Creates an ImmutableList
  5. IntLists.mutable.with — Creates a MutableIntList
  6. IntLists.immutable.with — Creates an ImmutableIntList
  7. asLazy — Returns a LazyIterable or LazyIntIterable
  8. asParallel — Returns a ParallelIterable
  9. toList — Converts the target Iterable into a MutableList
  10. var — Local variable Type Inference included in Java 10 (JEP 286)

Check out this presentation to learn more about the origins, design and evolution of the Eclipse Collections API. There is also a video covering the slides that was recorded at an Eclipse Community Virtual Meet-up.

Eclipse Collections is open for contributions. If you like the library, you can let us know by starring it on GitHub.


by Donald Raab at April 05, 2018 08:05 AM