Recent Posts
Archives

Posts Tagged ‘Java’

PostHeaderIcon [DevoxxFR2013] Regular or Decaffeinated? Java’s Future in the Cloud

Lecturer

Alexis Moussine-Pouchkine, a veteran of Sun Microsystems, currently serves as a Developer Relations lead at Google in Paris, assisting developers in achieving success. With over a decade at Sun and nearly two years at Oracle, he brings extensive experience in Java ecosystems and cloud technologies.

Abstract

Alexis Moussine-Pouchkine’s presentation examines Java’s evolution and its potential trajectory in cloud computing. Reflecting on historical shifts in technology, he critiques current limitations and advocates for advancements like multi-tenancy and resource management to ensure Java’s relevance. Through industry examples and forward-looking analysis, the talk underscores the need for adaptation to maintain Java’s position amid resource rationalization and emerging paradigms.

Java’s Maturation and the Cloud Imperative

Moussine-Pouchkine opens by recounting his transition from Sun Microsystems to Oracle and then Google, highlighting how each company has shaped computing history. At Sun, innovation abounded but market fit was inconsistent; Oracle emphasized acquisitions over novelty, straining community ties; Google prioritizes engineers, fostering rapid development.

He likens Java’s current state to emerging from adolescence, facing challenges in cloud environments where resource optimization is paramount. Drawing from his engineering school days with strict quotas on compilation and connection time, Alexis contrasts this with Java’s initial promise of freedom and flexibility. Early experiences with Linux provided boundless experimentation, mirroring Java’s liberating potential in 1997.

The speaker invokes historical predictions: IBM’s CEO allegedly foresaw a market for only five computers in 1943, possibly prescient regarding cloud providers. Bill Gates’ 640K memory quip and Greg Papadopoulos’ 2003 vision of five to seven massive global computers underscore consolidation trends. Papadopoulos envisioned entities like Google, eBay, Salesforce, Microsoft, Amazon, and a Chinese cloud, a perspective less radical today given web evolution.

Java’s centrality in tomorrow’s cloud is questioned. While present in many offerings, most implementations remain prototypes, circumventing Java’s constraints. The cloud demands shared resources and concentration of expertise, yet Java’s future here is uncertain, risking obsolescence like COBOL.

Challenges and Necessary Evolutions for Java in Multi-Tenant Environments

A core issue is Java’s adaptation to multi-tenancy, where multiple applications share a JVM without interference. Current JVMs lack robust isolation, leading to inefficiencies in cloud settings. Moussine-Pouchkine notes Java’s success in Android and Chrome, where processes are segregated, but enterprise demands shared instances for cost savings.

He critiques the stalled JSR-284 for resource management, essential for quotas and usage-based billing. Without these, Java lags in cloud viability. Examples like Google’s App Engine illustrate Java’s limitations: no threads, file system restrictions, and 30-second request limits, forcing workarounds.

Commercial solutions emerge: Waratek’s hypervisor on HotSpot, IBM’s J9 VM, and SAP’s container enable multi-tenancy. Yet, quotas remain crucial for responsible computing, akin to not overindulging at a buffet to ensure sustainability.

Java 9 priorities include modularity (Jigsaw), potentially aiding resource management. Cloud Foundry’s varying memory allocations by language highlight Java’s inefficiencies. Moussine-Pouchkine urges a “slider” for JVM scaling, from minimal to robust, without API fractures.

The community, pioneers in agile practices, continuous integration, and dependency management, must embrace modularity and quotas. Java 7 introduced dynamic languages; Java 8 tackles multicore with lambdas. Recent Oracle slides affirm multi-tenancy and resource management in Java 9 and beyond.

Implications for Sustainable and Credible Informatics

Moussine-Pouchkine advocates responsible informatics: quotas foster predictability, countering perceptions of IT as imprecise and costly. Developers, like artisans, must steward tools and design thoughtfully. Over-reliance on libraries (90% bloat) signals accumulated technical debt.

Quotas enhance credibility, enabling commitments and superior delivery. Java’s adaptive history positions it well, provided the community envisions it “caffeinated” – vibrant and adult – rather than “decaffeinated” and stagnant.

In essence, Java must address multi-tenancy and resources to thrive in consolidated clouds, avoiding the fate of outdated technologies.

Relevant Links and Hashtags

Links:

PostHeaderIcon [DevoxxFR2013] Objects and Functions: Conflict Without a Cause?

Lecturer

Martin Odersky is a professor at EPFL in Lausanne, Switzerland, specializing in programming languages that blend object-oriented and functional paradigms. His research unifies these approaches, evidenced by designs like Pizza, GJ, and Functional Nets. He co-designed Java generics and authored the original javac compiler. Currently, he focuses on Scala, fusing functional and object-oriented programming while ensuring interoperability with Java and .NET.

Abstract

Martin Odersky’s lecture traces object-oriented programming’s (OOP) rise, parallels it with functional programming’s (FP) resurgence, and argues for their synthesis. Analyzing historical catalysts, methodological benefits, and modern hardware demands, he demonstrates how FP addresses parallelism and reactivity challenges. Through Scala examples, Odersky shows OOP and FP as complementary, enhancing modularity and abstraction without mutual exclusion.

Historical Roots and Catalysts for OOP Adoption

Odersky recounts his journey from Pascal compilers to modular languages, Java involvement, and Scala creation. OOP’s mainstreaming, he argues, stemmed not from encapsulation or reuse but practical necessities. Simula (1967) for simulations and Smalltalk (late 1970s) for GUI widgets inverted traditional data structures: fixed operations, unbounded implementations.

In procedural languages like C, this was cumbersome via function pointers; OOP simplified dynamic binding for unknown implementations. Odersky notes early confusion with Smalltalk, mirroring current FP bewilderment. OOP’s advantages – modeling, dependency inversion – emerged post-adoption, sparked by widget programming appeal.

Functional Programming’s Resurgence and Methodological Strengths

FP offers fewer errors, superior modularity, and productivity via higher abstractions and shorter code. Yet, despite 50-year existence, mainstream adoption lagged. Odersky identifies multicore and cloud computing as catalysts: parallelism for hardware utilization, reactivity for asynchronous events, distribution for delays/failures.

Mutable state exacerbates issues like cache coherence and non-determinism in concurrent environments. Immutable data mitigates races, enabling safe caching. Locks/threads scale poorly; even transactions retain problems. FP’s immutability fosters determinism, crucial for scalable systems.

Addressing Modern Computing Challenges with FP

Odersky outlines a triple challenge: parallel, reactive, distributed programming. Mutable state hinders each; FP excels by avoiding it.

For parallelism, he exemplifies mapping developer names: sequential in Scala, parallel via .par, yielding a parallel collection. Side effects risk chaos due to reordered executions, necessitating functional purity for correctness.

Reactivity example: an online store querying users, orders, products, stock. Synchronous calls block threads on slow services, degrading performance. Asynchronous futures prevent blocking; Scala’s for-comprehensions compose them elegantly, hiding complexity. Refactoring for parallelism pairs futures, executing concurrently.

For-comprehensions translate universally to map, flatMap, filter, allowing library-defined interpretations – sequential or async – without polluting domain logic.

Synthesizing OOP and FP: Beyond False Dichotomies

Communities often view OOP and FP oppositely, but Odersky argues orthogonality. Objects modularize (“what goes where”), providing containers with dynamic binding and inheritance. Essential for large codebases (e.g., million-line Scala systems), they prevent global namespace chaos – a Haskell limitation.

Redefine objects: eliminate mutable state dogma (e.g., immutable java.lang.String), structural equality over identity, focus on behavior. Scala embodies this fusion, modeling algebraic types with objects while importing FP capabilities.

Scala unifies scripting (winning JavaOne Script Bowl) and safe, performant systems (core for Twitter, LinkedIn). Odersky concludes OOP/FP synergy enhances development, urging exploration via Scala Days, courses, and talks.

Relevant Links and Hashtags

Links:

PostHeaderIcon [DevoxxFR2012] Java on Amazon Web Services: Building Scalable, Resilient Applications in the Cloud

Lecturer

Carlos Conde operates as a Solutions Architect within Amazon Web Services’ European team, where he partners with enterprises to design, migrate, and optimize Java applications on the AWS platform. His technical journey began over a decade ago when he founded IWorks, a company focused on Java web development atop legacy mainframe systems, followed by co-founding Newaxe, which delivered a Java-based ERP monitoring and management platform. After years as an independent consultant helping Fortune 500 companies modernize their stacks, Carlos joined AWS to bring cloud-native principles to the broader Java community. His expertise spans the full application lifecycle—from initial architecture through production operations—with a particular emphasis on cost optimization, high availability, and security.

Abstract

Carlos Conde provides an exhaustive guide to developing, deploying, and operating Java applications on Amazon Web Services, demonstrating how the platform’s breadth of services enables developers to build systems that are simultaneously scalable, resilient, and cost-effective. He introduces AWS Elastic Beanstalk as a fully managed Platform as a Service solution that abstracts infrastructure complexity, the AWS SDK for Java for programmatic service access, and the AWS Toolkit for Eclipse for seamless IDE integration. Through detailed live demonstrations and real-world case studies from companies like Viadeo and Netflix, Conde explores deployment strategies, database patterns, content delivery, monitoring, and disaster recovery. The presentation addresses hybrid cloud scenarios, data sovereignty requirements under European regulations, and advanced cost management techniques, concluding with a vision of serverless Java and containerized workloads that redefine operational excellence.

The AWS Java Ecosystem: Tools for Every Stage of Development

Carlos Conde opens by mapping the AWS service landscape to the Java developer’s workflow. At the foundation lies Amazon EC2, offering virtual servers with pre-built Java AMIs (Amazon Machine Images) that include OpenJDK, Tomcat, and Spring Boot. For developers seeking higher abstraction, AWS Elastic Beanstalk provides a PaaS experience where applications are deployed via simple commands:

eb init -p java-8 my-java-app
eb create production-env --instance_type t3.medium
eb deploy

Behind the scenes, Beanstalk provisions EC2 instances, Elastic Load Balancers, Auto Scaling groups, and CloudWatch alarms, while allowing full customization through .ebextensions configuration files. Conde demonstrates deploying a Spring Boot application that automatically scales from 2 to 10 instances based on CPU utilization, with zero-downtime blue/green deployments.

The AWS SDK for Java serves as the programmatic bridge to over 200 AWS services. Conde writes a service that stores user profiles in Amazon DynamoDB:

AmazonDynamoDB client = AmazonDynamoDBClientBuilder.standard()
    .withRegion(Regions.EU_WEST_3)
    .build();

Map<String, AttributeValue> item = new HashMap<>();
item.put("userId", AttributeValue.builder().s(userId).build());
item.put("email", AttributeValue.builder().s(email).build());
item.put("createdAt", AttributeValue.builder().n(Long.toString(timestamp)).build());

PutItemRequest request = PutItemRequest.builder()
    .tableName("Users")
    .item(item)
    .build();
client.putItem(request);

DynamoDB’s single-digit millisecond latency and automatic scaling make it ideal for high-throughput workloads. For relational data, Amazon RDS offers managed PostgreSQL, MySQL, or Oracle with automated backups, patching, and multi-AZ replication.

Content Delivery and Global Reach

For static assets, Conde integrates Amazon S3 with CloudFront:

AmazonS3 s3 = AmazonS3ClientBuilder.standard().build();
s3.putObject(PutObjectRequest.builder()
    .bucket("my-app-assets")
    .key("css/style.css")
    .acl(ObjectCannedACL.PublicRead)
    .build(), RequestBody.fromFile(Paths.get("style.css")));

CloudFront’s 200+ edge locations cache content close to users, reducing latency from 180ms to 30ms for European customers. He demonstrates invalidating cache after deployments using the AWS CLI.

Cost Optimization Strategies

Conde presents a multi-layered approach to cost control. Reserved Instances provide up to 75% savings for predictable workloads, while Savings Plans offer flexibility across EC2, Lambda, and Fargate. For batch processing, Spot Instances deliver 90% discounts:

RunInstancesRequest request = RunInstancesRequest.builder()
    .instanceMarketOptions(InstanceMarketOptionsRequest.builder()
        .marketType(MarketType.SPOT)
        .spotOptions(SpotInstanceRequest.builder()
            .spotPrice("0.10")
            .instanceInterruptionBehavior(InstanceInterruptionBehavior.TERMINATE)
            .build())
        .build())
    .build();

He uses AWS Cost Explorer to visualize spending and set budget alerts.

High Availability and Disaster Recovery

Conde designs a multi-AZ architecture with RDS read replicas, ElastiCache for Redis caching, and S3 cross-region replication. He demonstrates failover using Route 53 health checks that automatically reroute traffic if a region fails.

Security and Compliance

Security is baked into every layer. AWS Identity and Access Management (IAM) enforces least-privilege access, while AWS KMS manages encryption keys. Conde enables S3 server-side encryption and RDS TDE (Transparent Data Encryption) with a single click.

Hybrid and Sovereign Cloud Deployments

For European data residency, Conde deploys entirely within the Paris region (eu-west-3). For hybrid scenarios, AWS Direct Connect establishes dedicated network connections to on-premises data centers, and AWS Outposts brings AWS services into private facilities.

Monitoring, Logging, and Observability

Amazon CloudWatch collects metrics, logs, and events. Conde instruments a Spring Boot application with Micrometer:

@Timed(value = "order.processing.time", description = "Time taken to process order")
public Order processOrder(OrderRequest request) {
    // Business logic
}

AWS X-Ray traces requests across services, identifying latency bottlenecks.

Real-World Case Studies

Conde shares Viadeo’s migration of their social platform to AWS, achieving 99.99% availability and reducing infrastructure costs by 60%. Netflix’s global streaming architecture leverages AWS for transcoding, personalization, and content delivery at petabyte scale.

The Future of Java on AWS

Conde previews AWS Lambda for serverless Java, AWS Fargate for containerized workloads without server management, and AWS Graviton2 processors offering 40% better price-performance for Java applications.

Implications for Java Enterprises

Carlos Conde concludes that AWS transforms Java development from infrastructure-constrained to innovation-focused. By leveraging managed services, developers build faster, operate more reliably, and scale globally—all while maintaining strict control over costs and compliance.

Links:

PostHeaderIcon [DevoxxFR2012] Practicing DDD in a Flash – Sculptor, the DDD Code Generator for Java

Ulrich Vachon is a DDD and agile practitioner with experience at software vendors. He promotes expressive modeling and rapid feedback.

This article expands the live coding demo of Sculptor, a DSL-based code generator for DDD applications in Java. Domain-Driven Design is powerful but verbose. Sculptor accelerates bootstrapping while preserving DDD principles. Using a simple DSL, developers define aggregates, value objects, services, and repositories. Sculptor generates Spring, JPA, REST, and MongoDB code.

Sculptor DSL and Code Generation

A live demo built a blog application:

Application Blog {
  Module posts {
    Entity Post {
      @Id String id;
      String title;
      String content;
      @ManyToOne Author author;
    }
    ValueObject Author {
      String name;
      String email;
    }
    Service PostService {
      Post save(Post post);
      List<Post> findAll();
    }
  }
}

Sculptor generated entities, repositories, services, controllers, and tests.

Customization with the Gap Mechanism

The gap keyword allows hand-written extensions without regeneration conflicts.

Links

Relevant links include the Sculptor Project at sites.google.com/site/fornaxsculptor and the original video at YouTube: Practicing DDD in a Flash.

PostHeaderIcon [DevoxxFR2012] Toward Sustainable Software Development – Quality, Productivity, and Longevity in Software Engineering

Frédéric Dubois brings ten years of experience in JEE architecture, agile practices, and software quality. A pragmatist at heart, he focuses on continuous improvement, knowledge sharing, and sustainable delivery over rigid processes.

This article expands Frédéric Dubois’s 2012 talk into a manifesto for sustainable software development. Rejecting the idea that quality is expensive, he proves that technical excellence drives long-term productivity. A three-year-old application should not be unmaintainable. Yet many teams face escalating costs with each new feature. Dubois challenged the audience: productivity is not about delivering more features faster today, but about maintaining velocity tomorrow, next year, and five years from now.

The True Cost of Technical Debt

Quality and productivity are intimately linked, but not in the way most assume. High quality reduces defects, simplifies evolution, and prevents technical debt. Low quality creates a vicious cycle of bugs, rework, and frustration. Dubois shared a case study: a banking application delivered on time but with poor design. Two years later, a simple change required three months of work. The same team, using TDD and refactoring, built a similar system in half the time with one-tenth the defects.

Agile Practices for Long-Term Velocity

Agile practices, when applied pragmatically, enable sustainability. Short feedback loops, automated tests, and collective ownership prevent knowledge silos. Fixed-price contracts and outsourcing often incentivize cutting corners. Transparency, shared metrics, and demo-driven development align business and technical goals.

Links

Relevant links include the original video at YouTube: Toward Sustainable Development.

PostHeaderIcon [DevoxxFR2012] “Obésiciel” and Environmental Impact: Green Patterns Applied to Java – Toward Sustainable Computing

Olivier Philippot is an electronics and computer engineer with over a decade of experience in energy management systems and sustainable technology design. Having worked in R&D labs and large industrial groups, he has dedicated his career to understanding the environmental footprint of digital systems. A founding member of the French Green IT community, Olivier contributes regularly to GreenIT.fr, participates in AFNOR working groups on eco-design standards, and trains organizations on sustainable IT practices. His work bridges hardware, software, and policy to reduce the carbon intensity of computing.

This article presents a comprehensively expanded analysis of Olivier Philippot’s 2012 DevoxxFR presentation, Obésiciel and Environmental Impact: Green Patterns Applied to Java, reimagined as a foundational text on software eco-design and technical debt’s environmental cost. The talk introduced the concept of obésiciel, software that grows increasingly resource-hungry with each release, driving premature hardware obsolescence. Philippot revealed a startling truth: manufacturing a single computer emits seventy to one hundred times more CO2 than one year of use, yet software bloat has tripled performance demands every five years, reducing average PC lifespan from six to two years.

Through Green Patterns, JVM tuning strategies, data efficiency techniques, and lifecycle analysis, this piece offers a practical framework for Java developers to build lighter, longer-lived, and lower-impact applications. Updated for 2025, it integrates GraalVM native images, Project Leyden, energy-aware scheduling, and carbon-aware computing, providing a complete playbook for sustainable Java development.

The Environmental Cost of Software Bloat

Manufacturing a laptop emits two hundred to three hundred kilograms of CO2 equivalent. The use phase emits twenty to fifty kilograms per year. Software-driven obsolescence forces upgrades every two to three years. Philippot cited Moore’s Law irony: while transistors double every eighteen months, software efficiency has decreased due to abstraction layers, framework overhead, and feature creep.

Green Patterns for Data Efficiency

Green Patterns for Java include data efficiency. String concatenation in loops is inefficient:

String log = "";
for (String s : list) log += s;

Use StringBuilder instead:

StringBuilder sb = new StringBuilder();
for (String s : list) sb.append(s);

Also use compression, binary formats like Protocol Buffers, and lazy loading.

JVM Tuning for Energy Efficiency

JVM optimization includes:

-XX:+UseZGC
-XX:ReservedCodeCacheSize=128m
-XX:+UseCompressedOops
-XX:+UseContainerSupport

GraalVM Native Image reduces memory by ninety percent, startup to fifty milliseconds, and energy by sixty percent.

Carbon-Aware Computing in 2025

EDIT:
In 2025, carbon-aware Java includes Project Leyden for static images without warmup, energy profilers like JFR and PowerAPI, cloud carbon APIs from AWS and GCP, and edge deployment to reduce data center hops.

Links

Relevant links include GreenIT.fr at greenit.fr, GraalVM Native Image at graalvm.org/native-image, and the original video at YouTube: Obésiciel and Environmental Impact.

PostHeaderIcon [DevoxxFR2012] Lily: Big Data for Dummies – A Comprehensive Journey into Democratizing Apache Hadoop and HBase for Enterprise Java Developers

Lecturers

Steven Noels stands as one of the most visionary figures in the evolution of open-source Java ecosystems, having co-founded Outerthought in the early 2000s with a mission to push the boundaries of content management, RESTful architecture, and scalable data systems. His flagship creation, Daisy CMS, became a cornerstone for large-scale, multilingual content platforms used by governments and global enterprises, demonstrating that Java could power mission-critical, document-centric applications at internet scale. But Noels’ ambition extended far beyond traditional CMS. Recognizing the seismic shift toward big data in the late 2000s, he pivoted Outerthought—and later NGDATA—toward building tools that would make the Apache Hadoop ecosystem accessible to the average enterprise Java developer. Lily, launched in 2010, was the culmination of this vision: a platform that wrapped the raw power of HBase and Solr into a cohesive, Java-friendly abstraction layer, eliminating the need for MapReduce expertise or deep systems programming.

Bruno Guedes, an enterprise Java architect at SFEIR with over a decade of experience in distributed systems and search infrastructure, brought the practitioner’s perspective to the stage. Having worked with Lily from its earliest alpha versions, Guedes had deployed it in production environments handling millions of records, integrating it with legacy Java EE applications, Spring-based services, and real-time analytics pipelines. His hands-on experience—debugging schema migrations, tuning SolrCloud clusters, and optimizing HBase compactions—gave him unique insight into both the promise and the pitfalls of big data adoption in conservative enterprise settings. Together, Noels and Guedes formed a perfect synergy: the visionary architect and the battle-tested engineer, delivering a presentation that was equal parts inspiration and practical engineering.

Abstract

This article represents an exhaustively elaborated, deeply extended, and comprehensively restructured expansion of Steven Noels and Bruno Guedes’ seminal 2012 DevoxxFR presentation, “Lily, Big Data for Dummies”, transformed into a definitive treatise on the democratization of big data technologies for the Java enterprise. Delivered in a bilingual format that reflected the global nature of the Apache community, the original talk introduced Lily as a groundbreaking platform that unified Apache HBase’s scalable, distributed storage with Apache Solr’s full-text search and analytics capabilities, all through a clean, type-safe Java API. The core promise was radical in its simplicity: enterprise Java developers could build petabyte-scale, real-time searchable data systems without writing a single line of MapReduce, without mastering Zookeeper quorum mechanics, and without abandoning the comforts of POJOs, annotations, and IDE autocompletion.

This expanded analysis delves far beyond the original demo to explore the philosophical foundations of Lily’s design, the architectural trade-offs in integrating HBase and Solr, the real-world production patterns that emerged from early adopters, and the lessons learned from scaling Lily to billions of records. It includes detailed code walkthroughs, performance benchmarks, schema evolution strategies, and failure mode analyses.

EDIT:
Updated for the 2025 landscape, this piece maps Lily’s legacy concepts to modern equivalents—Apache HBase 2.5, SolrCloud 9, OpenSearch, Delta Lake, Trino, and Spring Data Hadoop—while preserving the original vision of big data for the rest of us. Through rich narratives, architectural diagrams, and forward-looking speculation, this work serves not just as a historical archive, but as a practical guide for any Java team contemplating the leap into distributed, searchable big data systems.

The Big Data Barrier in 2012: Why Hadoop Was Hard for Java Developers

To fully grasp Lily’s significance, one must first understand the state of big data in 2012. The Apache Hadoop ecosystem—launched in 2006—was already a proven force in internet-scale companies like Yahoo, Facebook, and Twitter. HDFS provided fault-tolerant, distributed storage. MapReduce offered a programming model for batch processing. HBase, modeled after Google’s Bigtable, delivered random, real-time read/write access to massive datasets. And Solr, forked from Lucene, powered full-text search at scale.

Yet for the average enterprise Java developer, this stack was inaccessible. Writing a MapReduce job required:
– Learning a functional programming model in Java that felt alien to OO practitioners.
– Mastering job configuration, input/output formats, and partitioners.
– Debugging distributed failures across dozens of nodes.
– Waiting minutes to hours for job completion.

HBase, while promising real-time access, demanded:
– Manual row key design to avoid hotspots.
– Deep knowledge of compaction, splitting, and region server tuning.
– Integration with Zookeeper for coordination.

Solr, though more familiar, required:
– Separate schema.xml and solrconfig.xml files.
– Manual index replication and sharding.
– Complex commit and optimization strategies.

The result? Big data remained the domain of specialized data engineers, not the Java developers who built the business logic. Lily was designed to change that.

Lily’s Core Philosophy: Big Data as a First-Class Java Citizen

At its heart, Lily was built on a simple but powerful idea: big data should feel like any other Java persistence layer. Just as Spring Data made MongoDB, Cassandra, or Redis accessible via repositories and annotations, Lily aimed to make HBase and Solr feel like JPA with superpowers.

The Three Pillars of Lily

Steven Noels articulated Lily’s architecture in three interconnected layers:

  1. The Storage Layer (HBase)
    Lily used HBase as its primary persistence engine, storing all data as versioned, column-family-based key-value pairs. But unlike raw HBase, Lily abstracted away row key design, column family management, and versioning policies. Developers worked with POJOs, and Lily handled the mapping.

  2. The Indexing Layer (Solr)
    Every mutation in HBase triggered an asynchronous indexing event to Solr. Lily maintained tight consistency between the two systems, ensuring that search results reflected the latest data within milliseconds. This was achieved through a message queue (Kafka or RabbitMQ) and idempotent indexing.

  3. The Java API Layer
    The crown jewel was Lily’s type-safe, annotation-driven API. Developers defined their data model using plain Java classes:

@LilyRecord
public class Customer {
    @LilyId
    private String id;

    @LilyField(family = "profile")
    private String name;

    @LilyField(family = "profile")
    private int age;

    @LilyField(family = "activity", indexed = true)
    private List<String> recentSearches;

    @LilyFullText
    private String bio;
}

The @LilyRecord annotation told Lily to persist this object in HBase. @LilyField specified column families and indexing behavior. @LilyFullText triggered Solr indexing. No XML. No schema files. Just Java.

The Lily Repository: Spring Data, But for Big Data

Lily’s LilyRepository interface was modeled after Spring Data’s CrudRepository, but with big data superpowers:

public interface CustomerRepository extends LilyRepository<Customer, String> {
    List<Customer> findByName(String name);

    @Query("age:[* TO 30]")
    List<Customer> findYoungCustomers();

    @Query("bio:java AND recentSearches:hadoop")
    List<Customer> findJavaHadoopEnthusiasts();
}

Behind the scenes, Lily:
– Translated method names to HBase scans.
– Converted @Query annotations to Solr queries.
– Executed searches across sharded SolrCloud clusters.
– Returned fully hydrated POJOs.

Bruno Guedes demonstrated this in a live demo:

CustomerRepository repo = lily.getRepository(CustomerRepository.class);
repo.save(new Customer("1", "Alice", 28, Arrays.asList("java", "hadoop"), "Java dev at NGDATA"));
List<Customer> results = repo.findJavaHadoopEnthusiasts();

The entire operation—save, index, search—took under 50ms on a 3-node cluster.

Under the Hood: How Lily Orchestrated HBase and Solr

Lily’s magic was in its orchestration layer. When a save() was called:
1. The POJO was serialized to HBase Put operations.
2. The mutation was written to HBase with a version timestamp.
3. A change event was published to a message queue.
4. A Solr indexer consumed the event and updated the search index.
5. Near-real-time consistency was guaranteed via HBase’s WAL and Solr’s soft commits.

For reads:
findById → HBase Get.
findByName → HBase scan with secondary index.
@Query → Solr query with HBase post-filtering.

This dual-write, eventual consistency model was a deliberate trade-off for performance and scalability.

Schema Evolution and Versioning: The Enterprise Reality

One of Lily’s most enterprise-friendly features was schema evolution. In HBase, adding a column family requires manual admin intervention. In Lily, it was automatic:

// Version 1
@LilyField(family = "profile")
private String email;

// Version 2
@LilyField(family = "profile")
private String phone; // New field, no migration needed

Lily stored multiple versions of the same record, allowing old code to read new data and vice versa. This was critical for rolling deployments in large organizations.

Production Patterns and Anti-Patterns

Bruno Guedes shared war stories from production:
Hotspot avoidance: Never use auto-incrementing IDs. Use hashed or UUID-based keys.
Index explosion: @LilyFullText on large fields → Solr bloat. Use @LilyField(indexed = true) for structured search.
Compaction storms: Schedule major compactions during low traffic.
Zookeeper tuning: Increase tick time for large clusters.

The Lily Ecosystem in 2012

Lily shipped with:
Lily CLI for schema inspection and cluster management.
Lily Maven Plugin for deploying schemas.
Lily SolrCloud Integration with automatic sharding.
Lily Kafka Connect for streaming data ingestion.

Lily’s Legacy After 2018: Where the Ideas Live On

EDIT
Although Lily itself was archived in 2018, its core concepts continue to thrive in modern tools.

The original HBase POJO mapping is now embodied in Spring Data Hadoop.

Lily’s Solr integration has evolved into SolrJ + OpenSearch.

The repository pattern that Lily pioneered is carried forward by Spring Data R2DBC.

Schema evolution, once a key Lily feature, is now handled by Apache Atlas.

Finally, Lily’s near-real-time search capability lives on through the Elasticsearch Percolator.

Conclusion: Big Data Doesn’t Have to Be Hard

Steven Noels closed with a powerful message:

“Big data is not about MapReduce. It’s not about Zookeeper. It’s about solving business problems at scale. Lily proved that Java developers can do that—without becoming data engineers.”

EDIT:
In 2025, as lakehouse architectures, real-time analytics, and AI-driven search dominate, Lily’s vision of big data as a first-class Java citizen remains more relevant than ever.

Links

PostHeaderIcon [DevoxxFR2012] .NET for the Java Developer: A Source of Inspiration? A Profound Cross-Platform Exploration of Language Design, Ecosystem Evolution, and the Future of Enterprise Programming

Lecturers

Cyrille Martraire stands as one of the most influential figures in the French software craftsmanship movement, having co-founded Arolla, a boutique consultancy that has redefined how enterprise teams approach code quality, domain-driven design, and technical excellence. With nearly two decades of experience building mission-critical financial systems at investment banks and fintech startups, Cyrille has cultivated a philosophy that places expressiveness, readability, and long-term maintainability at the heart of software development. He is the founder of the Software Craftsmanship Paris community, a regular speaker at international conferences, and a passionate advocate for learning across technological boundaries. His ability to draw meaningful insights from seemingly disparate ecosystems—such as .NET—stems from a deep curiosity about how different platforms solve similar problems, and how those solutions can inform better practices in Java.

Rui Carvalho, a veteran .NET architect and ASP.NET MVC specialist, brings a complementary perspective rooted in over fifteen years of web development across startups, agencies, and large-scale enterprise platforms. A fixture in the ALT.NET Paris community and a recurring speaker at Microsoft TechDays, Rui has witnessed the entire arc of .NET’s evolution—from the monolithic WebForms era to the open-source, cross-platform renaissance of .NET Core and beyond. His expertise lies not merely in mastering Microsoft’s tooling, but in understanding how framework design influences developer productivity, application architecture, and long-term system evolution. Together, Martraire and Carvalho form a dynamic duo capable of transcending platform tribalism to deliver a nuanced, humorous, and technically rigorous comparison that resonates deeply with developers on both sides of the Java–.NET divide.

Abstract

This article represents a comprehensive, elaborately expanded re-interpretation of Cyrille Martraire and Rui Carvalho’s landmark 2012 DevoxxFR presentation, “.NET pour le développeur Java : une source d’inspiration ?”, transformed into a definitive treatise on the parallel evolution of Java and C# and their mutual influence over nearly three decades of enterprise software development. Delivered with wit, mutual respect, and a spirit of ecumenical dialogue, the original talk challenged the audience to look beyond platform loyalty and recognize that Java and C# have been engaged in a continuous, productive exchange of ideas since their inception. From the introduction of lambda expressions in C# 3.0 (2007) to Java 8 (2014), from LINQ’s revolutionary query comprehension to Java’s Streams API, from async/await to Project Loom’s virtual threads, the presenters traced a lineage of innovation where each platform borrowed, refined, and occasionally surpassed the other.

This expanded analysis delves far beyond surface-level syntax comparisons to explore the philosophical underpinnings of language design decisions, the ecosystem implications of framework choices, and the cultural forces that shaped adoption. It examines how .NET’s bold experimentation with expression trees, dynamic types, extension methods, and Razor templating offered Java developers a vision of what was possible—and in many cases, what Java later adopted or still lacks.

EDIT
Updated for the 2025 landscape, this piece integrates the latest advancements: C# 13’s primary constructors and source generators, Java 21’s pattern matching and virtual threads, Spring Fu’s functional web framework, GraalVM’s native compilation, and the convergence of both platforms under cloud-native, polyglot architectures. Through rich code examples, architectural deep dives, performance analyses, and forward-looking speculation, this work offers not just a historical retrospective, but a roadmap for cross-platform inspiration in the age of cloud, AI, and real-time systems.

The Shared Heritage: A Tale of Two Languages in Constant Dialogue

To fully appreciate the depth of inspiration between Java and C#, one must first understand their shared origin story. Java was released in 1995 as Sun Microsystems’ answer to the complexity of C++, promising “write once, run anywhere” through the JVM. C#, announced by Microsoft in 2000, was explicitly positioned as a modern, type-safe, component-oriented language for the .NET Framework, but its syntax, garbage collection, exception handling, and metadata system bore an uncanny resemblance to Java. This was no coincidence. Anders Hejlsberg, the architect of C#, had previously designed Turbo Pascal and Delphi, but he openly acknowledged Java’s influence. As Cyrille humorously remarked during the talk, “C# didn’t just look like Java—it was Java’s younger brother who went to a different school, wore cooler clothes, and occasionally got better grades.”

This fraternal relationship manifested in a decade-long game of leapfrog. When Java 5 introduced generics in 2004, C# 2.0 responded with generics, nullable types, and anonymous methods in 2005. When C# 3.0 unveiled LINQ and lambda expressions in 2007, Java remained silent until Java 8 in 2014. When Java 7 introduced the invokedynamic bytecode in 2011 to support dynamic languages, C# 4.0 had already shipped the dynamic keyword in 2010. This back-and-forth was not mere imitation—it was a refinement cycle where each platform stress-tested ideas in production before the other adopted and improved them.

Lambda Expressions and Functional Programming: From Verbosity to Elegance

One of the most visible and impactful areas of cross-pollination was the introduction of lambda expressions and functional programming constructs. In the pre-lambda era, both Java and C# relied on verbose anonymous inner classes to implement single-method interfaces. A simple event handler in Java 6 looked like this:

button.addActionListener(new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {
        System.out.println("Button clicked at " + e.getWhen());
    }
});

The equivalent in C# 2.0 was only marginally better, using anonymous delegates:

button.Click += delegate(object sender, EventArgs e) {
    Console.WriteLine("Button clicked");
};

But in 2007, C# 3.0 introduced lambda expressions with a syntax so clean it felt revolutionary:

button.Click += (sender, e) => Console.WriteLine("Clicked!");

This wasn’t just syntactic sugar. It was a paradigm shift toward functional programming, enabling higher-order functions, collection processing, and deferred execution. Rui demonstrated how this simplicity extended to LINQ:

var recentOrders = orders
    .Where(o => o.Date > DateTime.Today.AddDays(-30))
    .OrderBy(o => o.Total)
    .Select(o => o.CustomerName);

Java developers watched with envy. It took seven years for Java 8 to deliver lambda expressions in 2014, but when it did, it came with a more rigorous type system based on functional interfaces and default methods:

button.addActionListener(e -> System.out.println("Clicked!"));

The Java version was arguably more type-safe and extensible, but it lacked C#’s expression-bodied members and local functions.

EDIT:
In 2021, Java 21 has closed the gap further with pattern matching and unnamed variables, but C# 13’s primary constructors in records remain unmatched:

public record Person(string Name, int Age);

LINQ: The Query Comprehension Revolution That Java Never Fully Embraced

Perhaps the most profound inspiration from .NET—and the one Java has still not fully replicated—is LINQ (Language Integrated Query). Introduced in C# 3.0, LINQ was not merely a querying library; it was a language-level integration of query comprehension into the type system. Using a SQL-like syntax, developers could write:

var result = from p in people
             where p.Age >= 18
             orderby p.LastName
             select new { p.FirstName, p.LastName };

This syntax was compiled into method calls on IEnumerable<T>, but more importantly, it was extensible. Providers could translate LINQ expressions into SQL, XML, or in-memory operations. The secret sauce? Expression trees.

Expression<Func<Person, bool>> predicate = p => p.Age > 18;
var sql = SqlTranslator.Translate(predicate); // "SELECT * FROM People WHERE Age > 18"

Java’s Streams API in Java 8 was the closest analog:

List<Person> adults = people.stream()
    .filter(p -> p.getAge() >= 18)
    .sorted(Comparator.comparing(Person::getLastName))
    .map(p -> new PersonDto(p.getFirstName(), p.getLastName()))
    .toList();

But Streams are imperative in spirit, lack query syntax, and cannot be translated to SQL without external tools like jOOQ. Cyrille lamented: “Java gave us the pipeline, but not the language.”

Asynchronous Programming: async/await vs. the Java Journey

Concurrency has been another arena of inspiration. C# 5.0 introduced async/await in 2012, allowing developers to write asynchronous code that looked synchronous:

public async Task<string> FetchDataAsync()
{
    var client = new HttpClient();
    var html = await client.GetStringAsync("https://example.com");
    return Process(html);
}

The compiler transformed this into a state machine, eliminating callback hell. Java’s journey was more fragmented: Futures, CompletableFuture, Reactive Streams, and finally Project Loom’s virtual threads in Java 21:

try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
    return executor.submit(() -> client.get(url)).get();
}

Virtual threads are a game-changer, but they don’t offer the syntactic elegance of await. As Rui quipped, “In C#, you write synchronous code that runs asynchronously. In Java, you write asynchronous code that hopes to run efficiently.”

Web Frameworks: From WebForms to Razor and the Templating Renaissance

Rui traced .NET’s web framework evolution with particular passion. The early 2000s were dominated by ASP.NET WebForms, a drag-and-drop, event-driven model that promised rapid development but delivered ViewState bloat, postback hell, and untestable code. It was, in Rui’s words, “a productivity trap disguised as a framework.”

The community rebelled, giving rise to ALT.NET and frameworks like MonoRail. Microsoft responded with ASP.NET MVC in 2009, embracing separation of concerns, testability, and clean URLs. Then came Razor in 2010—a templating engine that felt like a revelation:

@model List<Person>
<h1>Welcome, @ViewBag.User!</h1>
<ul>
@foreach(var p in Model) {
    <li>@p.Name <em>(@p.Age)</em></li>
}
</ul>

No XML. No JSP-style scriptlets. Just C# and HTML in harmony. Java’s JSP, JSF, and even Thymeleaf felt antiquated by comparison. But in 2020, Spring Boot with Thymeleaf or Micronaut Views has narrowed the gap, though Razor’s layout system and tag helpers remain superior.

The Cutting Edge in 2025: Where Java and C# Stand Today

EDIT:
C# 13 and .NET 9 continue to innovate with source generators, record structs, and minimal APIs:

var builder = WebApplication.CreateBuilder();
var app = builder.Build();
app.MapGet("/", () => "Hello World");
app.Run();

Java 21 counters with pattern matching for switch, records, and virtual threads, but lacks native metaprogramming. Projects like Spring Fu and Quarkus are pushing functional and reactive paradigms, but the expressive gap remains.

Conclusion: Inspiration Without Imitation

Martraire and Carvalho’s core message endures: Java and .NET are not rivals—they are collaborators in the advancement of managed languages. The inspiration flows both ways, and the future belongs to developers who can transcend platform boundaries to build better systems.

EDIT:
In 2025, as cloud-native, AI-augmented, and real-time applications dominate, the lessons from this 2012 dialogue are more relevant than ever.

Links

PostHeaderIcon [DevoxxFR2012] Android Development Essentials: A Comprehensive Introduction to Core Concepts and Best Practices

Lecturer

Mathias Seguy founded Android2EE, specializing in Android training, expertise, and consulting. Holding a PhD in Fundamental Mathematics and an engineering degree from ENSEEIHT, he transitioned from critical J2EE projects—serving as technical expert, manager, project leader, and technical director—to focus on Android. Mathias authored multiple books on Android development, available via Android2ee.com, and contributes articles to Developpez.com.

Abstract

This article examines Mathias Seguy’s introductory session on Android development, designed to equip Java programmers with foundational knowledge for building mobile applications. It explores the Android ecosystem’s global context, core components like activities, intents, and services, and practical implementation strategies. Situated within the rapid evolution of mobile IT, the analysis reviews methodologies for UI construction, resource management, asynchronous processing, and data handling. Through code examples and architectural patterns, it assesses implications for application lifecycle management, performance optimization, and testing, providing a roadmap for novices to navigate Android’s intricacies effectively.

Positioning Android Within the Global IT Landscape

Android’s prominence in mobile computing stems from its open-source roots and widespread adoption. Mathias begins by contextualizing Android in the IT world, noting its Linux-based kernel enhanced with Java libraries for application development. This hybrid architecture leverages Java’s familiarity while optimizing for mobile constraints like battery life and varying screen sizes.

The ecosystem encompasses devices from smartphones to tablets, supported by Google’s Play Store for distribution. Key players include manufacturers (e.g., Samsung, Huawei) customizing the OS, and developers contributing via the Android Open Source Project (AOSP). Mathias highlights market dominance: by 2012, Android held significant share, driven by affordability and customization.

Development tools integrate with Eclipse (then primary IDE), using SDK for emulation and debugging. Best practices emphasize modular design to accommodate fragmentation—diverse API levels and hardware. This overview underscores Android’s accessibility for Java developers, bridging desktop/server paradigms to mobile’s event-driven model.

Core Components and Application Structure

Central to Android apps are activities—single screens with user interfaces. Mathias demonstrates starting with a minimal project: manifest.xml declares entry points, main_activity.java handles logic, and layout.xml defines UI via XML or code.

Code for a basic activity:

public class MainActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
}

Intents facilitate inter-component communication, enabling actions like starting activities or services. Explicit intents target specific classes; implicit rely on system resolution.

Services run background tasks, unbound for independence or bound for client interaction. Content Providers expose data across apps, using URIs for CRUD operations. Broadcast Receivers respond to system events.

Mathias stresses lifecycle awareness: methods like onCreate(), onPause(), onDestroy() manage state transitions, preventing leaks.

Handling Asynchronous Operations and Resources

Mobile apps demand responsive UIs; Mathias introduces Handlers and AsyncTasks for off-main-thread work. Handlers post Runnables to UI thread:

Handler handler = new Handler();
handler.post(new Runnable() {
    public void run() {
        // UI update
    }
});

AsyncTask abstracts background execution with doInBackground(), onPostExecute():

private class DownloadTask extends AsyncTask<String, Integer, String> {
    protected String doInBackground(String... urls) {
        // Download
        return result;
    }
    protected void onPostExecute(String result) {
        // Update UI
    }
}

Resources—strings, images, layouts—are externalized in res/ folder, supporting localization and densities. Access via R class: getString(R.string.app_name).

Data persistence uses SharedPreferences for simple key-values, SQLite for databases via SQLiteOpenHelper.

Advanced Patterns and Testing Considerations

Patterns address lifecycle challenges: Bind threads to activity states using booleans for running/pausing. onRetainNonConfigurationInstance() passes objects across recreations (pre-Fragments).

For REST services, use HttpClient or Volley; sensors via SensorManager.

Testing employs JUnit for units, AndroidJUnitRunner for instrumentation. Maven/Hudson automate builds, ensuring CI.

Implications: These elements foster robust, efficient apps. Lifecycle mastery prevents crashes; async patterns maintain fluidity. In fragmented ecosystems, adaptive resources ensure compatibility, while testing mitigates regressions.

Mathias’s approach demystifies Android, empowering Java devs to innovate in mobile spaces.

Links:

PostHeaderIcon Difference between wait() and sleep() in Java

Today in interview I have also been asked the following question: in Java, what is the difference between the methods wait() and sleep()?

First of all, wait() is a method of Object, meanwhile sleep() is a static method of Thread.

More important: Thread.sleep() freezes the execution of the complete thread for a given time. wait(), on its side, gives a maximum time on which the application is suspended: the waiting period may be interrupted by a call to the method notify() on the same object.