Programming languages allow us to communicate with computers, and they operate like sets of instructions. There are numerous types of languages, including procedural, functional, object-oriented, and more. Whether you’re looking to learn a new language or trying to find some tips or tricks, the resources in the Languages Zone will give you all the information you need and more.
The Database Evolution
Organizations are constantly working to build up their big data capabilities with hopes to compete in the modern economy. SQL and NoSQL database types were supposed to help organizations identify and make sense of hidden patterns in the data that businesses collected. In recent years, however, the momentum of the big data movement has seemed to slow as business leaders around the world have failed to realize the results that were promised several years ago. As the momentum has decelerated, how is the use of SQL and NoSQL databases evolving to support these efforts as businesses attempt to course correct in the big data era? In DZone’s 2020 Trend Report “Database Evolution: SQL or NoSQL in the Age of Big Data,” we explore the role of two popular database types SQL and NoSQL in big data initiatives over the next 6-12 months. Readers will find original research, interviews with industry experts, and additional resources with helpful tips, best practices, and more.
For the use cases I am going to describe here, I have two services: courses-service and reviews-service: Courses-service provides CRUD operations for dealing with courses and instructors. Reviews-service is another CRUD operations provider for dealing with reviews for courses that are completely agnostic of courses from courses-service. Both apps are written in Kotlin using Spring Boot and other libraries. Having these two services, we are going to discuss distributed caching with Hazelcast and Spring Boot and see how we can use user code-deployment to invoke some code execution via Hazelcast on a service. Spoiler alert: The examples/use cases presented here are designed purely for the sake of demonstrating integration with some of Hazelcast’s capabilities. The discussed problems here can be solved in various ways and maybe even in better ways, so don’t spend too much on thinking, “why?” So, without further ado, let’s dive into code. Note: here is the source code in case you want to follow along. Simple Distributed Caching We’ll focus on courses-service for now. Having this entity: Kotlin @Entity @Table(name = "courses") class Course( var name: String, @Column(name = "programming_language") var programmingLanguage: String, @Column(name = "programming_language_description", length = 3000, nullable = true) var programmingLanguageDescription: String? = null, @Enumerated(EnumType.STRING) var category: Category, @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "instructor_id") var instructor: Instructor? = null ) : AbstractEntity() { override fun toString(): String { return "Course(id=$id, name='$name', category=$category)" } } And this method in CourseServiceImpl: Kotlin @Transactional override fun save(course: Course): Course { return courseRepository.save(course) } I want to enhance every course that is saved with a programming language description for the programming language that has been sent by the user. For this, I created a Wikipedia API client that will make the following request every time a new course is added: Plain Text GET https://en.wikipedia.org/api/rest_v1/page/summary/java_(programming_language) So, my method looks like this now: Kotlin @Transactional override fun save(course: Course): Course { enhanceWithProgrammingLanguageDescription(course) return courseRepository.save(course) } private fun enhanceWithProgrammingLanguageDescription(course: Course) { wikipediaApiClient.fetchSummaryFor("${course.programmingLanguage}_(programming_language)")?.let { course.programmingLanguageDescription = it.summary } } Now here comes our use case, we want to cache the Wikipedia response so we don’t call every single time. Our courses will be mostly oriented to a set of popular programming languages like Java, Kotlin, C#, and popular programming languages. We also don’t want to decrease our save()’s performance by querying every time for mostly the same language. Also, this can act as a guard in case the API server is down. Time to introduce Hazelcast! Hazelcast is a distributed computation and storage platform for consistently low-latency querying, aggregation and stateful computation against event streams and traditional data sources. It allows you to quickly build resource-efficient, real-time applications. You can deploy it at any scale from small edge devices to a large cluster of cloud instances. You can read about lots of places where Hazelcast is the appropriate solution and all the advantages on their homepage. When it comes to integrating a Spring Boot app with Hazelcast (embedded), it is straightforward. There are a few ways of configuring Hazelcast, via XML, YAML, or the programmatic way. Also, there is a nice integration with Spring Boot’s cache support via @EnableCaching and @Cacheable annotations. I picked the programmatic way of configuring Hazelcast and the manual way of using it—a bit more control and less magic. Here are the dependencies: Kotlin implementation("com.hazelcast:hazelcast:5.2.1") implementation("com.hazelcast:hazelcast-spring:5.2.1") And here is the configuration that we are going to add to courses-service: Kotlin @Configuration class HazelcastConfiguration { companion object { const val WIKIPEDIA_SUMMARIES = "WIKIPEDIA_SUMMARIES" } @Bean fun managedContext(): SpringManagedContext { return SpringManagedContext() } @Bean fun hazelcastConfig(managedContext: SpringManagedContext): Config { val config = Config() config.managedContext = managedContext config.networkConfig.isPortAutoIncrement = true config.networkConfig.join.multicastConfig.isEnabled = true config.networkConfig.join.multicastConfig.multicastPort = 5777 config.userCodeDeploymentConfig.isEnabled = true config.configureWikipediaSummaries() return config } private fun Config.configureWikipediaSummaries() { val wikipediaSummaries = MapConfig() wikipediaSummaries.name = WIKIPEDIA_SUMMARIES wikipediaSummaries.isStatisticsEnabled = true wikipediaSummaries.backupCount = 1 wikipediaSummaries.evictionConfig.evictionPolicy = EvictionPolicy.LRU wikipediaSummaries.evictionConfig.size = 10000 wikipediaSummaries.evictionConfig.maxSizePolicy = MaxSizePolicy.PER_NODE addMapConfig(wikipediaSummaries) } } So we declare a managedContext() bean, which is a container-managed context initialized with a Spring context implementation that is going to work along with the @SpringAware annotation to allow us to initialize/inject fields in deserialized instances. We’ll take a look at why we need this later when we discuss user code deployment. Then, we declare a hazelcastConfig() bean, which represents the brains of the whole integration. We set the managedContext, enable user code deployment, and set the networkConfig’s join option to “multicast.” Basically, the NetworkConfig is responsible for defining how a member will interact with other members or clients—there are multiple parameters available for configuration like port, isPortAutoIncrement, sslConfig, restApiConfig, joinConfig, and others. We configured the isPortAutoIncrement to true to allow hazelcastInstance to auto-increment the port if the picked one is already in use until it runs out of free ports. Also, we configured the JoinConfig, which contains multiple member/client join configurations like Eureka, Kubernetes, and others. We enable MulticastConfig, which allows Hazelcast members to find each other without the need to know concrete addresses via multicasting to everyone listening. Also, I encountered some issues with the port used by Multicast, so I set it to a hard-coded one to avoid the address already in use. Then we configure a Map config that is going to act as our distributed cache. MapConfig contains the configuration of an IMap—concurrent, distributed, observable, and queryable map: name: the name of IMap, WIKIPEDIA_SUMMARIES. isStatisticsEnabled: this is to enable IMap’s statistics like the total number of hits and others. backupCount: number of synchronous backups, where “0” means no backup. evictionConfig.evictionPolicy: can be LRU (Least Recently Used), LFU (Least Frequently Used), NONE, and RANDOM. evictionConfig.size: the size used by MaxSizePolicy. evictionConfig.maxSizePolicy: PER_NODE (policy based on the maximum number of entries per the Hazelcast instance). Having this configuration, all that is left is to adjust our enhanceWithProgrammingLanguageDescription method to use the above configured IMap to cache the fetched Wikipedia summaries: Kotlin private fun enhanceWithProgrammingLanguageDescription(course: Course) { val summaries = hazelcastInstance.getMap<String, WikipediaApiClientImpl.WikipediaSummary>(WIKIPEDIA_SUMMARIES) log.debug("Fetched hazelcast cache [$WIKIPEDIA_SUMMARIES] = [${summaries}(${summaries.size})] ") summaries.getOrElse(course.programmingLanguage) { wikipediaApiClient.fetchSummaryFor("${course.programmingLanguage}_(programming_language)")?.let { log.debug("No cache value found, using wikipedia's response $it to update $course programming language description") summaries[course.programmingLanguage] = it it } }?.let { course.programmingLanguageDescription = it.summary } } Basically, we are using the autowired Hazelcast instance to retrieve our configured IMap. Each instance is a member and/or client in a Hazelcast cluster. When you want to use Hazelcast’s distributed data structures, you must first create an instance. In our case, we simply autowire it as it will be created by the previously defined config. After getting a hold of the distributed Map, it is a matter of some simple checks. If we have a summary for the programming language key in our map, then we use that one. If not, we fetch it from Wikipedia API, add it to the map, and use it. Now, if we are to start our app, we’ll first see the huge Hazelcast banner and the following lines, meaning that Hazelcast has started: Plain Text INFO 30844 --- [ main] com.hazelcast.core.LifecycleService : [ip]:5701 [dev] [5.2.1] [ip]:5701 is STARTING INFO 30844 --- [ main] c.h.internal.cluster.ClusterService : [ip]:5701 [dev] [5.2.1] Members {size:1, ver:1} [ Member [ip]:5701 - e2a90d3e-b112-4e78-aa42-58a959d9273d this ] INFO 30844 --- [ main] com.hazelcast.core.LifecycleService : [ip]:5701 [dev] [5.2.1] [ip]:5701 is STARTED If we execute the following HTTP request: Plain Text POST http://localhost:8081/api/v1/courses Content-Type: application/json { "name": "C++ Development", "category": "TUTORIAL", "programmingLanguage" : "C++", "instructor": { "name": "Bjarne Stroustrup" } } We’ll see in the logs: Plain Text DEBUG 30844 --- [nio-8080-exec-1] i.e.c.s.i.CourseServiceImpl$Companion : Fetched hazelcast cache [WIKIPEDIA_SUMMARIES] = [IMap{name='WIKIPEDIA_SUMMARIES'}(0)] INFO 30844 --- [nio-8080-exec-1] e.c.s.i.WikipediaApiClientImpl$Companion : Request GET:https://en.wikipedia.org/api/rest_v1/page/summary/C++_(programming_language) INFO 30844 --- [nio-8080-exec-1] e.c.s.i.WikipediaApiClientImpl$Companion : Received response from Wikipedia... DEBUG 30844 --- [nio-8080-exec-1] i.e.c.s.i.CourseServiceImpl$Companion : No cache value found, using wikipedia... That we retrieved the previously configured IMap for Wikipedia summaries, but its size is “0;” therefore, the update took place using the Wikipedia’s API. Now, if we are to execute the same request again, we’ll notice a different behavior: Plain Text DEBUG 30844 --- [nio-8080-exec-3] i.e.c.s.i.CourseServiceImpl$Companion : Fetched hazelcast cache [WIKIPEDIA_SUMMARIES] = [IMap{name='WIKIPEDIA_SUMMARIES'}(1)] Now the IMap has a size of “1” since it was populated by our previous request, so no request to Wikipedia’s API can be observed. The beauty and simplicity of Hazelcast’s integration comes when we start another instance of our app on a different port using -Dserver.port=8081, and we witness the distributed cache in action. Plain Text INFO 8172 --- [ main] com.hazelcast.core.LifecycleService : [ip]:5702 [dev] [5.2.1] [ip]:5702 is STARTING INFO 8172 --- [ main] c.h.i.cluster.impl.MulticastJoiner : [ip]:5702 [dev] [5.2.1] Trying to join to discovered node: [ip]:5701 INFO 8172 --- [.IO.thread-in-0] c.h.i.server.tcp.TcpServerConnection : [ip]:5702 [dev] [5.2.1] Initialized new cluster connection between /ip:55309 and /ip:5701 INFO 8172 --- [ration.thread-0] c.h.internal.cluster.ClusterService : [ip]:5702 [dev] [5.2.1] Members {size:2, ver:2} [ Member [ip]:5701 - 69d6721d-179b-4dc8-8163-e3cb00e703eb Member [ip]:5702 - 9b689155-d4c3-4169-ae53-ff5d687f7ad2 this ] INFO 8172 --- [ main] com.hazelcast.core.LifecycleService : [ip]:5702 [dev] [5.2.1] [ip]:5702 is STARTED We see that MulticastJoiner discovered an already running Hazelcast node on port 5701, running together with our first courses-service instance on port 8080. A new cluster connection is made, and we see in the “Members” list both Hazelcast nodes on ports 5701 and 5702. Now, if we are to make a new HTTP request to create a course on the 8081 instance, we’ll see the following: Plain Text DEBUG 8172 --- [nio-8081-exec-4] i.e.c.s.i.CourseServiceImpl$Companion : Fetched hazelcast cache [WIKIPEDIA_SUMMARIES] = [IMap{name='WIKIPEDIA_SUMMARIES'}(1)] Distributed Locks Another useful feature that comes with Hazelcast is the API for distributed locks. Suppose our enhanceWithProgrammingLanguageDescription method is a slow intensive operation dealing with cache and other resources and we wouldn’t want other threads on the same instance or even other requests on a different instance to interfere or alter something until the operation is complete. So, here comes FencedLock into play—a linearizable, distributed, and reentrant implementation of the Lock. It is consistent and partition tolerant in the sense that if a network partition occurs, it will stay available on, at most, one side of the partition. Mostly, it offers the same API as the Lock interface. So, with this in mind, let’s try and guard our so-called “critical section.” Kotlin private fun enhanceWithProgrammingLanguageDescription(course: Course) { val lock = hazelcastInstance.cpSubsystem.getLock(SUMMARIES_LOCK) if (!lock.tryLock()) throw LockAcquisitionException(SUMMARIES_LOCK, "enhanceWithProgrammingLanguageDescription") Thread.sleep(2000) val summaries = hazelcastInstance.getMap<String, WikipediaApiClientImpl.WikipediaSummary>(WIKIPEDIA_SUMMARIES) log.debug("Fetched hazelcast cache [$WIKIPEDIA_SUMMARIES] = [${summaries}(${summaries.size})] ") summaries.getOrElse(course.programmingLanguage) { wikipediaApiClient.fetchSummaryFor("${course.programmingLanguage}_(programming_language)")?.let { log.debug("No cache value found, using wikipedia's response $it to update $course programming language description") summaries[course.programmingLanguage] = it it } }?.let { course.programmingLanguageDescription = it.summary } lock.unlock() } As you can see, the implementation is quite simple. We obtain the lock via the cpSubsystem’s, getLock(), and then we try acquiring the lock with tryLock(). The locking will succeed if the acquired lock is available or already held by the current thread, and it will immediately return true. Otherwise, it will return false, and a LockAcquisitionException will be thrown. Next, we simulate some intensive work by sleeping for two seconds with Thread.sleep(2000), and in the end, we release the acquired lock with unlock(). If we run a single instance of our app on port 8080 and try two subsequent requests, one will pass, and the other one will fail with: Plain Text ERROR 28956 --- [nio-8081-exec-6] e.c.w.r.e.RESTExceptionHandler$Companion : Exception while handling request [summaries-lock] could not be acquired for [enhanceWithProgrammingLanguageDescription] operation. Please try again. inc.evil.coursecatalog.common.exceptions.LockAcquisitionException: [summaries-lock] could not be acquired for [enhanceWithProgrammingLanguageDescription] operation. Please try again The same goes if we are to make one request to an 8080 instance of our app and the next one in the two seconds timeframe to the 8081 instance; the first request will succeed while the second one will fail. User Code Deployment Now, let’s switch our attention to reviews-service and remember—this service is totally unaware of courses; it is just a way to add reviews for some course_id. With this in mind, we have this entity: Kotlin @Table("reviews") data class Review( @Id var id: Int? = null, var text: String, var author: String, @Column("created_at") @CreatedDate var createdAt: LocalDateTime? = null, @LastModifiedDate @Column("last_modified_at") var lastModifiedAt: LocalDateTime? = null, @Column("course_id") var courseId: Int? = null ) And we have this method in ReviewServiceImpl : Kotlin override suspend fun save(review: Review): Review { return reviewRepository.save(review).awaitFirst() } So, our new silly feature request would be to somehow check for the existence of the course that the review has been written for. How can we do that? The most obvious choice would be to invoke a REST endpoint on courses-service to check if we have a course for the review’s course_id, but that is not what this article is about. We have Hazelcast, right? We are going to deploy some user code from reviews-service that courses-service is aware of and can execute it via Hazelcast’s user code deployment. To do that, we need to create some kind of API or gateway module that we are going to publish as an artifact, so courses-service can implement it, and reviews-service can depend on and use it to deploy the code. First things first, let’s design the new module as a courses-api module: Kotlin plugins { id("org.springframework.boot") version "2.7.3" id("io.spring.dependency-management") version "1.0.13.RELEASE" kotlin("jvm") version "1.6.21" kotlin("plugin.spring") version "1.6.21" kotlin("plugin.jpa") version "1.3.72" `maven-publish` } group = "inc.evil" version = "0.0.1-SNAPSHOT" repositories { mavenCentral() } publishing { publications { create<MavenPublication>("maven") { groupId = "inc.evil" artifactId = "courses-api" version = "1.1" from(components["java"]) } } } dependencies { implementation("org.springframework.boot:spring-boot-starter-actuator") implementation("org.springframework.boot:spring-boot-starter-web") implementation("com.fasterxml.jackson.module:jackson-module-kotlin") implementation("org.jetbrains.kotlin:kotlin-reflect") implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8") implementation("org.jetbrains.kotlinx:kotlinx-coroutines-rx2:1.6.4") implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4") implementation("org.apache.commons:commons-lang3:3.12.0") implementation("com.hazelcast:hazelcast:5.2.1") implementation("com.hazelcast:hazelcast-spring:5.2.1") testImplementation("org.junit.jupiter:junit-jupiter-api:5.8.1") testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.8.1") } tasks.getByName<Test>("test") { useJUnitPlatform() } Nothing fancy here, except the maven-publish plugin that we’ll use to publish the artifact to the local maven repository. Here is the interface that courses-service will implement, and reviews-service will use: Kotlin interface CourseApiFacade { fun findById(id: Int): CourseApiResponse } data class InstructorApiResponse( val id: Int?, val name: String?, val summary: String?, val description: String? ) data class CourseApiResponse( val id: Int?, val name: String, val category: String, val programmingLanguage: String, val programmingLanguageDescription: String?, val createdAt: String, val updatedAt: String, val instructor: InstructorApiResponse ) Having this module properly configured, we can add it as a dependency in courses-service: Kotlin implementation(project(":courses-api")) And implement the exposed interface like this: Kotlin @Component class CourseApiFacadeImpl(val courseService: CourseService) : CourseApiFacade { override fun findById(id: Int): CourseApiResponse = courseService.findById(id).let { CourseApiResponse( id = it.id, name = it.name, category = it.category.toString(), programmingLanguage = it.programmingLanguage, programmingLanguageDescription = it.programmingLanguageDescription, createdAt = it.createdAt.toString(), updatedAt = it.updatedAt.toString(), instructor = InstructorApiResponse(it.instructor?.id, it.instructor?.name, it.instructor?.summary, it.instructor?.description) ) } } Now back to reviews-service where all the magic will happen. First of all, we want to add the required dependencies for Hazelcast: Kotlin implementation("com.hazelcast:hazelcast:5.2.1") implementation("com.hazelcast:hazelcast-spring:5.2.1") Previously, I mentioned that we are going to use that interface from courses-api. We can run the publishMavenPublicationToMavenLocal gradle task on courses-api to get our artifact published, and then we can add the following dependency to reviews-service: Kotlin implementation("inc.evil:courses-api:1.1") Now, is time to set up a Callable implementation on reviews-service that will be responsible for the code execution on courses-service, so, here it is: Kotlin @SpringAware class GetCourseByIdCallable(val id: Int) : Callable<CourseApiResponse?>, Serializable { @Autowired @Transient private lateinit var courseApiFacade: CourseApiFacade override fun call(): CourseApiResponse = courseApiFacade.findById(id) } Here we used the @SpringAware annotation to mark this class as a bean in Spring-Hazelcast way via SpringManagedContext since this class is going to be deployed in the cluster. Other than that, we have our courseApiFacade autowired and used in the overridden method call() from Callable interface. I decided to write another class to ease the Callable submission to the Hazelcast’s IExecutorService to act as some kind of a facade: Kotlin @Component class HazelcastGateway(private val hazelcastInstance: HazelcastInstance) { companion object { private const val EXECUTOR_SERVICE_NAME = "EXECUTOR_SERVICE" } fun <R> execute(executionRequest: Callable<R>): R { val ex = hazelcastInstance.getExecutorService(EXECUTOR_SERVICE_NAME) return ex.submit(executionRequest).get(15000L, TimeUnit.MILLISECONDS) } } We have a single method execution that is responsible to submit the passe—in Callable to the retrieved IExecutorService via the autowired hazelcastInstance. Now, this IExectuorService is a distributed implementation of ExecutorService that enables the running of Runnables/Callables on the Hazelcast cluster and has some additional methods that allows running the code on a particular member or multiple members. For example, we’ve used the submit, but there is also submitToMember and submitToAllMembers. For Runnable, there are the equivalents that start with execute***. Now, let’s use our newly defined HazelcastGateway in the save method from ReviewServiceImpl. Kotlin override suspend fun save(review: Review): Review { runCatching { hazelcastGateway.execute(GetCourseByIdCallable(review.courseId!!)).also { log.info("Call to hazelcast ended with $it") } }.getOrNull() ?: throw NotFoundException(CourseApiResponse::class, "course_id", review.courseId.toString()) return reviewRepository.save(review).awaitFirst() } The logic is as follows: before saving, we try to find the course by course_id from review by running GetCourseByIdCallable in our Hazelcast cluster. If we have an exception (CourseApiFacadeImpl will throw a NotFoundException if the requested course was not found), we swallow it and throw a reviews-service NotFoundException stating that the course could’ve not been retrieved. If a course was returned by our Callable, we proceed to save it—that’s it. All that is left is to configure Hazelcast, and I left this one last since we needed some of these classes to configure it. Kotlin @Configuration class HazelcastConfiguration { @Bean fun managedContext(): SpringManagedContext { return SpringManagedContext() } @Bean fun hazelcastClientConfig(managedContext: SpringManagedContext): ClientConfig { val config = ClientConfig() config.managedContext = managedContext config.connectionStrategyConfig.isAsyncStart = true config.userCodeDeploymentConfig.isEnabled = true config.classLoader = HazelcastConfiguration::class.java.classLoader config.userCodeDeploymentConfig.addClass(GetCourseByIdCallable::class.java) return config } } Regarding the managedContext bean, there’s nothing different from our previous member’s config. Now the hazelcastConfig bean is different. First of all, it is a ClientConfig now, meaning that reviews-service’s embedded Hazelcast will be a client to the courses-service Hazelcast member. We set the managedContext, the connectionStrategyConfig to be async (the client won’t wait for a connection to cluster, it will throw exceptions until connected and ready), and we will enable the user code deployment. Then we add GetCourseByIdCallable to be sent to the cluster. Now, having courses-service running and if we are to start reviews-service, we’ll see the following log: Plain Text INFO 34472 --- [nt_1.internal-1] com.hazelcast.core.LifecycleService : hz.client_1 [dev] [5.2.1] HazelcastClient 5.2.1 (20221114 - 531032a) is CLIENT_CONNECTED INFO 34472 --- [nt_1.internal-1] c.h.c.i.c.ClientConnectionManager : hz.client_1 [dev] [5.2.1] Authenticated with server [ip]:5701:69d6721d-179b-4dc8-8163-e3cb00e703eb, server version: 5.2.1, local address: /127.0.0.1:57050 The client is authenticated with courses-service’s Hazelcast member, where “69d6721d-179b-4dc8-8163-e3cb00e703eb” is the ID of the connected to server. Now, let’s try a request to add a review for an existing course (reviews-service is using GraphQL). Plain Text GRAPHQL http://localhost:8082/graphql Content-Type: application/graphql mutation { createReview(request: {text: "Amazing, loved it!" courseId: 2 author: "Mike Scott"}) { id text author courseId createdAt lastModifiedAt } } In the logs, we’ll notice: Plain Text INFO 34472 --- [actor-tcp-nio-1] i.e.r.s.i.ReviewServiceImpl$Companion : Call to hazelcast ended with CourseApiResponse(id=2, name=C++ Development, category=TUTORIAL, programmingLanguage=C++ ...) And in the courses-service logs, we’ll notice the code execution: Plain Text DEBUG 12608 --- [ached.thread-24] i.e.c.c.aop.LoggingAspect$Companion : before :: execution(public inc.evil.coursecatalog.model.Course inc.evil.coursecatalog.service.impl.CourseServiceImpl.findById(int)) Meaning that the request executed successfully. If we try the same request for a non-existent course, let’s say for ID 99, we’ll observe the NotFoundException in reviews-service: Plain Text WARN 34472 --- [actor-tcp-nio-2] .w.g.e.GraphQLExceptionHandler$Companion : Exception while handling request: CourseApiResponse with course_id equal to [99] could not be found! inc.evil.reviews.common.exceptions.NotFoundException: CourseApiResponse with course_id equal to [99] could not be found! Conclusion All-right folks, this is basically it. I hope you got a good feel of what Hazelcast is like. We took a look at how to design a simple distributed cache using Hazelcast and Spring Boot, made use of distributed locks to protect critical sections of code, and in the end, we’ve seen how Hazelcast’s user code deployment can be used to run some code in the cluster. In case you’ve missed it, all the source code can be found here. Happy coding!
Polymorphism is a concept in object-oriented programming that allows objects of different types to be treated as objects of a common type. This allows for more flexible and reusable code and a more intuitive way of thinking about objects and their interactions. Overview In JavaScript, polymorphism is implemented through the use of prototypes and prototypal inheritance. Every object in JavaScript has a prototype, which is another object that it inherits properties and methods from. This allows for the creation of a chain of prototypes, where an object can inherit from another object, which in turn inherits from another object, and so on. This chain of prototypes is what enables polymorphism in JavaScript. For example, let’s say we have two different objects, a dog and a cat. Both of these objects have a method called “speak,” but the dog’s “speak” method makes it bark, while the cat’s “speak” method makes it meow. If we create a new object called “animal” that inherits from both the dog and the cat, we can now call the “speak” method on the animals object, and it will automatically call the correct “speak” method based on the type of the object it is. To implement polymorphism in JavaScript, we can use the “prototype” property of an object to set the prototype of another object. For example, we can set the prototype of the dog object to be the animals object, so that the dog object inherits all of the properties and methods of the animals object. What Is Polymorphism in JavaScript? JavaScript is a prototype-based language, which means that instead of using classes like in other object-oriented languages, it uses prototypes to create objects and define their behavior. This is what allows for polymorphism in JavaScript. Polymorphism in JavaScript allows us to use the same method or property name on different objects and have each object respond or behave differently. This is possible because JavaScript’s prototype-based inheritance allows objects to inherit properties and methods from other objects and allows those properties and methods to be overridden. For example, let’s say we have a base object called “animal” that has a method called “speak.” We can then create two other objects, a “dog” object and a “cat” object that inherit from the “animals” object. The “dog” object can override the “speak” method to make the dog bark, while the “cat” object can override the “speak” method to make the cat meow. Now, if we have an array of these different animal objects, we can call the “speak” method on each of them, and it will automatically call the correct method, based on the type of the object. In this example, polymorphism allows for code reuse, where we don’t have to write separate methods for each animal and allows for flexibility, where we can add new animals to our array, and they will automatically have the “speak” method without having to add it manually. Another example of polymorphism in JavaScript is using the same method name on different objects, but each object implements the method differently. This allows you to use the same method name on different objects while still having the objects behave differently. Implementing Polymorphism in JavaScript There are several ways to implement polymorphism in JavaScript, each with its benefits and drawbacks. These include: Function overloading Method overriding Duck typing What Is Function Overloading? It is a technique where a single function can have multiple signatures or ways it can be called. This allows the function to behave differently based on the number and types of arguments passed to it. For example—let’s say we have a function called “calculate” that can be called with either one or two arguments. If it is called with one argument, it will return the square of that number, but if it is called with two arguments, it will return the product of those two numbers. JavaScript // In this example, the `calculate` function has two different signatures, one that takes one argument and one that takes two arguments. Depending on the number of arguments passed, the function will behave differently. function calculate(a, b) { if (b === undefined) { return a * a; } else { return a * b; } } console.log(calculate(5)); // 25 console.log(calculate(5, 6)); // 30 What Is Method Overriding? It is a technique where a child object can override a method that is defined in its parent object. This allows the child object to change the behavior of the method to better suit its own needs. For example—let’s say we have a parent object called “animals” that has a method called “speak.” We can then create a child object called “dog” that inherits from the “animals” object and overrides the “speak” method to make the dog bark. JavaScript //In this example, we have a base object called "Animals" that has a method called "speak". We then create two child objects called "Dog" and "Cat" that inherit from the "Animals" object and override the "speak" method to make the dog bark and the cat meow respectively. let Animal = { speak: function() { console.log("Animals make different sounds"); } }; let Dog = Object.create(Animal); Dog.speak = function() { console.log("Woof woof"); }; let Cat = Object.create(Animals); Cat.speak = function() { console.log("Meow meow"); }; let dog = Object.create(Dog); dog.speak(); // "Woof woof" let cat = Object.create(Cat); cat.speak(); // "Meow meow" What Is Duck Typing? It is a technique where an object is judged by its behavior rather than its type. This means that an object is considered to be of a certain type if it has the required properties or methods. For example—let’s say we have an object called “bird” that has a property called “canFly” and a method called “fly.” We can then create another object called “plane” that also has the “canFly” property and the “fly” method. Even though “plane” is not an instance of the “bird” object, it is considered to be a “bird” because it has the same properties and methods. JavaScript // In this example, we have two different objects, "Bird" and "Plane", that both have a property called "canFly" and a method called "fly". We then create a function called "fly" that checks if the object passed to it has the "canFly" property and the "fly" method. If it does, it calls the "fly" method on that object. Because both "Bird" and "Plane" objects have the required properties and methods, they are considered to be of the same "type" and the fly function works on both objects. let Bird = { canFly: true, fly: function() { console.log("I'm flying"); } }; let Plane = { canFly: true, fly: function() { console.log("I'm flying in the sky"); } }; function fly(obj) { if (obj.canFly && obj.fly) { obj.fly(); } } let sparrow = Bird; let boeing = Plane; fly(sparrow); // "I'm flying" fly(boeing); // "I'm flying in the sky" Polymorphism and Prototype Chain In JavaScript, objects are linked together through a prototype chain, which is a chain of prototypes where an object can inherit properties and methods from another object, which in turn inherits from another object, and so on. This chain of prototypes is what enables polymorphism in JavaScript. For example—let’s say we have a base object called “animals” that has a method called “speak.” We can then create a child object called “dog” that inherits from the “animals” object and overrides the “speak” method to make the dog bark. Now, let’s say we create another child object called “poodle” that also inherits from the “animals” object, but it doesn’t override the “speak” method. The “poodle” object will inherit the “speak” method from the “animals” object, so it will also have the ability to speak, but since it hasn’t overridden the method, it will use the implementation provided by the “animals” object. This allows for a more intuitive way of thinking about objects and their interactions, as well as more flexible and reusable code. For example, if we want to add a new method or property to all “animals” objects, we can simply add it to the “animals” object, and all child objects that inherit from it will automatically have access to that new method or property. Conclusion In this article, we discussed the different ways to implement polymorphism in JavaScript, including function overloading, method overriding, and duck typing. We also explained how the prototype chain in JavaScript affects polymorphism and how polymorphism can be implemented and inherited through the prototype chain. This post was not meant for beginners, but slowly I will post articles on many methods I mentioned above and other things. Thank you for reading.
Today I'm going to show you all the things to consider when building the perfect HTML input. Despite its seemingly simple nature, there's actually a lot that goes into it. How To Make the Control Well, we need to start somewhere. Might as well start with the control itself. HTML offers three different form controls to choose from: <input>, <textarea>, and <select>. Today, we'll use <input>, but the same rules will apply to the others. <input /> How To Make <input> Work Inputs are generally used to capture user data. To do so, they should be placed within a <form> element, but that's not quite enough. When the form is submitted, it won't know how to label the input's data. For a form to include an input's data when the form is submitted, the input needs a name attribute. You don't need state management or data binding. Just a name. <input name="data" /> How To Make the Input Accessible Now that we've made the robots happy, it's time to focus on the humans. Every input also needs a label, both for clarity and for accessibility. There are a few options: Add a <label> element with a for attribute and assign it to the input's id (explicit label). Wrap the input with a <label> element (implicit label). Add an aria-label attribute to the input. Add an aria-labeledby attribute to the input and assign it to the id of another element. Of all these options, the most reliable is an explicit label as it works across the most browsers, assistive technologies, and voice-control interfaces. Implicit labels do not work in Dragon Speech Recognition. ARIA attributes are finicky. The placeholder and title attributes are not proper labels. I recommend not wrapping everything in a <label> tag because: It's prone to include more content than what would be considered the label. This results in a poor experience for screen-reader users. It's common to add styles to the input's wrapper element. These styles may conflict with the default behavior of a <label>. In general, I prefer using a <div> to isolate the control. <div> <label for="input-id">Label</input> <input id="input-id" name="data" /> </div> If you ever want an input that does not show the label, don't remove the label from the HTML. Instead, hide it with CSS or use a less reliable option. Keep the label in the markup and visually hide it with a class with these styles. These styles keep it accessible to assistive technology, while also visually removing it: .visually-hidden { position: absolute; overflow: hidden; clip: rect(0 0 0 0); width: 1px; height: 1px; margin: -1px; border: 0; padding: 0; } Note that it's still generally advised to include a visible label to avoid any confusion. A placeholder should not serve as a label. How To Choose a Type (Or Not) In addition to the different tags listed above, you can change the control's behavior by setting an input's type attribute. For example, if you wanted to accept a user's email, you can set the type attribute to "email". Input types can change the behavior or appearance of the UI. Here are just a few examples: The "number" type changes behavior by preventing non-number value entries. The "color" type changes the UI by adding a button that opens a color picker. The "date" type improves the data entry experience by offering a date-picker. The "email" type enforces built-in constraint validation on form submission. However, some input types may be false friends. Consider an input that asks for a US zip code. Only numerical entries are valid, so it might make sense to use a "number" type. However, one issue with the "number" input is that it adds a scroll event feature such that a user can scroll up on the input to increment the value or down to decrement it. For a zip code input, it's possible that a user clicks on the input, enters their zip code, then tries to scroll down the page. This would decrement the value they entered, and it's very easy for the user to miss that change. As a result, the number they entered could be wrong. In this case, it may be better to avoid the type attribute completely and use a pattern such as [0-9]* if you want to limit the input to only numeric values. In fact, the "number" type is often more problematic than it's worth. Be Descriptive Since we've briefly touched on constraint validation, it's a good time to mention descriptions. Although HTML has built-in validation attributes and there are several more robust JavaScript validation libraries, there is another effective approach to getting users to fill in proper data that can be less annoying. Tell them exactly what it is you want. Some form controls like "name" or "email" may be obvious, but for those that are not, provide a clear description for what you need. For example, if you are asking users to create a new password, tell them what the requirements are before they try to submit the form. And don't forget about assistive technology users. We can associate an input with a description through visual proximity as well as using the aria-describedby attribute. <div> <label for="password">Password</input> <input id="password" name="password" type="password" aria-describedby="password-requirements" /> <p id="password-requirements">Please create a new password. Must contain at least 8 characters, one uppercase letter, one lowercase letter, and one special character.</p> </div> Descriptions are also an effective place to put any validation feedback messages. Be Flexible When creating inputs, it's often tempting to add constraints for the acceptable values to ensure the user only sends good data. But being too strict can lead to a poor user experience. For example, if you ask the user to enter a phone number, consider that there are several different acceptable formats: 8008675309 800 867 5309 800-867-5309 800.867.5309 (800) 867-5309 +1 (800) 867-5309 001 800 867 5309 All of the above represent the same phone number. Ideally, a user would be able to enter any of these formats and still be able to submit the form without issue. If you want your input to only send number characters, it's possible to allow the user to type in whatever format they want. Then you can use JavaScript to add an event handler to the blur event, and remove any unwanted characters (space, dash, period, and so on) from the input's value. This would leave only the numbers. Make It Easy If you've ever filled out a form using a mobile device, you may have noticed that your phone's keyboard looks different on different inputs. For a basic text input you see the standard keyboard, for email inputs you may see the @ symbol more conveniently placed, and for number inputs you may see the keyboard replaced with a number pad. In many cases, the browser will choose a more appropriate keyboard to show users if the input type is set. But as we saw above, it's often better to use just a basic text input. We can still offer a nicer user experience to mobile users by asking the browser to show specific keyboards despite the input missing a type attribute. We can accomplish this with the inputmode attribute which accepts eight different options. text (default value) none decimal numeric tel search email url Want to give it a try? Head over to inputmodes.com on your mobile device. It's pretty cool. Continue Learning That's over a thousand words I had to say about creating form controls. I hope you found it useful.
Since its conception in the late 1980s, HyperText Markup Language (HTML) has persisted as a critical element in displaying web pages online. This ubiquitous programming language continues to offer a detailed framework for structuring the content we see and interact with on the web, allowing us to format text and multimedia components in plain-text code, which is simple enough to change when the need arises. The Transformation of HTML As is the case with nearly all programming languages, HTML has transformed to incorporate dozens of new features over the decades since its introduction, accommodating typical contemporary pressures such as community feedback/critique and the rapid growth of adjacent web development technologies. The results of this transformation are easily visible to us in the output of modern HTML code; for example, the most recent HTML iteration–HTML5, introduced in 2014–offers new, simple elements for embedding video and audio files, as well as much-needed improvements in mobile display and overall mobile functionality. Of course, new elements and display-quality improvements are not the only reason a website’s HTML code will reliably change over time. Websites are designed and redesigned constantly, likely as much for the sake of innovation as in response to trends originating from user feedback. For example, a website developed in 2014 is likely to have incorporated a variety of contemporary design trends independent of the changes introduced in HTML5 that year. In the years following that project, developers of the website will likely have found themselves reiterating at least a portion of their HTML code numerous times, all the while building towards the inevitable release of HTML6 and the eventual incorporation of its new and improved features. Given these natural progressions in HTML development, an important question arises: How do we effectively track and document the growth of our website’s HTML code? The answer is a relatively simple one, given the inherently visual nature of HTML’s output. We can easily store iterations of our HTML websites in the form of static, two-dimensional image files–screenshots–and we can accomplish this transition programmatically with relative ease. HTML Screenshots Capturing an HTML screenshot has a multitude of practical business applications. When new HTML code is written for a website, rendering an image of that HTML code’s output works as a simple, easily shareable “status check” for how its content appears on a web browser at a given point in time. By the same token, such screenshots offer an excellent method for rapidly testing new, experimental iterations of HTML code, making it simple for a developer to create and store various versions–both successful and unsuccessful–of a project in development. Screenshots additionally provide an ideal means of visual documentation for the inevitable issues that occur with live websites, making it easier to keep track of nagging issues and track how they appear on different devices, browsers, or operating systems. With new HTML versions always looming on the horizon, all pre-existing screenshots provide a practical method of comparison with the output of older HTML versions, helping developers to advocate–among technical and non-technical audiences alike–for the incorporation of various new features, and subsequently justify the business expense that comes with those endeavors. Tutorial The purpose of this tutorial is to provide a simple, free, easy-to-use API solution for converting HTML strings to PNG screenshots in Java. This API will render a website fully, returning a screenshot of what the HTML displays in a regular web browser view. It supports all modern, advanced web development features, including those pertaining to HTML5, CSS, JavaScript, and more. For your convenience, ready-to-run Java code examples are supplied further down the page to help you structure your API call with ease. This API has two mandatory request parameters, which include the following: Your HTML String A free-tier Cloudmersive API key (you can get one by visiting our website and registering a free account). In addition to the mandatory inputs above, this API provides several optional parameters, which allow for further customization of your input request. These optional parameters include the following: Extra loading wait: the additional number of milliseconds to wait once the web page has finished loading before taking the screenshot (can be helpful for very asynchronous websites). Screenshot height: the desired height of your screenshot, expressed in terms of pixels (defaults to 1280 x 1024). Supplying the integer “0” triggers the default setting, while supplying the integer “-1” asks the API to measure and attempt a screen-height screenshot. Screenshot width: the desired width of your screenshot, expressed in terms of pixels (also defaults to a standard 1280 x 1024 measurement). Supplying the integers “0” or “-1” result in the same outcomes as described in the “Screenshot height” parameter above. In its response, this API will provide a string containing the encoding for your new PNG file. To structure your API call in Java, your first step is to install the SDK. This can be accomplished with Maven by first adding the following reference to the repository in pom.xml: XML <repositories> <repository> <id>jitpack.io</id> <url>https://jitpack.io</url> </repository> </repositories> Following that, add the reference below to the dependency in pom.xml: XML <dependencies> <dependency> <groupId>com.github.Cloudmersive</groupId> <artifactId>Cloudmersive.APIClient.Java</artifactId> <version>v4.25</version> </dependency> </dependencies> To install the SDK with Gradle instead, add your reference in the root build.gradle (at the end of repositories): allprojects { repositories { ... maven { url 'https://jitpack.io' } } } And then add the dependency in build.gradle: dependencies { implementation 'com.github.Cloudmersive:Cloudmersive.APIClient.Java:v4.25' } With the installation complete, all that’s left is to copy and paste the following code examples and fulfill the mandatory and optional request parameters as described above: Java // Import classes: //import com.cloudmersive.client.invoker.ApiClient; //import com.cloudmersive.client.invoker.ApiException; //import com.cloudmersive.client.invoker.Configuration; //import com.cloudmersive.client.invoker.auth.*; //import com.cloudmersive.client.ConvertWebApi; ApiClient defaultClient = Configuration.getDefaultApiClient(); // Configure API key authorization: Apikey ApiKeyAuth Apikey = (ApiKeyAuth) defaultClient.getAuthentication("Apikey"); Apikey.setApiKey("YOUR API KEY"); // Uncomment the following line to set a prefix for the API key, e.g. "Token" (defaults to null) //Apikey.setApiKeyPrefix("Token"); ConvertWebApi apiInstance = new ConvertWebApi(); HtmlToPngRequest input = new HtmlToPngRequest(); // HtmlToPngRequest | HTML to PNG request parameters try { byte[] result = apiInstance.convertWebHtmlToPng(input); System.out.println(result); } catch (ApiException e) { System.err.println("Exception when calling ConvertWebApi#convertWebHtmlToPng"); e.printStackTrace(); } Once you complete this step, you’re all done–you can now call this API and easily render HTML strings as PNG screenshots. Note: your free-tier API key will supply a limit of 800 API calls per month with no commitments. Once you reach that limit, your total will simply reset the following month.
Today I want to write about why comments are so important in software development and how you can improve them. For me, the most disappointing thing about good source code is when it is insufficiently commented on. And this happens with closed code in the same way as with Open Source code. In the worst case, and this is usually the most common, the source code is not commented on at all. Especially in open-source projects, this is a disdain against the community reading your code. But even in internal software projects, I consider missing comments to be bad behavior towards your colleagues, which should be avoided. Of course, we all know the funny answers in this context. “Good code doesn’t need any comments” or “Take a look into the source code if you don’t understand something.” Is it laziness or ignorance not to write comments? No, I believe that many developers simply do not know how to write good comments. For this reason, I would like to explain here a very simple rule on how to really improve your source code through comments. Don’t Explain to Me the What and How! The biggest mistake you can make when writing comments is assuming you have to explain what you are doing. In this case, you write comments that explain the obvious. Java /** * Builder Class to build Events */ public class EventBuilder { .... } As long as the name of a class or method is not absolutely meaningless, a name should explain the usage of a class or method. So please do not explain that a Builder Class builds an object or a getter method returns an object. Writing such a comment is a waste of time. By the way, the same applies to those who have to read this kind of useless comment. The second example of bad comments is explaining how the code works. Java /** * Build the event and return it. */ public T build() { .... return event; } Of course, you shouldn’t assume that the users reading your code are complete idiots. You can assume that other developers can code just as well as you do. And explaining software patterns in the comment of a method or class is pointless. So now we have seen what bad comments are. But what does a good comment look like? Start With Why There is a great book from Simon Sinek with the Title “Start with Why: How Great Leaders Inspire Everyone to Take Action“. The core message of this book is, that you always should start by explaining the ‘Why’. The ‘What’ and ‘How’ are usually explaining things later in more detail. For software development, the ‘What’ and ‘How’ is indeed in the code and need no comments in many cases. So starting with the "why" can make source code comments much more useful – for yourself and for others. Starting with "why" makes you think about what you’re actually doing. So going back to my previous examples a comment may look like this: Java /** * When using Events in the client, the EventBuilder may help * to construct them in the correct way more easily. */ public class EventBuilder { .... } Instead of describing what the class is, answer the question of why you have invented it. The same is true for methods. As explained before if I want to understand the "how," then I can read the source code in detail. But why does the class or method exist? This is what a comment is for: Java /** * To display the summary of an invoice in the client, * this method calculates the summary including all taxes. */ public float summarizeInvoice() { .... return event; } I don’t want to go into more detail. It’s obvious that the better you explain why you have written a class or method, the more it helps others like yourself to understand your code. So when you start writing your next code, think about why you’re doing this and comment that into your class or method header. You’ll see – your colleagues and your community will love it and will much prefer using your code.
This article is an (adapted) excerpt from the book C# 11 and .NET 7 – Modern Cross-Platform Development Fundamentals, and takes you step-by-step through creating a console app to explore regular expressions via some cool new .NET 7 features: the [StringSyntax] attribute and source-generated regular expressions. Pattern Matching With Regular Expressions Regular expressions are useful for validating input from the user. They are very powerful and can get very complicated. Almost all programming languages have support for regular expressions and use a common set of special characters to define them. Let's try out some example regular expressions. Use your preferred code editor to add a new Console App/console project named WorkingWithRegularExpressions to your solution/workspace. In Program.cs, delete the existing statements, and then import the following namespace: using System.Text.RegularExpressions; // Regex Checking for Digits Entered as Text We will start by implementing the common example of validating number input. In Program.cs, add statements to prompt the user to enter their age and then check that it is valid using a regular expression that looks for a digit character, as shown in the following code: Write("Enter your age: "); string input = ReadLine()!; // null-forgiving Regex ageChecker = new(@"\d"); if (ageChecker.IsMatch(input)) { WriteLine("Thank you!"); } else { WriteLine($"This is not a valid age: {input}"); } Note the following about the code: The @ character switches off the ability to use escape characters in the string. Escape characters are prefixed with a backslash. For example, \t means a tab and \n means a new line. When writing regular expressions, we need to disable this feature. To paraphrase the television show The West Wing, "Let backslash be backslash." Once escape characters are disabled with @, then they can be interpreted by a regular expression. For example, \d means digit. Run the code, enter a whole number such as 34 for the age, and view the result, as shown in the following output: Enter your age: 34 Thank you! Run the code again, enter carrots, and view the result, as shown in the following output: Enter your age: carrots This is not a valid age: carrots Run the code again, enter bob30smith, and view the result, as shown in the following output: Enter your age: bob30smith Thank you! The regular expression we used is \d, which means one digit. However, it does not specify what can be entered before and after that one digit. This regular expression could be described in English as "Enter any characters you want as long as you enter at least one digit character." In regular expressions, you indicate the start of some input with the caret ^ symbol and the end of some input with the dollar $ symbol. Let's use these symbols to indicate that we expect nothing else between the start and end of the input except for a digit. Change the regular expression to ^\d$, as shown in the following code: Regex ageChecker = new(@"^\d$"); Run the code again and note that it rejects any input except a single digit. We want to allow one or more digits. To do this, we add a + after the \d expression to modify the meaning to one or more. Change the regular expression, as shown in the following code: Regex ageChecker = new(@"^\d+$"); Run the code again and note the regular expression only allows zero or positive whole numbers of any length. Regular Expression Performance Improvements The .NET types for working with regular expressions are used throughout the .NET platform and many of the apps built with it. As such, they have a significant impact on performance, but until now, they have not received much optimization attention from Microsoft. With .NET 5 and later, the System.Text.RegularExpressions namespace has rewritten internals to squeeze out maximum performance. Common regular expression benchmarks using methods like IsMatch are now five times faster. And the best thing is, you do not have to change your code to get the benefits! With .NET 7 and later, the IsMatch method of the Regex class now has an overload for a ReadOnlySpan<char> as its input, which gives even better performance. Splitting a Complex Comma-Separated String Let's consider how we would split a complex string, like the following example of film titles: "Monsters, Inc.","I, Tonya","Lock, Stock and Two Smoking Barrels" The string value uses double quotes around each film title. We can use these to identify whether we need to split on a comma (or not). The Split method is not powerful enough, so we can use a regular expression instead. You can read a fuller explanation in this Stack Overflow article that inspired this task. To include double quotes inside a string value, we prefix them with a backslash, or we could use the C# 11 raw string literal feature in C# 11 or later. Add statements to store a complex comma-separated string variable, and then split it in a dumb way using the Split method, as shown in the following code: // C# 1 to 10: Use escaped double-quote characters \" // string films = "\"Monsters, Inc.\",\"I, Tonya\",\"Lock, Stock and Two Smoking Barrels\""; // C# 11 or later: Use """ to start and end a raw string literal string films = """ "Monsters, Inc.","I, Tonya","Lock, Stock and Two Smoking Barrels" """; WriteLine($"Films to split: {films}"); string[] filmsDumb = films.Split(','); WriteLine("Splitting with string.Split method:"); foreach (string film in filmsDumb) { WriteLine(film); } Add statements to define a regular expression to split and write the film titles in a smart way, as shown in the following code: Regex csv = new( "(?:^|,)(?=[^\"]|(\")?)\"?((?(1)[^\"]*|[^,\"]*))\"?(?=,|$)"); MatchCollection filmsSmart = csv.Matches(films); WriteLine("Splitting with regular expression:"); foreach (Match film in filmsSmart) { WriteLine(film.Groups[2].Value); } In the last section, you will see how you can get a source generator to auto-generate XML comments for a regular expression to explain how it works. This is really useful for regular expressions that you might have copied from a website. Run the code and view the result, as shown in the following output: Splitting with string.Split method: "Monsters Inc." "I Tonya" "Lock Stock and Two Smoking Barrels" Splitting with regular expression: Monsters, Inc. I, Tonya Lock, Stock and Two Smoking Barrels Activating Regular Expression Syntax Coloring If you use Visual Studio 2022 as your code editor, then you probably noticed that when passing a string value to the Regex constructor, you see color syntax highlighting, as shown below: Regular expression color syntax highlighting when using the Regex constructor Why does this string get syntax coloring for regular expressions when most string values do not? Let's find out. Right-click in the new constructor, select Go To Implementation, and note the string parameter named pattern is decorated with an attribute named StringSyntax that has the string constant Regex value passed to it, as shown in the following code: public Regex([StringSyntax(StringSyntaxAttribute.Regex)] string pattern) : this(pattern, culture: null) { } Right-click in the StringSyntax attribute, select Go To Implementation, and note there are 12 recognized string syntax formats that you can choose from as well as Regex, as shown in the following partial code: [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)] public sealed class StringSyntaxAttribute : Attribute { public const string CompositeFormat = "CompositeFormat"; public const string DateOnlyFormat = "DateOnlyFormat"; public const string DateTimeFormat = "DateTimeFormat"; public const string EnumFormat = "EnumFormat"; public const string GuidFormat = "GuidFormat"; public const string Json = "Json"; public const string NumericFormat = "NumericFormat"; public const string Regex = "Regex"; public const string TimeOnlyFormat = "TimeOnlyFormat"; public const string TimeSpanFormat = "TimeSpanFormat"; public const string Uri = "Uri"; public const string Xml = "Xml"; … } In the WorkingWithRegularExpressions project, add a new class file named Program.Strings.cs, and modify its content to define some string constants, as shown in the following code: partial class Program { const string digitsOnlyText = @"^\d+$"; const string commaSeparatorText = "(?:^|,)(?=[^\"]|(\")?)\"?((?(1)[^\"]*|[^,\"]*))\"?(?=,|$)"; } Note that the two string constants do not have any color syntax highlighting yet. In Program.cs, replace the literal string with the string constant for the digits-only regular expression, as shown in the following code: Regex ageChecker = new(digitsOnlyText); In Program.cs, replace the literal string with the string constant for the comma separator regular expression, as shown in the following code: Regex csv = new(commaSeparatorText); Run the console app and confirm that the regular expression behavior is as before. In Program.Strings.cs, import the namespace for the [StringSyntax] attribute and then decorate both string constants with it, as shown in the following code: using System.Diagnostics.CodeAnalysis; // [StringSyntax] partial class Program { [StringSyntax(StringSyntaxAttribute.Regex)] const string digitsOnlyText = @"^\d+$"; [StringSyntax(StringSyntaxAttribute.Regex)] const string commaSeparatorText = "(?:^|,)(?=[^\"]|(\")?)\"?((?(1)[^\"]*|[^,\"]*))\"?(?=,|$)"; } In Program.Strings.cs, add another string constant for formatting a date, as shown in the following code: [StringSyntax(StringSyntaxAttribute.DateTimeFormat)] const string fullDateTime = ""; Click inside the empty string, type a letter d, and note the IntelliSense, as shown below: IntelliSense activated due to the StringSyntax attribute Finish entering the date format and as you type note the IntelliSense: dddd, d MMMM yyyy. Add at the end of the digitsOnlyText string literal, add a \, and note the IntelliSense to help you write a valid regular expression, as shown below: IntelliSense for writing a regular expression The [StringSyntax] attribute is a new feature introduced in .NET 7. It is up to your code editor to recognize it. .NET 7 libraries have more than 350 parameters, properties, and fields that are now decorated with this attribute. Improving Regular Expression Performance With Source Generators When you pass a string literal or string constant to the constructor of Regex, the class parses the string and transforms it into an internal tree structure that represents the expression in an optimized way that can be executed efficiently by a regular expression interpreter. You can also compile regular expressions by specifying a RegexOption, as shown in the following code: Regex ageChecker = new(digitsOnlyText, RegexOptions.Compiled); Unfortunately, compiling has the negative effect of slowing down the initial creation of the regular expression. After creating the tree structure that would then be executed by the interpreter, the compiler then has to convert the tree into IL code, and then that IL code needs to be JIT compiled into native code. If you only run the regular expression a few times, it is not worth compiling it, which is why it is not the default behavior. .NET 7 introduces a source generator for regular expressions which recognizes if you decorate a partial method that returns Regex with the [GeneratedRegex] attribute. It generates an implementation of that method which implements the logic for the regular expression. Let's see it in action. In the WorkingWithRegularExpressions project, add a new class file named Program.Regexs.cs, and modify its content to define some partial methods, as shown in the following code: using System.Text.RegularExpressions; // [GeneratedRegex] partial class Program { [GeneratedRegex(digitsOnlyText, RegexOptions.IgnoreCase)] private static partial Regex DigitsOnly(); [GeneratedRegex(commaSeparatorText, RegexOptions.IgnoreCase)] private static partial Regex CommaSeparator(); } In Program.cs, replace the new constructor with a call to the partial method that returns the digits-only regular expression, as shown in the following code: Regex ageChecker = DigitsOnly(); In Program.cs, replace the new constructor with a call to the partial method that returns the comma separator regular expression, as shown in the following code: Regex csv = CommaSeparator(); Hover your mouse pointer over the partial methods and note that the tooltip describes the behavior of the regular expression, as shown below: Tooltip for a partial method shows a description of the regular expression Right-click the DigitsOnly partial method, select Go To Definition, and note that you can review the implementation of the auto-generated partial methods, as shown below: The auto-generated source code for the regular expression Run the console app and confirm that the functionality is the same as before. You can learn more about the improvements to regular expressions with .NET 7.
CSS has a lot of different units that you can choose from. In many cases, there is one unit that’s clearly better than any others. However, one question that seems to come up throughout my career is whether you should use pixels or rems on certain properties where it doesn’t seem to make a clear difference. Today I’m going to answer that question. The Setup Let’s say we have a very basic HTML page with two paragraphs: one sized with pixels and the other with rems: HTML <!DOCTYPE html> <html> <body> <h1>PX vs. Rem</h1> <p style="font-size: 24px;">My font size is 24px</p> <p style="font-size: 1.5rem;">My font-size is 1.5rem</p> </body> </html> Pixels are an absolute unit, so when you set the font size to 24 pixels, it’s going to be 24 pixels. Rems, on the other hand, are relative units that are based on the document’s font size. The document’s default font size is 16 pixels, so 1.5rems x 16px is the equivalent of 24 pixels. The fact that rems are relative and they’re based on a default of 16 pixels makes them harder to work with than pixels. This is why, more often than not, I see folks using pixels. But I think it’s a mistake. The Not-So-Real Problem In the past, there was some confusion around how retina or high-density displays affected pixels and pixel density. Without getting too far into it, there was a moment when pixels scaled weirdly, but that issue has since been fixed. And since that has been fixed, an argument I keep seeing is that users can scale the font size up and down by zooming the browser in or out, and both the pixel and rem sizes grow at the same rate. As a result, some folks say that it no longer matters whether you use pixels or rems. But it does! The Real-Real Problem This issue actually relates to how CSS units respond to browser settings. Users can go to their browser settings and modify the browser’s default font size. This is great for users that have a visual impairment. However, if you check the browser, the paragraph with the absolute unit of 24 pixels will remain at 24 pixels the same regardless of what the user’s preference is. The relative unit, on the other hand, grows with the default settings of the browser. So this is really an accessibility concern. Therefore, any time you’re defining font sizes, you should absolutely (or relatively?) use rems. The Nuance Of course, because we’re talking web development, the discussion is nuanced. For example, what about other properties like padding and border? HTML <!DOCTYPE html> <html> <body> <h1>PX vs. Rem</h1> <p style=" font-size: 24px; padding: 16px; border: 2px solid; background: tomato; "> My font size is 24px </p> <p style=" font-size: 1.5rem; padding: 1rem; border: .125rem solid; background: powderblue; "> My font-size is 1.5rem </p> </body> </html> In this example, we’ll compare pixels vs. rems for padding and border width in addition to font size. As you might expect, the second paragraph has larger padding and border width in addition to the larger font size because rem units scale up with the browser settings. And this brings us to the “it depends” of the conversation. Padding and border width are not really strictly related to accessibility. So do you want your padding or border width or any other property that could otherwise be pixels to be set with rems and scale up if the user changes their settings? That’s up to you ¯\_(ツ)_/¯ It gets even more confusing when you introduce media queries or container queries. It’s very complicated to explain, but basically, if your fonts scale up with the browser settings, you probably also want your media queries to respond in kind. This means you should define them with REM (or EM) units. Of course, there are a lot of factors that play in there and I don’t want to get too lost in the weeds. Fortunately, there was an article in 2016 that basically tackled that question called “PX, EM or REM media queries” by Zell Liew. It basically concluded by saying that EM units and REM units are going to be better for media queries than pixel units and because EM units are based on their parent element, but media queries affect the root styles, both EM and REM are pretty much the same. The only difference was due to a bug in Safari with REM units, therefore EM was a little bit more consistent than, but I think that bug has been fixed. So I would just stick to using REM. The Short(ish) Answer So to answer the question, once and for all, whether you should use pixels or rems, the short answer is… Just use rems! Of course, there’s nuance. Of course, there are exceptions. Of course, there will be cases in which pixels actually are better. (You really should test your application with various browser settings to make those calls). BUT Rems are more accessible for font size. Rems are (probably) better for media queries. And I think it’s easier to just stick to one simple rule (with occasional exceptions) than it is to mix things. At the end of the day, it all depends on how things should scale based on the user’s browser settings, so it may not matter all that much to you. But it makes sense to me that everything scales up or down at the same rate. If you feel different or you would like to contribute to the conversation, I would love to hear your thoughts. Thank you so much for reading. If you liked this article, please share it. It's one of the best ways to support me. You can follow me on Twitter if you want to know when new articles are published.
Geolocation, a technology is allowing you to find out where your visitor’s location by using an IP address, is a ubiquitous solution that has been used in many industries. For example, if you are using Google Analytics or Matomo to track your visitors, you are using the geolocation solution. If you embed a cool widget to automatically fill the country or region information, you are using the geolocation solution. To name a few. There are many reasons to implement geolocation, such as to better serve your audiences, to tailor make a better browsing experience, to monitor the buying pattern, to combat fraud, and so on. In this article, I’m going to show you how to implement the geolocation features in an Apache server and touch base on some usage scenarios. Here, I will use the IP2Location geolocation library for Apache, PHP programming language, and Debian OS. The Web Server Modules Web server is the software to handle the HTTP/HTTPS requests so that the visitor can view and access your websites. This would be the first layer of processing before the request is sent over to your PHP program for further processing. This is also a logically good place to place your control or logic, such as to redirect, block, or deliver a response back to the caller. For example, to block the users coming from a specific country that you do not have the business present or services or to redirect the Korean viewers to your Korean-translated pages. Prerequisite In this tutorial, I will use Apache 2.4.54 in Debian 11 for the explanation. And I will assume you have this environment set up to follow the below sequences. Also, please make sure you have the unzip and wget packages installed. The Apache In order to use the geolocation feature in the Apache environment, you will need to build and install the module into the Apache server. You will need to install the development tools, which are different from your standard Apache installation, to build the module. You can run the below command to set up the development environment. Shell sudo apt install apache2-dev The IP2Location Apache Module The IP2Location Apache module is built using IP2Location C Library. Before you can build the module, you will need to download and install the C library. I will create a folder called ip2location for this building process. Shell mkdir ip2location && cd ip2location Inside the ip2location folder, we will download the IP2Location C library by running the below command. Shell wget https://github.com/chrislim2888/IP2Location-C-Library/archive/master.zip unzip master.zip && rm master.zip Besides we will also download the IP2Location Apache module by running the below command: Shell wget https://github.com/ip2location/ip2location-apache/archive/refs/heads/master.zip unzip master.zip && rm master.zip You should see both the IP2Location-C-Library-master and ip2location-apache-master inside the ip2location folder. Next, we are going to build and install the IP2Location C library. Execute the below commands: Shell cd IP2Locatoin-C-Library-master autorecof -i -v --force ./configure make make install You should see the below screenshot if the commands are running successfully. Please execute the ldconfig command to reload the shared library in Debian. Then, go into the ip2location-apache-master folder. Shell cd ../ip2location-apache-master/ Build the IP2Location Apache module by running the below command: Shell apxs2 -i -a -L ../IP2Location-C-Library-master/libIP2Location/ -I ../IP2Location-C-Library-master/libIP2Location/ -l IP2Location -c mod_ip2location.c You should see the below screen if the module was built successfully. Restart the Apache server to activate the module. Shell service apache2 restart Then, run the following command to confirm if the module has been enabled inside Apache Server. Shell apache2ctl -M If the above procedures were done correctly, you should see the IP2Location_module (shared) entry as shown in the following screenshot. Now, we have the module ready. Let’s explore how to use the module. Redirect Users to a Specific URL You can redirect the users to a specific URL at the Apache server before the request routes to your PHP page. Why do we want to do so? It’s because this approach could give you a faster response time and low CPU usage, as the request does not need to be sent to PHP for extra processing. In this example, I will show you how to do so using the .htaccess file. First of all, you must enable the rewrite module that allows you to perform the rewrite operation to the incoming URL. Run the following command to enable the rewrite module (It comes with Apache server installation. Therefore, no additional installation would be necessary.) Shell a2enmod rewrite Then, restart your Apache Server to activate the changes. Once restart, run the below commands to make sure the rewrite module has been enabled. Shell apachectl -M Then, we need to add the IP2Location Apache configuration into the apache2.conf file. You can locate the file at the /etc/apache2 folder. Please add the following lines into the apache2.conf file. Plain Text <IfModule mod_ip2location.c> IP2LocationEnable ON IP2LocationDetectProxy OFF IP2LocationSetmode ALL IP2LocationDBFile \var\www\html\DB1.BIN </IfModule> You may refer to the information in GitHub to learn more about the settings. In this tutorial, you just need to paste the above and make sure the IP2LocationDBFile was pointed to where you downloaded the BIN data file. You may download it from lite if you do not have one. Inside the apache2.conf file, please ensure that the AllowOverride is set to All to allow the rewrite action. The settings inside the .htacess file: Plain Text <Directory /var/www/> Options Indexes FollowSymLinks AllowOverride All Require all denied </Directory> You can now go to /var/www/HTML (the root directory of your domain) and create the .htaccess file and enter the following: Plain Text RewriteEngine On RewriteCond %{ENV:IP2LOCATION_COUNTRY_SHORT} ^US$ RewriteRule ^(.*)$ https://www.google.com [L] The above will redirect the traffic coming from the United States to Google. Instead, if you would like to block the visitors by returning a 404 page, you can do so as the following: Plain Text RewriteEngine On RewriteCond %{ENV:IP2LOCATION_COUNTRY_SHORT} ^US$ RewriteRule ^(.*)$ - [R=404] The [R=404] flag will instruct the web server to return the default 404 status back to the caller. How to Use the Geolocation in PHP Once you have the Apache module installed, you do not need to explicitly import a third party geolocation library. The geolocation information is automatically made available via the environment variables and ready to use anywhere in your PHP project. A simple code below will return the country short name, region name, and city name of the visitor’s IP address. Please note that you have to use DB3 BIN data in order to retrieve the region and city information. PHP <?php echo $_SERVER[IP2LOCATION_COUNTRY_SHORT]; echo $_SERVER[IP2LOCATION_REGION]; echo $_SERVER[IP2LOCATION_CITY]; ?> Conclusion: Pros and Cons Every approach has its pros and cons, and it’s just a matter of what fits your environment the best. The above approach provides you with an easy and faster way to retrieve the geolocation that you could use throughout all your PHP projects hosted under the same Apache server. One setup for all. Also, it provides you with a faster and more efficient way to perform redirection/blocking at the web server level. However, the main concern I can think of is the BIN file update. As you are aware that the BIN path was configured inside the apache2.conf and will only be loaded during the Apache loading process. Therefore, during the regular BIN data update, usually once a month, you have no choice but to restart the entire Apache server for the new BIN data to take effect. Having said that, if you are not running a mission-critical website, and are okay with the restart activity, then that would be no problem at all. Happy coding.
The Salesforce development platform offers many powerful features to provide your team with apps that can unlock new workflows. For many years, the platform has run on a trifecta of technologies: Visualforce (to view the data), Apex (to handle the data), and Salesforce itself (to store the data). Apex is like many other object-oriented languages, and it has a Java-like syntax. However, Apex runs directly on Salesforce servers, allowing developers to build and deploy an app without worrying about where to host it or how to secure their data. The Challenge of Apex and Long-Running Commands Applications built on Salesforce run on its multitenant architecture, so server resources like CPU and memory are shared among different organizations. With shared resources, however, wouldn’t it be possible for the code for one org’s app to consume so much processor time and memory space that it affects other users in that same org? An example of this could be a command that takes several minutes to complete. Salesforce prevents situations like this from occurring by restricting how apps on its platform run. If any app has the potential to interfere with other users, it is restricted from completing its command execution. Long-running programs can potentially interrupt other work by hogging the limited resources available. If so, how might Salesforce developers build apps that execute long-running commands which can scale with user growth while not impacting performance? Salesforce Functions to the Rescue Enter Salesforce Functions, which are small programs written in JavaScript, TypeScript, or Java and deployed onto Salesforce’s servers. Functions can perform the resource-intensive work and be invoked directly from Apex. When the Salesforce Function completes the request, it can send its results back to the originating Apex application, which then performs any additional tasks on those results. Salesforce Functions run in their own container and don’t affect other tenants on the Salesforce platform. The memory and CPU limits are much higher, allowing you to execute longer running and more complex tasks. Because Salesforce Functions run on the Salesforce platform, security is baked in; there’s no risk of privacy loss as everything runs within the same trust boundary. In this article, we’ll take a closer look at how to migrate a long-running Apex operation into a Salesforce Function, along with some general tips around the Salesforce DX suite. Prerequisites This article will require you to have some understanding of both Apex and TypeScript, but don’t worry if you’re not an expert. We’ll go through every piece of code to explain what is happening. Although the completed version of our code is available as a GitHub repository, you may also want to download some of the CLI tools which Salesforce provides to follow along (or for your own future projects). First, install the sfdx CLI, which is a tool that helps simplify common operations for Salesforce developers when building applications. It’s important that whatever you build and deploy doesn’t affect your “real” production Salesforce organization. So, create a separate scratch org that this app can interact with. To get your own scratch org, sign up for a free Developer Edition account. To use Salesforce Functions, you’ll need to register for a Salesforce Functions trial by contacting your Salesforce account executive. Finally, you can also install the Salesforce VS Code extension to make deploying your code a little bit easier. This step is optional. Touring the Existing Code Open up the file at force-app/main/default/classes/Dogshow.cls and take a look at its contents: public class Dogshow { public static void updateAccounts() { // Get the 5 oldest accounts Account[] accounts = [SELECT Id, Description FROM Account ORDER BY CreatedDate ASC LIMIT 5]; // Set HTTP request HttpRequest req = new HttpRequest(); req.setEndpoint('https://dog.ceo/api/breeds/image/random'); req.setMethod('GET'); // Create a new HTTP object to send the request object Http http = new Http(); HTTPResponse res = http.send(req); String body = res.getBody(); Map<String, String> m = (Map<String, String>) JSON.deserialize(jsonStr, Map<String, String>.class); String dogUrl = m.get('message'); // loop through accounts and update the Description field for (Account acct : oldAccounts) { acct.Description += ' And their favorite dog can be found at ' + dogUrl; } // save the change you made update accounts; } } These 30 lines perform a fairly simple function: It grabs the five oldest Salesforce accounts using SOQL. It issues an HTTP request to https://dog.ceo/dog-api/, which is a site that returns a JSON object with a random URL link to a very cute dog. The Apex code then updates the description for those five accounts to indicate that that dog is their favorite. This code is fairly innocuous, but it introduces two situations that present some real problems: When issuing an HTTP request, several factors can affect your program. For example, the server you issue requests to could be overloaded and slow to respond. While your own code may be performant, you’re at the mercy of anything outside of your control, such as an external data source. Although we only iterate on five accounts here, what if we built an app that needed to iterate over 500 or 5,000? Looping through them one by one would be a slow, albeit unavoidable, process. This is an opportunity for a Salesforce Function to take over some of this work. Our Apex class can provide a list of accounts to update to a Salesforce Function. That Salesforce Function can then issue HTTP requests and update the accounts. Before seeing what that full migration might look like, let’s first write that Salesforce Function. Developing the Salesforce Function Using the sfdx CLI, we can easily create a new Salesforce Function in TypeScript using a single command: $ sf generate function -n dogshowfunction -l typescript This creates a set of files under functions/dogshowfunction. The most important of these is index.ts, which is where our main Salesforce Function code will reside. However, the other files are also important, dealing with testing and linting code, as well as defining TypeScript generation and the Salesforce deployment process. Let’s focus on index.ts. In this file, you’ll note that there’s one function exported, and it takes three parameters: event: This describes the payload of data that is coming in from your Apex code. context: This contains the authorization logic necessary to communicate with Salesforce. logger: This is a simple logger that also integrates with Salesforce. The template code which the CLI generates shows how powerful Salesforce Functions are: They can accept any JSON data sent over from Apex. They can work directly with Salesforce data. They integrate directly with the platform in such a way as to handle all the authentication and security for you. Best of all, since this particular function runs on Node.js, you can install and use any NPM package to supplement your code. Let’s do that right now by installing node-fetch to issue our HTTP request: $ npm i node-fetch Our Salesforce Function will be responsible for issuing our HTTP requests and updating our five accounts. To implement that functionality, our function might look something like this: export default async function execute(event: InvocationEvent<any>, context: Context, logger: Logger): Promise<RecordQueryResult> { const accounts = event.data.accounts; accounts.forEach(async (account) => { const response = await fetch('https://dog.ceo/api/breeds/image/random'); const data = await response.json(); const message = ` And their favorite dog is ${data.message}` const recordForUpdate = { type: 'Account', fields: { id: account.id, Description: `${account.Description} ${message}` } } context.org.dataApi.updateRecord(recordForUpdate); }); } Let’s break down what’s going on in the code snippet above. Our event argument is essentially a JSON object which we will define in our Apex code. Although this JSON object doesn’t exist yet, we can assume that it will have the id and Description of the account we want to update, based on the behavior of our previous Apex code. From here, it’s important to note that the context argument is essentially an instantiation of the Node.js SDK for Salesforce Functions. Since we can assume that this account data is provided as an array, we can simply iterate through each item, plugging in the record data as part of our update command. Migrate the Apex Code With our Salesforce Function defined, we can now replace the previous Apex logic with a call to this new function. Issuing a call to a Salesforce Function is surprisingly simple: We only need to know the name of our function and provide the data we want to send it, as shown below: public class Dogshow { public static void updateAccounts() { // Get the 5 oldest accounts Account[] accounts = [SELECT Id, Description FROM Account ORDER BY CreatedDate ASC LIMIT 5]; // Set up the function functions.Function dogshowFunction = functions.Function.get('ApexToSalesforceFunctionsMigration.dogshowfunction'); // Create a list to hold the record data List<Map<String, String>> jsonObj = new List<Map<String, String>>(); for (Account account : accounts) { Map<String, Object> obj = new Map<String, Object>(); obj.put('id', account.Id); obj.put('Description', account.Description); // Add an object to the record list jsonObj.add(obj); } // Send the record list to the function functions.FunctionInvocation invocation = dogshowFunction.invoke(JSON.Serialize(jsonObj)); // if the function had a return value, it would be available here invocation.getResponse(); } } Just like that, we’ve taken the long-running operation out of our Apex code and offloaded it to a Salesforce Function. Now, we can be certain that our operation will continue to run even though it uses resources more heavily. The implications of Salesforce Functions cannot be overstated. With Salesforce Functions handling our need for time-consuming, resource-heavy operations, apps built on the Salesforce platform now have much more potential for scaling up seamlessly. Conclusion Salesforce Functions provide a way to improve workflows while building effective and durable projects. Without the memory and CPU limits you would experience in Apex, you can leverage Salesforce Functions for the execution of long-running commands, bringing scalability without a negative impact on performance.
Since the introduction of smart contract technology, Solidity has been the coding language of choice for smart contract developers. However, if you’re a Solidity developer, you already know it has drawbacks. Among other flaws, major security concerns can arise from the simple mishandling of certain data types, and there are no built-in access controls. A new smart contract language developed for the Flow blockchain, Cadence, learns from Solidity’s oversights and natively solves many of its inherent problems. And if you already know Solidity, it’s straightforward to learn! This article introduces the Cadence smart contract language, details how it improves over Solidity, and then walks through a side-by-side comparison of both languages on some common smart contract examples. By the end, you should be comfortable with Cadence and ready to get started on Flow! About Cadence Cadence is the programming language that powers the Flow blockchain, which was originally created by Dapper Labs to support large-scale crypto games and NFT projects. (You’ve probably heard of NBA Top Shots—one of their most successful projects.) Its syntax might be familiar, since it was inspired by popular modern languages such as Swift, Rust, and Kotlin. Cadence is statically and strongly typed, with a resource-oriented approach and capability-based security model. All of this means that Cadence is highly optimized for digital asset creation and management. The most substantial innovation Cadence introduces is its resource-based paradigm. This approach makes it significantly easier to create, track, and manage a user’s assets. Instead of relying on a central public ledger as the source of truth (as Ethereum does), assets are tied directly to a user’s account storage. So an asset created as a resource, such as an NFT, can only exist in one location at a time. This ensures they only have one owner—either an externally owned account or a smart contract. Resources are Linear Types with controlled creation and runtime support How Cadence Improves Over Solidity Cadence improves over Solidity in many ways. Let’s look at three examples—small coding mistakes, security and access control, and contract deployment. Those small coding mistakes Some of the biggest issues with Solidity usually stem from the smallest mistakes. For example, initializing a variable with just a default value—although convenient at times—can lead to unexpected results if that variable is not changed. And fixed-range data types can lead to potential underflow or overflow situations, which would be disastrous if that data type represents a monetary value. In Cadence, variable values must be set upon initialization, removing any unwanted results from default values. Additionally, integers in Cadence are automatically checked for underflow or overflow conditions, whereas you would need to inherit OpenZeppelin’s safe math library or use a version higher than 0.8 with Solidity. Security and access control When it comes to security and access control, Solidity requires you to create custom modifiers or inherit other security-based smart contracts, but also has many functions that are public by default. With Cadence’s capability-based security model, accounts can only perform functions they have access to. This means Cadence has access control fundamentally built into the language itself. Also, methods defined on resource objects in Cadence cannot be susceptible to reentrancy attacks, something Solidity developers have to be keenly aware of when creating the flow of their logic. Smart contract deployment When issues in Solidity smart contracts are found, developers are unable to fix them without deploying an entirely new contract. And even then, the vulnerable contract still exists. Developers must ensure their user base switches over to the new contract. In Cadence, smart contract upgradeability is built in and transparent. When the code is declared secure and final, the contract can be made immutable by removing the keys from the owner of the smart contract. Overall, Cadence is a safer and more secure smart contract language that leaves less room for error. Now let’s look in detail at the differences between smart contracts written in Solidity and Cadence. We’ll walk through a simple Hello World contract and then a more complicated NFT implementation. Simple Contract Let’s start with an all-time classic. We’ve all written “Hello World” in multiple languages, so it’s an easy intro to Cadence. Let’s go through it step by step. Contract Definition First, we have the contract definition. The obvious difference is that the Cadence contract has an access control modifier: in this case, pub. This modifier ensures that everyone in the Flow network can access the contract, the default behavior for Solidity contracts. However, in Cadence, we could also set the access control to access(account). This limits contract access to the account that deployed that contract. Here we already see a major difference between Flow and Ethereum. We don’t simply deploy contracts to the Flow blockchain; we deploy them to our account storage. On the Flow blockchain, each account is initialized with storage, where resources and structures can be stored. This storage has its own permissions, which allows us fine-grained control over who can execute the methods of our contract. Contract Variables The next line defines a string variable scoped to our contract. Semicolons are optional in Cadence, and a let keyword is used to define the variable. Cadence has two types of variables—mutable and immutable. Variables created with let are immutable, or otherwise known as constants; we can only set them once, and they cannot be changed for the lifetime of the contract. We define mutable variables (ones that can be changed) with the var keyword. In this case, we set the variable value in the init method, because Cadence ensures that this method is only called once for every contract deployment. Methods The Cadence equivalent to Solidity’s constructor is the init method. This method is called exactly once—at the time a contract is deployed. Inside the init method, we set the value for our greeting variable. While Solidity writes to contract variables by default, Cadence writes to local variables and requires you to use the self-object to access contract variables. This decision protects against you accidentally writing to a contract variable when making a typo. The second method of our contract returns the greeting variable. In both Cadence and Solidity, we have to declare access for the method to be public, and both languages require us to define the return type. In this case, it’s a string. But Solidity requires us to be more low-level here. It requires us to explicitly tell it where the string is located. It also makes us mark the functions as view, so that we don’t accidentally modify the state of the blockchain. Cadence, on the other hand, doesn’t need that low-level control, since it is strongly and statically typed. Potential errors are caught before the program is run on-chain, making the whole method declaration more readable by removing redundant keywords. NFT Contract Next, let’s look at a basic NFT contract from both languages: Since both languages have different approaches to this example, let’s look at them separately: first walking through the Solidity example, then Cadence. Solidity In Solidity, an NFT is basically a list of IDs. You must keep track of these IDs inside the smart contract and increment them manually to ensure uniqueness. Solidity doesn’t know anything about NFTs or their uniqueness. It’s just a list of IDs mapped to their owners, all managed manually within the contract. This leaves room for error if the ID incrementation is improperly handled, potentially resulting in multiple NFTs having identical IDs. In the example, the NFT doesn’t have any additional data attached, but you could add another mapping ID to a URI. The contract ensures every newly minted NFT is mapped to an owner's address. This is a simple example, of course. Usually, you would need to extend several interfaces to get a remotely secure NFT contract and features like metadata used to attach the well-known JPGs to your NFT, but the basic mechanisms are the same. Cadence Now, let’s look at the Cadence version and how it improves upon this Solidity example. Resource Definition The Cadence example starts with a resource type called NFT. Notice the @ symbol in front of NFT? This symbol is required, as it ensures the usage and behavior of resource types will remain explicit. We can create instances from a resource, and it can have attributes just like a struct. The difference from a regular struct is that a resource is a special type that handles ownership in addition to the data it stores. Inside the resource type NFT, there is an id field. The id field is an integer, UInt64, and it is a unique id given to each NFT resource. This id will be different for each NFT resource, meaning the resource cannot be duplicated or combined. Next, the id field is initialized using the init function. Similar to how Rust’s borrow checker ensures only one function can modify a variable, Cadence ensures the same for its resources. Resource Creation When we create a new resource, we have to move it around storage locations. Our smart contract will not run properly if we leave the resource as is, so this forces us to be deliberate with resources and their storage locations. This form of control also ensures that resources never get lost or accidentally deleted; they can only be in one location at a time. When we call the mint function, it will create a new instance of our NFT resource. This function returns a resource with a type of NFT and takes in the field, id, from the resource that was defined earlier. The create keyword is a bit like the new operator in object-oriented programming, creating a new resource. The <-, or move-operator, makes it explicit that this resource isn’t available at the source after we called it. Storing the Resource The self.account variable will point to the account we used as a deployment target for our contract. As we learned before: smart contracts aren’t deployed in a global namespace on the Flow network, but in special on-chain storage that belongs to your account. So, the smart contract knows which account it’s been deployed to and can use that information in its methods. In this example, we use the account’s save method. In the final init function, we move the resource into the save methods first argument and tell it which path inside the account should store our NFT. Since our NFT is a resource, no entity on the Flow network can copy it; we don’t have to keep track of its uniqueness explicitly. Summary Cadence is a fresh take on smart contract programming languages, optimized for asset creation and management. It’s a modern alternative that mitigates the deficiencies of Solidity through such means as forced management of variables and resources, security and access controls at a foundational level, and the ability to upgrade smart contracts before making them immutable. Cadence opens you to the possibilities of the Flow ecosystem, and incorporates many features from languages like Rust. So, if you are a developer looking to write smart contracts in a fundamentally safer and more secure language, Cadence is an excellent option. To learn more, check out the Cadence Documentation and Flow Developer Portal. Have a really great day!
Javin Paul
Lead Developer,
infotech
Reza Rahman
Principal Program Manager, Java on Azure,
Microsoft
Kai Wähner
Technology Evangelist,
Confluent
Alvin Lee
Founder,
Out of the Box Development, LLC