Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Repetition is Bad, Repetition is Bad

Avatar for smirne smirne
April 18, 2015

Repetition is Bad, Repetition is Bad

Quickie Talk at Voxxed Days Ticino 2016 about repetition in Java code.

Avatar for smirne

smirne

April 18, 2015

More Decks by smirne

Other Decks in Programming

Transcript

  1. title subtitle Repetition is bad, repetition is bad Michele Giacobazzi

    – WellD Sagl Voxxed days Ticino 18 april 2015 www.welld.ch
  2. article title A «harder» case of repetition  Repeated code

    is above and below the «Dynamic» part  Use of variables and resources makes refactoring harder  In our example there are 6 main actions: 1. User retrieval 2. Connection to remote system 3. Dynamic call 4. Exception handling 5. Closing the connection 6. Action auditing (log) REPETITION CODE REPETITION
  3. public Squid findSquids(String token, Map<String,Object> criteria) { BEConnector connector =

    null; Squid result = null; //1 Get the operator VoxxedOperator operator = login.getOperator(token); if (operator == null) { logger.warn("invalid operator token " + token); return null; } try { //2 estabilish connection and get remote service facade connector = ConnUtility.getBEConnector(); VoxxedFacade service = ConnUtility.getVoxxedFacade(connector); //3 The real action! result = service.findSquidByCriteria(operator, criteria); } catch (Exception e) { //4 log and handle exception logger.error("Error in findSquids", e); result = null; } finally { //5 release the connection ConnUtility.releaseBEConnector(connector); } // 6 audit the action (log) logger.info("findSquids executed with params " + criteria + " and result " + result); return result; }
  4. public Octopus findOctopusById(String token, long id) { BEConnector connector =

    null; Squid result = null; //1 Get the operator VoxxedOperator operator = login.getOperator(token); if (operator == null) { logger.warn("invalid operator token " + token); return null; } try { //2 estabilish connection and get remote service facade connector = ConnUtility.getBEConnector(); VoxxedFacade service = ConnUtility.getVoxxedFacade(connector); //3 The real action! result = service.findOctopusById(operator, id); } catch (Exception e) { //4 log and handle exception logger.error("Error in findOctopusById" + token, e); result = null; } finally { //5 release the connection ConnUtility.releaseBEConnector(connector); } // 6 audit the action (log) logger.info(“findOctopusById executed with params " + id + " and result " + result); return result; }
  5. article title Java reflection - Method  java.lang.reflect.Method  Obtained

    with class.getMethod(String name, Class<?>... parameterTypes)  Executed with method.invoke(Object context, Object... parameters)
  6. public Object executeMethod(Method method){ BEConnector connector = null; Object result

    = null; //1 Get the operator VoxxedOperator operator = loginManager.getOperator(token); if (operator == null) { logger.warn("invalid operator token " + token); return null; } try { //2 estabilish connection and get remote service facade connector = ConnUtility.getBEConnector(); VoxxedFacade service = ConnUtility.getVoxxedFacade(connector); //Invoke the method method.invoke(service, params); } catch (Exception e) { //4 log and handle exception logger.error("Error in retrieving objectType1 for user" + call.getParams(), e); result = null; } finally { //5 release the connection ConnUtility.releaseBEConnector(connector); } // 6 audit the action (log) logger.info(call.getMethod().getName() + " executed with params " + call.getParams() + " and result " + result); return result; }
  7. public Object executeMethod(Method method){ BEConnector connector = null; Object result

    = null; //1 Get the operator VoxxedOperator operator = loginManager.getOperator(token); if (operator == null) { logger.warn("invalid operator token " + token); return null; } try { //2 estabilish connection and get remote service facade connector = ConnUtility.getBEConnector(); VoxxedFacade service = ConnUtility.getVoxxedFacade(connector); //Invoke the method method.invoke(service, params); } catch (Exception e) { //4 log and handle exception logger.error("Error in retrieving objectType1 for user" + call.getParams(), e); result = null; } finally { //5 release the connection ConnUtility.releaseBEConnector(connector); } // 6 audit the action (log) logger.info(call.getMethod().getName() + " executed with params " + call.getParams() + " and result " + result); return result; }
  8. public class BeMethodCall{ //Token. To be provided. private String token;

    private Method method; private Object[] params; public BeMethodCall(Method method, String token, Object[] params) { super(); this.token = token; } //[getters and setters] }
  9. public Object executeMethod(BeMethodCall call){ BEConnector connector = null; Object result

    = null; //1 Get the operator VoxxedOperator operator = loginManager.getOperator(call.getToken()); if (operator == null) { logger.warn("invalid operator token " + call.getToken()); return null; } try { //2 estabilish connection and get remote service facade connector = ConnUtility.getBEConnector(); VoxxedFacade service = ConnUtility.getVoxxedFacade(connector); //3 Copy the param array, set the operator as first param and execute. Object[] params = new Object[call.getParams().length + 1]; System.arraycopy(call.getParams(), 0, params, 1, call.getParams().length); params[0] = operator; call.getMethod().invoke(service, params); } catch (Exception e) { //4 log and handle exception logger.error("Error in retrieving objectType1 for user" + call.getParams(), e); result = null; } finally { //5 release the connection ConnUtility.releaseBEConnector(connector); } // 6 audit the action (log) logger.info(call.getMethod().getName() + " executed with params " + call.getParams() + " and result " + result); return result; }
  10. public Squid getSquidsWithMethod(String token, final Map<String,Object> criteria) { Method method

    = null; try { method = VoxxedFacade.class.getMethod("findSquidByCriteria", new Class<?>[] {criteria.getClass()}); } catch (NoSuchMethodException | SecurityException e) { logger.warn("Could not create method findSquidByCriteria", e); return null; } BeMethodCall call = new BeMethodCall(method, token, new Object[]{criteria}); Object maybeSquid = beMethodExecutor.executeMethod(call); if (maybeSquid instanceof Squid){ return (Squid) maybeSquid; }else{ logger.warn("Unexpected return object " + maybeSquid); return null; } }
  11. article title Java reflection - Method  Standard Java 

    Only single method call  Returns Object  Needs wrapper object  Verbose syntax
  12. public <T> T executeBeCall(BeTask<T> task){ BEConnector connector = null; T

    result = null; //1 Get the operator VoxxedOperator operator = loginManager.getOperator(task.getToken()); if (operator == null) { logger.warn("invalid operator token " + task.getToken()); return null; } try { //2 estabilish connection and get remote service facade connector = ConnUtility.getBEConnector(); VoxxedFacade service = ConnUtility.getVoxxedFacade(connector); //3 Set variables to task and run! task.setService(service); task.setOperator(operator); return task.call(); } catch (Exception e) { //4 log and handle exception logger.error("Error in retrieving objectType1 for user" + task.getToken(), e); result = null; } finally { //5 release the connection ConnUtility.releaseBEConnector(connector); } // 6 audit the action (log) not possible here return result; }
  13. public abstract class BeTask<T> implements Callable<T> { //Token. To be

    provided. private String token; //Facade. Injected by BetaskExecutor. private VoxxedFacade service; //Operator. Injected by BetaskExecutor. private VoxxedOperator operator; public BeTask(String token) { super(); this.token = token; } //[..] Getters and setters }
  14. public Squid getSquidsWithTask(String token, final Map<String,Object> criteria) { Squid result=

    beTaskExecutor.executeBeCall(new BeTask<Squid>(token) { @Override public Squid call() throws Exception { return this.getService().findSquidByCriteria( this.getOperator(),criteria); } }); // 6 audit the action (log) logger.info("getSquidsWithTask executed with params " + criteria + " and result " + result); return result; }
  15. article title Callable Task  Standard Java  Simple code

    in DAO  Allows the use of generic  Parameters must be final  Resources must be injected  Does not allow auditing (log)
  16. article title Java 8 function  java.lang.FunctionalInterface  Must have

    a single method  Can be used with lambdas  Common function types in java.util.function  Create your own
  17. @FunctionalInterface public interface BeFunctionInterface<T> { T execute(VoxxedOperator operator, VoxxedFacade service);

    } public abstract class BeFunction<T> implements BeFunctionInterface<T>{ private String token; public BeFunction(String myToken) { super(); this.token = myToken; } public String getToken(){ return token; } public abstract T execute(VoxxedOperator operator, VoxxedFacade service); }
  18. public <T> T executeFunction(BeFunction<T> call){ BEConnector connector = null; T

    result = null; //1 Get the operator VoxxedOperator operator = loginManager.getOperator(call.getToken()); if (operator == null) { logger.warn("invalid operator token " + call.getToken()); return null; } try { //2 estabilish connection and get remote service facade connector = ConnUtility.getBEConnector(); VoxxedFacade service = ConnUtility.getVoxxedFacade(connector); //3 Execute the function! call.execute(operator, service); } catch (Exception e) { //4 log and handle exception logger.error("Error in function call", e); result = null; } finally { //5 release the connection ConnUtility.releaseBEConnector(connector); } // cannot log return result; }
  19. public Squid getSquidsWithFunction(String token, final Map<String,Object> criteria) { Squid result=

    beFunctionExecutor.executeFunction(new BeFunction<Squid>(token) { @Override public Squid execute(VoxxedOperator operator, VoxxedFacade service) { return service.findSquidByCriteria(operator, criteria); } }); // 6 audit the action (log) logger.info("getSquidWithFunction executed with params " + criteria + " and result " + result); return result; }
  20. article title Java 8 function  Needs Java 8 

    Better syntax and parameter handling  Does not allow auditing (log)  Parameters must be final
  21. article title Interceptor  javax.interceptor.Interceptor  Annotate a method with

    AroundInvoke  Access to InvocationContext  Returns a result
  22. @Interceptor @LogMethod //Methods annotated with @Logit would be intercepted by

    logMethodEntry public class LogInterceptor { private static final Logger LOGGER = Logger.getLogger(LogInterceptor.class); @AroundInvoke public Object logMethodEntry(InvocationContext ctx) throws Exception { //Execute the method Object b = ctx.proceed(); //Print method name and params. LOGGER.info(ctx.getMethod().getName() + " executed with params " + ctx.getParameters()); return b; } }
  23. @Interceptor @BEConnection public class ConnectionInterceptor{ @AroundInvoke public Object injectFacade(InvocationContext ctx)

    throws Exception { BEConnector connector = null; try { connector = ConnUtility.getBEConnector(); VoxxedFacade service = ConnUtility.getVoxxedFacade(connector); ctx.getParameters()[0] = service; Object b = ctx.proceed(); return b; } catch(OperatorNotFoundException oE){ logger.warn("Operator not found " , oE); return null; }catch (Exception e) { logger.warn("Error in be call", e); return null; } finally { ConnUtility.releaseBEConnector(connector); } }
  24. @LogMethod @BeConnection public Squid findSquids(@BeFacade VoxxedFacade service, String token, Map<String,Object>

    criteria) { Squid result = null; //1 Get the operator VoxxedOperator operator = loginManager.getOperator(token); if (operator == null) { logger.warn("invalid operator token " + token); return null; } //3 The real action! result = service.findSquidByCriteria(operator, criteria); //Exception and Connection close handling in interceptor. //Log handled in interceptor. return result; }
  25. article title Interceptor  Needs container support  Wraps the

    method execution  Has access to method data  Can manipulate parameter  Relies on method signature
  26. article title A small comparison Support Syntax Completeness Method Standard

    Java Mah.. Full Task Standard Java Acceptable Partial (no log) Function Java 8 Good! Partial (no log) Interceptor Container library Depends Partial
  27. article title Our solution: function + interceptor @LogMethod public Squid

    getSquidsWithFunctionAndLog(String token, final Map<String,Object> criteria) { return beFunctionExecutor.executeFunction(new BeFunction<Squid>(token) { @Override public Squid execute(VoxxedOperator operator, VoxxedFacade service) { return service.findSquidByCriteria(operator, criteria); } }); }
  28. article title Discussion!  Questions and suggestions  Find me

    around or at WellD stand  Other approaches: dependency injection, decorator, filters…  Contacts: [email protected]  Twitter: @Smirne  WellD: www.welld.ch