今天打开myeclipse的时候,所有项目消失
之前一直有边写边保存的习惯
今天再打开myeclipse的时候,就跟第一次打开myeclipse一样
整个栏都是空的
之前关闭后再次打开就恢复为关闭前的状态了啊!
我应该怎么恢复?
当打开Eclipse / MyEclipse, 发现左侧项目导航栏里面空白时,十有八九是工作空间的路径改变了,,只需要在菜单栏 选择“File” ->“Switch WorkSpace” ->“ Other”,然后在弹出的对话框中,选择你之前的项目路径 确定即可
今天打开myeclipse的时候,所有项目消失
之前一直有边写边保存的习惯
今天再打开myeclipse的时候,就跟第一次打开myeclipse一样
整个栏都是空的
之前关闭后再次打开就恢复为关闭前的状态了啊!
我应该怎么恢复?
当打开Eclipse / MyEclipse, 发现左侧项目导航栏里面空白时,十有八九是工作空间的路径改变了,,只需要在菜单栏 选择“File” ->“Switch WorkSpace” ->“ Other”,然后在弹出的对话框中,选择你之前的项目路径 确定即可
Eclipse JNoSQL: One API to many NoSQL databases
Eclipse JNoSQL is a Java framework that streamlines the integration of Java applications with NoSQL databases. It defines a set of APIs and provides a standard implementation for most NoSQL databases. This clearly helps to achieve very low coupling with the underlying NoSQL technologies used in applications. The project has two layers:
The project has two layers:
Communication Layer: A set of APIs that defines communication with NoSQL databases. Compared with traditional the RDBMS world, they are like the JDBC API. It contains four modules, one for each NoSQL database type: Key-Value, Column Family, Document, and Graph.
Mapping Layer: These APIs help developers to integrate their Java application with the NoSQL database. This layer is annotation-driven and uses technologies like CDI and Bean Validation, making it simple for developers to use. In the traditional RDBMS world, this layer can be compared to the Java Persistence API or object-relational mapping frameworks such as Hibernate.
One Mapping API, multiples databases
Eclipse NoSQL has one API for each NoSQL database type. However, it uses the same annotations to map Java objects. Therefore, with just these annotations that look like JPA, there is support for more than twenty NoSQL databases.
@Entity
public class God {
@Id
private String id;
@Column
private String name;
@Column
private String power;
//…
}
Another example can be found in an article that demonstrates the same annotated entity used across different NoSQL databases: Redis, Cassandra, Couchbase, and Neo4J. The approach is “stick to the API”: the developer can replace Redis with Hazelcast, as both implement the Key-Value API, thus avoiding vendor lock-in with one of these databases.
Vendor lock-in is one of the things any Java project needs to consider when choosing NoSQL databases. If there’s a need for a switch, other considerations include: time spent on the change, the learning curve of a new API to use with this database, the code that will be lost, the persistence layer that needs to be replaced, etc. Eclipse JNoSQL avoids most of these issues through the Communication APIs. It also has template classes that apply the design pattern ‘template method’ to databases operations. And the Repository interface allows Java developers to create and extend interfaces, with implementation automatically provided by Eclipse JNoSQL: support method queries built by developers will automatically be implemented for them.
public interface GodRepository extends Repository<God, String> {
Optional<God> findByName(String name);
}
GodRepository repository = …;
God diana = God.builder().withId(“diana”).withName(“Diana”).withPower(“hunt”).builder();
repository.save(diana);
Optional idResult = repository.findById(“diana”);
Optional nameResult = repository.findByName(“Diana”);
Beyond JPA
JPA is a good API for object-relationship mapping and it’s already a standard in the Java world defined in JSRs. It would be great to use the same API for both SQL and NoSQL, but there are behaviors in NoSQL that SQL does not cover, such as time to live and asynchronous operations. JPA was simply not made to handle those features.
ColumnTemplateAsync templateAsync = …;
ColumnTemplate template = …;
God diana = God.builder().withId(“diana”).withName(“Diana”).withPower(“hunt”).builder();
Consumer<God> callback = g -> System.out.println(“Insert completed to: ” + g);
templateAsync.insert(diana, callback);
Duration ttl = Duration.ofSeconds(1);
template.insert(diana, Duration.ofSeconds(1));
A Fluent API
Eclipse JNoSQL is a fluent API that makes it easier for Java developers create queries that either retrieve or delete information in a Document type, for example.
DocumentTemplate template = //;//a template to document nosql operations
God diana = God.builder().withId(“diana”).withName(“Diana”).withPower(“hunt”).builder();
template.insert(diana);//insert an entity
DocumentQuery query = select().from(God.class).where(“name”).eq(“Diana”).build();//select god where name equals “Diana”
List<God> gods = template.select(query);//execute query
DocumentDeleteQuery delete = delete().from(“god”).where(“name”).eq(“Diana”).build();//delete query
template.delete(delete);
Let’s not reinvent the wheel: Graph
The Communication Layer defines three new APIs: Key-Value, Document and Column Family. It does not have new Graph API, because a very good one already exists. Apache TinkerPop is a graph computing framework for both graph databases (OLTP) and graph analytic systems (OLAP). Using Apache TinkerPop as Communication API for Graph databases, the Mapping API has a tight integration with it.
Particular behavior matters in NoSQL database
Particular behavior matters. Even within the same type, each NoSQL database has a unique feature that is a considerable factor when choosing a database over another. This ‘’feature’’ might make it easier to develop, make it more scaleable or consistent from a configuration standpoint, have the desired consistency level or search engine, etc. Some examples are Cassandra and its Cassandra Query Language and consistency level, OrientDB with live queries, ArangoDB and its Arango Query Language, Couchbase with N1QL – the list goes on. Each NoSQL has a specific behavior and this behavior matters, so JNoSQL is extensible enough to capture this substantiality different feature elements.
public interface PersonRepository extends CouchbaseRepository {
@N1QL(“select * from Person”)
List<Person> findAll();
@N1QL(“select * from Person where name = $name”)
List<Person> findByName(@Param(“name”) String name);
}
Person person = …
CassandraTemplate template = …
ConsistencyLevel level = ConsistencyLevel.THREE;
template.save(person, level);
A Standard, Easy-to-Use Extension API
The Eclipse JNoSQL API has a simple, user-friendly interface that makes it easy to implement to a new database. As mentioned in the previous section, NoSQL database behavior matters — that why the JNoSQL API is so extensible.
Having a simple and extensible interface is important, but having an easy implementation process is just as important. That’s why in JNoSQL we are building a Technology Compatibility Kit, called the TCK. TCK is a suite of tests that nominally checks a particular (alleged) implementation of a NoSQL type API, provided by Eclipse JNoSQL. NoSQL vendors don’t need to create two drivers, because the communication layer works very well with an adapter to the mapping layer, including its specific behavior.
Conclusion
Eclipse JNoSQL is an excellent tool to use when Java EE developers want to integrate with NoSQL databases. This is especially true because it already supports more than twenty NoSQL databases, such as Cassandra, Redis, Neo4J, Couchbase, ArangoDB, Riak, and MongoDB. The unique and fluent API to support this persistence technology, with both synchronous and asynchronous calls through either a template class or just an interface, managed by CDI, plus extensions that make particular features available by the NoSQL provider: this is a formula that makes Eclipse JNoSQL a tool for polyglot persistence, big data on the NoSQL world.
Find out more information and get involved!
Website: http://www.jnosql.org/
Twitter: https://twitter.com/jnosql
GitHub Repo: https://github.com/eclipse?q=Jnosql
Mailing List: https://accounts.eclipse.org/mailing-list/jnosql-dev
Optimization Strategies with Eclipse Collections
I am the creator of an open source Java Collections framework called Eclipse Collections which was inspired by my experiences programming in Smalltalk back in the 90’s. I am currently a project lead and committer at the Eclipse Foundation for Eclipse Collections. Eclipse Collections has been evolving for well over a decade solving many different problems in a large variety of Financial Services applications. It was originally open sourced in January 2012 as GS Collections from Goldman Sachs. The project was migrated to the Eclipse Foundation in 2015 and became Eclipse Collections. Eclipse Collections can be used in conjunction with or as a replacement for the standard Java Collections interfaces (Iterable, Collection, List, Set and Map). It is completely compatible with the JDK standard Collection interfaces as well as new library features like Streams. Eclipse Collections was built from the ground up waiting for lambdas to arrive in Java 8. It began its development life in JDK 1.4. There are many things you will find in Eclipse Collections that are not available today in the Java Collections framework. These include functional, fluent and friendly Collections interfaces with a rich set of eager protocols. The library also includes data types like Bag, BiMap, and Multimap that are not currently available in the JDK. There is also a full complement of primitive data structures supporting all eight Java primitive types for Lists, Sets, Stacks, Bags, and Maps. And the feature list goes on and on.
In this article, I am going to illustrate different iteration pattern optimization strategies you can use with Eclipse Collections and Java Streams. I will cover eager, lazy and parallel patterns with performance benchmarks comparing both object and primitive collections. Enjoy!
The 4am Jamestown-Scotland ferry and other optimization strategies
When performance is important, so is understanding your available options.
Shortcuts sometimes aren’t.
I thought I would start out 2018 with a performance optimization story from 2017.
Takeaways from this blog
Java Iteration Pattern Optimization Strategies
A few Eclipse Collections and Java Stream Iteration Pattern options
Recommendations at the end
A shortcut with a twist
On January 2nd 2017, I sat with my family in our Honda Pilot on a pier at 3:30am for a half hour waiting for the 4am Jamestown-Scotland ferry to arrive. I had come to the literal end of the road on a shortcut that wasn’t exactly as I had expected. I decided to take the shorter distance route on my car’s Nav System to avoid having to go north on Interstate 95 only then to have to go south to get to Williamsburg, Virginia. I’ve gotten stuck in bumper to bumper traffic in Virginia on Route 95 on late night rides coming back from Florida a few times before. When we got to the end of the road on our shorter route, the Nav System indicated the next turn was to get on the ferry (see picture above).
I was willing to take slower local roads, especially since it was early in the morning and there would be no traffic on them. We discovered too late that the path that our chosen path included a ferry ride. At this point, we only had two options. We could wait for the ferry and hope it was running, or turn around and add another 3 to 4 hours to our trip. A classic Hobson’s Choice. We waited for the ferry. It turned out to be a fun experience once we parked our car on the ferry, but I would have preferred an alternative at 4am after driving 14 hours.
“Two roads diverged in a wood…” — Robert Frost
I certainly took the one less traveled by. I did learn a new route that I didn’t know before for getting to Williamsburg from Orlando, as well as the planning required to optimize that route with the ferry schedule.
What does this trip have to do with Eclipse Collections, you may ask? Well, the path I took was the Serial (one lane Colonial era roads), Lazy (ferry does the work until you get to the dock), and Boxed (your car is literally boxed in on the ferry by other cars) — just one of many options you can choose with Eclipse Collections and Java Streams.
“Premature optimization is the root of all evil” — Donald Knuth
Readability should be prioritized above performance when writing code. However, it helps to know what your available performance optimization options are, before you discover last minute your only option is stopping and waiting for the next ferry. You may actually be able to achieve better performance without sacrificing readability. In fact, there may be options you were unaware of previously that improve both readability and performance.
There is a set of Iteration Pattern Optimization Strategies that I believe all developers should become aware of so they can appropriately tune their code for the best performance.
Don’t guess when optimizing code. First prove you have a problem that needs to be fixed. Then benchmark any solutions you think may help to prove that they actually do.
Travelers Beware: You can lose many hours of your life measuring performance optimization benefits. The tests I have run below take 45–50 minutes to run each time. I had to run them several times along with unit tests to validate that the results were the same across all similar tests. When you see the charts, you may be at first compelled by the graphs in terms of wanting to change your code to be more “optimal”. Optimal may not equate to noticeably faster in terms of your application’s overall performance. Each of these tests take at most hundreds of milliseconds to run. They are all “fast”, because they are all in memory. The optimal solutions may only accumulate savings over a large number of executions. If you happen to see a more readable solution you were not aware of here, go for that one.
Iteration Pattern Optimization Strategies
Do you know how to leverage all of these strategies separately and together to increase performance without sacrificing readability?
Eager — executes immediately with potential optimizations specific to each algorithm and data structure. Eager algorithms are as close to a hand coded for-loop as you will get, so they are easy to understand and debug. I prefer eager as the default option for iterating over collections. It is the simplest and usually most succinct and readable solution available. I consider every other solution a potential optimization, which may prove pre-mature.
Primitive — If you can avoid boxing primitives, you can reduce memory cost and potentially increase performance. I always use primitive collections and algorithms when I can.
Lazy — executes only when a terminal operation is called. Optimizations include reducing the amount of memory required and total computation when multiple operation are executed. Short-circuiting effects can really help performance when run lazily. I prefer lazy as soon as I am executing multiple operations that would result in temporary collections being created.
Parallel — It costs more to run in parallel. You need the right data size, algorithm and multiple cores. If you have all of these, you may benefit from running in parallel. Measure it to prove it.
Eager vs. Lazy — Understanding how they work
Let’s take a list of five integers and perform a filter, map, and reduce set of operations both eagerly and lazily.
@Test
public void eagerVsLazy()
{
long eagerSum = Lists.mutable.with(1, 2, 3, 4, 5)
.tap(i -> System.out.println(“eager select: ” + i))
.select(i -> i % 2 == 0)
.tap(i -> System.out.println(“eager collect: ” + i))
.collectInt(i -> i * 2)
.tap(i -> System.out.println(“eager sum: ” + i))
.sum();
System.out.println(eagerSum);
long lazySum = Lists.mutable.with(1, 2, 3, 4, 5)
.asLazy()
.tap(i -> System.out.println(“lazy select: ” + i))
.select(i -> i % 2 == 0)
.tap(i -> System.out.println(“lazy collect: ” + i))
.collectInt(i -> i * 2)
.tap(i -> System.out.println(“lazy sum: ” + i))
.sum();
System.out.println(lazySum);
Assert.assertEquals(eagerSum, lazySum);
}
Except for the additional call to asLazy in the lazy example, the code should look identical. The printed results are as follows:
eager select: 1
eager select: 2
eager select: 3
eager select: 4
eager select: 5
eager collect: 2
eager collect: 4
eager sum: 4
eager sum: 8
12
lazy select: 1
lazy select: 2
lazy collect: 2
lazy sum: 4
lazy select: 3
lazy select: 4
lazy collect: 4
lazy sum: 8
lazy select: 5
12
Notice how the order of execution changes on the lambdas in the lazy case. In the eager case, two additional lists are created as intermediate results during the execution. A List of Integer with two Integers (2, 4) and then an IntList with two ints (4, 8) are created before the final call to sum. In the lazy case, there are no intermediate collections created. This results in less garbage being generated. This is why I prefer lazy execution when there are multiple operations involved. If there was a single operation involved, then I would default to using the eager solution.
If we look at the serial Stream solution, it’s execution order will be the same as the lazy Eclipse Collections solution.
@Test
public void stream()
{
int streamSum = Lists.mutable.with(1, 2, 3, 4, 5)
.stream()
.peek(i -> System.out.println(“stream filter: “+ i))
.filter(i -> i % 2 == 0)
.peek(i -> System.out.println(“stream map: “+ i))
.mapToInt(i -> i * 2)
.peek(i -> System.out.println(“stream sum: “+ i))
.sum();
System.out.println(streamSum);
}
Here is the output:
stream filter: 1
stream filter: 2
stream map: 2
stream sum: 4
stream filter: 3
stream filter: 4
stream map: 4
stream sum: 8
stream filter: 5
12
Lazy + Parallel = Harder to Follow
Using Eclipse Collections lazy parallel with a batch size of one so we can see the results for a very small list.
@Test
public void parallel()
{
long parallelSum = Lists.mutable.with(1, 2, 3, 4, 5)
.asParallel(Executors.newWorkStealingPool(), 1)
.select(i -> {
System.out.println(“parallel select: ” + i);
return i % 2 == 0;
})
.collect(i -> {
System.out.println(“parallel collect: ” + i);
return i * 2;
})
.sumOfInt(i -> {
System.out.println(“parallel sum: ” + i);
return i;
});
System.out.println(parallelSum);
}
Run 1:
parallel select: 2
parallel select: 1
parallel select: 4
parallel collect: 4
parallel select: 3
sum: 8
parallel select: 5
parallel collect: 2
sum: 4
12
Run 2:
parallel select: 1
parallel select: 3
parallel select: 2
parallel select: 5
parallel select: 4
parallel collect: 2
parallel collect: 4
parallel sum: 4
parallel sum: 8
12
Run 3:
parallel select: 4
parallel select: 2
parallel collect: 2
parallel select: 5
parallel select: 3
parallel select: 1
parallel sum: 4
parallel collect: 4
parallel sum: 8
12
The result is consistent between runs, but the order of execution of lambdas is not guaranteed nor consistent.
Using parallel Streams:
@Test
public void parallelStream()
{
int streamSum = Interval.oneTo(5).toList()
.parallelStream()
.peek(i -> System.out.println(“stream filter: “+ i))
.filter(i -> i % 2 == 0)
.peek(i -> System.out.println(“stream map: “+ i))
.mapToInt(i -> i * 2)
.peek(i -> System.out.println(“stream sum: “+ i))
.sum();
System.out.println(streamSum);
}
Run 1:
stream filter: 4
stream filter: 1
stream map: 4
stream filter: 2
stream sum: 8
stream filter: 3
stream filter: 5
stream map: 2
stream sum: 4
12
Run 2:
stream filter: 5
stream filter: 1
stream filter: 3
stream filter: 2
stream filter: 4
stream map: 2
stream map: 4
stream sum: 4
stream sum: 8
12
Run 3:
stream filter: 2
stream filter: 4
stream map: 2
stream map: 4
stream sum: 8
stream filter: 1
stream filter: 3
stream filter: 5
stream sum: 4
12
Measure, Execute and Repeat.
I am going to show different options and their performance characteristics for a set of use cases using a million randomly generated integers stored in Lists. These are not likely to be the use cases you will encounter in production code, but they should hopefully illustrate some options you may not have been aware of next time you find a bottleneck you were not expecting in your basic Java data structures and algorithms. I will demonstrate the performance differences between using object and primitive lists, eager and lazy APIs, with both serial and parallel execution, with four different use cases.
In each use case, I share what I observed — expected and unexpected. I only observed. I have not dug into the why the results were what they were. “The why” perhaps is a topic for another blog.
Use Cases — Filter, Map, Reduce, and Filter/Map/Reduce
1. Filter even integers into a List
2. Multiply the integers by 2 and storing the result in a List
3. Sum all the integers into a long
4. Filter/Map/Reduce (Filter Evens, Multiply x 2, Sum into long)
The Data — 1,000,000 Integers
private List jdkList;
private MutableList ecList;
private IntList ecPrimitiveList;
private ExecutorService executorService;
@Setup
public void setup()
{
PrimitiveIterator.OfInt intGenerator =
new Random(1L).ints(-1000, 1000).iterator();
this.ecList =
FastList.newWithNValues(1_000_000, intGenerator::nextInt);
this.jdkList = new ArrayList<>(1_000_000);
this.jdkList.addAll(this.ecList);
this.ecPrimitiveList =
this.ecList.collectInt(i -> i, new IntArrayList(1_000_000));
this.executorService = Executors.newWorkStealingPool();
}
Hardware
I will be using a MacPro with the following hardware specs to measure the benchmarks:
Processor Name: 12-Core Intel Xeon E5
Processor Speed: 2.7 GHz
Number of Processors: 1
Total Number of Cores: 12
L2 Cache (per Core): 256 KB
L3 Cache: 30 MB
Memory: 64 GB
Software
To illustrate the different options that are available for these particular use cases, I will be using JDK 1.8.0_152 with Eclipse Collections and Streams.
Benchmarking
I am using JMH version 1.19 as the benchmark harness for my tests. I am running 30 warmup iterations, and 20 measurement iterations with 2 forks. I am using Mode.Throughput with the tests so they are easy to read. The numbers are in Operations per Second. The bigger the number, the better the result.
public static void main(String[] args) throws RunnerException
{
Options options = new OptionsBuilder()
.include(“.*” + IntListJMHTest.class.getSimpleName() + “.*”)
.forks(2)
.mode(Mode.Throughput)
.timeUnit(TimeUnit.SECONDS)
.warmupIterations(30)
.build();
new Runner(options).run();
}
I will highlight in dark green the best overall result in the run. I will highlight in light green the best serial execution result. Where I use EC in a label in the chart it stands for a solution using Eclipse Collections. Where I used JDK, the solution uses a standard JDK approach.
Filter even integers
Filter even numbers from a List of 1,000,000 Integers
Expected:
I expected ECParallelEager to perform better.
I expected primitive collections to outperform boxed collections.
I expected serial eager to outperform serial lazy.
Unexpected:
I did not expect parallel streams to perform this poorly.
@Benchmark
public MutableList filterECBoxedEager()
{
return this.ecList.select(i -> i % 2 == 0);
}
@Benchmark
public MutableList filterECBoxedLazy()
{
return this.ecList
.asLazy()
.select(i -> i % 2 == 0)
.toList();
}
@Benchmark
public MutableList filterECParallelEager()
{
return ParallelIterate.select(
this.ecList,
i -> i % 2 == 0,
new CompositeFastList<>(),
false);
}
@Benchmark
public MutableList filterECParallelLazy()
{
return this.ecList
.asParallel(this.executorService, 50_000)
.select(i -> i % 2 == 0)
.toList();
}
@Benchmark
public IntList filterECPrimitiveEager()
{
return this.ecPrimitiveList.select(i -> i % 2 == 0);
}
@Benchmark
public IntList filterECPrimitiveLazy()
{
return this.ecPrimitiveList
.asLazy()
.select(i -> i % 2 == 0)
.toList();
}
@Benchmark
public List filterJDKBoxedParallelStream()
{
return this.jdkList
.parallelStream()
.filter(i -> i % 2 == 0)
.collect(Collectors.toList());
}
@Benchmark
public List filterJDKBoxedStream()
{
return this.jdkList
.stream()
.filter(i -> i % 2 == 0)
.collect(Collectors.toList());
}
Map each integer x 2
Multiply times two, each integer in a List of 1,000,000 Integers
Expected:
I expected primitive collections to outperform boxed collections.
I expected serial eager to outperform serial lazy.
Unexpected:
I did not expected ECParallelLazy to perform so poorly.
I did not expect either Stream solutions to perform so poorly.
@Benchmark
public MutableList mapECBoxedEager()
{
return this.ecList.collect(i -> i * 2);
}
@Benchmark
public MutableList mapECBoxedLazy()
{
return this.ecList
.asLazy()
.collect(i -> i * 2)
.toList();
}
@Benchmark
public MutableList mapECParallelEager()
{
return ParallelIterate.collect(
this.ecList, i -> i * 2,
new CompositeFastList<>(),
false);
}
@Benchmark
public MutableList mapECParallelLazy()
{
return this.ecList
.asParallel(this.executorService, 50_000)
.collect(i -> i * 2)
.toList();
}
@Benchmark
public IntList mapECPrimitiveEager()
{
return this.ecPrimitiveList
.collectInt(i -> i * 2, IntLists.mutable.empty());
}
@Benchmark
public IntList mapECPrimitiveLazy()
{
return this.ecPrimitiveList
.asLazy()
.collectInt(i -> i * 2)
.toList();
}
@Benchmark
public List mapJDKBoxedParallelStream()
{
return this.jdkList
.parallelStream()
.mapToInt(i -> i * 2)
.boxed()
.collect(Collectors.toList());
}
@Benchmark
public List mapJDKBoxedStream()
{
return this.jdkList
.stream()
.mapToInt(i -> i * 2)
.boxed()
.collect(Collectors.toList());
}
Sum all integers
Multiply times two, each integer in a List of 1,000,000 Integers
Expected:
I expected primitive collections to outperform boxed collections.
I expected little benefit from parallelization here. Summing ints is a very fast operation. I expected eager primitive to be faster than most of the parallel options.
Unexpected:
I did not expect serial streams to get crushed. There seems to have been an improvement made in Java 9. I ran the benchmarks again with Java 9 and this particular benchmark improved by ~7–8x.
@Benchmark
public long sumECBoxedEager()
{
return this.ecList.sumOfInt(Integer::intValue);
}
@Benchmark
public long sumECBoxedLazy()
{
return this.ecList
.asLazy()
.sumOfInt(Integer::intValue);
}
@Benchmark
public long sumECParallelEager()
{
return ParallelIterate.sumByInt(
this.ecList,
i -> Integer.valueOf(0),
Integer::intValue).get(0);
}
@Benchmark
public long sumECParallelLazy()
{
return this.ecList
.asParallel(this.executorService, 50_000)
.sumOfInt(Integer::intValue);
}
@Benchmark
public long sumECPrimitiveEager()
{
return this.ecPrimitiveList.sum();
}
@Benchmark
public long sumECPrimitiveLazy()
{
return this.ecPrimitiveList
.asLazy()
.sum();
}
@Benchmark
public long sumJDKBoxedParallelStream()
{
return this.jdkList
.parallelStream()
.mapToLong(Integer::longValue)
.sum();
}
@Benchmark
public long sumJDKBoxedStream()
{
return this.jdkList
.stream()
.mapToLong(Integer::longValue)
.sum();
}
Filter, Map, Sum
Multiply times two, each integer in a List of 1,000,000 Integers
Expected:
I expected lazy operations to outperform eager.
I expected primitive lazy would outperform all of the other serial operations.
I expected JDKBoxedParallelStream would perform well with this use case.
Unexpected:
I did not expect ECParallelEager to do as well as or better than ECParallelLazy, even though it was optimized.
I did not expect JDKBoxedParallelStream to do better than ECParallelLazy.
@Benchmark
public long filterMapSumECBoxedEager()
{
return this.ecList
.select(i -> i % 2 == 0)
.sumOfInt(i -> i * 2);
}
@Benchmark
public long filterMapSumECBoxedLazy()
{
return this.ecList
.asLazy()
.select(i -> i % 2 == 0)
.sumOfInt(i -> i * 2);
}
@Benchmark
public long filterMapSumECOptimizedParallelEager()
{
return ParallelIterate.sumByInt(
this.ecList,
i -> i % 2,
i -> i * 2).get(0);
}
@Benchmark
public long filterMapSumECOptimizedParallelLazy()
{
return this.ecList
.asParallel(this.executorService, 50_000)
.sumOfInt(i -> i % 2 == 0 ? i * 2 : 0);
}
@Benchmark
public long filterMapSumECParallelLazy()
{
return this.ecList
.asParallel(this.executorService, 50_000)
.select(i -> i % 2 == 0)
.sumOfInt(i -> i * 2);
}
@Benchmark
public long filterMapSumECPrimitiveEager()
{
return this.ecPrimitiveList
.select(i -> i % 2 == 0)
.collectInt(i -> i * 2, IntLists.mutable.empty())
.sum();
}
@Benchmark
public long filterMapSumECPrimitiveLazy()
{
return this.ecPrimitiveList
.asLazy()
.select(i -> i % 2 == 0)
.collectInt(i -> i * 2)
.sum();
}
@Benchmark
public long filterMapSumJDKBoxedParallelStream()
{
return this.jdkList
.parallelStream()
.filter(i -> i % 2 == 0)
.mapToLong(i -> (long) (i * 2))
.sum();
}
@Benchmark
public long filterMapSumJDKBoxedStream()
{
return this.jdkList
.stream()
.filter(i -> i % 2 == 0)
.mapToLong(i -> (long) (i * 2))
.sum();
}
Congratulations!
I hope you enjoyed the blog and learned some new things about Iteration Pattern Options and Optimization Strategies using Eclipse Collections and Java Streams. If your only tool is a hammer, everything else is a nail. Knowing your available options before you get started on your journey and adapting as needs arise is one of the keys to writing better and more responsive applications. This can also help you execute a less stressful trip from Orlando to Williamsburg, if ever that occasion happens to arise.
Recommendations
Prefer Primitives over Boxing.
Prefer Eager iteration for single or fused operations.
Prefer Lazy iteration for multi-step operations.
Prove it before going Parallel.
Try Eclipse Collections if you want more than Hobson’s Choice.
Eclipse Collections is open for contributions. If you like the library, you can let us know by starring it on GitHub.
Eclipse OpenJ9; not just any Java Virtual Machine
openj9 logo
OpenJ9 is a JVM implementation, but not just any JVM!
Although the Eclipse OpenJ9 project hasn’t been around for very long, the VM itself has been around for years. Where? Well, certainly not as an experimental prototype. OpenJ9 has been earning its reputation as an Enterprise grade, production-level component of the IBM® Java development kit, which has been powering IBM middleware products for the last decade or more. IBM contributed the VM to the Eclipse Foundation back in 2017 and more than 70 IBM developers are actively involved in the project. The Eclipse Foundation has a great reputation for Java open source development, so moving OpenJ9 development into this innovative ecosystem puts it in the right place, with the right people.
A large part of OpenJ9 has in fact been living at the Eclipse Foundation for a couple of years now. Eclipse OpenJ9 embeds Eclipse OMR, which provides core runtime components that can be used to build runtime environments for different programming languages. IBM contributed that code too and you can find out more about it on the Eclipse OMR page. At Eclipse OpenJ9, we take OMR and we add extra code that turns it into a runtime environment for Java applications.
So it’s been around a long time and it’s got a proven track record for reliability, scalability, and performance. Why else would you want to choose it to run your Java applications?
Well, the same version of the VM can be used in an OpenJDK for Java 8, Java 9, and beyond. With a single development head stream, any improvements to the VM can be exploited across multiple versions of Java, providing continuity. And if you aren’t ready to leap into the modular world of Java 9 or Java 10 just yet, you can still take advantage of new VM enhancements on Java 8.
We’ve also got a great story to tell for cloud deployments….
Fast startup and small footprint
Eclipse OpenJ9 has a great story to tell for applications that run in the cloud. With a little configuration, you can almost double the startup speed with about half the memory footprint, compared to other VM implementations. With market trends moving towards the cloud for application development and deployment, this is a compelling reason to choose OpenJ9 as the virtual machine for your runtime environment.
Here are some charts from a recent benchmark test that we ran to compare an OpenJDK 8 with an OpenJ9 VM against an OpenJDK 8 with a Hotspot VM.
A comparison of startup time between and OpenJDK 8 with OpenJ9 and an OpenJDK8 with Hotspot.
A comparison of footprint size after startup between and OpenJDK 8 with OpenJ9 and an OpenJDK8 with Hotspot.
That’s a 42% faster startup time and a 66% smaller footprint after startup. Impressive huh?! You can read more about these performance results on the OpenJ9 website. If you want to see what OpenJ9 can do for your Java application, here are some configuration tips for tuning an OpenJDK with OpenJ9:
Use a shared classes cache (-Xshareclasses -XX:SharedCacheHardLimit=200m -Xscmx60m) with Ahead-Of-Time (AOT) compilation
Use our idle VM settings (-XX:+IdleTuningGcOnIdle)
Use our cloud optimization setting (-Xtune:virtualized)
Class data sharing and AOT
Creating and using shared classes in a cache isn’t a new thing, but it is a fundamentally simple way of improving startup performance. The first time an application runs, the VM must load all the classes that are needed to get going. This initialization process takes time. However, if you can store these classes in a cache, the second time the application runs it takes much less time to initialize. It’s that simple! In fact, because it makes sense to do so, OpenJ9 always shares both the bootstrap and application classes that are loaded by the default system class loader.
By dynamically compiling methods into AOT code at runtime, the VM can start an application faster because it doesn’t need to spend time interpreting Java methods. The VM automatically chooses which methods should be AOT-compiled based on heuristics that identify the start-up phase of large applications. AOT code is always used in combination with shared classes, which means that startup performance gets even better; the cached AOT code can be used to quickly enable native code performance for subsequent runs of your application. When a cached AOT method is run it might also be optimized further by the Just-In-Time (JIT) compiler.
Using a shared classes cache also reduces your memory footprint because multiple VMs can share the same class data. In addition, those parts of the class data that are used for debugging remain on disk instead of in memory, which keeps the footprint as small as possible.
The -Xshareclasses option is highly configurable, allowing you to specify where to create the cache, how much space to allocate for AOT code and more. You can also set the cache size by using the -Xscmx option. Until the default size of 16 MB changes (work in progress), we recommend setting a size of at least 60 MB to provide ample space for shared classes and AOT code. As for AOT itself, it works straight out of the box when you enable shared classes and doesn’t require any special tuning. For small or short running applications, make sure you use the -Xtune:virtualized option, which helps you get the most out of AOT-compiled code.
For a deep dive into OpenJ9 shared classes technology, read this Class sharing article.
Idle VM settings
Idle tuning is all about keeping your memory footprint small, which can offer cost savings to cloud users and cloud providers. Like it or not, memory footprint tends to grow during the lifecycle of an application because the Java heap fills up with objects that, once used, are no longer referenced. These objects, commonly known as garbage, are removed by a process called garbage collection (GC) when space is needed by an application but nothing is left. However, if an application has a quiet period, the heap might be bloated with tons of garbage but no memory gets reclaimed because GC isn’t triggered.
By using the idle tuning feature, cloud users can reduce the cost of running applications on some cloud services, where the usage-charging model is based on memory. For cloud providers, idle tuning offers a unique opportunity to manage their memory resources more efficiently, allowing more virtual machines to run per server.
So how does it work? When you start your application, specify -XX:+IdleTuningGcOnIdle on the command line. When set, OpenJ9 determines whether an application is idle based on CPU utilization and other internal heuristics. When an idle state is recognized, a GC cycle runs if there is significant garbage in the heap and releases unused memory back to the operating system. This option can be used along with other options that determine how long the VM should wait before triggering the process, and what percentage of free memory pages in the heap should be released. You can also tell the GC to compact the heap before releasing free memory, which might make further memory savings.
Idle tuning is covered in more detail in this Developer Center article.
Cloud optimization setting
The -Xtune:virtualized option is designed to configure OpenJ9 for typical cloud deployments where VM guests are provisioned with a small number of virtual CPUs to maximize the number of applications that can be run.
When this option is enabled, OpenJ9 adapts its internal processes to reduce the amount of CPU consumed and trim down the memory footprint. These changes come at the expense of only a small loss in throughput, so are well worth the tradeoff.
If we’ve got you itching to try it out already, you could hop straight over to the AdoptOpenJDK project where you can pick up prebuilt, JCK-certified, OpenJDK binaries that run with the OpenJ9 VM. But before you do, read on to find out more about the OpenJ9 JIT compiler, our choice of garbage collection policies, and how you can get involved in the project.
The JIT compiler
The Just-In-Time compiler is a key component of the OpenJ9 VM, and improves the performance of Java applications by compiling platform-neutral Java bytecode into native machine code at run time. Without the JIT, the VM has to interpret the bytecodes itself – a process that requires extra CPU and memory, which effectively applies the brakes!
The JIT compiler doesn’t compile every method that gets called. After all, thousands of methods can be called at startup and if the JIT tried to compile all of them, the application would take way too long to start. Instead, OpenJ9 records the number of times a method is called. When the count reaches a pre-defined invocation threshold, JIT compilation is triggered. Once a method has been compiled by the JIT, the VM can call the compiled method rather than interpreting it.
The JIT compiler can compile a method at different optimization levels: cold, warm, hot, very hot (with profiling), or scorching. The hotter the optimization level, the better the expected performance, but the higher the cost in terms of CPU and memory.
cold is used during startup processing for large applications where the goal is to achieve the best compiled code speed for as many methods as possible.
warm is the workhorse; after start-up, most methods are compiled when they reach the invocation threshold.
For higher optimization levels, the VM uses a sampling thread to identify methods that continue to take a lot of time. Methods that consume more than 1% are compiled at hot. Methods that consume more than 12.5% are scheduled for a scorching compilation. However, before that happens the methods are compiled at very hot with profiling to collect detailed profile data that is used by the scorching compilation.
The higher optimization levels use special techniques such as escape analysis and partial redundancy elimination, or loop through certain optimization sequences more times. Although these techniques use more CPU and memory, the improved performance that is delivered by the optimizations can make the tradeoff worthwhile.
Garbage collection
To prevent applications running out of memory, objects in the Java heap that are no longer required must be reclaimed. This process is known as garbage collection (GC). When garbage is collected, the garbage collector must obtain exclusive access to the heap, which causes an application to pause while all the tidying up is done. This pause is often referred to as a stop-the-world pause because an application can do absolutely nothing until the process completes. In general, the first step in the process is to mark the objects that are reachable, which means they are still in use. The next step is to sweep away the unmarked objects to reclaim memory. To an end user, an obvious pause in application processing might look like an application has hung, so you might want to choose when and how this housework is done.
Eclipse OpenJ9 has a number of GC policies designed around different types of applications and workloads. Picking the right policy very much depends on your usage and performance goals.
If you have a transactional application, with many short lived objects, the Generational Concurrent (-Xgcpolicy:gencon) GC policy is probably best suited, which aims to minimize GC pause times without compromising throughput. This is the default policy employed by the VM, so if you want to use it you don’t need to specify it on the command line when you start your application.
We also have the following alternative GC policies:
-Xgcpolicy:balanced divides the Java heap into regions, which are individually managed to reduce the maximum pause time on large heaps and increase the efficiency of garbage collection. The aim of the policy is to avoid global collections by matching object allocation and survival rates. If you have problems with application pause times that are caused by global garbage collections, particularly compactions, this policy might improve application performance, particularly on large systems that have Non-Uniform Memory Architecture (NUMA) characteristics (x86 and POWER™ platforms).
-Xgcpolicy:metronome is designed for applications that require precise response times. Garbage collection occurs in small interruptible steps to avoid stop-the-world pauses.
-Xgcpolicy:optavgpause uses concurrent mark and sweep phases, which means that pause times are reduced when compared to optthruput, but at the expense of some performance throughput.
-Xgcpolicy:optthruput is optimized for throughput by disabling the concurrent mark phase, which means that applications will stop for long pauses while garbage collection takes place. You might consider using this policy when high application throughput, rather than short garbage collection pauses, is the main performance goal.
A little more about the Metronome GC policy, which is receiving quite a bit of interest lately. If your application depends on precise response times and you are running on x86 Linux™ or AIX®, you might be interested in using the Metronome (-Xgcpolicy:metronome) policy. The key difference between Metronome and other policies is that garbage collection occurs in small interruptible steps rather than stopping an application completely while garbage is marked and collected. By default, Metronome pauses for 3 milliseconds (ms) at a time. A full garbage collection cycle requires many pauses, which are spread out to give the application enough time to run. You can limit the amount of CPU that the GC process uses and you can control the pause time. In the following example, yourApp runs for 80% in every 60 ms with the remaining 20% of the time allocated for garbage collection, if there is garbage to be collected. Additionally, individual pauses for Metronome are set to be no longer than 10 ms:
java -Xgcpolicy=metronome -Xgc:targetUtilization=80 -Xgc:targetPauseTime=10 yourApp
The Metronome GC policy guarantees utilization levels if it has been given sufficient resources. Garbage collection begins only when the amount of free space in the heap falls below a dynamically determined threshold. Choosing the optimum settings for your application is best achieved with a monitoring tool like Garbage Collection Memory Visualizer (GCMV), where you can monitor the GC pause times for your application and adjust the settings to maximize performance.
There’s another special GC mode of the gencon policy that deserves a separate mention: Concurrent Scavenge (-Xgc:concurrentScavenge). This mode works with the Guarded Storage (GS) Facility, which is a feature of the IBM z14™ mainframe system. The aim is to minimize the time spent in stop-the-world pauses by collecting garbage in parallel with running application threads. The GS Facility provides hardware-based support to detect when potentially stale references to objects are accessed by an application. This means that the garbage collector can start processing objects in parts of the heap without halting an application because the GS Facility is on hand to spot accesses to an object and send a notification. The object that was ready to be swept away can be moved, and references to it can be reset. The complex interactions that are involved between the VM and the GS Facility are beyond the scope of this article, but you can read more about it in the following blog posts:
Reducing Garbage Collection pause times with Concurrent Scavenge and the Guarded Storage Facility
How Concurrent Scavenge using the Guarded Storage Facility Works
Note: Concurrent scavenge mode is available only on the z/OS® platform.
We hope this article has given you some insight into the pedigree and strengths of the Eclipse OpenJ9 VM. Remember: Fast startup, small footprint, great throughput performance, optimized for cloud, reliable, scalable, and secure. Why not try it out and tell us what you think? Go on.. give your Java application a thrill. Run it on OpenJDK with Eclipse OpenJ9!
If you want to find out more about the project, our Eclipse OpenJ9 website is a good place to start. You’ll find some key information about OpenJ9, including our impressive performance measurements, and links to all sorts of useful resources.
Recent Webinar
vECM: Eclipse OpenJ9- Eclipse’s own JVM
If you missed the recent Virtual Eclipse Community Meetup (vECM) given by Dan Heidinga on Eclipse OpenJ9, you can catch up on YouTube.
Want to get involved?
Why not join us on slack where you can chat to our developers? Request an invitation
Come along to one of our weekly “Ask the OpenJ9 community!” calls. For eMeeting links and agendas, check the #planning channel in our slack workspace. To add these meetings to your Google calendar, add the OpenJ9 hangouts calendar.
Useful links
Blogs, articles, and presentations
OpenJ9 GitHub repository
Eclipse OpenJ9 project page
Scalable Open APIs with Eclipse Vert.x
The HTTP/2 protocol is slowly but surely eating market share of the public web and if you are browsing a website proxied by a CDN (like the https://vertx.io website) with a modern browser, there is a high chance that your browser and the web server are talking with HTTP/2.
HTTP/1 was designed more than 20 years ago to render very simple web pages. Since then, the complexity of web pages has exploded, and web browsers have had to develop a variety of techniques to work around HTTP/1’s limitations; managing impressive performance that keeps load times reasonable, even for complex pages.
HTTP/2 was introduced a few years ago as a better transport for HTTP requests and responses. It does not actually redefine how client and server interact, but instead tries to optimize the data exchange in the most efficient possible fashion.
Web APIs can also benefit from the improvements provided by HTTP/2, whether it is a publicly hosted API endpoint or private interactions between microservices in the same data center.
A multiplexed protocol
If you need to know a single thing about HTTP/2, it is certainly that HTTP/2 is a multiplexed protocol.
An HTTP/1 connection can only transport one request / response at a time. Of course, connections can be reused to avoid the creation cost of the connection, but when the client needs to achieve simultaneously several requests to the same server, it has no choice to open several connections; one for each request.
In addition, the client has to wait until it receives the response, with the connection remaining unused during the service time of the server. While it can be acceptable in many situations, it is certainly a very poor usage of the connection which is ultimately a resource cost for the client, the server, and the intermediaries.
HTTP/2 is a multiplexed protocol: multiplexed means that a single connection can transport multiple requests and responses simultaneously between the client and the server.
Let’s have look at the benefits of using HTTP/2.
Better capacity planning
The most important configuration aspect of non-multiplexed clients is to get the number of connections for the client right. A too low value creates unnecessary waiting for the client which leads to higher response times.
Conversely, HTTP/2 uses a single connection and the number of requests is actually defined by the server: the server tells the client how many requests it can handle simultaneously; this magic number is called the connection concurrency.
Better concurrency control
The server defining the concurrency gives the unique opportunity for the server to size the value according to a variety of criteria, such as the client QoS or the actual server resources.
The concurrency value is initially sent by the server when the connection is created and this value can be updated whenever the server needs to, e.g when the server has resources scaling down it can notify the client with a lower concurrency.
Better latency
The connection pool size has a direct impact on application performance and QoS: when the value is too low it creates unnecessary waiting on the client which results in higher service time from the perspective of the client.
With HTTP/2 server controlled concurrency, the client always uses optimal concurrency according to the server’s capacity. When the client is an intermediary like an API gateway (a glorified HTTP reverse proxy) this results in lower response times at the edge.
What is Eclipse Vert.x ?
Eclipse Vert.x is a toolkit to build distributed reactive systems on the top of the Java Virtual Machine using an asynchronous and non-blocking development model. As a toolkit, Vert.x can be used in many contexts: in a standalone application or embedded in a Spring application.
Vert.x and its ecosystem are just jar files used as any other library: just place them in your classpath and you are done.
HTTP/2 with Eclipse Vert.x
Vert.x supports HTTP/2 for server and client since the version 3.3 with a variety of options:
The h2 protocol:HTTP/2 over TLS sockets
The h2c with prior knowledge protocol: HTTP/2 over plain sockets
The h2c upgrade protocol: HTTP/1.1 request upgraded to the HTTP/2 protocol
Creating an HTTP/2 server with Vert.x is very simple
public class Http2Server {
public static void main(String[] args) {
// Create a Vert.x instance
Vertx vertx = Vertx.vertx();
// Generate the certificate for https
SelfSignedCertificate cert = SelfSignedCertificate.create();
// Start an HTTP/2 server that prints hello world!
vertx.createHttpServer(new HttpServerOptions()
.setSsl(true)
.setUseAlpn(true)
.setKeyCertOptions(cert.keyCertOptions())
.setPort(8443))
.requestHandler(request -> {
request.response().end(“Hello World”);
}).listen();
}
}
We chose to use the h2 protocol for our server and we need to configure the TLS layer accordingly:
The SSL is set along with the usage of ALPN – ALPN is a TLS extension used by the client and the server to negotiate the usage of h2
A self-signed certificate is generated on the fly with the SelfSignedCertificate class and set on options for the server
Contract First APIs Development
When you need to develop a Web API and want high productivity you might want to consider OpenAPI.
OpenAPI is an open standard that empowers developers to formally document web APIs. It provides a document and schema-centric workflow for creating Web APIs. You can adopt the Contract-First approach, enabling teams to deeply connect design and implementation phases of your Web APIs development.
You can document every aspect of your endpoints: the request parameters, the request body, the different response bodies, the security requirements and so on.
With this approach, you create an OpenAPI contract during the design phase and you reuse it during the implementation phase using specific OpenAPI tooling. You can even go beyond that and manage every part of the lifecycle of your product.
For more info give a look at OpenAPI API Development.
Vert.x meets OpenAPI
In this article, we will implement an API for managing users based on a simple OpenAPI contract. The document is in YAML format and is very human readable; you can create such documents easily with a tool, for this example we used Apicur.io Studio.
—
openapi: 3.0.0
info:
title: Users API
description: An API for managing users
version: 1.0.0
paths:
/users:
get:
summary: All users
operationId: listUsers
description: List all users
responses:
200:
description: Returns the list of all users
content:
application/json:
schema:
type: array
items:
$ref: ‘#/components/schemas/User’
post:
summary: Creates a user
operationId: addUser
requestBody:
content:
application/json:
schema:
$ref: ‘#/components/schemas/User’
required: true
responses:
200:
description: A user was created successfully
content:
application/json:
schema:
type: string
components:
schemas:
User:
required:
– firstName
properties:
firstName:
type: string
lastName:
type: string
Now let’s see how we can implement a web service for this API with Vert.x.
Since version 3.5, Vert.x provides the vertx-web-api-contract component that creates a Vert.x Web Router. The main entry point is the router factory that performs two important things:
First, the router factory ensures that every request to the API conforms to the OpenAPI contract such as parsing, validating the HTTP request body, path, and query. This part is transparent and happens before the API is called.
Then the router factory maps the OpenAPI endpoint definitions to the service implementation we create: Vert.x Web API Contracts allow us to mount operations with handlers, which are simple Java lambdas that will react to an event you can handle in your code.
This factory also helps you to mount the right auth handlers, to mount the right failure handlers for validation and so on.
The contract defines two operations for an API exposed on the /users URL
GET : list all users in JSON format – the listUsers operation
POST : inserts a new user in JSON format and returns the user identifier – the addUser operation
In Vert.x, you map each operation to a handler that processes the request and produces a response.
The listUsers operation
For the sake of simplicity, we implemented the users as a single in-memory list of users, obviously a real application would instead use a persistent storage.
void listUsers(RoutingContext routingContext) {
// Returns the list of users encoded to a JSON array
routingContext
.response()
.setStatusCode(200)
.end(users.encode());
}
The implementation uses the RoutingContext which is provided by Vert.x Web, among other things this context provides the current HTTP response:
Upon a successful GET request, listUsers returns a JSON encoded array of a schema named User; representing all of the users in the system.
Schemas are effectively data types, and allow us to describe the format of inputs and outputs in our APIs. In this instance, a reference ($ref) is used, allowing us to reuse our Schema many times without repetition.
If you know JSON Schema, then many of the properties will be familiar, allowing for the definition of types, formats, lengths, patterns, and myriad other elements.
The addUser operation
For the addUser implementation, we need to use the parameters parsed by the API that gives us the JSON object representing the user.
void addUser(RoutingContext routingContext) {
// Get the parsed parameters
RequestParameters params = routingContext.get(“parsedParameters”);
// We get an user JSON object validated by Vert.x Open API validator
JsonObject user = params.body().getJsonObject();
// Generate a user id
String userId = “” + users.size();
// Add the user to the users list
users.add(user);
// Send the user id as JSON response
routingContext
.response()
.setStatusCode(200)
.end(userId);
}
The addUser operation accepts a JSON encoded user, as defined in our User schema, returning a string containing the newly created user’s unique ID upon success.
We retrieve all of the relevant data from the RoutingContext, including the body, which is conveniently available as a JsonObject, and store it our users list.
Handily, much of the input validation can be performed on our behalf by Vert.x, which ensures the incoming object conforms to our OpenAPI definition. This means that, for example, all required fields will be present and of the correct type when our handler is executed. The validator will validate request body but also take care of deserializing and validating the query, path, cookie and header parameters, and Vert.x Web Open API does the heaving lifting for you, providing you the guarantee that the request you receive conforms to the defined contract.
Final steps
Now we have all the parts of our small application, we can wire them together.
// Start creating the router factory from openapi.yaml
OpenAPI3RouterFactory.create(vertx, “openapi.yaml”, ar -> {
// The router factory instantiation could fail
if (ar.succeeded()) {
OpenAPI3RouterFactory factory = ar.result();
// Now you can use the factory to mount map endpoints to Vert.x handlers
factory.addHandlerByOperationId(“listUsers”, this::listUsers);
factory.addHandlerByOperationId(“addUser”, this::addUser);
// Build the router with factory.getRouter()
Router router = factory.getRouter();
// Generate the certificate for https
SelfSignedCertificate cert = SelfSignedCertificate.create();
// Start the HTTP/2 server with the OpenAPI router
vertx.createHttpServer(new HttpServerOptions()
.setSsl(true)
.setUseAlpn(true)
.setKeyCertOptions(cert.keyCertOptions())
.setPort(8443))
.requestHandler(router::accept)
.listen();
}
});
This code is executed during the start of the application in several steps:
The first step is the creation of the Vert.x Web router from the openapi.yaml file
After the router is created, we map the listUser and addUser methods to the corresponding API operations
Finally the HTTP/2 server is started
Conclusion
In this article, we have seen how OpenAPI’s Contract First approach can be effectively implemented with Vert.x and deliver a highly scalable implementation. If you are interested in HTTP/2 performance you can watch this presentation that explains more in-depth the technology. The entire source code for this article is shared on GitHub.
For further details about Vert.x and how you can build your next application with it, check http://vertx.io
See you soon on our Gitter channel!
Building Your Next Microservice With Eclipse MicroProfile
This quick tutorial will show you how to build your next microservice with the latest version of Eclipse MicroProfile APIs.
Eclipse MicroProfile aims to deliver a growing set of APIs for Java applications composed of multiple microservices. The project has been gaining a lot of attention recently, with a growing list of corporate supporters that also includes Oracle and IBM. There are many servers and frameworks providing the API and that means you can choose the best tool to run your microservices while keeping the same familiar API and behavior. This article is a quick tutorial to using the MicroProfile API for building your next microservice.
MicroProfile is built from core JavaEE, now called Jakarta EE, technologies:
JAX-RS 2.0
CDI 1.2
JSON-P
While adding to them a set of specifications that make your microservices ready for the cloud including:
Configuration Management
Fault Tolerance
Metrics
Health Checks
JWT Authorization
Type-safe REST Client
OpenAPI
OpenTracing
These specifications together make up Eclipse MicroProfile 1.3.
Initial project setup
So how do you use all of this? This is a quick guide to writing your first application. MicroProfile only specifies the API and the behavior but doesn’t include the specified functionality. It’s up to an implementation like Payara® Micro to provide the functionality. With Payara Micro, you can run a WAR file from command line but it’s also possible to assemble a single executable JAR file. There are many other implementations and you can find them in the list of MicroProfile implementations.
If you choose to run your microservice with Payara Micro, first create a web project that produces a WAR file. If you use Maven or Gradle for your projects, you would set up a standard web application project (with war packaging or war plugin). Once you build the WAR file, you can download Payara Micro from https://www.payara.fish/downloads and run your application from the command line with:
java -jar payara-micro.jar application.war
Then, add the MicroProfile dependency to your project.
Maven:
<dependency>
<groupId>org.eclipse.microprofile</groupId>
<artifactId>microprofile</artifactId>
<version>1.3</version>
<type>pom</type>
<scope>provided</scope>
</dependency>
Gradle
dependencies {
providedCompile ‘org.eclipse.microprofile:microprofile:1.3’
}
This one dependency brings in all of the needed APIs to build your application. So what would a typical microservice look like?
A JAX-RS Controller. Since we’re exposing a REST API, we want a controller to handle the API calls.
A service of some kind. You need some backing component to generate or consume data. We’re going to be using some mock data, for now, just to explain the paradigm.
Configurability. We don’t want the client specifying the data volume, we want to do it declaratively.
Security. Need both declarative and business logic driven security to know how to respond to requests.
Fault Tolerance. We care about any services we consume and ensuring we can fail fast or recover from failures
Monitoring. We want to know how often this service is invoked, and how long each request takes.
A REST controller and service
First, we have our rest controller, which should look very familiar to Java EE developers:
@Path(“/api/books”) // just a basic JAX-RS resource
@Counted // track the number of times this endpoint is invoked
@RequestScoped
public class BooksController {
@Inject //use CDI to inject a service
private BookService bookService;
@GET
@RolesAllowed(“read-books”)
// uses common annotations to declare a role required
public Books findAll() {
return bookService.getAll();
}
}
For small services, the controller can also contain the service logic. However, it would usually delegate handling of the business logic to another service bean like bookService in our example.
If we dive in further to the book service, we can start to see how configurability works.
@ApplicationScoped
public class BookService {
@Inject
// JPA is not provided out of the box, but most providers support it at
// some level. worst case, create your own producer for the field
private EntityManager entityManager;
@Inject
// use configuration to control how much data you want to supply at
// a given time
@ConfigProperty(name = “max.books.per.page”, defaultValue = “20”)
private int maxBooks;
public Books getAll() {
List < Book > bookList = entityManager
.createQuery(“select b from Book b”, Book.class)
.setMaxResults(maxBooks) // use that configuration to do a paginated look up
.getResultList();
return new Books(bookList);
}
}
Configurability
Configuration values can be simply injected into the service using the @ConfigProperty annotation on the injection point. The configuration is supplied based on the configuration name, which is used as a key to retrieve the configuration value from the container. Other optional attributes can be supplied, such as the defaultValue, which is used if there’s no configuration for the given name. Even the name attribute is optional. If not provided, it will be generated based on the class and field names so that the configuration value can still be provided later.
So the configuration can also be injected simply like this:
@Inject
@ConfigProperty
private int maxBooks
If the default value isn’t provided, a configuration for the name generated according to the specified algorithm has to be available when the application starts.
The configuration is decoupled from bookService and can be supplied by the configuration inside the application or even later, from external sources such as system properties when the application is started.
Security
Next, let’s suppose we also want to handle the creation of books, the publication process. And we want to secure the service so that this process is allowed only for callers with a certain role.
MicroProfile offers a solution based on JSON tokens according to the JWT standard. We can inject a JsonWebToken object into our service and easily find out whether the caller has a required role by calling getClaim method:
@Inject
private JsonWebToken jsonWebToken;
And then in a method:
boolean createAny = jsonWebToken.getClaim(“create.books.for.other.authors”);
if (!createAny) {
throw new NotAuthorizedException(“Cannot create book, wrong author”);
}
The caller is then required to add a valid JWT token with the required claim to the header of the REST call.
A complete publication service to support that may look like this:
@RequestScoped
public class PublishBookService {
@Inject
// we can inject a JsonWebToken, a Principal specific to the JWT specification
private JsonWebToken jsonWebToken;
// we could also inject individual ClaimValue objects.
@Inject
private AuthorService authorService;
@Inject
private EntityManager entityManager;
@Timeout(500)
// we want to limit how long it takes to publish and if it
// exceeds, return an exception to the caller.
public BookId publish(PublishBook publishBook) {
// we check the standard claim of subject
if (!publishBook.getAuthor().equals(jsonWebToken.getSubject())) {
// as well as a custom claim as a boolean
boolean createAny = jsonWebToken.getClaim(“create.books.for.other.authors”);
if (!createAny) {
throw new NotAuthorizedException(“Cannot create book, wrong author”);
}
}
Author author = authorService.findAuthor(publishBook.getAuthor());
if (author == null) {
throw new NotAuthorizedException(“The list author is not an author”);
}
Book book = entityManager.merge(new Book(publishBook.getIsbn(),
publishBook.getAuthor()));
return new BookId(book.getIsbn(), book.getAuthor());
}
}
For all the above to work, it’s also necessary to enable the JWT security on the JAX-RS application class with the LoginConfig annotation. It’s also important to turn that class into a CDI bean, e.g. by adding ApplicationScoped annotation, because JAX-RS classes aren’t automatically CDI-enabled.
This is how it may look like in the code:
@LoginConfig(authMethod = “MP-JWT”, realmName = “admin-realm”)
@ApplicationScoped
@ApplicationPath(“/”)
public class BookServiceConfig extends javax.ws.rs.Application {
}
Adding Fault Tolerance
If we consider that managing authors is a separate bounded context, we want that to be represented as a discreet service. Therefore we’ll implement it as a separate REST service in the same manner as the book service. As a result, we want the book service to check that the author exists by connecting to the new author REST service. Below is the complete code for the connector to an external author service:
@ApplicationScoped
public class AuthorService {
@Inject
@RestClient
AuthorConnector authorConnector;
// inject a REST proxy for a URL given by a generated config property
private ConcurrentMap < String, Author > authorCache = new ConcurrentHashMap < > ();
@Retry
// Retry indicates that this should trigger retry the method call several times in case the remote server call results in an exception
@CircuitBreaker
// CircuiBreaker wraps the call in a circuit breaker which opens after several failures and closes again after some time
@Fallback(fallbackMethod = “getCachedAuthor”)
// Fallback indicates that we should fall back to the local cache
// if the method fails even after several retries
// or the circuit is open
public Author findAuthor(String id) {
// call to an external Author service
Author author = authorConnector.get(id);
// Ideally we want to read from the remote server.
// However, we can build
// a cache as a fallback when the server is down
authorCache.put(id, author);
return author;
}
public Author getCachedAuthor(String id) {
return authorCache.get(id);
}
}
Annotations Retry, CircuitBreaker, Timeout and others trigger interceptors that implement respective fault tolerance patterns in case of a failure of the underlying action. They are used on an individual method or on a class to apply them for all methods. The Fallback annotation specifies which method should be called if the interceptors cannot recover from failures. This method can provide an alternative result or notify about the error.
Configurability is also fully supported by the fault tolerance annotations. The attributes of the annotations can be overridden via the same configuration mechanism that we used earlier. When any of the interceptors is enabled for a method, it reads the configuration from configuration names generated from the class and field names. For example, to specify the number of retries for the method findAuthor, we can specify a configuration property with the name ws.ament.microprofile.gettingstarted.AuthorService/findAuthor/Retry/maxRetries. That also means that you can use the annotations without any attributes in the code and configure them later and with different values for each environment.
In the code, we also see a REST client proxy provided by the MicroProfile container. The URL is specified by an external configuration for a generated configuration name, similar to the fault tolerance annotations. And the rest is just calling a method on the proxy which does all the work to do the remote call and return an Author instance.
Monitor what’s going on
So there you have it! A couple of rest controllers, services, and you have a microservice built with Eclipse MicroProfile to manage books.
The last thing is to find out what’s going on inside your application. Metrics and Health Check functionality in MicroProfile containers provide a lot of information out of the box. It’s available via REST endpoints.
Various metrics collected during the lifetime of the application are automatically exposed via REST over HTTP under the /metrics base path, in either JSON or Prometheus format. Common metrics about JVM, threads, loaded classes and operating system are provided out of the box. Other custom metrics can be provided by the implementation. The application can also collect metrics very easily using method interceptors or producer methods.
For example, if a service is running on localhost and port 8080, you can simply access http://localhost:8080/metrics with the HTTP header Accept = application/json and you’ll get something like this:
{
“base”: {
“classloader.totalLoadedClass.count”: 16987,
“cpu.systemLoadAverage”: 1.22,
“thread.count”: 141,
“classloader.currentLoadedClass.count”: 16986,
“jvm.uptime”: 52955,
“memory.committedNonHeap”: 131727360,
“gc.PS MarkSweep.count”: 3,
“memory.committedHeap”: 503316480,
“thread.max.count”: 143,
“gc.PS Scavenge.count”: 20,
“cpu.availableProcessors”: 8,
“thread.daemon.count”: 123,
“classloader.totalUnloadedClass.count”: 2,
“memory.usedNonHeap”: 117340624,
“memory.maxHeap”: 503316480,
“memory.usedHeap”: 139449848,
“gc.PS MarkSweep.time”: 428,
“memory.maxNonHeap”: -1,
“gc.PS Scavenge.time”: 220
}
}
You can also access http://localhost:8080/health to find out whether the service is running OK or has some errors. This is a simple Yes/No check, giving HTTP 200 status code if all is OK. This is suitable in systems that can detect and restart services with failures automatically, such as Kubernetes.
There are still some more components of MicroProfile 1.3, such as Open API and Open Tracing. We won’t cover them here and let it up to you to explore the API and the documentation, which you can find at microprofile.io. You can find more documentation about Microprofile API, including additional enhancements added by Payara Micro, in the Payara MicroProfile Documentation.
You can also download the full sample code used in this article on GitHub.
Eclipse MicroProfile: What it has to offer to the Java community
April 26, 2018 JAX Editorial Team
Eclipse MicroProfile
What is Eclipse MicroProfile and what does it have to offer to the community of Java developers? Join Emily Jiang as she offers us an introduction to Eclipse MicroProfile and its goals.
While there are likely a dozen or more Java-based microservice/cloud-native initiatives in the industry, Eclipse MicroProfile provides a platform for Java developers to collaborate and innovate on areas of common interest. In this session, Emily Jiang gives a short overview of MicroProfile and how it plans to optimize microservices for Enterprise Java, followed by the demonstration of the MicroProfile config and Fault Tolerance specification being prototyped in MicroProfile.
The end goal of MicroProfile is to feed the Java Community Process (JCP) with JSR submissions backed by well-thought-out concepts and even implementations that developers and enterprises can rely on. Emily Jiang also invites everyone to join the community and provides ways in which you can engage yourself in it.
Emily Jiang is MicroProfile Development Lead and CDI Architect for IBM. Based at IBM’s Hursley laboratory in the UK, she has worked on WebSphere Application Server since 2006 and heavily involved in JavaEE 7 support in WebSphere Application Server releases. She is an active member of MicroProfile, OSGi Enterprise Expert Group and CDI Expert Group. Emily is currently leading the effort to define Config and Fault Tolerance programming models to be used by microservices in MicroProfile.io. Follow @emilyfhjian on Twitter.
CloudNativeCon 2018, May 2-4
Come see us at the CloudNativeCon in Copenhagen, Denmark on May 2 – 4, 2018. CloudNativeCon gathers adopters and technologists from leading open source and cloud native communities.
Featured Speakers
Aparna Sinha
Google
Aparna Sinha leads the product team at Google for Kubernetes and Container Engine. She started and co-leads the community PM Special Interest Group (SIG) to maintain an open backlog for the Kubernetes project on Github. Aparna is currently a secondary member of the CNCF Governing Board. She has worked in enterprise software for 15+ years, and was formerly a Director of Product at NetApp. Aparna holds a PhD in Electrical Engineering from Stanford and has co-authored several technical papers during her research.
Lew Tucker
Cisco
As VP/CTO of Cloud Computing at Cisco, Lew is responsible for shaping Cisco’s strategy and products in cloud computing and leads several of the company’s open source initiatives. He also serves as a member of the OpenStack Foundation board of directors. Lew brings to the CNCF more than 25 years of experience in the high-tech industry, ranging from distributed systems and artificial intelligence to software development and parallel system architecture. Prior to joining Cisco, Lew was VP/CTO Cloud Computing at Sun Microsystems where he led the development of the Sun Cloud platform. While at Sun, Lew was also a member of the JavaSoft executive team, launched java.sun.com, and helped to bring Java into the developer ecosystem. Intrigued by the software-as-a-service model for application delivery, Lew joined Salesforce.com and led the development of Salesforce.com’s AppExchange. In the 1980’s Lew was a research director at Thinking Machines, where he developed algorithms for AI and machine vision for the massively parallel Connection Machine. Lew moved into technology following a career in Neurobiology at Cornell University Medical school and has a BA in Biology from Cornell and a Ph.D. in computer science from New York University.
Liz Rice
Aqua Security
Liz Rice is the Technology Evangelist with container security specialists Aqua Security, and also works on container-related open source projects including manifesto and kube-bench. She has a wealth of software development, team, and product management experience from working on network protocols and distributed systems, and in digital technology sectors such as VOD, music, and VoIP. When not writing code, or talking about it, Liz loves riding bikes in places with better weather than her native London.
Ralph Squillace
Microsoft
Ralph Squillace is a Principal Program Manager for the open-source, container-native dev and ops Kubernetes tooling built by Azure. He oversees contributions to Helm, Draft, Brigade, VS Code Kubernetes extensions, and any random thing that makes building apps in containers easier, faster, and more secure.
Eclipse Orion项目下载页
Eclipse che 部署命令
无需下载 eclipse che 官网的安装包,仅需要在 docker 上 pull codenvy/che 的镜像即可。
建议j提前 pull 开发所用的 ide 镜像,可以避免进入开发界面时的等待时间。
eclipse che 4.2.0 支持的 ide 有:
codenvy/aspnet
codenvy/cpp_gcc
codenvy/node
codenvy/php
codenvy/ubuntu_android
codenvy/ubuntu_jdk8
codenvy/ubuntu_python
codenvy/ubuntu_rails
看名字就知道是干什么的了,访问 eclipse che docker 镜像站,发现还有其他的 ide,会在以后陆续添加。
docker 启动命令(不要在 cmd 中执行,会由于特殊字符问题出错,在 shell 中执行则么有问题):
docker run -v //var/run/docker.sock:/var/run/docker.sock -v //home/user/che/lib:/home/user/che/lib-copy -v //home/user/che/workspaces:/home/user/che/workspaces -v //home/user/che/tomcat/temp/local-storage:/home/user/che/tomcat/temp/local-storage -e CHE_DOCKER_MACHINE_HOST=$IPv4 –name che -d –net=host codenvy/che bash -c ‘sudo rm -rf /home/user/che/lib-copy/* && mkdir -p /home/user/che/lib-copy/ && sudo chown -R user:user /home/user && cp -rf /home/user/che/lib/* /home/user/che/lib-copy && cd /home/user/che/bin/ && ./che.sh -p:8082 –skip:client –debug run’
其中 $IPv4 需要换成 docker 服务器 ip 地址。尝试 127.0.0.1 无效。
本命令会在 docker 中启动 docker,并在宿主机上建立 /home/user/che 目录。
eclipse che 本身会自己运行 docker run 命令,以及自己保存 docker image。
如果二次启动后出错,可以尝试删除 docker 服务器 /home/user/che 目录,需要 root 权限。