We have background job running that requires a user to be active. The following way works fine but we have to hard-code the password which is not ideal.
try {
session = repository.login(new SimpleCredentials("admin", "admin".toCharArray()));
} catch (RepositoryException ex) {
log.error("SessionHelper - login issue", ex);
}
We attempt a better way to set active user without setting password as follows:
Map<String, Object> params = new HashMap<String, Object>();
params.put(ResourceResolverFactory.SUBSERVICE, "theService");
ResourceResolver resolver = null;
try {
resolver = resolverFactory.getServiceResourceResolver(params);
} catch (LoginException e) {
log.error("LoginException", e);
}
Session session = resolver.adaptTo(Session.class);
// Next, create pages and add properties ...
We then try to create pages and set properties. This works fine for couple of milliseconds where some pages are created but then throws Exception to indicate session is closed although never closed and the location where exception gets thrown is unpredictable.
javax.jcr.RepositoryException: This session has been closed. See the chained exception for a trace of where the session was closed.
...
Caused by: java.lang.Exception: Stack trace of where session-admin-20077 was originally closed
We want to know whether there is any way to set the timeout? Any recommendations appreciated.
You should obtain the session always through the ResourceResolverFactory using the getServiceResourceResolver method (getAdministrativeResourceResolver method is actually deprecated and should be avoided), execute than your code in the same try/catch block and define a finally block where you can make sure that the obtained resolver/session is closed properly. If you follow this princip, you will probably never experience problems with closed or unclosed sessions.
#org.apache.felix.scr.annotations.Component(...)
public class MyComponent {
#org.apache.felix.scr.annotations.Reference
private org.apache.sling.api.resource.ResourceResolverFactory resourceResolverFactory;
public void myaction() {
org.apache.sling.api.resource.ResourceResolver resolver = null;
try {
Map<String, Object> authInfo = new HashMap<String, Object>();
authInfo.put(ResourceResolverFactory.SUBSERVICE, getClass.getName());
resolver = resourceResolverFactory.getServiceResourceResolver(authInfo);
javax.jcr.Session session = resolver.adaptTo(javax.jcr.Session.class);
javax.jcr.Node node = session.getNode("/jcr/path/to/the-node");
// do something with the node
session.save();
} catch(LoginException e) {
// Handle cannot obtain instance of the resource resolver
} catch(RepositoryException e) {
//handle the repository exception
} finally {
//do not forget to close the resolver, otherwise this can cause huge performance problems
if(resolver != null) {
resolver.close();
}
}
}
}
In order to obtain service resource resolver, you need also to configure the user.mapping in the OSGI-Service org.apache.sling.serviceusermapping.impl.ServiceUserMapperImpl for example as follows.:
<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:sling="http://sling.apache.org/jcr/sling/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0"
jcr:primaryType="sling:OsgiConfig"
user.mapping="[tld.mycompany.mypackage=admin]"
user.default="admin"/>
This way you can set and offer very advanced access control policies for your services.
If you are in a sling servlet, be carefull which resource resolver you are using. In the normal case, you will not need a resource resolver with administrative rights, but take the one provided by the SlingHttpServletRequest. The resource resolver is closed by sling at the end of the request, don't close it manually.
If you are using the admin session I suggest to not log it in like you did, but with the following method (asuming from your code you have the SlingRepository already injected with #Reference):
repository.loginAdministrative(null);
And to prevent the error I would use the following pattern:
Session session = null;
try {
session = repository.loginAdministrative(null);
//do what you need to do
} catch (RepositoryException e) {
//handle exception
} finally {
if (session != null && session.isLive()) {
session.logout();
}
}
And one last note, open and close the sessionin the same thread, so not keeping the session alive in a service, except it is an EventListener.
Related
I am using WebClient from Spring WebFlux to communicate with a REST API backend from a Spring client.
When this REST API backend throws an exception, it answers with a specific format (ErrorDTO) that I would like to collect from my client.
What I have tried to do is to make my client throw a GestionUtilisateurErrorException(ErreurDTO) containing this body once the server answers with a 5xx HTTP status code.
I have tried several options :
I/ onStatus
#Autowired
WebClient gestionUtilisateursRestClient;
gestionUtilisateursRestClient
.post()
.uri(profilUri)
.body(Mono.just(utilisateur), UtilisateurDTO.class)
.retrieve()
.onStatus(HttpStatus::is5xxServerError,
response -> {
ErreurDTO erreur = response.bodyToMono(ErreurDTO.class).block();
return Mono.error(new GestionUtilisateursErrorException(erreur));
}
)
.bodyToMono(Void.class)
.timeout(Duration.ofMillis(5000))
.block();
This method doesn't work because webclient doesn't allow me to call the block method in the onStatus. I am only able to get a Mono object and I can't go further from here.
It seems like "onStatus" method can't be used in a WebClient blocking method, which means I can throw a custom Exception, but I can't populate it with the data from the response body.
II/ ExchangeFilterFunction
#Bean
WebClient gestionUtilisateursRestClient() {
return WebClient.builder()
.baseUrl(gestionUtilisateursApiUrl)
.defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.filter(ExchangeFilterFunction.ofResponseProcessor(this::gestionUtilisateursExceptionFilter))
.build();
}
private Mono<ClientResponse> gestionUtilisateursExceptionFilter(ClientResponse clientResponse) {
if(clientResponse.statusCode().isError()){
return clientResponse.bodyToMono(ErreurDTO.class)
.flatMap(erreurDto -> Mono.error(new GestionUtilisateursErrorException(
erreurDto
)));
}
return Mono.just(clientResponse);
}
This method works but throw a reactor.core.Exceptions$ReactiveException that I am struggling to catch properly (reactor.core.Exceptions is not catchable, and ReactiveException is private).
This Exception contains in its Cause the exception I need to catch (GestionUtilisateurErrorException) but I need a way to catch it properly.
I also tried to use "onErrorMap" and "onErrorResume" methods but none of them worked the way I needed.
Edit 1 :
I am now using the following workaround even if I feel it's a dirty way to do what I need :
gestionUtilisateursRestClient
.post()
.uri(profilUri)
.body(Mono.just(utilisateur), UtilisateurDTO.class)
.retrieve()
.onStatus(h -> h.is5xxServerError(),
response -> {
return response.bodyToMono(ErreurDTO.class).handle((erreur, handler) -> {
LOGGER.error(erreur.getMessage());
handler.error(new GestionUtilisateursErrorException(erreur));
});
}
)
.bodyToMono(String.class)
.timeout(Duration.ofMillis(5000))
.block();
}
catch(Exception e) {
LOGGER.debug("Erreur lors de l'appel vers l'API GestionUtilisateur (...)");
if(ExceptionUtils.getRootCause(e) instanceof GestionUtilisateursErrorException) {
throw((GestionUtilisateursErrorException) e.getCause());
}
else {
throw e;
}
}
Here, it throws the expected GestionUtilisateursErrorException that I can handle synchronously.
I might implement this in a global handler to avoid writing this code around each call to my API.
Thank you.
Kevin
I've encountered a similar case for accessing the response body that might be of use to you using the Mono.handle() method (see https://projectreactor.io/docs/core/release/api/index.html?reactor/core/publisher/Mono.html).
Here handler is a SynchronousSink (see https://projectreactor.io/docs/core/release/api/reactor/core/publisher/SynchronousSink.html) and can call at most next(T) one time, and either complete() or error().
In this case, I call 'handler.error()' with a new GestionUtilisateursErrorException constructed with the 'erreur'.
.onStatus(h -> h.is5xxServerError(),
response -> {
return response.bodyToMono(ErreurDTO.class).handle((erreur, handler) -> {
// Do something with erreur e.g.
log.error(erreur.getErrorMessage());
// Call handler.next() and either handler.error() or handler.complete()
handler.error(new GestionUtilisateursErrorException(erreur));
});
}
)
Is there a way to wait for a future to complete without blocking the event loop?
An example of a use case with querying Mongo:
Future<Result> dbFut = Future.future();
mongo.findOne("myusers", myQuery, new JsonObject(), res -> {
if(res.succeeded()) {
...
dbFut.complete(res.result());
}
else {
...
dbFut.fail(res.cause());
}
}
});
// Here I need the result of the DB query
if(dbFut.succeeded()) {
doSomethingWith(dbFut.result());
}
else {
error();
}
I know the doSomethingWith(dbFut.result()); can be moved to the handler, yet if it's long, the code will get unreadable (Callback hell ?) It that the right solution ? Is that the omny solution without additional libraries ?
I'm aware that rxJava simplifies the code, but as I don't know it, learning Vert.x and rxJava is just too much.
I also wanted to give a try to vertx-sync. I put the dependency in the pom.xml; everything got downloaded fine but when I started my app, I got the following error
maurice#mickey> java \
-javaagent:~/.m2/repository/co/paralleluniverse/quasar-core/0.7.5/quasar-core-0.7.5-jdk8.jar \
-jar target/app-dev-0.1-fat.jar \
-conf conf/config.json
Error opening zip file or JAR manifest missing : ~/.m2/repository/co/paralleluniverse/quasar-core/0.7.5/quasar-core-0.7.5-jdk8.jar
Error occurred during initialization of VM
agent library failed to init: instrument
I know what the error means in general, but I don't know in that context... I tried to google for it but didn't find any clear explanation about which manifest to put where. And as previously, unless mandatory, I prefer to learn one thing at a time.
So, back to the question : is there a way with "basic" Vert.x to wait for a future without perturbation on the event loop ?
You can set a handler for the future to be executed upon completion or failure:
Future<Result> dbFut = Future.future();
mongo.findOne("myusers", myQuery, new JsonObject(), res -> {
if(res.succeeded()) {
...
dbFut.complete(res.result());
}
else {
...
dbFut.fail(res.cause());
}
}
});
dbFut.setHandler(asyncResult -> {
if(asyncResult.succeeded()) {
// your logic here
}
});
This is a pure Vert.x way that doesn't block the event loop
I agree that you should not block in the Vertx processing pipeline, but I make one exception to that rule: Start-up. By design, I want to block while my HTTP server is initialising.
This code might help you:
/**
* #return null when waiting on {#code Future<Void>}
*/
#Nullable
public static <T>
T awaitComplete(Future<T> f)
throws Throwable
{
final Object lock = new Object();
final AtomicReference<AsyncResult<T>> resultRef = new AtomicReference<>(null);
synchronized (lock)
{
// We *must* be locked before registering a callback.
// If result is ready, the callback is called immediately!
f.onComplete(
(AsyncResult<T> result) ->
{
resultRef.set(result);
synchronized (lock) {
lock.notify();
}
});
do {
// Nested sync on lock is fine. If we get a spurious wake-up before resultRef is set, we need to
// reacquire the lock, then wait again.
// Ref: https://stackoverflow.com/a/249907/257299
synchronized (lock)
{
// #Blocking
lock.wait();
}
}
while (null == resultRef.get());
}
final AsyncResult<T> result = resultRef.get();
#Nullable
final Throwable t = result.cause();
if (null != t) {
throw t;
}
#Nullable
final T x = result.result();
return x;
}
I'm trying to get some user data out of Keycloack using the admin-client SDK. I've built the client like so:
Keycloak kc = KeycloakBuilder.builder() //
.serverUrl("some_url")
.realm("some-realm")
.username("admin") //
.password("password") //
.clientId("curl")
.resteasyClient(new ResteasyClientBuilder().connectionPoolSize(10).connectionCheckoutTimeout(10, TimeUnit.SECONDS).build()) //
.build();
System.out.println("built");
UsersResource baz = kc.realm(keycloakConfiguration.getRealm()).users();
System.out.println(baz.count());
What seems to happen is that my program hangs indefinitely when attempting to fetch baz - my debugger never hits it. I'm not quite sure what's going on - my credentials are correct. What is the correct way to cause the builder to either 1. fail after a certain time period, or 2.verify that my credentials are correct? It's maddeninly frustrating to debug.
You could create a custom method to check if you client is "online". This method could look like:
public boolean isKeycloakClientValid(Keycloak keycloakClient) {
try {
tryToPingKeycloak(keycloakClient);
} catch (Exception e) {
logger.error("Error while pinging the keycloak server", e);
return false;
}
return true;
}
With the help method:
private void tryToPingKeycloak(KeycloakClient keycloakClient) {
keycloakClient.serverInfo().getInfo();
}
Now you could check your client before using it:
if (isKeycloakClientValid(kc)) {
UsersResource baz = kc.realm(keycloakConfiguration.getRealm()).users();
}
Server: WildFly10
JPA with eclipseLink 2.6.3-M1
JavaEE7
I have the following EJB:
#Stateless
#LocalBean
public class HandleRollbackComponent {
private static Logger logger = Logger.getLogger(HandleRollbackComponent.class);
#EJB
private Tws14WSBatchChRequestsFacade tws14wsBatchChRequestsFacade;
public void doSomething() {
// first off go and fetch an instance of tws14 from the db
logger.debug("*************************************************");
logger.debug("1. First off go and fetch an instance of tws14 from the db");
String batchChReqId = "103";
Tws14WSBatchChRequests tws14wsBatchChRequests = tws14wsBatchChRequestsFacade.find(new BigDecimal(batchChReqId));
logger.debug("2. Found instance of tws14: " + tws14wsBatchChRequests);
logger.debug("2.1 CARD PLASTIC : " + tws14wsBatchChRequests.getCardPlastic());
try {
logger.debug("3. Now call a method that throws the EJBTrxnRollBackException....");
doSomethingThatThrowsEJBTransactionRolledbackException(tws14wsBatchChRequests);
logger.debug("---> This line should not be logged if exception was thrown....");
} catch (Exception e) {
logger.debug("5. Caught the exception....");
} finally {
logger.debug("6. Finally try and get a fresh instance from the db again...");
tws14wsBatchChRequests = tws14wsBatchChRequestsFacade.find(new BigDecimal(batchChReqId));
logger.debug("7. Was able to get instance from db: " + tws14wsBatchChRequests);
logger.debug("8. Try and update the instance of tws again...");
tws14wsBatchChRequestsFacade.edit(tws14wsBatchChRequests);
logger.debug("9. Could update the instance without problems.....");
logger.debug("10. Check the OrderCards value: " + tws14wsBatchChRequests.getOrderCards() );
}
logger.debug("11. Done...");
}
public void doSomethingThatThrowsEJBTransactionRolledbackException(Tws14WSBatchChRequests tws14wsBatchChRequests) {
logger.debug("4. Set some invalid values on tws14 in an attempt to get exception thrown...");
tws14wsBatchChRequests.setOrderCards("N");
tws14wsBatchChRequests.setOrderCards("");
tws14wsBatchChRequests.setCardPlastic(null);
tws14wsBatchChRequestsFacade.edit(tws14wsBatchChRequests);
}
}
When I call doSomething() this is what I see:
First off go and fetch an instance of tws14 from the db
Found instance of tws14: za.co.fnds.persistence.entities.Tws14WSBatchChRequests[ batchChRequestId=103 ]
2.1 CARD PLASTIC : NBCRFLI_PIN
Now call a method that throws the EJBTrxnRollBackException....
Set some invalid values on tws14 in an attempt to get exception thrown...
---> This line should not be logged if exception was thrown....
Finally trying to get a fresh instance from the db again...
Was able to get instance from db: za.co.fnds.persistence.entities.Tws14WSBatchChRequests[ batchChRequestId=103 ]
Try and update the instance of tws again...
Could update the instance without problems.....
Check the OrderCards value:
Done...
My question is why is the program not going into the catch clause because my logs indicates that a javax.validation.ConstraintViolationException was thrown. Why is the bold log above still show? What am I missing? Is there a way I'm supposed to be handling this program structure in an EJB?
To verify that the implementation is wrong is the method doSomethingThatThrowsEJBTransactionRolledbackException. You can explicitly throw the exception and see if the cath works.
public void doSomething() {
try {
doSomethingThatThrowsEJBTransactionRolledbackException(new Tws14WSBatchChRequests());
} catch (Exception e) {
logger.warn(e.getMessage(), e);
}
}
public void doSomethingThatThrowsEJBTransactionRolledbackException(Tws14WSBatchChRequests tws14wsBatchChRequests) {
throw new EJBTransactionRolledbackException();
}
If the exception is catching, then your code is not throwing anything
I have the following in my base controller:
protected override void OnException(ExceptionContext filterContext)
{
if (filterContext == null)
{
throw new ArgumentNullException("filterContext");
}
// If custom errors are disabled, we need to let the normal ASP.NET exception handler
// execute so that the user can see useful debugging information.
if (filterContext.ExceptionHandled || !filterContext.HttpContext.IsCustomErrorEnabled)
{
return;
}
Exception exception = filterContext.Exception;
// If this is not an HTTP 500 (for example, if somebody throws an HTTP 404 from an action method),
// ignore it.
if (new HttpException(null, exception).GetHttpCode() != 500)
{
return;
}
// TODO: What is the namespace for ExceptionType?
//if (!ExceptionType.IsInstanceOfType(exception))
//{
// return;
//}
// Send Email
MailException(exception);
// TODO: What does this line do?
base.OnException(filterContext);
filterContext.Result = new ViewResult
{
ViewName = "Error"
};
filterContext.ExceptionHandled = true;
filterContext.HttpContext.Response.Clear();
filterContext.HttpContext.Response.StatusCode = 500;
}
In my Shared folder, I have an Error.aspx View.
Web.config
<customErrors mode="On" />
I am still seeing the yellow screen when an exception occurs. What am I doing incorrectly?
I would imagine that invoking base.OnException handler is what is causing your problem. Without actually looking at the code, I would imagine that it is what is responsible for handling the error and generating a response with the exception and stack trace. Remove that line from your code -- it's not needed as since you're replacing the ViewResult anyway.
I would recommend that you use ELMAH and implement a HandleError attribute that works with it: see this question. ELMAH is very flexible and configuration driven, rather than code driven.
Server.ClearError()
What happens if you call that?