Apache HttpComponents CookieStore Not Storing Cookies - apache-httpclient-4.x

I'm using HttpComponents 4.5.2 and I'm trying to store cookies as I need to use them for login and other requests. The code works fine whilst the application is still running, but the problem here is when I restart it, the cookies that were supposed to be stored in CookieStore are not there. Here's what I've written:
public static void main( String[] args ) throws InterruptedException
{
RequestConfig globalConfig = RequestConfig.custom()
.setCookieSpec(CookieSpecs.STANDARD).build();
BasicCookieStore cookieStore = new BasicCookieStore();
HttpClientContext context = HttpClientContext.create();
context.setCookieStore(cookieStore);
CloseableHttpAsyncClient httpclient = HttpAsyncClients.custom()
.setDefaultRequestConfig(globalConfig)
.setDefaultCookieStore(cookieStore)
.build();
httpclient.start();
login(httpclient, context);
}
public static void login(CloseableHttpAsyncClient httpClient, HttpClientContext context) throws InterruptedException
{
JSONObject json = new JSONObject("{ email : blahblahblah1, password : blahblahblah2 }");
StringEntity requestEntity = new StringEntity(
json.toString(),
ContentType.APPLICATION_JSON);
HttpPost postMethod = new HttpPost("http://localhost:8080/login");
postMethod.setEntity(requestEntity);
final CountDownLatch latch = new CountDownLatch(1);
httpClient.execute(postMethod, context, new FutureCallback<HttpResponse>() {
public void completed(final HttpResponse response) {
latch.countDown();
System.out.println(postMethod.getRequestLine() + "->" + response.getStatusLine());
//System.out.println(context.getCookieStore().getCookies().size());
}
public void failed(final Exception ex) {
latch.countDown();
System.out.println(postMethod.getRequestLine() + "->" + ex);
}
public void cancelled() {
latch.countDown();
System.out.println(postMethod.getRequestLine() + " cancelled");
}
});
latch.await();
}
I've read the HttpComponents documentation and the section 3.5 about cookies says:
HttpClient can work with any physical representation of a persistent cookie store that implements the CookieStore interface. The default CookieStore implementation called BasicCookieStore is a simple implementation backed by a java.util.ArrayList. Cookies stored in an BasicClientCookie object are lost when the container object get garbage collected. Users can provide more complex implementations if necessary
So I'm wondering if it's left to it's users to implement some kind of structure that can effectively store cookies or if I'm missing something.

Yes, using BasicCookieStore backed by ArrayList means that when your jvm exists, the data there is being lost just like any ArrayList in memory.
BasicCookieStore class also implements Serializable so you can use that to persist it to disk and restore back on your app startup if the file was there.
You can borrow some code from the tests verifying that flow TestBasicCookieStore#testSerialization.

Related

Apache Camel: Unit testing for file and http components

I am fairly new to Camel & just managed to implement a use case as below with 2 routes which is using file & http components. Looking for some leads on writing junits for the same. Have tried some sample test case below based on the inputs that i found on the net. Not sure if that suffices. Appreciate your help!
Implementation:
#Override
public void configure() throws Exception {
// Global Exception Handling block
onException(FileWatcherException.class).process(new Processor() {
public void process(Exchange exchange) throws Exception {
System.out.println("Exception handled");
}
}).to("file:C:/error?recursive=true").handled(true);
// Actively listen to the input folder for an incoming file
from("file:C:/input?noop=true&recursive=true&delete=true")
.process(new Processor() {
public void process(Exchange exchange) throws Exception {
String fileName = exchange.getIn().getHeader("CamelFileName").toString();
exchange.getIn().setHeader("fileName", fileName);
}
})
// Call the Get endpoint with fileName as input parameter
.setHeader(Exchange.HTTP_METHOD, simple("GET"))
.toD("http://localhost:8090/fileWatcher?fileName=${header.fileName}")
.choice()
// if the API returns true, move the file to the outbox folder
.when(header(Exchange.HTTP_RESPONSE_CODE).isEqualTo(constant(200)))
.to("file:C:/outbox?noop=true&recursive=true")
.endChoice()
// If the API's response code is other than 200, move the file to error folder
.otherwise()
.log("Moving the file to error folder")
.to("file:C:/error?recursive=true")
.end();
// Listen to the outbox folder for file arrival after it gets moved in the above step
from("file:C:/outbox?noop=true&recursive=true")
// Request Body for POST call is set in FileDetailsProcessor class
.process(new FileDetailsProcessor())
.marshal(jsonDataFormat)
.setHeader(Exchange.HTTP_METHOD, simple("POST"))
.setHeader(Exchange.CONTENT_TYPE, constant("application/json"))
// Call the Rest endpoint with fileName & filePath as RequestBody
.to("http://localhost:8090/fileWatcher")
.process(new MyProcessor())
.end();
}
Junit
#Test
public void checkFileWatcherFunctionality() throws Exception {
context.getRouteDefinitions().get(0).adviceWith(context, new AdviceWithRouteBuilder() {
#Override
public void configure() throws Exception {
// mocking all endpoints. **QUESTION** - Is this required?
mockEndpointsAndSkip("http://localhost:8090:fileWatcher?fileName=loan.csv");
mockEndpointsAndSkip("file:C:/processing");
mockEndpointsAndSkip("file:C:/error");
mockEndpointsAndSkip("http://localhost:8090:fileWatcher");
}
});
context.start();
// **QUESTION** - This is a GET call. Expecting only the HTTP status code from it. How to check that?
getMockEndpoint("mock:http://localhost:8090:fileWatcher?fileName=abc.txt").expectedBodyReceived();
// **QUESTION** - This is a POST call. How to send request body along? Expecting only the HTTP status code from it. How to check that?
getMockEndpoint("mock:http://localhost:8090:fileWatcher").expectedBodyReceived();
// **QUESTION** - Is this the right way to check?
getMockEndpoint("mock:file:C:/processing").expectedFileExists("loan.csv");;
template.sendBodyAndHeader("file:C:/inbound", "", Exchange.FILE_NAME, "loan.csv");
// QUESTION - What can be asserted now?
}
Also - How to write test cases for negative flow (exception scenario)? Looking for suggestions.
I have managed to draft the test case. Is this the right approach or can there be a better way?
This might be more of an integration test i suppose.
The issue i see now is that the test case doesn't report at the end (success or failure), instead it keeps waiting for file arrival in the input folder. What am i missing?
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class FileWatcherRouteBuilderTest extends CamelTestSupport {
#Autowired
private TestRestTemplate restTemplate;
#Override
public RoutesBuilder createRouteBuilder() throws Exception {
return new FileWatcherRouteBuilder();
}
#Test
public void testFileCopy() throws Exception {
template.sendBodyAndHeader("file:C:/inbound", "", Exchange.FILE_NAME, "abc.csv");
// Call the GET endpoint
ResponseEntity<String> getResponse = restTemplate.getForEntity("http:localhost:8090/fileWatcher?fileName=abc.csv",
String.class);
assertTrue("Get call is unsuccessful", getResponse.getStatusCode().is2xxSuccessful());
String response = getResponse.getBody();
assertTrue(!response.isEmpty());
// The file would have moved to output folder now.
File targetFile = new File("C:/processing");
assertTrue(targetFile.isDirectory());
assertEquals(1, targetFile.listFiles().length);
// Since we need to extract the file name, doing the below step
Exchange exchange = consumer.receive("file:C:/processing");
String fileName = exchange.getIn().getHeader("CamleFileName").toString();
// RequestBody needed for POST call
FileDetails fileDetails = new FileDetails(fileName, "C:/processing/"+fileName);
HttpHeaders headers = new HttpHeaders();
HttpEntity<FileDetails> request = new HttpEntity<FileDetails>(fileDetails, headers);
// Call the POST endpoint
ResponseEntity<String> postResponse = restTemplate.postForEntity("http://localhost:8090/fileWatcher", request, String.class);
assertTrue("Post call is unsuccessful", postResponse.getStatusCode().is2xxSuccessful());
// Asserting that after both the web service calls, the file is still available in the output folder
assertEquals(1, targetFile.listFiles().length);
}
}

CAS cannot find authentication handler that supports UsernamePasswordCredential

I have a custom handler like this:
Public class DatabaseAuthenticationHandler extends AbstractJdbcUsernamePasswordAuthenticationHandler {
#Override
protected AuthenticationHandlerExecutionResult authenticateUsernamePasswordInternal(
UsernamePasswordCredential credential, String originalPassword) throws GeneralSecurityException, PreventedException {
final String username = credential.getUsername();
logger.debug("***Username:"+username);
logger.debug("***Password:"+credential.getPassword());
return createHandlerResult(credential, new SimplePrincipal(), null);
}
#Override
public boolean supports(final Credential credential) {
return true;
}
}
To me, this should always log a user in no matter what. But I see in the logs this:
ERROR [org.apereo.cas.authentication.PolicyBasedAuthenticationManager]
- <Authentication has failed. Credentials may be incorrect or CAS cannot find authentication handler that supports
[UsernamePasswordCredential(username=sadf, source=MyJDBCAuthenticationManager)] of type [UsernamePasswordCredential].
Examine the configuration to ensure a method of authentication is defined and analyze CAS logs at DEBUG level to trace the authentication event.
which makes no sense to me as I can see in the logs that cas is calling the authenticatUsernamePasswordInternal method. Obviously this handler supports, well everything.
Why can't I log in?
I think you best use principalFactory.createPrincipal to create the principal rather than returning an new SimplePrincipal().
In your AuthenticationEventExecutionPlanConfigurer & DatabaseAuthenticationHandler, add the following:
AuthenticationEventExecutionPlanConfigurer.java
#Autowired
#Qualifier("principalFactory")
private PrincipalFactory principalFactory;
#Bean
public DatabaseAuthenticationHandler databaseAuthenticationHandler() {
return new DatabaseAuthenticationHandler(principalFactory);
}
DatabaseAuthenticationHandler
Public class DatabaseAuthenticationHandler extends AbstractJdbcUsernamePasswordAuthenticationHandler {
private final PrincipalFactory principalFactory;
public DatabaseAuthenticationHandler (PrincipalFactory principalFactory){
this.principalFactory = principalFactory;
}
#Override
protected AuthenticationHandlerExecutionResult authenticateUsernamePasswordInternal(
UsernamePasswordCredential credential, String originalPassword) throws GeneralSecurityException, PreventedException {
final String username = credential.getUsername();
logger.debug("***Username:"+username);
logger.debug("***Password:"+credential.getPassword());
/////// below here's the change /////////
return createHandlerResult(credential, this.principalFactory.createPrincipal(username), null);
}
#Override
public boolean supports(final Credential credential) {
return true;
}
}
See if the above works, thanks.
The root cause of this problem is that you pass a null parameter to createHandlerResult method,you can change it to new ArrayList<>. I also encountered this problem(My CAS version is 5.3.9).And I also tried the solution gaving by Ng Sek Long,but it didn't work.Then I tried to solve it by myself. I searched for the error message in CAS code and found it in PolicyBasedAuthenticationManager class.
try {
PrincipalResolver resolver = this.getPrincipalResolverLinkedToHandlerIfAny(handler, transaction);
LOGGER.debug("Attempting authentication of [{}] using [{}]", credential.getId(), handler.getName());
this.authenticateAndResolvePrincipal(builder, credential, resolver, handler);
AuthenticationCredentialsThreadLocalBinder.bindInProgress(builder.build());
Pair<Boolean, Set<Throwable>> failures = this.evaluateAuthenticationPolicies(builder.build(), transaction);
proceedWithNextHandler = !(Boolean)failures.getKey();
} catch (Exception var15) {
LOGGER.error("Authentication has failed. Credentials may be incorrect or CAS cannot find authentication handler that supports [{}] of type [{}]. Examine the configuration to ensure a method of authentication is defined and analyze CAS logs at DEBUG level to trace the authentication event.", credential, credential.getClass().getSimpleName());
this.handleAuthenticationException(var15, handler.getName(), builder);
proceedWithNextHandler = true;
}
In the above code snippet, the authenticateAndResolvePrincipal method declaired two kinds of exception.Looked at this method, I found there is a line of code which may throws that two.
AuthenticationHandlerExecutionResult result = handler.authenticate(credential);
The key code which lead to this problem is in DefaultAuthenticationHandlerExecutionResult class.
public DefaultAuthenticationHandlerExecutionResult(final AuthenticationHandler source, final CredentialMetaData metaData, final Principal p, #NonNull final List<MessageDescriptor> warnings) {
this(StringUtils.isBlank(source.getName()) ? source.getClass().getSimpleName() : source.getName(), metaData, p, warnings);
if (warnings == null) {
throw new NullPointerException("warnings is marked #NonNull but is null");
}
}
So, if you use createHandlerResult(credential, new SimplePrincipal(), null), NullPointerException will throw at runtime.It will be catched by catch (Exception var15) code bock and log the error message you see.

How to correctly load Firebase ServiceAccount json resource with Spring MVC?

I'm trying to connect my Spring MVC (not Spring Boot) application to Firebase. My application's folder structure looks like this:
folder structure
The problem is that I don't know where to place the api key json file, how to load the resource, and the correct order of the method calls.
I tried loading the resource the way shown below. Before that I also tried using ClassLoader to load it from the WEB-INF folder and it worked, but changed the code and kept receiving NullPointer Exception (why not FileNotFound Exception?) for the InputStream and couldn't restore the previous state.
With the current state I keep receiving FileNotFound Exception as I'm am not able to load the resource no matter how much I googled "Spring MVC load resource" and as I checked the debugger the service account's "init" method with #PostConstruct isn't running at starting the server.
I understand that I should be able to load the resource and call the "init" method in order to make it work. (I suppose it's enough to call it once after creating the bean and before using firebase methods) But I just couldn't come up with a working implementation.
I used examples from here:
https://github.com/savicprvoslav/Spring-Boot-starter
(Bottom of the Page)
My Controller Class:
#Controller
#RequestMapping("/firebase")
public class FirebaseController {
#Autowired
private FirebaseService firebaseService;
#GetMapping(value="/upload/maincategories")
public void uploadMainRecordCategories() {
firebaseService.uploadMainRecordCategories();
}
My Service Class:
#Service
public class FirebaseServiceBean implements FirebaseService {
#Value("/api.json")
Resource apiKey;
#Override
public void uploadMainRecordCategories() {
// do something
}
#PostConstruct
public void init() {
try (InputStream serviceAccount = apiKey.getInputStream()) {
FirebaseOptions options = new FirebaseOptions.Builder()
.setCredentials(GoogleCredentials.fromStream(serviceAccount))
.setDatabaseUrl(FirebaseStringValue.DB_URL).build();
FirebaseApp.initializeApp(options);
} catch (IOException e) {
e.printStackTrace();
}
}
}
how about saving value in a spring property and using #Value("${firebase.apiKey}")?
Alternatively, save path to file in property and reference that in #Value()
#Value("${service.account.path}")
private String serviceAccountPath;
In application.properties:
service.account.path = /path/to/service-account.json
then config code:
private String getAccessToken() throws IOException {
GoogleCredential googleCredential = GoogleCredential
.fromStream(getServiceAccountInputStream())
.createScoped(Collections.singletonList("https://www.googleapis.com/auth/firebase.messaging"));
googleCredential.refreshToken();
return googleCredential.getAccessToken();
}
private InputStream getServiceAccountInputStream() {
File file = new File(serviceAccountPath);
try {
return new FileInputStream(file);
} catch (FileNotFoundException e) {
throw new RuntimeException("Couldn't find service-account.json");
}
}

JAX-RS Exception Mapper not working in Grizzly container

Working on a Jersey web application with a team, as the project got bigger and bigger, we decided to switch from Tomcat to Grizzly to allow deploying parts of the project on different port numbers. What I've found out now, that the custom exception handling we have fails to work now, instead I always get the grizzly html page.
Example exception:
public class DataNotFoundException extends RuntimeException{
private static final long serialVersionUID = -1622261264080480479L;
public DataNotFoundException(String message) {
super(message);
System.out.println("exception constructor called"); //this prints
}
}
Mapper:
#Provider
public class DataNotFoundExceptionMapper implements ExceptionMapper<DataNotFoundException>{
public DataNotFoundExceptionMapper() {
System.out.println("mapper constructor called"); //doesnt print
}
#Override
public Response toResponse(DataNotFoundException ex) {
System.out.println("toResponse called"); //doesnt print
ErrorMessage errorMessage = new ErrorMessage(ex.getMessage(), 404, "No documentation yet.");
return Response.status(Status.NOT_FOUND)
.entity(errorMessage)
.build();
//ErrorMessage is a simple POJO with 2 string and 1 int field
}
}
I'm not sure where is the problem source, if needed I can provide more information/code. What's the problem, what can I try?
EDIT:
Main.class:
public class Main {
/**
* Main method.
* #param args
* #throws Exception
*/
public static void main(String[] args) throws Exception {
...
List<ServerInfo> serverList = new ArrayList<ServerInfo>();
serverList.add(new ServerInfo(
"api",8450,
new ResourceConfig().registerClasses(
the.package.was.here.ApiResource.class)
));
for(ServerInfo server : serverList) {
server.start();
}
System.out.println("Press enter to exit...");
System.in.read();
for(ServerInfo server : serverList) {
server.stop();
}
}
}
EDIT2:
based on this question I've tried using this ServerProperties.RESPONSE_SET_STATUS_OVER_SEND_ERROR, "true"property, which only helped a little. I still get the html grizzly page when the exception happens, but now I see my exception (+stack trace) in the body of the page.
You're only registering one resource class for the entire application
new ResourceConfig().registerClasses(
eu.arrowhead.core.api.ApiResource.class
)
The mapper needs to be registered also
new ResourceConfig().registerClasses(
eu.arrowhead.core.api.ApiResource.class,
YourMapper.class)
)
You can also use package scanning, which will pick up all classes and automatically register them, if they are annotated with #Path or #Provider
new ResourceConfig().packages("the.packages.to.scan")

Camel route loop not working

I am trying to insert json data in mySQL database using camel and hibernate.
Everything is working.
for (Module module : modules) {
from("timer://foo?delay=10000")
.loop(7)//not working
.to(module.getUrl() + "/api/json")
.convertBodyTo(String.class)
.process(new Processor() {
#Override
public void process(Exchange exchange) throws Exception {
int index = (Integer)exchange.getProperty("CamelLoopIndex"); // not working
ObjectMapper mapper = new ObjectMapper();
JsonNode root = mapper.readTree(exchange.getIn().getBody().toString());
String[] lijst = {"lastBuild", "lastCompletedBuild", "lastFailedBuild", "lastStableBuild", "lastSuccessfulBuild", "lastUnstableBuild", "lastUnsuccessfulBuild"};
JSONObject obj = new JSONObject();
JsonNode node = root.get(lijst[index]);
JsonNode build = node.get("number");
obj.put("description", lijst[index]);
obj.put("buildNumber", build);
exchange.getIn().setBody(obj.toString());
}
})
.unmarshal(moduleDetail)
.to("hibernate:be.kdg.teamf.model.ModuleDetail")
.end();
}
When I debug, my CamelLoopIndex remains 0 so it is not incremented every time it goes through the loop.
All help is welcome!
In your case the only first instruction is processed in scope of the loop: .to(module.getUrl() + "/api/json"). You can add more instructions into a loop using Spring DSL, but I don't know how to declare a loop scope using Java DSL explicitly. I hope experts will explain more about a loop scope in Java DSL.
As a workaround I suggest to move all iteration instructions to a separate direct: route.
I can't reproduce your problem. This works:
from("restlet:http://localhost:9010}/loop?restletMethod=get")
.loop(7)
.process(new Processor() {
#Override
public void process(Exchange exchange) throws Exception {
int index = (int) exchange.getProperty("CamelLoopIndex");
exchange.getIn().setBody("index=" + index);
}
})
.convertBodyTo(String.class)
.end();
Output:
index=6