Apache HttpClient Official Example - Releasing Resources method - apache

In this official example for Apache HttpClient, there's no mention of releasing request or response objects. Are they released as part of httpclient.close() or releaseResources method needs to be overridden with something?
final CountDownLatch latch2 = new CountDownLatch(1);
final HttpGet request3 = new HttpGet("http://www.apache.org/");
HttpAsyncRequestProducer producer3 = HttpAsyncMethods.create(request3);
AsyncCharConsumer<HttpResponse> consumer3 = new AsyncCharConsumer<HttpResponse>() {
HttpResponse response;
#Override
protected void onResponseReceived(final HttpResponse response) {
this.response = response;
}
#Override
protected void onCharReceived(final CharBuffer buf, final IOControl ioctrl) throws IOException {
// Do something useful
}
#Override
protected void releaseResources() {
}
#Override
protected HttpResponse buildResult(final HttpContext context) {
return this.response;
}
};
httpclient.execute(producer3, consumer3, new FutureCallback<HttpResponse>() {
public void completed(final HttpResponse response3) {
latch2.countDown();
System.out.println(request2.getRequestLine() + "->" + response3.getStatusLine());
}
public void failed(final Exception ex) {
latch2.countDown();
System.out.println(request2.getRequestLine() + "->" + ex);
}
public void cancelled() {
latch2.countDown();
System.out.println(request2.getRequestLine() + " cancelled");
}
});
latch2.await();
} finally {
httpclient.close();
}

One needs to override #releaseResources() only if the consumer makes use of system resources such as files, pipes, etc. If response content is always held in memory it gets GCed the normal way.

Related

Retrofit error response handling

I am using retrofit 2.3.0 to consume API's in my app but a week ago I started receiving error message and existing code was not able to display error message in UI.
Previously, I was using errorBody.toString() then suddenly after few months I got error and then last week I tried with errorBody.string() but it dodn't work. Now today it's working.
I have attached screenshots of response from server and my error handling also. Here is my code to display error message.
private static void showToastForError(retrofit2.Response<Object> response, int requestType) {
if (response != null && response.errorBody() != null) {
try {
JSONObject jObjError = null;
try {
jObjError = new JSONObject(response.errorBody() != null ? response.errorBody().toString() : "");
Toast.makeText(Application.getAppContext(), jObjError.getString("message"), Toast.LENGTH_LONG).show();
} catch (JSONException e) {
e.printStackTrace();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
I think you should custom call adapter to handle error.
Here my custom adapter
public final class ErrorHandlingAdapter {
/**
* A callback which offers granular callbacks for various conditions.
*/
public interface MyCallback<T> {
/**
* Called for [200, 300) responses.
*/
void success(Response<T> response);
/**
* Called for 401 responses.
*/
void unauthenticated(Response<?> response);
/**
* Called for [400, 500) responses, except 401.
*/
void clientError(Response<?> response);
/**
* Called for [500, 600) response.
*/
void serverError(Response<?> response);
/**
* Called for network errors while making the call.
*/
void networkError(IOException e);
/**
* Called for unexpected errors while making the call.
*/
void unexpectedError(Throwable t);
}
public interface MyCall<T> {
void cancel();
void enqueue(MyCallback<T> callback);
MyCall<T> clone();
boolean isExcute();
}
public static class ErrorHandlingCallAdapterFactory extends CallAdapter.Factory {
#Override
public CallAdapter<?> get(Type returnType, Annotation[] annotations,
Retrofit retrofit) {
if (getRawType(returnType) != MyCall.class) {
return null;
}
if (!(returnType instanceof ParameterizedType)) {
throw new IllegalStateException(
"MyCall must have generic type (e.g., MyCall<ResponseBody>)");
}
Type responseType = getParameterUpperBound(0, (ParameterizedType) returnType);
Executor callbackExecutor = retrofit.callbackExecutor();
return new ErrorHandlingCallAdapter<>(responseType, callbackExecutor);
}
private static final class ErrorHandlingCallAdapter<R> implements CallAdapter<R> {
private final Type responseType;
private final Executor callbackExecutor;
ErrorHandlingCallAdapter(Type responseType, Executor callbackExecutor) {
this.responseType = responseType;
this.callbackExecutor = callbackExecutor;
}
#Override
public Type responseType() {
return responseType;
}
#Override
public <R1> R adapt(Call<R1> call) {
return (R) new MyCallAdapter(call, callbackExecutor);
}
}
}
/**
* Adapts a {#link Call} to {#link MyCall}.
*/
static class MyCallAdapter<T> implements MyCall<T> {
private final Call<T> call;
private final Executor callbackExecutor;
MyCallAdapter(Call<T> call, Executor callbackExecutor) {
this.call = call;
this.callbackExecutor = callbackExecutor;
}
#Override
public void cancel() {
call.cancel();
}
#Override
public void enqueue(final MyCallback<T> callback) {
call.enqueue(new Callback<T>() {
#Override
public void onResponse(Call<T> call, Response<T> response) {
// on that executor by submitting a Runnable. This is left as an exercise for the reader.
callbackExecutor.execute(new Runnable() {
#Override
public void run() {
int code = response.code();
if (code >= 200 && code < 300) {
callback.success(response);
} else if (code == 401) {
if (Storage.getInstance().isLogin())
Storage.getInstance().logout(App.self().getApplicationContext());
} else if (code >= 400 && code < 500) {
callback.clientError(response);
} else if (code >= 500 && code < 600) {
callback.serverError(response);
} else {
callback.unexpectedError(new RuntimeException("Unexpected response " + response));
}
}
});
}
#Override
public void onFailure(Call<T> call, Throwable t) {
// on that executor by submitting a Runnable. This is left as an exercise for the reader.
callbackExecutor.execute(new Runnable() {
#Override
public void run() {
if (t instanceof IOException) {
if (call.isCanceled()) {
return;
}
callback.networkError((IOException) t);
Toast.makeText(App.self(), R.string.error_no_connect_internet, Toast.LENGTH_SHORT).show();
} else {
callback.unexpectedError(t);
}
}
});
}
});
}
#Override
public MyCall<T> clone() {
return new MyCallAdapter<>(call.clone(), callbackExecutor);
}
#Override
public boolean isExcute() {
return call.isExecuted();
}
}
}
Here my config to add custom call adapter
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(baseUrl)
.addCallAdapterFactory(new ErrorHandlingAdapter.ErrorHandlingCallAdapterFactory()) // custom call adapter
.addConverterFactory(GsonConverterFactory.create())
.client(getHeader())
.build();
And handle request, ex:
#GET("api/getSomething")
ErrorHandlingAdapter.MyCall<BaseResponse> getSomething(#Query("param"),...)
Handle response:
ErrorHandlingAdapter.MyCall<BaseResponse> mCalls = ApiUtils.getSomething(...);
mCalls.enqueue(new ErrorHandlingAdapter.MyCallback<BaseResponse>() {
#Override
public void success(Response<BaseResponse> response) {
//handle response
}
#Override
public void unauthenticated(Response<?> response) {
//handle unauthenticated error
}
#Override
public void clientError(Response<?> response) {
//handle clientError error
}
#Override
public void serverError(Response<?> response) {
//handle serverError error
}
#Override
public void networkError(IOException e) {
//handle networkError error
}
#Override
public void unexpectedError(Throwable t) {
//handle unexpectedError error
}
}

Spring Security authorize in REST (or form-login) app by HTTP GET

I'm writing backend app based on Spring Boot without any views (templates), because client app will use it's own HTML.
I'm trying change default behavior (HTTP POST) Spring Security form-login authentication - use HTTP GET and POST. Yes, I know, it's bad for security, but it's requirement.
How I can do it?
My app:
Application
package net.company.rest;
#EnableAutoConfiguration
#ComponentScan
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
SecurityConfig
package net.company.rest.config;
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private AuthSuccessHandler authSuccessHandler;
#Autowired
private AuthFailureHandler authFailureHandler;
#Autowired
private AuthEntryPoint authEntryPoint;
// configure security
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated();
http.exceptionHandling().authenticationEntryPoint(authEntryPoint);
http.formLogin().usernameParameter("user").passwordParameter("pass");
http.formLogin().successHandler(authSuccessHandler).failureHandler(authFailureHandler);
http.logout().permitAll();
http.cors();
http.csrf().disable();
}
// enable security
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
for (int i = 1; i <= 10; i++) {
auth.inMemoryAuthentication().withUser("user" + i).password("user" + i).roles("USER");
}
}
}
AuthEntryPoint
package net.company.rest.component;
#Component
public class AuthEntryPoint implements AuthenticationEntryPoint {
#Override
public void commence(HttpServletRequest req,
HttpServletResponse resp,
AuthenticationException ex) throws IOException, ServletException {
resp.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
UniversalMessage msg = new UniversalMessage(1, "not authenticated");
try {
resp.getWriter().print(new ObjectMapper().writeValueAsString(msg));
} catch (JsonProcessingException e) {
resp.getWriter().print(e.toString());
}
resp.getWriter().flush();
}
}
AuthSuccessHandler
package net.company.rest.component;
#Component
public class AuthSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {
#Override
public void onAuthenticationSuccess(HttpServletRequest req,
HttpServletResponse resp,
Authentication auth) throws ServletException, IOException {
resp.setStatus(HttpServletResponse.SC_OK);
UniversalMessage msg = new UniversalMessage(0, "auth success");
try {
resp.getWriter().print(new ObjectMapper().writeValueAsString(msg));
} catch (JsonProcessingException e) {
resp.getWriter().print(e.toString());
}
resp.getWriter().flush();
clearAuthenticationAttributes(req);
}
}
AuthFailureHandler
package net.company.rest.component;
#Component
public class AuthFailureHandler extends SimpleUrlAuthenticationFailureHandler {
#Override
public void onAuthenticationFailure(HttpServletRequest req,
HttpServletResponse resp,
AuthenticationException ex) throws IOException, ServletException {
resp.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
UniversalMessage msg = new UniversalMessage(1, "auth error");
try {
resp.getWriter().print(new ObjectMapper().writeValueAsString(msg));
} catch (JsonProcessingException e) {
resp.getWriter().print(e.toString());
}
resp.getWriter().flush();
}
}
The problem was that above code didn't solve problem, login by HTTP GET didn't work anyway.
There is steps for solve:
Add new class extends UsernamePasswordAuthenticationFilter
package net.company.rest.config.web;
#Slf4j
public class AuthByGetFilter extends UsernamePasswordAuthenticationFilter {
public AuthByGetFilter() {
super();
// change auth parameters
setUsernameParameter("user");
setPasswordParameter("pass");
// allow GET
setPostOnly(false);
setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher("/login", "GET"));
}
#Override
protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException {
SecurityContextHolder.clearContext();
log.debug("Method: {}, Request: {}, params was hidden for security", request.getMethod(), request.getRequestURL());
log.debug("Authentication request failed: {}", failed.toString());
log.debug("Updated SecurityContextHolder to contain null Authentication");
log.debug("Delegating to authentication failure handler " + getFailureHandler());
getRememberMeServices().loginFail(request, response);
getFailureHandler().onAuthenticationFailure(request, response, failed);
}
}
Use this class as filter in the above described SecurityConfig class
...
// configure security
#Override
protected void configure(HttpSecurity http) throws Exception {
http.addFilterBefore(authByGetFilter(), UsernamePasswordAuthenticationFilter.class); // add authByGetFilter
...
}
// allow authentication by http GET method
#Bean
public AuthByGetFilter authByGetFilter() throws Exception {
AuthByGetFilter authByGetFilter = new AuthByGetFilter();
authByGetFilter.setAuthenticationManager(authenticationManagerBean());
authByGetFilter.setAuthenticationFailureHandler(authFailureHandler);
authByGetFilter.setAuthenticationSuccessHandler(authSuccessHandler);
return authByGetFilter;
}
...

Cant get authentication filter working with embedded jetty

I have a REST handler servlet defined as follows (this works perfectly):
//REST handler context
ServletContextHandler restHandler = new ServletContextHandler(ServletContextHandler.SESSIONS);
restHandler.setContextPath("/");
// Jersey REST handling servlet
ServletHolder jerseyServlet = restHandler.addServlet(org.glassfish.jersey.servlet.ServletContainer.class, "/*");
jerseyServlet.setInitOrder(0);
// Tell Jersey which REST service class to load....
jerseyServlet.setInitParameter("jersey.config.server.provider.classnames", RestHandler.class.getCanonicalName());
I now want to add a authentication filter, which I do as:
FilterHolder authFilter = restHandler.addFilter(AuthFilter.class, "/",
EnumSet.of( DispatcherType.ASYNC,
DispatcherType.ERROR,
DispatcherType.FORWARD,
DispatcherType.INCLUDE,
DispatcherType.REQUEST));
if (authFilter == null) {
dlog.debug("Failed to load authentication filter");
};
All good so far, however, the filter does not fire on incoming REST. Calls still go through. The AuthFilter is straight from sample code:
public class AuthFilter implements javax.servlet.Filter {
private static final Logger dlog = Dlog.get();
public static final String AUTHENTICATION_HEADER = "Authorization";
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filter)
throws IOException, ServletException {
dlog.entry(request, response, filter);
if (request instanceof HttpServletRequest) {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
String authCredentials = httpServletRequest.getHeader(AUTHENTICATION_HEADER);
AuthService authenticationService = new AuthService();
boolean authenticationStatus = authenticationService.authenticate(authCredentials);
if (authenticationStatus) {
filter.doFilter(request, response);
} else {
if (response instanceof HttpServletResponse) {
HttpServletResponse httpServletResponse = (HttpServletResponse) response;
httpServletResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
}
}
}
dlog.exit();
}
#Override
public void destroy() {
}
#Override
public void init(FilterConfig arg0) throws ServletException {
}
}
I use handler collection as I also have a resource handler to serve static web pages besides the REST calls.
HandlerCollection handlerList = new HandlerCollection();
handlerList.setHandlers(new Handler[] { resourceHandler,
restHandler,
new DefaultHandler(),
requestLogHandler });
What else I need to do? I have scanned through number of related posts and come up empty. Thanks in advance.

Manipulating request entity using HttpRequestInterceptor in Apache HttpClient

HttpRequest does not have getters for the Request entity to allow me to manipulate it in an interceptor. Checking the type using instanceof is not working either. Would anybody have any ideas about how I can accomplish this?
public class FastinfosetRequestInterceptor implements HttpRequestInterceptor
{
#Override
public void process(HttpRequest request, HttpContext context)
throws HttpException, IOException
{
if(request instanceof HttpPost)
{
HttpEntity rqEntity = ((HttpPost)request).getEntity();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
rqEntity.writeTo(baos);
byte[] encodedRq;
try
{
encodedRq = FastInfosetUtils.encodeToFastInfoSet(baos.toByteArray());
}
catch (ParserConfigurationException | SAXException
| TransformerException e)
{
throw new IOException("Error while encoding request to FastInfoSet", e);
}
((HttpPost) request).setEntity(new ByteArrayEntity(encodedRq));
}
}
}
This should fix the problem
if(request instanceof HttpEntityEnclosingRequest)
{
HttpEntity rqEntity = ((HttpEntityEnclosingRequest) request).getEntity();

question about simple MINA client and server

I am just trying to create a simple MINA server and client to evaluate. Here is my code.
public class Server {
private static final int PORT = 8080;
static class ServerHandler extends IoHandlerAdapter {
#Override
public void exceptionCaught(IoSession session, Throwable cause) throws Exception {
cause.printStackTrace();
}
#Override
public void sessionCreated(IoSession session) {
System.out.println("session is created");
session.write("Thank you");
}
#Override
public void sessionClosed(IoSession session) throws Exception {
System.out.println("session is closed.");
}
#Override
public void messageReceived(IoSession session, Object message) {
System.out.println("message=" + message);
session.write("Reply="+message);
}
}
/**
* #param args
*/
public static void main(String[] args) throws Exception {
SocketAcceptor acceptor = new NioSocketAcceptor();
acceptor.getFilterChain().addLast( "logger", new LoggingFilter() );
acceptor.getFilterChain().addLast( "codec", new ProtocolCodecFilter( new TextLineCodecFactory( Charset.forName( "UTF-8" ))));
acceptor.setHandler(new Server.ServerHandler());
acceptor.getSessionConfig().setReadBufferSize( 2048 );
acceptor.getSessionConfig().setIdleTime( IdleStatus.BOTH_IDLE, 10 );
acceptor.bind(new InetSocketAddress(PORT));
System.out.println("Listening on port " + PORT);
for (;;) {
Thread.sleep(3000);
}
}
}
public class Client {
private static final int PORT = 8080;
private IoSession session;
private ClientHandler handler;
public Client() {
super();
}
public void initialize() throws Exception {
handler = new ClientHandler();
NioSocketConnector connector = new NioSocketConnector();
connector.getFilterChain().addLast( "codec", new ProtocolCodecFilter( new TextLineCodecFactory( Charset.forName( "UTF-8" ))));
connector.getFilterChain().addLast("logger", new LoggingFilter());
connector.setHandler(handler);
for (;;) {
try {
ConnectFuture future = connector.connect(new InetSocketAddress(PORT));
future.awaitUninterruptibly();
session = future.getSession();
break;
} catch (RuntimeIoException e) {
System.err.println("Failed to connect.");
e.printStackTrace();
Thread.sleep(5000);
}
}
if (session == null) {
throw new Exception("Unable to get session");
}
Sender sender = new Sender();
sender.start();
session.getCloseFuture().awaitUninterruptibly();
connector.dispose();
System.out.println("client is done.");
}
/**
* #param args
*/
public static void main(String[] args) throws Exception {
Client client = new Client();
client.initialize();
}
class Sender extends Thread {
#Override
public void run() {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
handler.messageSent(session, "message");
}
}
class ClientHandler extends IoHandlerAdapter {
#Override
public void sessionOpened(IoSession session) {
}
#Override
public void messageSent(IoSession session, Object message) {
System.out.println("message sending=" + message);
session.write(message);
}
#Override
public void messageReceived(IoSession session, Object message) {
System.out.println("message receiving "+ message);
}
#Override
public void exceptionCaught(IoSession session, Throwable cause) {
cause.printStackTrace();
}
}
}
When I execute this code, the Client seems to keep sending a message instead of stopping after it sends. It looks to me that there is a recursive call in underlying MINA code. I know that I am doing something wrong.
Can somebody tell me how to fix this?
Thanks.
Try to initialize and start your sender and use the session within sessionOpened (ClientHandler)