Ce diaporama a bien été signalé.
Le téléchargement de votre SlideShare est en cours. ×

The Java Chamber of Horrors

Publicité
Publicité
Publicité
Publicité
Publicité
Publicité
Publicité
Publicité
Publicité
Publicité
Publicité
Publicité
Chargement dans…3
×

Consultez-les par la suite

1 sur 43 Publicité

The Java Chamber of Horrors

Télécharger pour lire hors ligne

DEVOXX UK 2019, London: Talk by Andreas Zitzelsberger (@andreasz82, Principal Software Architect at QAware)

=== Please download slides if blurred! ===

Watch the full talk on YouTube: https://www.youtube.com/watch?v=Gcwlt6_uhQU

Abstract:
There’s good code, and there’s bad code. There also is code so spectacularly bad that it leaves a lasting impression. This talk will show some of the most egregious code from several years of developing software so that we may learn from the mistakes of the past and be entertained.
All code is real (but anonymized) and ran in production.

DEVOXX UK 2019, London: Talk by Andreas Zitzelsberger (@andreasz82, Principal Software Architect at QAware)

=== Please download slides if blurred! ===

Watch the full talk on YouTube: https://www.youtube.com/watch?v=Gcwlt6_uhQU

Abstract:
There’s good code, and there’s bad code. There also is code so spectacularly bad that it leaves a lasting impression. This talk will show some of the most egregious code from several years of developing software so that we may learn from the mistakes of the past and be entertained.
All code is real (but anonymized) and ran in production.

Publicité
Publicité

Plus De Contenu Connexe

Plus par QAware GmbH (20)

Plus récents (20)

Publicité

The Java Chamber of Horrors

  1. 1. The Java Chamber of Horrors Andreas Zitzelsberger @andreasz82 QAware
  2. 2. 1≅0
  3. 3. if (true == lUsingHttps) { // ... }
  4. 4. if (true == lUsingHttps) { //.. if (lUsingSSLContext) { /* ... */ } //.. if (true == lUsingHttps) { if (true == lUsingSSLContext) { /* ... */ } } // ... } Better double check! Interleave control flows
  5. 5. „true".equals(request.getParameter("statsRequired")) ? true: false Make the result extra-boolean If only there was a parseBoolean method
  6. 6. if (dateCache.after(dateUser) || dateCache.equals(dateUser)) { return false; } else { return true; }
  7. 7. if (dateCache.after(dateUser) || dateCache.equals(dateUser)) { return true; } else { return false; }
  8. 8. Strings!
  9. 9. if (clazz != null) { header.concat(LayoutConst.CLASS_NAME_STR); header.concat(clazz.getName()); header.concat(LayoutConst.NEW_LINE); } Early implementation of github.com/kelseyhightower/noco de
  10. 10. public final static String NAME = new String("PIE DIAGRAM"); String surplus = new String(" "); new String(Long.toString(rightPoint)); new String("col" + String.valueOf(z + 2)); Me want objects! GC
  11. 11. Objects.equals( query.getHost(), hostName) Which of these are equal? • Localhost • LoCaLhOsT • localhost.localdomain • 127.0.0.1 • 127.0.0.2 • 192.168.178.5 • my-computer • my-computer.my-domain
  12. 12. Exceptional exception handling
  13. 13. } catch (Exception ex) { throw ex; }
  14. 14. } catch (Exception e) { // improbable exceptions, throw a runtime exception // to avoid the declaration in the method-signature throw new RuntimeException( "... failed", e); }
  15. 15. } catch (Exception ex) { // this exception will be ignored // because it cannot be sensibly handled ; } Why is there a semicolon?
  16. 16. } catch (SQLException sqlException) { ; // to be compliant with rule NoEmptyCatchBlocks } Ah, that’s why!
  17. 17. try { Thread.sleep(retryWaitTime); } catch (InterruptedException e) { ; // nothing to do } Unless you want your thread to be able to shut down …
  18. 18. try { nextLogger.setLogger( logManager.currentLogger(nextLoggerName)); loggerTable.remove(nextLoggerName); } catch (Throwable t) { failed = true; }
  19. 19. @SuppressFBWarnings(value=„REC_CATCH_EXCEPTION“, justification="silly rule")
  20. 20. Security!
  21. 21. HttpServletResponseWrapper { /* ... */ public void setHeader(String arg0, String arg1) { // in this case do nothing here, // because we are setting it in the filter } public void setContentType(String arg0) { // in this case do nothing here, // because we are setting it in the filter } Breaks contract of HttpServletResponse
  22. 22. public class PasswordEncryptor { private static final byte[] ALGO = new byte[]{68, 69, 83}; public static String encrypt(String str) throws BadPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, UnsupportedEncodingException { // ... Cipher cipher = Cipher.getInstance(new String(ALGO)); cipher.init(1, new SecretKeySpec(key, new String(ALGO))); // ... } } D E S
  23. 23. Misoverengineerin g!
  24. 24. public class ServiceInvocationHandler implements InvocationHandler { private final BusinessService underlyingBusinessService; private final BusinessService derivedFromBusinessService; public Object invoke(/* ... */ ) throws Throwable { /* ... */ } public static Object getService(/* ... */) { /* ... */ } } Let’s call these delegate and caller for the sake of clarity…
  25. 25. public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable { try { if (caller == null) delegate.setSession(HibernateUtil.getSessionFactory() .getCurrentSession()); else { Assert.isNotNull(caller.getSession()); delegate.setSession(caller.getSession()); } delegate.beginTransaction(); final Object ret = method.invoke(delegate, args); // continued ... No Caller → New Hibernate session Otherwise, use the caller’s session Wait, what if there already is an open transaction?
  26. 26. if (caller == null) delegate.commit(); if (delegate instanceof ServiceWithAssociatedThread && ((ServiceWithAssociatedThread) delegate) .hasAssociatedThread()) { ((ServiceWithAssociatedThread) delegate) .startAssociateThread(); ((ServiceWithAssociatedThread) delegate) .removeAssociateThread(); } return ret; } catch (final Throwable throwable) { // continued … } Unfinished transaction if there is a caller What could go wrong?
  27. 27. } catch (final Throwable throwable) { Logger.error(ServiceInvocationHandler.class, „Error ...", throwable); if (caller == null && delegate.hasTransaction()) delegate.rollback(); else if (caller != null && caller.hasTransaction()) caller.rollback(); throw throwable.getCause(); } finally { if (delegate.hasSession()) { //if (caller != null) // delegate.close(); delegate.setSession(null); } Wait, what? Wait for the GC to clean up after us Modify stack trace, possible NullPointerException
  28. 28. Integration Business What (probably) has happened Presentation Service Service Service Transaction Facade
  29. 29. The setting: A massive custom framework for web apps. Think JEE built in-house
  30. 30. protected void reset() { String[] classesForReset = { „com.acmecorp...legitimaton...XService", „com.acmecorp...legitimaton...YService", "com.acmecorp...legitimaton...ZService", // ... }; // continued ... Boss music starts…
  31. 31. for (int i = 0; i < classesForReset.length; i++) { try { Class class = class.forName(classesForReset[i]); Method method = class.getDeclaredMethod( „reset", new Class[0]); method.invoke(null, null); } catch Exception e) { System.err.println(/* ... */); e.printStackTrace(System.err); } } Call a static reset method on each class for each request
  32. 32. How bad can it be? public static void reset() { ldapFilters = new HashMap(); } Filters for per- user LDAP queries public static void reset() { sslSocketFactory = null; } New connections for each request
  33. 33. What (probably) has happened • Stored session state in static variables (maybe a former C programmer?) • We need a reset() method, but it has to be static • Can’t use an interface for static methods • Call them by reflection • Forget about this code and have it run in production... • ...for over 10 years • ...in almost 300 applications
  34. 34. What language do you want to have bad code in?
  35. 35. Thank you! @andreasz82
  36. 36. Bonus: More bad code
  37. 37. private static void copyHashMap(HashMap<String,String> source, HashMap<String,String> destination) { if ((source != null) && (destination != null)) { if (!destination.isEmpty()) { destination.clear(); } for(HashMap.Entry<String, String> entry: source.entrySet()) { String key = entry.getKey(); String value = entry.getValue(); destination.put(new String(key), new String(value)); } } }
  38. 38. return super.isIdentical(msg) && germanMsg != null ? germanMsg.getMsg().equals(msg.getMsg(Locale.GERMAN)): true && defaultMsg != null ? defaultMsg.getMsg().equals(msg.getMsg(DEFAULT_LOCALE)): true;
  39. 39. public AcmeCorpInteger(int intValue) { this(); try { setValue(new BigInteger("" + intValue)); } catch (AcmeCorpDatatypeConditionError ex) { } }
  40. 40. Integer enterpriseNo = new Integer( enterpriseDTO.getEnterpriseNo().getValue().intValue());
  41. 41. setCookie( (new Long(userSession.getAccessTime())).toString(), response);
  42. 42. public URLConnection(String[] urls, int maxRetries, long waitMs) { super(getUrlFromString(urls[0])); if ((null == urls) || (urls.length <= 0)) { throw new RuntimeException("empty urls"); } // ... } //NOBUG //Only to satisfy constructor

×