What is the AffinityThreadFactory error message in spring integration? - java

This is Adapter config code..
private static final String THREAD_NAME_PREFIX = "PlainAdapterTaskScheduler";
private static final int PROCESS_SIZE = 200;
private static final int BACKLOG = 2 * 1024;
private static final long FIXED_RATE = 500;
#Value("${tcp.plain.inbound.port}")
private String inboundPort;
#Autowired
private EchoSerializer echoSerializer;
#Bean
public TcpReceivingChannelAdapter plainAdapter() {
TcpReceivingChannelAdapter adapter = new TcpReceivingChannelAdapter();
adapter.setConnectionFactory(plainServerFactory());
adapter.setOutputChannel(inputWithPlain());
return adapter;
}
#Bean
public TcpSendingMessageHandler plainHandler() {
TcpSendingMessageHandler handler = new TcpSendingMessageHandler();
handler.setConnectionFactory(plainServerFactory());
return handler;
}
#Bean
public AbstractConnectionFactory plainServerFactory() {
int port = Integer.parseInt(inboundPort);
TcpNioServerConnectionFactory factory = new TcpNioServerConnectionFactory(port);
factory.setBacklog(BACKLOG);
factory.setTaskExecutor(taskSchedulerWithPlain());
factory.setLookupHost(false);
factory.setSerializer(echoSerializer);
factory.setDeserializer(echoSerializer);
// Nagle's algorithm disabled
factory.setSoTcpNoDelay(true);
return factory;
}
#Bean
public Executor taskSchedulerWithPlain() {
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
scheduler.setPoolSize(PROCESS_SIZE);
scheduler.setThreadFactory(plainAffinityThreadFactory());
scheduler.setWaitForTasksToCompleteOnShutdown(true);
return scheduler;
}
private AffinityThreadFactory plainAffinityThreadFactory() {
return new AffinityThreadFactory(THREAD_NAME_PREFIX, SAME_CORE, DIFFERENT_SOCKET, ANY);
}
#Bean
public MessageChannel inputWithPlain() {
return MessageChannels.queue(PROCESS_SIZE).get();
}
#Bean(name = PollerMetadata.DEFAULT_POLLER)
public PollerMetadata poller() {
return Pollers.fixedRate(FIXED_RATE).get();
}
Operating the service, and
If the test comes the following message.
2016-10-14 14:07:28.679 DEBUG 10716 --- [task-scheduler-7] o.s.i.endpoint.PollingConsumer : Received no Message during the poll, returning 'false'
2016-10-14 14:07:28.682 DEBUG 10716 --- [task-scheduler-1] o.s.i.endpoint.PollingConsumer : Received no Message during the poll, returning 'false'
2016-10-14 14:07:28.875 WARN 10716 --- [PlainAdapterTaskScheduler-2] net.openhft.affinity.LockInventory : No reservable CPU for Thread[PlainAdapterTaskScheduler-2,5,main]
2016-10-14 14:07:28.879 DEBUG 10716 --- [PlainAdapterTaskScheduler-2] o.s.i.i.tcp.connection.TcpNioConnection : 192.168.2.93:49531:5001:713a1d01-3fdf-4965-a457-fcc3837b2adf Reading...
2016-10-14 14:07:28.882 DEBUG 10716 --- [PlainAdapterTaskScheduler-2] o.s.i.i.tcp.connection.TcpNioConnection : 192.168.2.93:49531:5001:713a1d01-3fdf-4965-a457-fcc3837b2adf Running an assembler
2016-10-14 14:07:28.886 DEBUG 10716 --- [PlainAdapterTaskScheduler-2] o.s.i.i.tcp.connection.TcpNioConnection : Read 1 into raw buffer
2016-10-14 14:07:28.889 WARN 10716 --- [PlainAdapterTaskScheduler-3] net.openhft.affinity.LockInventory : No reservable CPU for Thread[PlainAdapterTaskScheduler-3,5,main]
2016-10-14 14:07:29.014 DEBUG 10716 --- [PlainAdapterTaskScheduler-3] o.s.i.i.tcp.connection.TcpNioConnection : 192.168.2.93:49531:5001:713a1d01-3fdf-4965-a457-fcc3837b2adf Reading...
2016-10-14 14:07:29.022 DEBUG 10716 --- [PlainAdapterTaskScheduler-3] o.s.i.i.tcp.connection.TcpNioConnection : Read 242 into raw buffer
2016-10-14 14:07:29.024 WARN 10716 --- [PlainAdapterTaskScheduler-4] net.openhft.affinity.LockInventory : No reservable CPU for Thread[PlainAdapterTaskScheduler-4,5,main]
2016-10-14 14:07:29.217 DEBUG 10716 --- [PlainAdapterTaskScheduler-3] o.s.i.i.tcp.connection.TcpNioConnection : 192.168.2.93:49531:5001:713a1d01-3fdf-4965-a457-fcc3837b2adf Reading...
2016-10-14 14:07:29.220 DEBUG 10716 --- [PlainAdapterTaskScheduler-3] o.s.i.i.tcp.connection.TcpNioConnection : Read 49 into raw buffer
2016-10-14 14:07:29.227 DEBUG 10716 --- [PlainAdapterTaskScheduler-2] c.m.c.i.serializer.EchoSerializer : payload size is : 292
2016-10-14 14:07:29.229 WARN 10716 --- [PlainAdapterTaskScheduler-5] net.openhft.affinity.LockInventory : No reservable CPU for Thread[PlainAdapterTaskScheduler-5,5,main]
2016-10-14 14:07:29.298 DEBUG 10716 --- [PlainAdapterTaskScheduler-2] o.s.integration.channel.QueueChannel : preSend on channel 'inputWithPlain', message: GenericMessage [payload=byte[292], headers={ip_address=192.168.2.93, id=2ec06f67-f8e1-3645-f096-8748c566d7f3, ip_hostname=192.168.2.93, ip_tcp_remotePort=49531, ip_connectionId=192.168.2.93:49531:5001:713a1d01-3fdf-4965-a457-fcc3837b2adf, timestamp=1476421649296}]
What are the warning messages?
Why does this message appear?
[PlainAdapterTaskScheduler-2] net.openhft.affinity.LockInventory : No reservable CPU for Thread[PlainAdapterTaskScheduler-2,5,main]
The windows and linux these messages come out to normal operation.
However, it hpux not in normal operation.
Is there anyone who experienced the same thing?
Thank you.

The message is form OpenHFT's Java-Thread-Affinity class. And there is a closed issue seems same: https://github.com/OpenHFT/Java-Thread-Affinity/issues/3
And Peter Lawrey answer is :
Since you have only two cpus and your OS has to run somewhere (the library assumes that cpu 0 is for the OS,) so you have only one cpu 1 you can use for binding. As you can see, it reserves the first thread to cpu 1 and any additional threads it leaves as there is only one cpu left. Affinity works best if the cpus are isolated AND you have more cpus than you have critical threads.

Related

Make FileAlterationListenerAdaptor.onFileCreate() always single thread, apache.commons.io.monitor

I'm overriding the onFileCreate() method of the org.apache.commons.io.monitor.FileAlterationListener interface.
The method works, but I found that sometimes it spawns two threads and I don't fully understand what is triggering this behaviour.
WatchService Class
#Component
#Slf4j(topic="watchService")
public class WatchService {
private static RestTemplate restTemplate;
private static Environment env;
#Autowired
public WatchService(RestTemplate restTemplate, Environment env) {
WatchService.restTemplate = restTemplate;
WatchService.env = env;
}
//When a new file is created inside a folder, the file content is sent to kafka
#Bean
public static void startFolderPolling() throws Exception {
FileAlterationObserver observer = new FileAlterationObserver(env.getRequiredProperty("folder"));
FileAlterationMonitor monitor = new FileAlterationMonitor(5000);
log.info("setup completed");
FileAlterationListener listener = new FileAlterationListenerAdaptor() {
#Override
public void onFileCreate(File file) {
log.info("are you single thread ?");
try {
String data = FileUtils.readFileToString(file, "UTF-8");
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<String> entity = new HttpEntity<String>(data,headers);
log.info("Calling Kakfa microservice");
String answer = restTemplate.postForObject("http://kafka/api/messages/receiveSapOrder", entity, String.class);
log.info("sending SAP Order result:" + answer);
} catch (IOException e) {
e.printStackTrace();
}
}
};
observer.addListener(listener);
monitor.addObserver(observer);
monitor.start();
}
}
Main Method
#SpringBootApplication
#EnableEurekaClient
public class DirectoryListenerApplication {
public static void main(String[] args) throws Exception {
SpringApplication.run(DirectoryListenerApplication.class, args);
startFolderPolling();
}
}
With the same file created in the folder sometimes the method logs two calls in two separated threads, sometimes the method log only one call in a single thread.
2022-05-10 09:46:42.382 INFO 88330 --- [ main] watchService : setup completed
2022-05-10 09:46:42.397 INFO 88330 --- [nfoReplicator-0] com.netflix.discovery.DiscoveryClient : DiscoveryClient_SAP-LISTENER/192.168.2.63:sap-listener:8095 - registration status: 204
2022-05-10 09:46:57.394 INFO 88330 --- [ Thread-4] watchService : are you single thread ?
2022-05-10 09:46:57.423 INFO 88330 --- [ Thread-4] watchService : Calling Kakfa microservice
2022-05-10 09:46:58.788 INFO 88330 --- [ Thread-4] watchService : sending SAP Order result:{"message":"Uploaded the file successfully"}
2022-05-10 09:47:00.108 INFO 88330 --- [ Thread-2] watchService : are you single thread ?
2022-05-10 09:47:00.112 INFO 88330 --- [ Thread-2] watchService : Calling Kakfa microservice
2022-05-10 09:47:00.197 INFO 88330 --- [ Thread-2] watchService : sending SAP Order result:{"message":"Uploaded the file successfully"}
Is it possible to force the single thread behaviour ?
I removed the SprigBoot #Bean notation over my startFolderPolling method and now only one thread is created.

Spring Webflux: How to use different thread for request and response

I'm using Spring Webflux and as I understand it, by using this, the thread used for receiving request and the one used for response should be different. However, whether I use netty or undertow, I end up using the same thread.
My app is a simple crud app with MySQL DB. I'm not using r2dbc but a jdbc coupled with Executor and Scheduler.
As shown in the log below, request is handled by thread [ XNIO-1 I/O-6] and the response is given by the same one.
By this, I'm assuming the thread is blocked until db operation has finished. How can I fix this?
Here's the log
2019-07-23 17:49:10.051 INFO 132 --- [ main] org.xnio : XNIO version 3.3.8.Final
2019-07-23 17:49:10.059 INFO 132 --- [ main] org.xnio.nio : XNIO NIO Implementation Version 3.3.8.Final
2019-07-23 17:49:10.114 INFO 132 --- [ main] o.s.b.w.e.undertow.UndertowWebServer : Undertow started on port(s) 8080 (http)
2019-07-23 17:49:10.116 INFO 132 --- [ main] c.n.webflux.demo.WebfluxFunctionalApp : Started WebfluxFunctionalApp in 1.262 seconds (JVM running for 2.668)
2019-07-23 17:49:10.302 DEBUG 132 --- [ XNIO-1 I/O-6] o.s.w.s.adapter.HttpWebHandlerAdapter : [4c85975] HTTP GET "/api/findall"
2019-07-23 17:49:10.322 DEBUG 132 --- [ XNIO-1 I/O-6] s.w.r.r.m.a.RequestMappingHandlerMapping : [4c85975] Mapped to public reactor.core.publisher.Mono<java.util.List<com.webflux.demo.model.TypeStatus>> com.webflux.demo.controller.MonitoringController.findAll()
2019-07-23 17:49:10.337 DEBUG 132 --- [ XNIO-1 I/O-6] o.s.w.r.r.m.a.ResponseBodyResultHandler : Using 'application/json;charset=UTF-8' given [*/*] and supported [application/json;charset=UTF-8, application/*+json;charset=UTF-8, text/event-stream]
2019-07-23 17:49:10.338 DEBUG 132 --- [ XNIO-1 I/O-6] o.s.w.r.r.m.a.ResponseBodyResultHandler : [4c85975] 0..1 [java.util.List<com.webflux.demo.model.TypeStatus>]
2019-07-23 17:49:10.347 INFO 132 --- [pool-1-thread-1] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting...
2019-07-23 17:49:10.785 INFO 132 --- [pool-1-thread-1] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed.
2019-07-23 17:49:10.838 DEBUG 132 --- [pool-1-thread-1] org.springframework.web.HttpLogging : [4c85975] Encoding [[com.webflux.demo.model.TypeStatus#7b4509cb, com.webflux.demo.model.TypeStatus#22676ebe, (truncated)...]
2019-07-23 17:49:10.949 DEBUG 132 --- [ XNIO-1 I/O-6] o.s.w.s.adapter.HttpWebHandlerAdapter : [4c85975] Completed 200 OK
Also my dao is
#Repository
public class TypeStatusJdbcTemplate {
private JdbcTemplate jdbcTemplate;
public TypeStatusJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
private final static String SQL_FIND_ALL = "select * from `monitoring`.`type_status` limit 3";
public List<TypeStatus> findAll() {
return jdbcTemplate.query(SQL_FIND_ALL,
new TypeStatusMapper());
}
}
service is
#Service
public class MonitoringService {
private final Scheduler scheduler;
private TypeStatusJdbcTemplate repository;
public MonitoringService(Scheduler scheduler, TypeStatusJdbcTemplate repository) {
this.scheduler = scheduler;
this.repository = repository;
}
public Mono<List<TypeStatus>> findAll() {
return Mono.fromCallable(repository::findAll).subscribeOn(scheduler);
}
}
controller is
#RestController
#RequestMapping("/api")
public class MonitoringController {
private final MonitoringService monitoringService;
private static final Logger logger = LoggerFactory.getLogger(MonitoringController.class);
public MonitoringController(MonitoringService monitoringService) {
this.monitoringService = monitoringService;
}
#GetMapping(value="/findall")
public Mono<List<TypeStatus>> findAll() {
return monitoringService.findAll();
}
}
main file (showing scheduler)
#SpringBootApplication
public class WebfluxFunctionalApp {
public static void main(String[] args){
SpringApplication.run(WebfluxFunctionalApp.class, args);
}
#PostConstruct
public void init(){
// Setting Spring Boot SetTimeZone
TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
}
#Bean
public Scheduler jdbcScheduler() {
return Schedulers.fromExecutor(Executors.newFixedThreadPool(30));
}
}
Thread execution does not always have to be different threads. Taken from the Reactive documentation:
Reactive Schedulers
Obtaining a Flux or a Mono doesn’t necessarily mean it will run in a dedicated Thread. Instead, most operators continue working in the Thread on which the previous operator executed. Unless specified, the topmost operator (the source) itself runs on the Thread in which the subscribe() call was made.
So there is nothing that says that it has to be a new thread.

Spring - All request are going to the same mapping

I don't know how it happened because I didn't do big changes but now all the request I do to my spring server application are respond by the same service.
My build.gradle (no security at all)
My main (no big deal)
#SpringBootApplication
public class PolifoniaApplication {
public static void main(String[] args) {
SpringApplication.run(PolifoniaApplication.class, args);
}
}
My Controller (every single request goes to '/login' even from another classes, and I even comment the Mapping line and it keeps going to login). Also even non existing URIs it will go to the "/login" service
#CrossOrigin
#RestController
public class UsuarioController {
Logger logger = LoggerFactory.getLogger(UsuarioController.class);
private static final String ESTUDIANTE_GROUP = "ou=people,dc=springframework,dc=org";
private static final String PROFESOR_GROUP = "ou=otherpeople,dc=springframework,dc=org";
private static final String ADMINISTRATIVO_GROUP = "ou=space cadets,dc=springframework,dc=org";
#Autowired
private LdapTemplate ldapTemplate;
#Autowired
private UsuarioRepository usuarioRepository;
#Autowired
private SesionRepository sesionRepository;
#GetMapping("/all")
public List<String> getAllPersonNames() {
return ldapTemplate.search(query().where("objectclass").is("person"), new AttributesMapper<String>() {
public String mapFromAttributes(Attributes attrs) throws NamingException {
return attrs.get("sn").get().toString();
}
});
}
/**
* Servicio de confirmación de login con LDAP o por token
* #param authData - información de seguridad
* #return String con token si se generó
*/
#PostMapping("/login")
#RequestMapping(produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<String> autenticar(#RequestBody AuthData authData) {
ResponseEntity<String> response;
boolean correcto = ldapTemplate.authenticate("", String.format("(uid=%s)", authData.getUsername()), authData.getPassword());
if (correcto) {
Usuario usuario = buscarUsuarioLdap(authData);
Sesion sesion = new Sesion(authData, usuario.getId());
response = ResponseEntity.status(HttpStatus.OK).body(Utilities.stringToJson("token", sesion.getToken()));
sesionRepository.save(sesion);
} else {
throw new AuthPolifoniaException();
}
return response;
}
/**
* Método que registra un usuario que ingresa por primera vez a la aplicación
* #param authData - Datos del usuario
*/
private Usuario buscarUsuarioLdap(AuthData authData) {
Usuario usuario = usuarioRepository.findByUsername(authData.getUsername());
if (usuario == null) {
TipoUsuario tipoUsuario = TipoUsuario.ESTUDIANTE;
LdapQuery estudianteQuery = LdapQueryBuilder.query().base(ESTUDIANTE_GROUP).searchScope(SearchScope.SUBTREE)
.filter(new EqualsFilter("uid", authData.getUsername()));
List<String> result = ldapTemplate.search(estudianteQuery, new AttributesMapper<String>() {
public String mapFromAttributes(Attributes attrs) throws NamingException {
return attrs.get("cn").get().toString();
}
});
if (result.isEmpty()) {
tipoUsuario = TipoUsuario.PROFESOR;
LdapQuery profesorQuery = LdapQueryBuilder.query().base(PROFESOR_GROUP).searchScope(SearchScope.SUBTREE)
.filter(new EqualsFilter("uid", authData.getUsername()));
result = ldapTemplate.search(profesorQuery, new AttributesMapper<String>() {
public String mapFromAttributes(Attributes attrs) throws NamingException {
return attrs.get("cn").get().toString();
}
});
}
if (result.isEmpty()) {
tipoUsuario = TipoUsuario.ADMINISTRATIVO;
LdapQuery administrativoQuery = LdapQueryBuilder.query().base(ADMINISTRATIVO_GROUP)
.searchScope(SearchScope.SUBTREE).filter(new EqualsFilter("uid", authData.getUsername()));
result = ldapTemplate.search(administrativoQuery, new AttributesMapper<String>() {
public String mapFromAttributes(Attributes attrs) throws NamingException {
return attrs.get("cn").get().toString();
}
});
}
if (result.isEmpty()) {
tipoUsuario = null;
}
usuario = new Usuario(result.get(0), authData.getUsername(), tipoUsuario);
usuarioRepository.save(usuario);
}
return usuario;
}
/**
* Servicio que destruye el token de sesión
* #param authData - Datos del usuario (token)
*/
#PostMapping("/logout")
public void logout(#RequestHeader(HttpHeaders.WWW_AUTHENTICATE) String token) {
sesionRepository.deleteById(token);
}
And I have an advice that was working. It seems that if I run my tests it works fine. I cannot find any logic involved, it's madness.
And I tried to comment the method but then there is no answer at all by my server.
This is the log since I send the request (in this case "localhost:8080/comunidades", but it happens with all of the services even "/logout")
2018-11-25 10:50:12.981 DEBUG 11840 --- [nio-8080-exec-3] o.a.tomcat.util.net.SocketWrapperBase : Socket: [org.apache.tomcat.util.net.NioEndpoint$NioSocketWrapper#d601e02:org.apache.tomcat.util.net.NioChannel#75d2ecd0:java.nio.channels.SocketChannel[connected local=/0:0:0:0:0:0:0:1:8080 remote=/0:0:0:0:0:0:0:1:7687]], Read from buffer: [0]
2018-11-25 10:50:12.982 DEBUG 11840 --- [nio-8080-exec-3] org.apache.tomcat.util.net.NioEndpoint : Socket: [org.apache.tomcat.util.net.NioEndpoint$NioSocketWrapper#d601e02:org.apache.tomcat.util.net.NioChannel#75d2ecd0:java.nio.channels.SocketChannel[connected local=/0:0:0:0:0:0:0:1:8080 remote=/0:0:0:0:0:0:0:1:7687]], Read direct from socket: [301]
2018-11-25 10:50:12.983 DEBUG 11840 --- [nio-8080-exec-3] o.a.coyote.http11.Http11InputBuffer : Received [GET /comunidades HTTP/1.1
Content-Type: application/json
cache-control: no-cache
Postman-Token: e30db580-fcdd-4c95-8195-f16b9186420a
User-Agent: PostmanRuntime/7.4.0
Accept: */*
Host: localhost:8080
accept-encoding: gzip, deflate
content-length: 17
Connection: keep-alive
{
"pageNum": 1
}]
2018-11-25 10:50:12.998 DEBUG 11840 --- [nio-8080-exec-3] o.a.c.authenticator.AuthenticatorBase : Security checking request GET /comunidades
2018-11-25 10:50:12.998 DEBUG 11840 --- [nio-8080-exec-3] org.apache.catalina.realm.RealmBase : No applicable constraints defined
2018-11-25 10:50:12.998 DEBUG 11840 --- [nio-8080-exec-3] o.a.c.authenticator.AuthenticatorBase : Not subject to any constraint
2018-11-25 10:50:12.999 DEBUG 11840 --- [nio-8080-exec-3] org.apache.tomcat.util.http.Parameters : Set encoding to UTF-8
2018-11-25 10:50:12.999 DEBUG 11840 --- [nio-8080-exec-3] o.s.web.servlet.DispatcherServlet : GET "/comunidades", parameters={}
2018-11-25 10:50:12.999 DEBUG 11840 --- [nio-8080-exec-3] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to public org.springframework.http.ResponseEntity<java.lang.String> com.poligran.polifonia.controllers.UsuarioController.autenticar(com.poligran.polifonia.utilities.AuthData)
2018-11-25 10:50:13.000 DEBUG 11840 --- [nio-8080-exec-3] o.j.s.OpenEntityManagerInViewInterceptor : Opening JPA EntityManager in OpenEntityManagerInViewInterceptor
2018-11-25 10:50:13.001 DEBUG 11840 --- [nio-8080-exec-3] m.m.a.RequestResponseBodyMethodProcessor : Read "application/json;charset=UTF-8" to [AUTHDATA - User: null, Password: null]
2018-11-25 10:50:13.003 DEBUG 11840 --- [nio-8080-exec-3] o.s.l.c.support.AbstractContextSource : Got Ldap context on server 'ldap://localhost:12345'
2018-11-25 10:50:13.004 INFO 11840 --- [nio-8080-exec-3] o.s.ldap.core.LdapTemplate : No results found for search, base: ''; filter: '(uid=null)'.
2018-11-25 10:50:13.004 DEBUG 11840 --- [nio-8080-exec-3] .m.m.a.ExceptionHandlerExceptionResolver : Using #ExceptionHandler com.poligran.polifonia.utilities.ErrorMessage com.poligran.polifonia.advices.AuthAdvice.dataNotFoundHandler(com.poligran.polifonia.exceptions.AuthPolifoniaException)
2018-11-25 10:50:13.005 DEBUG 11840 --- [nio-8080-exec-3] m.m.a.RequestResponseBodyMethodProcessor : Using 'application/json', given [*/*] and supported [application/json, application/*+json, application/json, application/*+json]
2018-11-25 10:50:13.005 DEBUG 11840 --- [nio-8080-exec-3] m.m.a.RequestResponseBodyMethodProcessor : Writing [com.poligran.polifonia.utilities.ErrorMessage#6fd9c4ca]
2018-11-25 10:50:13.008 WARN 11840 --- [nio-8080-exec-3] .m.m.a.ExceptionHandlerExceptionResolver : Resolved [com.poligran.polifonia.exceptions.AuthPolifoniaException: Error en la autenticación del usuario]
2018-11-25 10:50:13.008 DEBUG 11840 --- [nio-8080-exec-3] o.j.s.OpenEntityManagerInViewInterceptor : Closing JPA EntityManager in OpenEntityManagerInViewInterceptor
2018-11-25 10:50:13.008 DEBUG 11840 --- [nio-8080-exec-3] o.s.orm.jpa.EntityManagerFactoryUtils : Closing JPA EntityManager
2018-11-25 10:50:13.008 DEBUG 11840 --- [nio-8080-exec-3] o.s.web.servlet.DispatcherServlet : Completed 401 UNAUTHORIZED
2018-11-25 10:50:13.009 DEBUG 11840 --- [nio-8080-exec-3] o.a.tomcat.util.net.SocketWrapperBase : Socket: [org.apache.tomcat.util.net.NioEndpoint$NioSocketWrapper#d601e02:org.apache.tomcat.util.net.NioChannel#75d2ecd0:java.nio.channels.SocketChannel[connected local=/0:0:0:0:0:0:0:1:8080 remote=/0:0:0:0:0:0:0:1:7687]], Read from buffer: [0]
2018-11-25 10:50:13.009 DEBUG 11840 --- [nio-8080-exec-3] org.apache.tomcat.util.net.NioEndpoint : Socket: [org.apache.tomcat.util.net.NioEndpoint$NioSocketWrapper#d601e02:org.apache.tomcat.util.net.NioChannel#75d2ecd0:java.nio.channels.SocketChannel[connected local=/0:0:0:0:0:0:0:1:8080 remote=/0:0:0:0:0:0:0:1:7687]], Read direct from socket: [0]
2018-11-25 10:50:13.009 DEBUG 11840 --- [nio-8080-exec-3] o.apache.coyote.http11.Http11Processor : Socket: [org.apache.tomcat.util.net.NioEndpoint$NioSocketWrapper#d601e02:org.apache.tomcat.util.net.NioChannel#75d2ecd0:java.nio.channels.SocketChannel[connected local=/0:0:0:0:0:0:0:1:8080 remote=/0:0:0:0:0:0:0:1:7687]], Status in: [OPEN_READ], State out: [OPEN]
2018-11-25 10:50:22.583 DEBUG 11840 --- [l-1 housekeeper] com.zaxxer.hikari.pool.HikariPool : HikariPool-1 - Pool stats (total=10, active=0, idle=10, waiting=0)
Thanks so much for your time.

Spring unit test MockMvc fails when using custom filter in Spring Security

I have a web application that should only be callable from specific IP addresses. Other than that, there is no need for authentication or for authorization; if you're coming from the right IP, you can see everything.
To that end, searching StackOverflow and other places, I found a number of suggestions for filtering requests by IP address in Spring Security. They all took this form (extending WebSecurityConfigurerAdapter using java configuration):
http.authorizeRequests().anyRequest().access("hasIpAddress('127.0.0.1/0')");
However, that never worked for me; it never rejected any request, no matter what IP address I made the request from. Instead, I implemented my IP filtering with a custom filter like this:
#Configuration
#EnableWebSecurity
#PropertySource("classpath:config.properties")
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private static final Logger logger = LoggerFactory.getLogger(SecurityConfig.class);
#Autowired
private Environment env;
#Override
protected void configure(HttpSecurity http) throws Exception {
String ipRange = env.getRequiredProperty("trusted_ips");
logger.info("##### SETTING UP SECURITY CONFIGURATION #############");
logger.info("## trusted_ips: " + ipRange);
logger.info("##### SETTING UP SECURITY CONFIGURATION - END #############");
http.addFilterBefore(new IPSecurityFilter(ipRange), J2eePreAuthenticatedProcessingFilter.class)
.authorizeRequests().antMatchers("/**").permitAll();
}
}
My IPSecurityFilter:
public class IPSecurityFilter extends OncePerRequestFilter {
private static final Logger logger = LoggerFactory.getLogger(IPSecurityFilter.class);
private String[] ipAddresses;
public IPSecurityFilter(String strIPAddresses) {
logger.info("#### Our IP Address whitelist: " + strIPAddresses);
this.ipAddresses = strIPAddresses.split(",");
}
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
logger.info("Checking whether request should be allowed: " + request.getRequestURI());
logger.info("### Request is coming from IP address: " + request.getRemoteAddr());
for (String ipAddress : ipAddresses) {
if (ipAddress.equals(request.getRemoteAddr())) {
logger.info("### Allowing request from ip address: " + request.getRemoteAddr());
return; // We accept requests from this IP address
}
}
// The remote IP address isn't on our white list; throw an exception
throw new AccessDeniedException("Access has been denied for your IP address: " + request.getRemoteAddr());
}
}
This seems to work in that the request is rejected if it originates from an IP Address that isn't on my white list.
However, with this configuration, my unit (using MockMvc) test fails; and it fails in a way that I would never have expected. When the unit test runs, it appears to use the Spring Security configuration correctly and the request passes the security test (the IP white list includes 127.0.0.1 and according to the log that is generated while the test is being run, the request is coming from that IP). However, the request never seems to be routed to my controller.
Here is my test:
#RunWith(SpringRunner.class)
#WebMvcTest()
//#WebMvcTest(value = HandlerController.class)
#AutoConfigureMockMvc
#Import(SecurityConfig.class)
public class HandlerControllerTest {
#Autowired
private MockMvc mvc;
#Test
public void getIndex() throws Exception {
mvc.perform(MockMvcRequestBuilders.get("/").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(content().json("{\"services\":[\"OutboundMessageService\"]}", true));
}
}
And finally, here is my controller (please ignore the idiotic way that I'm generating the JSON return value, it's still very early in development):
#RestController
public class HandlerController {
private static final Logger logger = LoggerFactory.getLogger(HandlerController.class);
#RequestMapping("/")
public String index() {
logger.info("### handling a request for / ###");
return "{\"services\":[\"OutboundMessageService\"]}";
}
}
And here are the test results:
2017-11-14 08:29:12.151 INFO 25412 --- [ main] c.z.s.controllers.HandlerControllerTest : Starting HandlerControllerTest on 597NLL1 with PID 25412 (started by User in C:\Development\KnowledgeBin\NewArchitecture\OutboundMessageHandler)
2017-11-14 08:29:12.152 INFO 25412 --- [ main] c.z.s.controllers.HandlerControllerTest : No active profile set, falling back to default profiles: default
2017-11-14 08:29:12.178 INFO 25412 --- [ main] o.s.w.c.s.GenericWebApplicationContext : Refreshing org.springframework.web.context.support.GenericWebApplicationContext#209da20d: startup date [Tue Nov 14 08:29:12 MST 2017]; root of context hierarchy
2017-11-14 08:29:13.883 INFO 25412 --- [ main] b.a.s.AuthenticationManagerConfiguration :
Using default security password: 56e3fab8-f7fb-4fbd-b2d2-e37eae8cef5e
2017-11-14 08:29:13.962 INFO 25412 --- [ main] c.z.services.security.IPSecurityFilter : #### Our IP Address whitelist: 122.22.22.22,127.0.0.1
2017-11-14 08:29:14.086 INFO 25412 --- [ main] o.s.s.web.DefaultSecurityFilterChain : Creating filter chain: org.springframework.security.web.util.matcher.AnyRequestMatcher#1, [org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter#3f4f9acd, org.springframework.security.web.context.SecurityContextPersistenceFilter#470a9030, org.springframework.security.web.header.HeaderWriterFilter#60c16548, org.springframework.security.web.csrf.CsrfFilter#435ce306, org.springframework.security.web.authentication.logout.LogoutFilter#607b2792, com.zpaper.services.security.IPSecurityFilter#46baf579, org.springframework.security.web.savedrequest.RequestCacheAwareFilter#27494e46, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter#36453307, org.springframework.security.web.authentication.AnonymousAuthenticationFilter#4bf324f9, org.springframework.security.web.session.SessionManagementFilter#452c8a40, org.springframework.security.web.access.ExceptionTranslationFilter#39ce27f2, org.springframework.security.web.access.intercept.FilterSecurityInterceptor#5767b2af]
2017-11-14 08:29:14.183 INFO 25412 --- [ main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/]}" onto public java.lang.String com.zpaper.services.controllers.HandlerController.index()
2017-11-14 08:29:14.184 INFO 25412 --- [ main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/OutboundMessageService]}" onto public java.lang.String com.zpaper.services.controllers.HandlerController.outboundMessage()
2017-11-14 08:29:14.189 INFO 25412 --- [ main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error]}" onto public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.BasicErrorController.error(javax.servlet.http.HttpServletRequest)
2017-11-14 08:29:14.190 INFO 25412 --- [ main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error],produces=[text/html]}" onto public org.springframework.web.servlet.ModelAndView org.springframework.boot.autoconfigure.web.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)
2017-11-14 08:29:14.243 INFO 25412 --- [ main] c.z.s.config.HandlerWebConfiguration : #### My Configuration handler was called ####
2017-11-14 08:29:14.253 INFO 25412 --- [ main] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/**] onto handler of type [class org.springframework.web.servlet.resource.DefaultServletHttpRequestHandler]
2017-11-14 08:29:14.313 INFO 25412 --- [ main] s.w.s.m.m.a.RequestMappingHandlerAdapter : Looking for #ControllerAdvice: org.springframework.web.context.support.GenericWebApplicationContext#209da20d: startup date [Tue Nov 14 08:29:12 MST 2017]; root of context hierarchy
2017-11-14 08:29:14.784 INFO 25412 --- [ main] o.s.b.t.m.w.SpringBootMockServletContext : Initializing Spring FrameworkServlet ''
2017-11-14 08:29:14.784 INFO 25412 --- [ main] o.s.t.web.servlet.TestDispatcherServlet : FrameworkServlet '': initialization started
2017-11-14 08:29:14.805 INFO 25412 --- [ main] o.s.t.web.servlet.TestDispatcherServlet : FrameworkServlet '': initialization completed in 21 ms
2017-11-14 08:29:14.897 INFO 25412 --- [ main] c.z.s.controllers.HandlerControllerTest : Started HandlerControllerTest in 3.095 seconds (JVM running for 3.995)
2017-11-14 08:29:14.981 INFO 25412 --- [ main] c.z.services.security.IPSecurityFilter : Checking whether request should be allowed: /
2017-11-14 08:29:14.981 INFO 25412 --- [ main] c.z.services.security.IPSecurityFilter : ### Request is coming from IP address: 127.0.0.1
2017-11-14 08:29:14.981 INFO 25412 --- [ main] c.z.services.security.IPSecurityFilter : ### Allowing request from ip address: 127.0.0.1
MockHttpServletRequest:
HTTP Method = GET
Request URI = /
Parameters = {}
Headers = {Accept=[application/json]}
Handler:
Type = null
Async:
Async started = false
Async result = null
Resolved Exception:
Type = null
ModelAndView:
View name = null
View = null
Model = null
FlashMap:
Attributes = null
MockHttpServletResponse:
Status = 200
Error message = null
Headers = {X-Content-Type-Options=[nosniff], X-XSS-Protection=[1; mode=block], Cache-Control=[no-cache, no-store, max-age=0, must-revalidate], Pragma=[no-cache], Expires=[0], X-Frame-Options=[DENY]}
Content type = null
Body =
Forwarded URL = null
Redirected URL = null
Cookies = []
Tests run: 1, Failures: 0, Errors: 1, Skipped: 0, Time elapsed: 3.363 sec <<< FAILURE! - in com.zpaper.services.controllers.HandlerControllerTest
getIndex(com.zpaper.services.controllers.HandlerControllerTest) Time elapsed: 0.12 sec <<< ERROR!
org.json.JSONException: Unparsable JSON string:
at org.skyscreamer.jsonassert.JSONParser.parseJSON(JSONParser.java:42)
As can be seen in the log messages, the IP filter is being invoked and is allowing the request to continue. However, the debug string that is being emitted in my handler is nowhere to be seen and the return body is blank. Can anyone tell me why my security filter would prevent MockMvc from having its request successfully routed to my controller?
Final Note: if I use the http.authorizeRequests().anyRequest().access("hasIpAddress('127.0.0.1/0')"); configuration that I first listed or completely get rid of Spring Security by removing my SecurityConfig class, the request is routed successfully to my handler.
I figured out how to make the test work. I was not able to find a single article that answered my question but by taking different suggestions from multiple blog posts, I came up with this which works for me:
#RunWith(SpringRunner.class)
#WebMvcTest(controllers = HandlerController.class)
public class HandlerControllerTest {
private MockMvc mvc;
#Autowired
private WebApplicationContext webApplicationContext;
#Before
public void setUp() {
// mvc = MockMvcBuilders.standaloneSetup(new HandlerController()).build();
mvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
}
#Test
public void getIndex() throws Exception {
mvc.perform(get("/").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(content().json("{\"services\":[\"OutboundMessageService\"]}", true));
}
#Test
public void getMessageService() throws Exception {
mvc.perform(get("/OutboundMessageService").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(content().json("{\"status\": \"SUCCESS\"}", true));
}
}
As you can see, I am no longer auto-wiring the MockMvc object and allowing it be automatically set up but am instead setting it up myself in the setUp() method. The commented-out line in the setUp() method works to successfully test my controller also but it doesn't route the request through my Spring Security IP address filter. I'm leaving it in so that users that don't need to test Spring Security can see an alternate method to set up the MockMvc object. The uncommented line sets up a MockMvc object such that it runs the request through both the security filters and my controller.
I know it is late but for others looking for the answer, you can add that filter to the MockMvc object like this:
#Autowired
private MockMvc mvc;
#Autowired
private YourCustomFilter filter;
#Before
public void setUp() {
mvc = MockMvcBuilders.webAppContextSetup(webApplicationContext)
.addFilter(filter).build();
}

Junit parallel execution of same task

I found a bug in code, and I know that when 20 threads are running the code in parallel something fails. Now I'd like to create test for it, but I don't know how to write it.
#Test
public void testAuthPerformance() {
int numberOfCalls = 20;
CompletableFuture<ResponseEntity<OAuth2AccessToken>>[] tasksArray = new CompletableFuture[numberOfCalls];
for (int i = 0; i < numberOfCalls; i++) {
tasksArray[i] = CompletableFuture
.supplyAsync(() -> new OauthCaller().callAuth(), executorService);
}
CompletableFuture.allOf(tasksArray).join();
for (CompletableFuture<ResponseEntity<OAuth2AccessToken>> task : tasksArray) {
ResponseEntity<OAuth2AccessToken> responseEntity = task.join();
assertEquals(HttpStatus.OK, responseEntity.getStatusCode());
}
}
problem is that task.join seems like it's performing in sequence instead of parallel.
In logs I can see following which seems to me like sequential execution:
2017-03-27 13:00:13.263 DEBUG 37381 --- [nio-9999-exec-4] .s.o.p.e.FrameworkEndpointHandlerMapping : Looking up handler method for path /oauth/token
2017-03-27 13:00:13.263 DEBUG 37381 --- [nio-9999-exec-4] .s.o.p.e.FrameworkEndpointHandlerMapping : Returning handler method [public org.springframework.http.ResponseEntity<org.springframework.security.oauth2.common.OAuth2AccessToken> org.springframework.security.oauth2.provider.endpoint.TokenEndpoint.postAccessToken(java.security.Principal,java.util.Map<java.lang.String, java.lang.String>) throws org.springframework.web.HttpRequestMethodNotSupportedException]
2017-03-27 13:00:13.264 DEBUG 37381 --- [nio-9999-exec-4] .o.p.p.ResourceOwnerPasswordTokenGranter : Getting access token for: demo
2017-03-27 13:00:13.342 DEBUG 37381 --- [nio-9999-exec-6] .s.o.p.e.FrameworkEndpointHandlerMapping : Looking up handler method for path /oauth/token
2017-03-27 13:00:13.342 DEBUG 37381 --- [nio-9999-exec-6] .s.o.p.e.FrameworkEndpointHandlerMapping : Returning handler method [public org.springframework.http.ResponseEntity<org.springframework.security.oauth2.common.OAuth2AccessToken> org.springframework.security.oauth2.provider.endpoint.TokenEndpoint.postAccessToken(java.security.Principal,java.util.Map<java.lang.String, java.lang.String>) throws org.springframework.web.HttpRequestMethodNotSupportedException]
2017-03-27 13:00:13.345 DEBUG 37381 --- [nio-9999-exec-6] .o.p.p.ResourceOwnerPasswordTokenGranter : Getting access token for: demo
2017-03-27 13:00:13.482 DEBUG 37381 --- [nio-9999-exec-7] .s.o.p.e.FrameworkEndpointHandlerMapping : Looking up handler method for path /oauth/token
2017-03-27 13:00:13.482 DEBUG 37381 --- [nio-9999-exec-7] .s.o.p.e.FrameworkEndpointHandlerMapping : Returning handler method [public org.springframework.http.ResponseEntity<org.springframework.security.oauth2.common.OAuth2AccessToken> org.springframework.security.oauth2.provider.endpoint.TokenEndpoint.postAccessToken(java.security.Principal,java.util.Map<java.lang.String, java.lang.String>) throws org.springframework.web.HttpRequestMethodNotSupportedException]
2017-03-27 13:00:13.483 DEBUG 37381 --- [nio-9999-exec-7] .o.p.p.ResourceOwnerPasswordTokenGranter : Getting access token for: demo
With following code I can simulate the issue, but there's no simple way for assertions.
ExecutorService executorService = Executors.newFixedThreadPool(20);
try {
for (int i = 0; i < 20; i++) {
executorService.execute(() -> new OauthCaller().callAuth());
}
} finally {
executorService.shutdown();
}
can someone please suggest the way how to unit test such code? Thanks

Categories