-
Notifications
You must be signed in to change notification settings - Fork 37
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Added auto-configuration for Spring Boot #103
Changes from 4 commits
c1c6524
78a150d
5d3d400
1c284f0
1e187fe
5ed2d6e
6a7d496
445ed8d
7dce3bc
4fb15dc
1665ee6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -28,16 +28,16 @@ | |
|
||
import static com.hazelcast.internal.config.ConfigLoader.locateConfig; | ||
|
||
class ClientServerConfigLoader { | ||
public class ClientServerConfigLoader { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. An idea: maybe we could get rid of the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I thought about it, but I think it will be better to keep There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Isn't it possible to put tests in a separate package, but keep the code together? Like we already have separate packages for different tests: There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yes, surely we can do it. I just thought it is tidier :) I removed the |
||
private ConfigRecognizer xmlConfigRecognizer; | ||
private ConfigRecognizer yamlConfigRecognizer; | ||
|
||
ClientServerConfigLoader() throws Exception { | ||
public ClientServerConfigLoader() throws Exception { | ||
xmlConfigRecognizer = new ClientXmlConfigRootTagRecognizer(); | ||
yamlConfigRecognizer = new ClientYamlConfigRootTagRecognizer(); | ||
} | ||
|
||
ClientConfig load(final String path) throws Exception { | ||
public ClientConfig load(final String path) throws Exception { | ||
final URL url = locateConfig(path); | ||
if (url == null) { | ||
return null; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
/* | ||
* Copyright 2020 Hazelcast Inc. | ||
* | ||
* Licensed under the Hazelcast Community License (the "License"); you may not use | ||
* this file except in compliance with the License. You may obtain a copy of the | ||
* License at | ||
* | ||
* http://hazelcast.com/hazelcast-community-license | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||
* WARRANTIES OF ANY KIND, either express or implied. See the License for the | ||
* specific language governing permissions and limitations under the License. | ||
*/ | ||
|
||
package com.hazelcast.session.springboot; | ||
|
||
import com.hazelcast.client.config.ClientConfig; | ||
import com.hazelcast.config.Config; | ||
import com.hazelcast.core.HazelcastInstance; | ||
import com.hazelcast.session.ClientServerConfigLoader; | ||
import com.hazelcast.session.ClientServerLifecycleListener; | ||
import com.hazelcast.session.HazelcastSessionManager; | ||
import com.hazelcast.session.P2PConfigLoader; | ||
import com.hazelcast.session.SessionManager; | ||
import org.apache.catalina.Context; | ||
import org.apache.juli.logging.Log; | ||
import org.apache.juli.logging.LogFactory; | ||
import org.springframework.beans.factory.annotation.Value; | ||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; | ||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; | ||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; | ||
import org.springframework.boot.web.embedded.tomcat.TomcatContextCustomizer; | ||
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory; | ||
import org.springframework.boot.web.server.WebServerFactoryCustomizer; | ||
import org.springframework.context.annotation.Bean; | ||
|
||
@org.springframework.context.annotation.Configuration | ||
@ConditionalOnClass(HazelcastSessionManager.class) | ||
public class HazelcastSessionManagerConfiguration { | ||
private final Log log = LogFactory.getLog(HazelcastSessionManager.class); | ||
|
||
@Value("${tsm.hazelcast.config.location:hazelcast-default.xml}") | ||
private String configLocation; | ||
@Value("${tsm.hazelcast.client.config.location:hazelcast-client-default.xml}") | ||
private String clientConfigLocation; | ||
@Value("${tsm.client.only:false}") | ||
private boolean clientOnly; | ||
@Value("${tsm.map.name:default}") | ||
private String mapName; | ||
@Value("${tsm.sticky:true}") | ||
private boolean sticky; | ||
@Value("${tsm.process.expires.frequency:6}") | ||
private int processExpiresFrequency; | ||
@Value("${tsm.deferred.write:true}") | ||
private boolean deferredWrite; | ||
@Value("${tsm.hazelcast.instance.name:" + SessionManager.DEFAULT_INSTANCE_NAME + "}") | ||
private String hazelcastInstanceName; | ||
|
||
@Bean | ||
@ConditionalOnMissingBean(type = "com.hazelcast.config.Config") | ||
@ConditionalOnProperty(name = "tsm.client.only", havingValue = "false", matchIfMissing = true) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wonder, how Spring Boot now detects if P2P or Client-Server is used? Maybe we can piggy-back on this and not introduce our own parameter There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I will check this 👍 |
||
public Config hazelcastConfig() throws Exception { | ||
Config config = new P2PConfigLoader().load(configLocation); | ||
if (config.getInstanceName() == null) { | ||
config.setInstanceName(SessionManager.DEFAULT_INSTANCE_NAME); | ||
} | ||
return config; | ||
} | ||
|
||
@Bean | ||
@ConditionalOnMissingBean(type = "com.hazelcast.client.config.ClientConfig") | ||
@ConditionalOnProperty(name = "tsm.client.only", havingValue = "true") | ||
public ClientConfig hazelcastClientConfig() throws Exception { | ||
ClientConfig clientConfig = new ClientServerConfigLoader().load(clientConfigLocation); | ||
ClientServerLifecycleListener.setConfig(clientConfig); | ||
enozcan marked this conversation as resolved.
Show resolved
Hide resolved
|
||
return clientConfig; | ||
} | ||
|
||
@Bean(name = "hazelcastTomcatSessionManagerCustomizer") | ||
public WebServerFactoryCustomizer<TomcatServletWebServerFactory> customizeTomcat(HazelcastInstance hazelcastInstance) { | ||
return new WebServerFactoryCustomizer<TomcatServletWebServerFactory>() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I afraid in
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @enozcan yes, you were correct. I fixed it by using the Hazelcast client instance initiated by Spring Boot. |
||
@Override | ||
public void customize(TomcatServletWebServerFactory factory) { | ||
factory.addContextCustomizers(new TomcatContextCustomizer() { | ||
enozcan marked this conversation as resolved.
Show resolved
Hide resolved
|
||
@Override | ||
public void customize(Context context) { | ||
HazelcastSessionManager manager = new HazelcastSessionManager(); | ||
manager.setClientOnly(clientOnly); | ||
manager.setMapName(mapName); | ||
manager.setSticky(sticky); | ||
manager.setProcessExpiresFrequency(processExpiresFrequency); | ||
manager.setDeferredWrite(deferredWrite); | ||
manager.setHazelcastInstanceName(hazelcastInstanceName); | ||
context.setManager(manager); | ||
log.info(String.format( | ||
"Tomcat context is configured with HazelcastSessionManager => clientOnly: %s, mapName: %s, " | ||
+ "isSticky: %s, processExpiresFrequency: %d, deferredWrite: %s, " | ||
+ "hazelcastInstanceName: %s", | ||
clientOnly, mapName, sticky, processExpiresFrequency, deferredWrite, hazelcastInstanceName)); | ||
} | ||
}); | ||
} | ||
}; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
/* | ||
* Copyright 2020 Hazelcast Inc. | ||
* | ||
* Licensed under the Hazelcast Community License (the "License"); you may not use | ||
* this file except in compliance with the License. You may obtain a copy of the | ||
* License at | ||
* | ||
* http://hazelcast.com/hazelcast-community-license | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||
* WARRANTIES OF ANY KIND, either express or implied. See the License for the | ||
* specific language governing permissions and limitations under the License. | ||
*/ | ||
|
||
/** | ||
* This package provides Spring Boot auto-configuration classes | ||
*/ | ||
package com.hazelcast.session.springboot; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As I already mentioned somewhere, consider removing this package. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Replied under the other comment. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
org.springframework.boot.autoconfigure.EnableAutoConfiguration= \ | ||
com.hazelcast.session.springboot.HazelcastSessionManagerConfiguration |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
package com.hazelcast.session.springboot; | ||
|
||
import com.hazelcast.core.DistributedObject; | ||
import com.hazelcast.core.HazelcastInstance; | ||
import com.hazelcast.map.IMap; | ||
import org.apache.http.client.CookieStore; | ||
import org.apache.http.client.HttpClient; | ||
import org.apache.http.client.methods.HttpGet; | ||
import org.apache.http.impl.client.BasicCookieStore; | ||
import org.apache.http.impl.client.HttpClientBuilder; | ||
import org.junit.Test; | ||
import org.springframework.context.ApplicationContext; | ||
|
||
import java.util.LinkedList; | ||
|
||
import static org.junit.Assert.assertEquals; | ||
import static org.junit.Assert.assertNotNull; | ||
|
||
public abstract class AbstractSpringBootConfigurationTest { | ||
ApplicationContext applicationContext; | ||
|
||
abstract void setup(); | ||
|
||
abstract void clean(); | ||
|
||
@Test() | ||
public void testManagerCustomizerBean() { | ||
assertNotNull(applicationContext.getBean("hazelcastTomcatSessionManagerCustomizer")); | ||
} | ||
|
||
@Test() | ||
public void testSessionManager() | ||
throws Exception { | ||
HazelcastInstance hazelcastInstance = (HazelcastInstance) applicationContext.getBean("hazelcastInstance"); | ||
LinkedList<DistributedObject> distributedObjects = (LinkedList<DistributedObject>) hazelcastInstance.getDistributedObjects(); | ||
assertEquals("Session map should be created.", 1, distributedObjects.size()); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's not a good practice to mix assertions with the test preparation. The test should have a clear structure There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I changed them, but I am not sure if we should follow |
||
|
||
CookieStore cookieStore = new BasicCookieStore(); | ||
HttpClient client = HttpClientBuilder.create().setDefaultCookieStore(cookieStore).build(); | ||
HttpGet request = new HttpGet("http://localhost:9999/set"); | ||
client.execute(request); | ||
|
||
IMap<Object, Object> sessionMap = hazelcastInstance.getMap(distributedObjects.get(0).getName()); | ||
assertEquals("Session should be created.",1, sessionMap.size()); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
package com.hazelcast.session.springboot; | ||
|
||
import org.springframework.boot.SpringApplication; | ||
import org.springframework.boot.autoconfigure.SpringBootApplication; | ||
import org.springframework.context.annotation.PropertySource; | ||
|
||
@SpringBootApplication | ||
@PropertySource("classpath:clientServer.properties") | ||
public class ClientServerApplication { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How about embedding this class into There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done 👍 |
||
public static void main(String[] args) { | ||
SpringApplication.run(ClientServerApplication.class, args); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
package com.hazelcast.session.springboot; | ||
|
||
import com.hazelcast.core.Hazelcast; | ||
import com.hazelcast.core.HazelcastInstance; | ||
import org.junit.After; | ||
import org.junit.Before; | ||
import org.springframework.boot.SpringApplication; | ||
|
||
public class ClientServerSpringBootConfigurationTest | ||
extends AbstractSpringBootConfigurationTest { | ||
|
||
private HazelcastInstance hazelcastInstance; | ||
|
||
@Before | ||
public void setup() { | ||
hazelcastInstance = Hazelcast.newHazelcastInstance(); | ||
applicationContext = SpringApplication.run(ClientServerApplication.class); | ||
} | ||
|
||
@After | ||
public void clean() { | ||
SpringApplication.exit(applicationContext); | ||
hazelcastInstance.shutdown(); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
package com.hazelcast.session.springboot; | ||
|
||
import org.springframework.boot.SpringApplication; | ||
import org.springframework.boot.autoconfigure.SpringBootApplication; | ||
import org.springframework.context.annotation.ComponentScan; | ||
import org.springframework.context.annotation.FilterType; | ||
|
||
@SpringBootApplication | ||
@ComponentScan(excludeFilters = @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, | ||
classes = ClientServerApplication.class)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why do you need There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
public class P2PApplication { | ||
public static void main(String[] args) { | ||
SpringApplication.run(P2PApplication.class, args); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
package com.hazelcast.session.springboot; | ||
|
||
import org.junit.After; | ||
import org.junit.Before; | ||
import org.springframework.boot.SpringApplication; | ||
|
||
public class P2PSpringBootConfigurationTest | ||
extends AbstractSpringBootConfigurationTest { | ||
|
||
@Before | ||
public void setup() { | ||
applicationContext = SpringApplication.run(P2PApplication.class); | ||
} | ||
|
||
@After | ||
public void clean() { | ||
SpringApplication.exit(applicationContext); | ||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
package com.hazelcast.session.springboot; | ||
|
||
import org.springframework.web.bind.annotation.RequestMapping; | ||
import org.springframework.web.bind.annotation.RestController; | ||
|
||
import javax.servlet.http.HttpSession; | ||
|
||
@RestController | ||
public class TestController { | ||
|
||
@RequestMapping("/set") | ||
public void defaultMapping(HttpSession session){ | ||
session.setAttribute("testAttr", 1); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
logging.level.root=INFO | ||
server.port=9999 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it common to use such abbreviations as prefix? Like
tsm
? Did you see other projects using it?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, I haven't checked but I used it to separate from other properties with the same name for other modules. Do you have any other suggestion?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see. Yeah, makes sense, otherwise, they'd collide with other properties. But would it maybe make sense to stick to some convention from Spring, like here: https://docs.spring.io/spring-boot/docs/2.1.18.RELEASE/reference/html/boot-features-hazelcast.html
For example, in Spring you define the Hazelcast configuration with
spring.hazelcast.config
and in your configuration it'stsm.hazelcast.config.location
. So from what I see, Spring just added a prefixspring.
to all the Hazelcast properties. So, maybe you should call yours liketsm.hazelcast.config
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I preferred to use the same config property names as we use in Tomcat Session Manger itself, just adding
tsm.
in front of them (See: https://github.com/hazelcast/hazelcast-tomcat-sessionmanager#configuring-manager-element-for-tomcat). I think this makes more sense from the user's point of view. I changed thetsm.hazelcast.config.location
totsm.config.location
accordingly.