diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index fddac5c7..56801429 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -5,8 +5,8 @@ Make sure that: --> -- [ ] You have read the [Spring Data contribution guidelines](https://github.com/spring-projects/spring-data-build/blob/master/CONTRIBUTING.adoc). -- [ ] You use the code formatters provided [here](https://github.com/spring-projects/spring-data-build/tree/master/etc/ide) and have them applied to your changes. Don’t submit any formatting related changes. +- [ ] You have read the [Spring Data contribution guidelines](https://github.com/spring-projects/spring-data-build/blob/main/CONTRIBUTING.adoc). +- [ ] You use the code formatters provided [here](https://github.com/spring-projects/spring-data-build/tree/main/etc/ide) and have them applied to your changes. Don’t submit any formatting related changes. - [ ] You submit test cases (unit or integration tests) that back your changes. - [ ] You added yourself as author in the headers of the classes you touched. Amend the date range in the Apache license header if needed. For new types, add the license header (copy from another file and set the current year only). diff --git a/mongodb/text-search/src/test/resources/spring-blog.atom.json b/mongodb/text-search/src/test/resources/spring-blog.atom.json index 6c4c2674..fddc6b8e 100644 --- a/mongodb/text-search/src/test/resources/spring-blog.atom.json +++ b/mongodb/text-search/src/test/resources/spring-blog.atom.json @@ -53,7 +53,7 @@ "_id" : "tag:spring.io,2014-08-20:1741", "_class" : "example.springdata.mongodb.textsearch.BlogPost", "title" : "Building a RESTful quotation service with Spring", - "content" : "
I was recently made aware that a public API we were using for one of our guides contained objectionable material. After confirming this, I immediately responded that we would pick another source. Wishing to avoid such an issue in the future, I decided the best solution was to build our own RESTful quote service. So I decided to use the best tools to do so, the Spring stack, and was able to migrate the very next day.
\n\nTo kick things off, I made a check list of what I knew would be the right tools for the job of creating a RESTful web service.
\n\nI quickly set aside the desire to add, delete, manage, or view the data through a web page. Instead, my focus was to serve up a fixed set of content with the exact same structure that the guide expected to consume.
\n\nThe original content for the guide was a series of \"Chunk Norris\" jokes. I like a good laugh. But when I revisited the public API, I saw that several of the jokes were a bit rancid. After a brief discussion with colleagues, the idea came up to cite historical quotes. I took that idea and bent it a little. I had recently collected quotes from various developers about Spring Boot for personal reasons, so I decided to use that as the curated content.
\n\nTo kick things off, I visited http://start.spring.io. This Spring Boot app lets you enter the details of your new project, pick the Java level, and select the Spring Boot starters you need. I used my checklist up above and created a new gradle-based project.
\n\nWith the project unpacked and imported into my IDE, the first thing I did was copy the domain objects shown in the Reactor guide. This way, I could ensure that the data sent out by my REST service was correct. Since the POJOs in my Quoters Incorporated app are almost identical, I won't post them here.
\n\nThen I created a Spring Data repository.
\n\npublic interface QuoteRepository extends CrudRepository<Quote, Long> {}\nThis empty interface definition handles Quote objects with an internal primary key of type Long. By extending the Spring Data Commons CrudRepository, it inherits a fistful of database operations we'll use later on.
Next step? Initialize some data. I created a DatabaseLoader like this:
@Service\npublic class DatabaseLoader {\n\n private final QuoteRepository repository;\n\n @Autowired\n public DatabaseLoader(QuoteRepository repository) {\n this.repository = repository;\n }\n\n @PostConstruct\n void init() {\n repository.save(new Quote(\"Working with Spring Boot is like pair-programming with the Spring developers.\"));\n // more quotes...\n }\n\n}\n@Service so it will be automatically picked up by @ComponentScan when the app starts.QuoteRepository is made available.@PostConstruct tells Spring MVC to run the data loading method after all beans have been created.init() method uses Spring Data JPA to create a whole slew of quotations.Because I have H2 as my database of choice (com.h2database:h2) in build.gradle, there is no database set up at all (thanks to Spring Boot).
After I built this database layer, I went on to create the APIs. With Spring MVC, it wasn't hard at all.
\n\n@RestController\npublic class QuoteController {\n\n private final QuoteRepository repository;\n\n private final static Quote NONE = new Quote(\"None\");\n\n private final static Random RANDOMIZER = new Random();\n\n @Autowired\n public QuoteController(QuoteRepository repository) {\n this.repository = repository;\n }\n\n @RequestMapping(value = \"/api\", method = RequestMethod.GET)\n public List<QuoteResource> getAll() {\n return StreamSupport.stream(repository.findAll().spliterator(), false)\n .map(q -> new QuoteResource(q, \"success\"))\n .collect(Collectors.toList());\n }\n\n @RequestMapping(value = \"/api/{id}\", method = RequestMethod.GET)\n public QuoteResource getOne(@PathVariable Long id) {\n if (repository.exists(id)) {\n return new QuoteResource(repository.findOne(id), \"success\");\n } else {\n return new QuoteResource(NONE, \"Quote \" + id + \" does not exist\");\n }\n }\n\n @RequestMapping(value = \"/api/random\", method = RequestMethod.GET)\n public QuoteResource getRandomOne() {\n return getOne(nextLong(1, repository.count() + 1));\n }\n\n private long nextLong(long lowerRange, long upperRange) {\n return (long)(RANDOMIZER.nextDouble() * (upperRange - lowerRange)) + lowerRange;\n }\n\n}\nLet's break it down:
\n\n@RestController. This means all routes return objects not views.NONE quote and a Java 8 Random for randomly picking quotes.QuoteRepository.| API | \nDescription | \n
|---|---|
| /api | \nFetch ALL quotes | \n
| /api/{id} | \nFetch quote id\n | \n
| /api/random | \nFetch a random quote | \n
To fetch ALL quotes, I use a Java 8 stream to wrap the Spring data's findAll() and in turn, wrap each one into a QuoteResource. The results are turned into a List.
To fetch a single quote, it first tests if a given id exists. If not, return NONE. Otherwise, return a wrapped quote.
Finally, to fetch a random quote, I use Java 8's Random utility inside the nextLong() utility method to fetch a Long with the lowerRange and upperRange, inclusively.
\n\n\nQUESTION: Why am I using
\nQuoteResource?Quoteis the core domain object returned by theQuoteRepository. To match the previous public API, I wrap each instance in aQuoteResourcewhich includes a status code.
With this in place, the default Application class created by http://start.spring.io was ready to run.
$ curl localhost:8080/api/random\n{\n type: \"success\",\n value: {\n id: 1,\n quote: \"Working with Spring Boot is like pair-programming with the Spring developers.\"\n }\n}\n\n\nTa dah!
\n\nTo wrap things up, I built the JAR file and pushed it up to Pivotal Web Services. You can view the site yourself at http://gturnquist-quoters.cfapps.io/api/random.
\n\nSuffice it to say, I was able to tweak the Reactor guide by altering ONE LINE OF CODE. With that in place, I did some other clean up of the content and was done!
\n\nTo see the code, please visit https://github.com/gregturn/quoters.
\n\nThese are some outstanding things that didn't fit inside the time budget and weren't required to solve the original problem involving the Reactor guide. But they are good exercises you can explore! You can clone the project in github and take a shot at it yourself!
\n\nBook your place at SpringOne in Dallas, TX for Sept 8-11 soon. It's simply the best opportunity to find out first hand all that's going on and to provide direct feedback. You can see myself and Roy Clarkson talk about Spring Data REST - Data Meets Hypermedia to see how to merge Spring Data and RESTful services.
", + "content" : "I was recently made aware that a public API we were using for one of our guides contained objectionable material. After confirming this, I immediately responded that we would pick another source. Wishing to avoid such an issue in the future, I decided the best solution was to build our own RESTful quote service. So I decided to use the best tools to do so, the Spring stack, and was able to migrate the very next day.
\n\nTo kick things off, I made a check list of what I knew would be the right tools for the job of creating a RESTful web service.
\n\nI quickly set aside the desire to add, delete, manage, or view the data through a web page. Instead, my focus was to serve up a fixed set of content with the exact same structure that the guide expected to consume.
\n\nThe original content for the guide was a series of \"Chunk Norris\" jokes. I like a good laugh. But when I revisited the public API, I saw that several of the jokes were a bit rancid. After a brief discussion with colleagues, the idea came up to cite historical quotes. I took that idea and bent it a little. I had recently collected quotes from various developers about Spring Boot for personal reasons, so I decided to use that as the curated content.
\n\nTo kick things off, I visited http://start.spring.io. This Spring Boot app lets you enter the details of your new project, pick the Java level, and select the Spring Boot starters you need. I used my checklist up above and created a new gradle-based project.
\n\nWith the project unpacked and imported into my IDE, the first thing I did was copy the domain objects shown in the Reactor guide. This way, I could ensure that the data sent out by my REST service was correct. Since the POJOs in my Quoters Incorporated app are almost identical, I won't post them here.
\n\nThen I created a Spring Data repository.
\n\npublic interface QuoteRepository extends CrudRepository<Quote, Long> {}\nThis empty interface definition handles Quote objects with an internal primary key of type Long. By extending the Spring Data Commons CrudRepository, it inherits a fistful of database operations we'll use later on.
Next step? Initialize some data. I created a DatabaseLoader like this:
@Service\npublic class DatabaseLoader {\n\n private final QuoteRepository repository;\n\n @Autowired\n public DatabaseLoader(QuoteRepository repository) {\n this.repository = repository;\n }\n\n @PostConstruct\n void init() {\n repository.save(new Quote(\"Working with Spring Boot is like pair-programming with the Spring developers.\"));\n // more quotes...\n }\n\n}\n@Service so it will be automatically picked up by @ComponentScan when the app starts.QuoteRepository is made available.@PostConstruct tells Spring MVC to run the data loading method after all beans have been created.init() method uses Spring Data JPA to create a whole slew of quotations.Because I have H2 as my database of choice (com.h2database:h2) in build.gradle, there is no database set up at all (thanks to Spring Boot).
After I built this database layer, I went on to create the APIs. With Spring MVC, it wasn't hard at all.
\n\n@RestController\npublic class QuoteController {\n\n private final QuoteRepository repository;\n\n private final static Quote NONE = new Quote(\"None\");\n\n private final static Random RANDOMIZER = new Random();\n\n @Autowired\n public QuoteController(QuoteRepository repository) {\n this.repository = repository;\n }\n\n @RequestMapping(value = \"/api\", method = RequestMethod.GET)\n public List<QuoteResource> getAll() {\n return StreamSupport.stream(repository.findAll().spliterator(), false)\n .map(q -> new QuoteResource(q, \"success\"))\n .collect(Collectors.toList());\n }\n\n @RequestMapping(value = \"/api/{id}\", method = RequestMethod.GET)\n public QuoteResource getOne(@PathVariable Long id) {\n if (repository.exists(id)) {\n return new QuoteResource(repository.findOne(id), \"success\");\n } else {\n return new QuoteResource(NONE, \"Quote \" + id + \" does not exist\");\n }\n }\n\n @RequestMapping(value = \"/api/random\", method = RequestMethod.GET)\n public QuoteResource getRandomOne() {\n return getOne(nextLong(1, repository.count() + 1));\n }\n\n private long nextLong(long lowerRange, long upperRange) {\n return (long)(RANDOMIZER.nextDouble() * (upperRange - lowerRange)) + lowerRange;\n }\n\n}\nLet's break it down:
\n\n@RestController. This means all routes return objects not views.NONE quote and a Java 8 Random for randomly picking quotes.QuoteRepository.| API | \nDescription | \n
|---|---|
| /api | \nFetch ALL quotes | \n
| /api/{id} | \nFetch quote id\n | \n
| /api/random | \nFetch a random quote | \n
To fetch ALL quotes, I use a Java 8 stream to wrap the Spring data's findAll() and in turn, wrap each one into a QuoteResource. The results are turned into a List.
To fetch a single quote, it first tests if a given id exists. If not, return NONE. Otherwise, return a wrapped quote.
Finally, to fetch a random quote, I use Java 8's Random utility inside the nextLong() utility method to fetch a Long with the lowerRange and upperRange, inclusively.
\n\n\nQUESTION: Why am I using
\nQuoteResource?Quoteis the core domain object returned by theQuoteRepository. To match the previous public API, I wrap each instance in aQuoteResourcewhich includes a status code.
With this in place, the default Application class created by http://start.spring.io was ready to run.
$ curl localhost:8080/api/random\n{\n type: \"success\",\n value: {\n id: 1,\n quote: \"Working with Spring Boot is like pair-programming with the Spring developers.\"\n }\n}\n\n\nTa dah!
\n\nTo wrap things up, I built the JAR file and pushed it up to Pivotal Web Services. You can view the site yourself at http://gturnquist-quoters.cfapps.io/api/random.
\n\nSuffice it to say, I was able to tweak the Reactor guide by altering ONE LINE OF CODE. With that in place, I did some other clean up of the content and was done!
\n\nTo see the code, please visit https://github.com/gregturn/quoters.
\n\nThese are some outstanding things that didn't fit inside the time budget and weren't required to solve the original problem involving the Reactor guide. But they are good exercises you can explore! You can clone the project in github and take a shot at it yourself!
\n\nBook your place at SpringOne in Dallas, TX for Sept 8-11 soon. It's simply the best opportunity to find out first hand all that's going on and to provide direct feedback. You can see myself and Roy Clarkson talk about Spring Data REST - Data Meets Hypermedia to see how to merge Spring Data and RESTful services.
", "categories" : [ "Engineering" ] @@ -143,7 +143,7 @@ "_id" : "tag:spring.io,2014-07-24:1697", "_class" : "example.springdata.mongodb.textsearch.BlogPost", "title" : "Extending Spring Cloud", - "content" : "One of the most interesting capabilities of Spring Cloud is its extensibility. You can extend it to support additional clouds, enhance already supported clouds, support new services, new service connectors--all without modifying the Spring Cloud code itself. In this blog, we explore this capability. If you haven’t done so already, please read the first and second blog in this series to acquire sufficient background.
\n\nSpring Cloud provides extensibility along three orthogonal directions. You may extend it in one of these directions and orthogonality ensures that you continue to benefit from the others.
\n\nCloud Platforms: While Spring Cloud supports Cloud Foundry, Heroku, and a Local Config cloud (to test locally in a cloud-like environment), you aren’t limited by these choices. You can add your own cloud platform and take advantage of the rest of Spring Cloud capability such as Spring Java Config.
Cloud Services: Cloud platforms offer a variety of services ranging from relational databases to messaging. Services offered by each cloud platform vary a lot, even for multiple installations of the same platform. This is especially true for PaaS offerings such as Cloud Foundry, where private instances of Cloud Foundry tend to have services specific to each installation. Spring Cloud offers an easy way to extend to services beyond its core offering. Just like cloud platform extensibility, you don’t have to change Spring Cloud code to extend it to new services and you continue to take advantage of the other parts.
Frameworks: Spring Cloud currently supports Spring frameworks through the spring-service-connector module. However, except for that module, nothing in Spring Cloud depends on Spring. As such, you should be able to either use other parts from any JVM-based framework or extend it for a framework by adding a new module.
In the previous blog, we looked at how you would use CloudFactory and Cloud to programmatically use Spring Cloud. When it comes to extensibility, you will not be working with either of these; instead you will implement other types in the core module. Let’s take a look at them.
The main type you will need to be familiar with to extend Spring Cloud to a new cloud platform is CloudConnector, which is a simple three-method interface:
public interface CloudConnector {\n boolean isInMatchingCloud();\n ApplicationInstanceInfo getApplicationInstanceInfo();\n List<ServiceInfo> getServiceInfos();\n}\nThe isInMatchingCloud() method should examine its environment to decide if it is operating in the right environment. For example, the Cloud Foundry connector checks the existence of the VCAP_APPLICATION environment variable, whereas the Heroku connector looks for the existence of the DYNO environment variable. The getApplicationInstanceInfo() method returns information about the current application instance (app name, host, port, and application properties). The most interesting method getServiceInfos() returns a list with each element containing enough information so that applications know how to connect to each service. Exact information contained in each ServiceInfo object is left up to each implementation (the ServiceInfo as such defines only one method: getId()).
Once you create an implementation of CloudConnector, you need to make Spring Cloud aware of it. For all extension points, Spring Cloud uses a uniform mechanism based on ServiceLoader. As applied to Spring Cloud for platform extensibility, it boils down to including a file named /META-INF/services/org.springframework.cloud.CloudConnector with an entry with the fully-qualified name of the implementation class. Typically, you will bundle this file along with your implementation and supporting classes. Then all an app has to do is include this jar on the classpath.
The ServiceInfoCreator interface provides an extension point to work with a new service.
public interface ServiceInfoCreator<SI extends ServiceInfo, SD> {\n public boolean accept(SD serviceData);\n public SI createServiceInfo(SD serviceData);\n}\nThe generic parameter SI defines the kind of ServiceInfo it will create, whereas the SD parameter defines the raw service data type it can work with. The raw service data type depends on the cloud platform. For example, in Cloud Found, it will be a Map based on the VCAP_SERVICES environment variable, whereas in Heroku, it will be a pair containing the service-specific environment variables and its value. Since the raw data type depends on the platform, so does implementations of ServiceInfoCreator. The accept() method examines the service data and determines if it can deal with it. For example, it can look at the URL scheme and determine if it can consume that service data. If it can, the createServiceInfo() must return a ServiceInfo object. If it is a completely new service, you may also have to implement ServiceInfo for that, else you can use one of the existing ones.
Once you have implemented a ServiceInfoCreator, you will have to let Spring Cloud know about it. This follows the same idea as discussed earlier for cloud platform extensibility. In this case, the file name you use is CloudConnector dependent. For Cloud Foundry, it is /META-INF/services/org.springframework.cloud.cloudfoundry.CloudFoundryServiceInfoCreator (theoretically, a CloudConnector implementation may decide to use another extension mechanism, but Spring Cloud doesn’t recommend that).
As discussed in the previous blog, a cloud app developer may decide to work directly with a ServiceInfo object. As such, if you just implement a ServiceInfoCreator, you would have provided some benefit already. However, working with a raw ServiceInfo object may not be appreciated by many developers focused on developing apps, so you will implement the next extension as well.
The last extensibility point is ServiceConnectorCreator. Its job is to transform ServiceInfo into a service connector that is suitable for consumption in the framework the app is using. For example, it could transform MysqlServiceInfo into a DataSource object. Out of the box, Spring Cloud supports connectors for DataSource and a few Spring Data and Spring AMQP types. If you wish to extend Spring Cloud to other frameworks or if you wanted to support other Spring Data types (such as Neo4J, Elasticsearch, Cassandra) or Spring-compatible types (such as S3) not yet supported directly by Spring Cloud, this is the extension point you need.
public interface ServiceConnectorCreator<SC, SI extends ServiceInfo> {\n SC create(SI serviceInfo, ServiceConnectorConfig serviceConnectorConfig);\n ...\n}\nThere are a couple more methods; however, you will normally extend the AbstractServiceConnectorCreator that takes care of implementing those.
The SC generic parameter to ServiceConnectorCreator is bound to the type of connector it will create, such as DataSource, whereas the SI parameter signifies the type of ServiceInfo it can work with.
The create() method is supplied with a ServiceInfo object and a configuration object, that carries service-specific info such as pooling parameters. It needs to use these parameters to create an appropriate connector.
Once the implementation is ready, just put it in a file named /META-INF/services/org.springframework.cloud.service.ServiceConnectorCreator. Spring Cloud will use the Service Loader mechanism described earlier.
As you can see Spring Cloud offers substantial extensibility along cloud platform, services, and framework axis. Next time, you come across a new kind of these, you should be able to extend Spring Cloud to work with them. If you open-source your extensions, let us know, so that we can showcase it for others to benefit. If it is a common enough extension, consider making a pull request.
", + "content" : "One of the most interesting capabilities of Spring Cloud is its extensibility. You can extend it to support additional clouds, enhance already supported clouds, support new services, new service connectors--all without modifying the Spring Cloud code itself. In this blog, we explore this capability. If you haven’t done so already, please read the first and second blog in this series to acquire sufficient background.
\n\nSpring Cloud provides extensibility along three orthogonal directions. You may extend it in one of these directions and orthogonality ensures that you continue to benefit from the others.
\n\nCloud Platforms: While Spring Cloud supports Cloud Foundry, Heroku, and a Local Config cloud (to test locally in a cloud-like environment), you aren’t limited by these choices. You can add your own cloud platform and take advantage of the rest of Spring Cloud capability such as Spring Java Config.
Cloud Services: Cloud platforms offer a variety of services ranging from relational databases to messaging. Services offered by each cloud platform vary a lot, even for multiple installations of the same platform. This is especially true for PaaS offerings such as Cloud Foundry, where private instances of Cloud Foundry tend to have services specific to each installation. Spring Cloud offers an easy way to extend to services beyond its core offering. Just like cloud platform extensibility, you don’t have to change Spring Cloud code to extend it to new services and you continue to take advantage of the other parts.
Frameworks: Spring Cloud currently supports Spring frameworks through the spring-service-connector module. However, except for that module, nothing in Spring Cloud depends on Spring. As such, you should be able to either use other parts from any JVM-based framework or extend it for a framework by adding a new module.
In the previous blog, we looked at how you would use CloudFactory and Cloud to programmatically use Spring Cloud. When it comes to extensibility, you will not be working with either of these; instead you will implement other types in the core module. Let’s take a look at them.
The main type you will need to be familiar with to extend Spring Cloud to a new cloud platform is CloudConnector, which is a simple three-method interface:
public interface CloudConnector {\n boolean isInMatchingCloud();\n ApplicationInstanceInfo getApplicationInstanceInfo();\n List<ServiceInfo> getServiceInfos();\n}\nThe isInMatchingCloud() method should examine its environment to decide if it is operating in the right environment. For example, the Cloud Foundry connector checks the existence of the VCAP_APPLICATION environment variable, whereas the Heroku connector looks for the existence of the DYNO environment variable. The getApplicationInstanceInfo() method returns information about the current application instance (app name, host, port, and application properties). The most interesting method getServiceInfos() returns a list with each element containing enough information so that applications know how to connect to each service. Exact information contained in each ServiceInfo object is left up to each implementation (the ServiceInfo as such defines only one method: getId()).
Once you create an implementation of CloudConnector, you need to make Spring Cloud aware of it. For all extension points, Spring Cloud uses a uniform mechanism based on ServiceLoader. As applied to Spring Cloud for platform extensibility, it boils down to including a file named /META-INF/services/org.springframework.cloud.CloudConnector with an entry with the fully-qualified name of the implementation class. Typically, you will bundle this file along with your implementation and supporting classes. Then all an app has to do is include this jar on the classpath.
The ServiceInfoCreator interface provides an extension point to work with a new service.
public interface ServiceInfoCreator<SI extends ServiceInfo, SD> {\n public boolean accept(SD serviceData);\n public SI createServiceInfo(SD serviceData);\n}\nThe generic parameter SI defines the kind of ServiceInfo it will create, whereas the SD parameter defines the raw service data type it can work with. The raw service data type depends on the cloud platform. For example, in Cloud Found, it will be a Map based on the VCAP_SERVICES environment variable, whereas in Heroku, it will be a pair containing the service-specific environment variables and its value. Since the raw data type depends on the platform, so does implementations of ServiceInfoCreator. The accept() method examines the service data and determines if it can deal with it. For example, it can look at the URL scheme and determine if it can consume that service data. If it can, the createServiceInfo() must return a ServiceInfo object. If it is a completely new service, you may also have to implement ServiceInfo for that, else you can use one of the existing ones.
Once you have implemented a ServiceInfoCreator, you will have to let Spring Cloud know about it. This follows the same idea as discussed earlier for cloud platform extensibility. In this case, the file name you use is CloudConnector dependent. For Cloud Foundry, it is /META-INF/services/org.springframework.cloud.cloudfoundry.CloudFoundryServiceInfoCreator (theoretically, a CloudConnector implementation may decide to use another extension mechanism, but Spring Cloud doesn’t recommend that).
As discussed in the previous blog, a cloud app developer may decide to work directly with a ServiceInfo object. As such, if you just implement a ServiceInfoCreator, you would have provided some benefit already. However, working with a raw ServiceInfo object may not be appreciated by many developers focused on developing apps, so you will implement the next extension as well.
The last extensibility point is ServiceConnectorCreator. Its job is to transform ServiceInfo into a service connector that is suitable for consumption in the framework the app is using. For example, it could transform MysqlServiceInfo into a DataSource object. Out of the box, Spring Cloud supports connectors for DataSource and a few Spring Data and Spring AMQP types. If you wish to extend Spring Cloud to other frameworks or if you wanted to support other Spring Data types (such as Neo4J, Elasticsearch, Cassandra) or Spring-compatible types (such as S3) not yet supported directly by Spring Cloud, this is the extension point you need.
public interface ServiceConnectorCreator<SC, SI extends ServiceInfo> {\n SC create(SI serviceInfo, ServiceConnectorConfig serviceConnectorConfig);\n ...\n}\nThere are a couple more methods; however, you will normally extend the AbstractServiceConnectorCreator that takes care of implementing those.
The SC generic parameter to ServiceConnectorCreator is bound to the type of connector it will create, such as DataSource, whereas the SI parameter signifies the type of ServiceInfo it can work with.
The create() method is supplied with a ServiceInfo object and a configuration object, that carries service-specific info such as pooling parameters. It needs to use these parameters to create an appropriate connector.
Once the implementation is ready, just put it in a file named /META-INF/services/org.springframework.cloud.service.ServiceConnectorCreator. Spring Cloud will use the Service Loader mechanism described earlier.
As you can see Spring Cloud offers substantial extensibility along cloud platform, services, and framework axis. Next time, you come across a new kind of these, you should be able to extend Spring Cloud to work with them. If you open-source your extensions, let us know, so that we can showcase it for others to benefit. If it is a common enough extension, consider making a pull request.
", "categories" : [ "Engineering" ]