all that jazz

james' blog about scala and all that jazz

List of SuppressWarnings arguments

I hate compiler warnings showing in my IDE. It's messy, and it usually indicates that something is bad. But not always. Sometimes, when working with third party libraries, you can't avoid warnings like unchecked assignments. These are easy to get around though, by using the @SuppressWarnings annotation. The unchecked assignment warning is a standard Java compiler warning. A quick Google will reveal that the name of the unchecked assignment warning to pass to @SuppressWarnings is "unchecked".

IDE's however tend to flag warnings for much more than what the Java compiler flags warnings for. An example that I encountered today is in unit tests, if you have no assertions in your unit test, IDEA will flag a warning telling you so. In my case however, I was using mockito, and my assertions were in the form of verify calls. IDEA does not yet understand mockito, so I am faced with compiler warnings in my code, which, as I said before, I hate. Surely I can use @SuppressWarnings to suppress this warning, but what's its name? Google didn't help me at all here, so I decided to do a little investigation.

After writing a quick shell script that unpacked IDEAs jar files and grepped for the description of the warning, I found that the code that generates these warnings is an IDEA plugin called InspectionGadgets. I found that each warning was checked for by sub classes of com.siyeh.ig.BaseInspection, and that the name of the warning, and its description, can be obtained by instantiating each sub class, and calling methods on them. In the case of my test method with no assertions warning, the class that flagged this was com.siyeh.ig.junit.TestMethodWithoutAssertionInspection, and the name of the warning was JUnitTestMethodWithNoAssertions. I can now suppress my warning.

All that work was probably too much to do for each time that I want to suppress an IDEA warning. So I decided to find every warning, and store it in a table. Obviously I wasn't going to do this manually, so using this neat utility for finding all the classes on the classpath, with a few modifications to limit where it searched, I was able to generate a table of every warning that IDEA can flag. Here it is, sorted in alphabetical order of the description:

Warning DescriptionWarning Name
"Magic character"MagicCharacter
"Magic number"MagicNumber
'Comparator.compare()' method does not use parameterComparatorMethodParameterNotUsed
'Connection.prepare*()' call with non-constant stringJDBCPrepareStatementWithNonConstantString
'Iterator.hasNext()' which calls 'next()'IteratorHasNextCallsIteratorNext
'Iterator.next()' which can't throw 'NoSuchElementException'IteratorNextCanNotThrowNoSuchElementException
'Statement.execute()' call with non-constant stringJDBCExecuteWithNonConstantString
'String.equals("")'StringEqualsEmptyString
'StringBuffer' may be 'StringBuilder' (JDK 5.0 only)StringBufferMayBeStringBuilder
'StringBuffer.toString()' in concatenationStringBufferToStringInConcatenation
'assert' statementAssertStatement
'assertEquals()' between objects of inconvertible typesAssertEqualsBetweenInconvertibleTypes
'await()' not in loopAwaitNotInLoop
'await()' without corresponding 'signal()'AwaitWithoutCorrespondingSignal
'break' statementBreakStatement
'break' statement with labelBreakStatementWithLabel
'catch' generic classCatchGenericClass
'clone()' does not call 'super.clone()'CloneDoesntCallSuperClone
'clone()' does not declare 'CloneNotSupportedException'CloneDoesntDeclareCloneNotSupportedException
'clone()' instantiates objects with constructorCloneCallsConstructors
'clone()' method in non-Cloneable classCloneInNonCloneableClass
'compareto()' instead of 'compareTo()'MisspelledCompareTo
'continue' or 'break' inside 'finally' blockContinueOrBreakFromFinallyBlock
'continue' statementContinueStatement
'continue' statement with labelContinueStatementWithLabel
'default' not last case in 'switch'DefaultNotLastCaseInSwitch
'equal()' instead of 'equals()'MisspelledEquals
'equals()' between objects of inconvertible typesEqualsBetweenInconvertibleTypes
'equals()' called on array typeArrayEquals
'equals()' called on java.math.BigDecimalBigDecimalEquals
'equals()' method which does not check class of parameterEqualsWhichDoesntCheckParameterClass
'equals()' or 'hashCode()' called on java.net.URL objectEqualsHashCodeCalledOnUrl
'final' classFinalClass
'final' methodFinalMethod
'final' method in 'final' classFinalMethodInFinalClass
'finalize()' called explicitlyFinalizeCalledExplicitly
'finalize()' declarationFinalizeDeclaration
'finalize()' does not call 'super.finalize()'FinalizeDoesntCallSuperFinalize
'finalize()' not declared 'protected'FinalizeNotProtected
'finally' block which can not complete normallyfinally
'for' loop may be replaced by 'while' loopForLoopReplaceableByWhile
'for' loop replaceable by 'for each'ForLoopReplaceableByForEach
'for' loop where update or condition does not use loop variableForLoopThatDoesntUseLoopVariable
'for' loop with missing componentsForLoopWithMissingComponent
'hashcode()' instead of 'hashCode()'MisspelledHashcode
'if' statement with identical branchesIfStatementWithIdenticalBranches
'if' statement with negated conditionIfStatementWithNegatedCondition
'if' statement with too many branchesIfStatementWithTooManyBranches
'indexOf()' expression is replaceable by 'contains()'ListIndexOfReplaceableByContains
'instanceof' a concrete classInstanceofInterfaces
'instanceof' check for 'this'InstanceofThis
'instanceof' on 'catch' parameterInstanceofCatchParameter
'instanceof' with incompatible interfaceInstanceofIncompatibleInterface
'notify()' or 'notifyAll()' called on java.util.concurrent.locks.Condition objectNotifyCalledOnCondition
'notify()' or 'notifyAll()' while not syncedNotifyNotInSynchronizedContext
'notify()' or 'notifyAll()' without corresponding state changeNakedNotify
'notify()' without corresponding 'wait()'NotifyWithoutCorrespondingWait
'private' method declared 'final'FinalPrivateMethod
'protected' member in 'final' classProtectedMemberInFinalClass
'public' constructor in non-public classPublicConstructorInNonPublicClass
'readObject()' or 'writeObject()' not declared 'private'NonPrivateSerializationMethod
'readResolve()' or 'writeReplace()' not declared 'protected'ReadResolveAndWriteReplaceProtected
'return' inside 'finally' blockReturnInsideFinallyBlock
'serialPersistentFields' field not declared 'private static final ObjectStreamField[]'SerialPersistentFieldsWithWrongSignature
'serialVersionUID' field not declared 'private static final long'SerialVersionUIDWithWrongSignature
'setUp()' does not call 'super.setUp()'SetUpDoesntCallSuperSetUp
'setUp()' with incorrect signatureSetUpWithIncorrectSignature
'setup()' instead of 'setUp()'MisspelledSetUp
'signal()' without corresponding 'await()'SignalWithoutCorrespondingAwait
'size() == 0' replaceable by 'isEmpty()'SizeReplaceableByIsEmpty
'static' method declared 'final'FinalStaticMethod
'static', non-'final' fieldStaticNonFinalField
'suite()' method not declared 'static'SuiteNotDeclaredStatic
'switch' statementSwitchStatement
'switch' statement with too few branchesSwitchStatementWithTooFewBranches
'switch' statement with too low of a branch densitySwitchStatementDensity
'switch' statement with too many branchesSwitchStatementWithTooManyBranches
'switch' statement without 'default' branchSwitchStatementWithoutDefaultBranch
'synchronized' methodSynchronizedMethod
'tearDown()' does not call 'super.tearDown()'TearDownDoesntCallSuperTearDown
'tearDown()' with incorrect signatureTearDownWithIncorrectSignature
'teardown()' instead of 'tearDown()'MisspelledTearDown
'this' reference escaped in object constructionThisEscapedInObjectConstruction
'throw' caught by containing 'try' statementThrowCaughtLocally
'throw' inside 'catch' block which ignores the caught exceptionThrowInsideCatchBlockWhichIgnoresCaughtException
'throw' inside 'finally' blockThrowFromFinallyBlock
'tostring()' instead of 'toString()'MisspelledToString
'wait()' called on java.util.concurrent.locks.Condition objectWaitCalledOnCondition
'wait()' not in loopWaitNotInLoop
'wait()' or 'await()' without timeoutWaitOrAwaitWithoutTimeout
'wait()' while holding two locksWaitWhileHoldingTwoLocks
'wait()' while not syncedWaitWhileNotSynced
'wait()' without corresponding 'notify()'WaitWithoutCorrespondingNotify
'while' loop replaceable by 'for each'WhileLoopReplaceableByForEach
* importOnDemandImport
Abstract class extends concrete classAbstractClassExtendsConcreteClass
Abstract class which has no concrete subclassAbstractClassNeverImplemented
Abstract class which has only one direct inheritorAbstractClassWithOnlyOneDirectInheritor
Abstract class without abstract methodsAbstractClassWithoutAbstractMethods
Abstract method call in constructorAbstractMethodCallInConstructor
Abstract method overrides abstract methodAbstractMethodOverridesAbstractMethod
Abstract method overrides concrete methodAbstractMethodOverridesConcreteMethod
Abstract method with missing implementationsAbstractMethodWithMissingImplementations
Access of system propertiesAccessOfSystemProperties
Access to non thread-safe static field from instanceAccessToNonThreadSafeStaticFieldFromInstance
Access to static field locked on instance dataAccessToStaticFieldLockedOnInstance
Accessing a non-public field of another objectAccessingNonPublicFieldOfAnotherObject
AnnotationAnnotation
Annotation classAnnotationClass
Annotation naming conventionAnnotationNamingConvention
Anonymous class variable hides variable in containing methodAnonymousClassVariableHidesContainingMethodVariable
Anonymous inner classAnonymousInnerClass
Anonymous inner class may be a named static inner classAnonymousInnerClassMayBeStatic
Anonymous inner class with too many methodsAnonymousInnerClassWithTooManyMethods
Arithmetic operation on volatile fieldArithmeticOnVolatileField
Array.length in loop conditionArrayLengthInLoopCondition
Assignment replaceable with operator assignmentAssignmentReplaceableWithOperatorAssignment
Assignment to 'for' loop parameterAssignmentToForLoopParameter
Assignment to 'null'AssignmentToNull
Assignment to Collection or array field from parameterAssignmentToCollectionOrArrayFieldFromParameter
Assignment to Date or Calendar field from parameterAssignmentToDateFieldFromParameter
Assignment to catch block parameterAssignmentToCatchBlockParameter
Assignment to method parameterAssignmentToMethodParameter
Assignment to static field from instance methodAssignmentToStaticFieldFromInstanceMethod
Assignment used as conditionAssignmentUsedAsCondition
Auto-boxingAutoBoxing
Auto-unboxingAutoUnboxing
Boolean constructor callBooleanConstructorCall
Boolean method name must start with question wordBooleanMethodNameMustStartWithQuestion
Busy waitBusyWait
C-style array declarationCStyleArrayDeclaration
Call to 'Collection.toArray()' with zero-length array argumentToArrayCallWithZeroLengthArrayArgument
Call to 'Date.toString()'CallToDateToString
Call to 'Runtime.exec()'CallToRuntimeExecWithNonConstantString
Call to 'String.compareTo()'CallToStringCompareTo
Call to 'String.concat()' can be replaced by '+'CallToStringConcatCanBeReplacedByOperator
Call to 'String.equals()'CallToStringEquals
Call to 'String.equalsIgnoreCase()'CallToStringEqualsIgnoreCase
Call to 'String.toUpperCase()' or 'toLowerCase()' without a LocaleStringToUpperCaseOrToLowerCaseWithoutLocale
Call to 'System.exit()' or related methodsCallToSystemExit
Call to 'System.getenv()'CallToSystemGetenv
Call to 'System.loadLibrary()' with non-constant stringLoadLibraryWithNonConstantString
Call to 'System.runFinalizersOnExit()'CallToSystemRunFinalizersOnExit
Call to 'System.setSecurityManager()'CallToSystemSetSecurityManager
Call to 'Thread.dumpStack()'CallToThreadDumpStack
Call to 'Thread.run()'CallToThreadRun
Call to 'Thread.setPriority()'CallToThreadSetPriority
Call to 'Thread.sleep()' while synchronizedSleepWhileHoldingLock
Call to 'Thread.start()' during object constructionCallToThreadStartDuringObjectConstruction
Call to 'Thread.stop()', 'suspend()' or 'resume()'CallToThreadStopSuspendOrResumeManager
Call to 'Thread.yield()'CallToThreadYield
Call to 'Time.toString()'CallToTimeToString
Call to 'intern()' on String constantConstantStringIntern
Call to 'notify()' instead of 'notifyAll()'CallToNotifyInsteadOfNotifyAll
Call to 'printStackTrace()'CallToPrintStackTrace
Call to 'signal()' instead of 'signalAll()'CallToSignalInsteadOfSignalAll
Call to Numeric 'toString()'CallToNumericToString
Call to String.replaceAll(".", ...)ReplaceAllDot
Call to a native method while lockedCallToNativeMethodWhileLocked
Call to default 'toString()'ObjectToString
Call to simple getter from within classCallToSimpleGetterFromWithinClass
Call to simple setter from within classCallToSimpleSetterFromWithinClass
Calls to 'System.gc()' or 'Runtime.gc()'CallToSystemGC
Cast conflicts with 'instanceof'CastConflictsWithInstanceof
Cast to a concrete classCastToConcreteClass
Casting to incompatible interfaceCastToIncompatibleInterface
Caught exception is immediately rethrownCaughtExceptionImmediatelyRethrown
Chain of 'instanceof' checksChainOfInstanceofChecks
Chained equality comparisonsChainedEqualityComparisons
Chained method callsChainedMethodCall
Channel opened but not safely closedChannelOpenedButNotSafelyClosed
Character comparisonCharacterComparison
Checked exception classCheckedExceptionClass
Class escapes defined scopeClassEscapesDefinedScope
Class explicitly extends a Collection classClassExtendsConcreteCollection
Class explicitly extends java.lang.ObjectClassExplicitlyExtendsObject
Class explicitly extends java.lang.ThreadClassExplicitlyExtendsThread
Class extends annotation interfaceClassExplicitlyAnnotation
Class extends utility classExtendsUtilityClass
Class may be interfaceClassMayBeInterface
Class name differs from file nameClassNameDiffersFromFileName
Class name prefixed with package nameClassNamePrefixedWithPackageName
Class name same as ancestor nameClassNameSameAsAncestorName
Class naming conventionClassNamingConvention
Class references one of its subclassesClassReferencesSubclass
Class too deep in inheritance treeClassTooDeepInInheritanceTree
Class with multiple loggersClassWithMultipleLoggers
Class with too many constructorsClassWithTooManyConstructors
Class with too many fieldsClassWithTooManyFields
Class with too many methodsClassWithTooManyMethods
Class without 'toString()'ClassWithoutToString
Class without constructorClassWithoutConstructor
Class without loggerClassWithoutLogger
Class without no-arg constructorClassWithoutNoArgConstructor
Class without package statementClassWithoutPackageStatement
ClassLoader instantiationClassLoaderInstantiation
Cloneable class in secure contextCloneableClassInSecureContext
Cloneable class without 'clone()'CloneableClassWithoutClone
Collection added to selfCollectionAddedToSelf
Collection declared by class, not interfaceCollectionDeclaredAsConcreteClass
Collection without initial capacityCollectionWithoutInitialCapacity
Comparable implemented but 'equals()' not overriddenComparableImplementedButEqualsNotOverridden
Comparator class not declared SerializableComparatorNotSerializable
Comparison of 'short' and 'char' valuesComparisonOfShortAndChar
Comparison to Double.NaN or Float.NaNComparisonToNaN
Concatenation with empty stringConcatenationWithEmptyString
Conditional expression (?:)ConditionalExpression
Conditional expression with identical branchesConditionalExpressionWithIdenticalBranches
Conditional expression with negated conditionConditionalExpressionWithNegatedCondition
Conditional that can be simplified to && or ||SimplifiableConditionalExpression
Confusing 'else' branchConfusingElseBranch
Confusing 'main()' methodConfusingMainMethod
Confusing 'null' argument to var-arg methodNullArgumentToVariableArgMethod
Confusing floating-point literalConfusingFloatingPointLiteral
Confusing octal escape sequenceConfusingOctalEscapeSequence
Confusing primitive array argument to var-arg methodPrimitiveArrayArgumentToVariableArgMethod
Connection opened but not safely closedConnectionOpenedButNotSafelyClosed
Constant StringBuffer may be StringStringBufferReplaceableByString
Constant call to java.lang.Math or StrictMathConstantMathCall
Constant conditional expressionConstantConditionalExpression
Constant declared in abstract classConstantDeclaredInAbstractClass
Constant declared in interfaceConstantDeclaredInInterface
Constant if statementConstantIfStatement
Constant naming conventionConstantNamingConvention
Constant on left side of comparisonConstantOnLeftSideOfComparison
Constant on right side of comparisonConstantOnRightSideOfComparison
Constructor not 'protected' in 'abstract' classConstructorNotProtectedInAbstractClass
Constructor with too many parametersConstructorWithTooManyParameters
Control flow statement without bracesControlFlowStatementWithoutBraces
Covariant 'compareTo()'CovariantCompareTo
Covariant 'equals()'CovariantEquals
Custom ClassLoaderCustomClassloader
Custom SecurityManagerCustomSecurityManager
Deserializable class in secure contextDeserializableClassInSecureContext
Design for extensionDesignForExtension
Division by zerodivzero
Double negationDoubleNegation
Double-checked lockingDoubleCheckedLocking
Duplicate condition in 'if' statementDuplicateCondition
Duplicate condition on '&&' or '||'DuplicateBooleanBranch
Empty 'catch' blockEmptyCatchBlock
Empty 'finally' blockEmptyFinallyBlock
Empty 'synchronized' statementEmptySynchronizedStatement
Empty 'try' blockEmptyTryBlock
Empty classEmptyClass
Empty class initializerEmptyClassInitializer
Enum 'switch' statement that misses caseEnumSwitchStatementWhichMissesCases
Enumerated classEnumClass
Enumerated class naming conventionEnumeratedClassNamingConvention
Enumerated constant naming conventionEnumeratedConstantNamingConvention
Enumeration can be iterationEnumerationCanBeIteration
Exception class name does not end with 'Exception'ExceptionClassNameDoesntEndWithException
Extended 'for' statementForeachStatement
Externalizable class with 'readObject()' or 'writeObject()'ExternalizableClassWithSerializationMethods
Fallthrough in 'switch' statementfallthrough
Feature envyFeatureEnvy
Field accessed in both synchronized and unsynchronized contextsFieldAccessedSynchronizedAndUnsynchronized
Field has setter but no getterFieldHasSetterButNoGetter
Field may be 'static'FieldMayBeStatic
Field name hides field in superclassFieldNameHidesFieldInSuperclass
Field repeatedly accessed in methodFieldRepeatedlyAccessedInMethod
Floating point equality comparisonFloatingPointEquality
Hardcoded file separatorHardcodedFileSeparator
Hardcoded line separatorHardcodedLineSeparator
Hibernate resource opened but not safely closedHibernateResourceOpenedButNotSafelyClosed
I/O resource opened but not safely closedIOResourceOpenedButNotSafelyClosed
If statement may be replaced by && or || expressionSimplifiableIfStatement
Implicit call to 'super()'ImplicitCallToSuper
Implicit call to array '.toString()'ImplicitArrayToString
Implicit numeric conversionImplicitNumericConversion
Import from same packageSamePackageImport
Incompatible bitwise mask operationIncompatibleBitwiseMaskOperation
Infinite loop statementInfiniteLoopStatement
Infinite recursionInfiniteRecursion
Inner class field hides outer class fieldInnerClassFieldHidesOuterClassField
Inner class may be 'static'InnerClassMayBeStatic
Inner class of interfaceInnerClassOfInterface
Inner class too deeply nestedInnerClassTooDeeplyNested
Insecure random number generationUnsecureRandomNumberGeneration
Inspection suppression annotationSuppressionAnnotation
Instance method naming conventionInstanceMethodNamingConvention
Instance variable may not be initializedInstanceVariableMayNotBeInitialized
Instance variable may not be initialized by 'readObject()'InstanceVariableMayNotBeInitializedByReadObject
Instance variable naming conventionInstanceVariableNamingConvention
Instance variable of concrete classInstanceVariableOfConcreteClass
Instance variable used before initializedInstanceVariableUsedBeforeInitialized
Instantiating a SimpleDateFormat without a LocaleSimpleDateFormatWithoutLocale
Instantiating a Thread with default 'run()' methodInstantiatingAThreadWithDefaultRunMethod
Instantiating object to get Class objectInstantiatingObjectToGetClassObject
Instantiation of utility classInstantiationOfUtilityClass
Integer division in floating point contextIntegerDivisionInFloatingPointContext
Integer multiplication or shift implicitly cast to longIntegerMultiplicationImplicitCastToLong
Interface naming conventionInterfaceNamingConvention
Interface which has no concrete subclassInterfaceNeverImplemented
Interface which has only one direct inheritorInterfaceWithOnlyOneDirectInheritor
JDBC resource opened but not safely closedJDBCResourceOpenedButNotSafelyClosed
JNDI resource opened but not safely closedJNDIResourceOpenedButNotSafelyClosed
JUnit TestCase in product sourceJUnitTestCaseInProductSource
JUnit TestCase with non-trivial constructorsJUnitTestCaseWithNonTrivialConstructors
JUnit abstract test class naming conventionJUnitAbstractTestClassNamingConvention
JUnit test case with no testsJUnitTestCaseWithNoTests
JUnit test class naming conventionJUnitTestClassNamingConvention
JUnit test method in product sourceJUnitTestMethodInProductSource
JUnit test method without any assertionsJUnitTestMethodWithNoAssertions
Labeled statementLabeledStatement
Large array allocation with no OutOfMemoryError checkCheckForOutOfMemoryOnLargeArrayAllocation
Limited-scope inner classLimitedScopeInnerClass
Local variable hides member variableLocalVariableHidesMemberVariable
Local variable naming conventionLocalVariableNamingConvention
Local variable of concrete classLocalVariableOfConcreteClass
Local variable used and declared in different 'switch' branchesLocalVariableUsedAndDeclaredInDifferentSwitchBranches
Lock acquired but not safely unlockedLockAcquiredButNotSafelyReleased
Long literal ending with 'l' instead of 'L'LongLiteralEndingWithLowercaseL
Loop statement that does not loopLoopStatementThatDoesntLoop
Loop variable not updated inside loopLoopConditionNotUpdatedInsideLoop
Loop with implicit termination conditionLoopWithImplicitTerminationCondition
Malformed @Before or @After methodBeforeOrAfterWithIncorrectSignature
Malformed @BeforeClass or @AfterClass methodBeforeOrAfterWithIncorrectSignature
Malformed XPath expressionMalformedXPath
Malformed format stringMalformedFormatString
Malformed regular expressionMalformedRegex
Manual array copyManualArrayCopy
Manual array to collection copyManualArrayToCollectionCopy
Map or Set may contain java.net.URL objectsCollectionContainsUrl
Map replaceable by EnumMapMapReplaceableByEnumMap
Marker interfaceMarkerInterface
Message missing on JUnit assertionMessageMissingOnJUnitAssertion
Method call in loop conditionMethodCallInLoopCondition
Method call violates Law of DemeterLawOfDemeter
Method is identical to its super methodRedundantMethodOverride
Method may be 'static'MethodMayBeStatic
Method name same as class nameMethodNameSameAsClassName
Method name same as parent class nameMethodNameSameAsParentName
Method names differing only by caseMethodNamesDifferingOnlyByCase
Method overloads method of superclassMethodOverloadsMethodOfSuperclass
Method overrides package local method of superclass located in other packageMethodOverridesPrivateMethodOfSuperclass
Method overrides private method of superclassMethodOverridesPrivateMethodOfSuperclass
Method overrides static method of superclassMethodOverridesStaticMethodOfSuperclass
Method parameter naming conventionMethodParameterNamingConvention
Method parameter of concrete classMethodParameterOfConcreteClass
Method return of concrete classMethodReturnOfConcreteClass
Method with more than three negationsMethodWithMoreThanThreeNegations
Method with multiple loopsMethodWithMultipleLoops
Method with multiple return points.MethodWithMultipleReturnPoints
Method with synchronized block could be synchronized methodMethodMayBeSynchronized
Method with too many exceptions declaredMethodWithTooExceptionsDeclared
Method with too many parametersMethodWithTooManyParameters
Mismatched query and update of collectionMismatchedQueryAndUpdateOfCollection
Mismatched read and write of arrayMismatchedReadAndWriteOfArray
Misordered 'assertEquals()' parametersMisorderedAssertEqualsParameters
Missing @Deprecated annotationMissingDeprecatedAnnotation
Missing @Override annotationoverride
Missorted modifersMissortedModifiers
Multiple top level classes in single fileMultipleTopLevelClassesInFile
Multiple variables in one declarationMultipleVariablesInDeclaration
Multiply or divide by power of twoMultiplyOrDivideByPowerOfTwo
Native methodNativeMethod
Nested 'switch' statementNestedSwitchStatement
Nested 'synchronized' statementNestedSynchronizedStatement
Nested 'try' statementNestedTryStatement
Nested assignmentNestedAssignment
Nested conditional expressionNestedConditionalExpression
Nested method callNestedMethodCall
No-op method in abstract classNoopMethodInAbstractClass
Non-boolean method name must not start with question wordNonBooleanMethodNameMayNotStartWithQuestion
Non-constant String should be StringBufferNonConstantStringShouldBeStringBuffer
Non-constant field with upper-case nameNonConstantFieldWithUpperCaseName
Non-constant loggerNonConstantLogger
Non-exception class name ends with 'Exception'NonExceptionNameEndsWithException
Non-final 'clone()' in secure contextNonFinalClone
Non-final field of exception classNonFinalFieldOfException
Non-final field referenced in 'compareTo()'CompareToUsesNonFinalVariable
Non-final field referenced in 'equals()'NonFinalFieldReferenceInEquals
Non-final field referenced in 'hashCode()'NonFinalFieldReferencedInHashCode
Non-final static variable is used during class initializationNonFinalStaticVariableUsedInClassInitialization
Non-private field accessed in synchronized contextNonPrivateFieldAccessedInSynchronizedContext
Non-reproducible call to java.lang.MathNonReproducibleMathCall
Non-serializable class with 'readObject()' or 'writeObject()'NonSerializableClassWithSerializationMethods
Non-serializable class with 'serialVersionUID'NonSerializableClassWithSerialVersionUID
Non-serializable field in a Serializable classNonSerializableFieldInSerializableClass
Non-serializable object bound to HttpSessionNonSerializableObjectBoundToHttpSession
Non-serializable object passed to ObjectOutputStreamNonSerializableObjectPassedToObjectStream
Non-short-circuit boolean expressionNonShortCircuitBooleanExpression
Non-static initializerNonStaticInitializer
Non-static inner class in secure contextNonStaticInnerClassInSecureContext
Non-synchronized method overrides synchronized methodNonSynchronizedMethodOverridesSynchronizedMethod
Number comparison using '==', instead of 'equals()'NumberEquality
Number constructor call with primitive argumentCachedNumberConstructorCall
Numeric cast that loses precisionNumericCastThatLosesPrecision
Object allocation in loopObjectAllocationInLoop
Object comparison using ==, instead of 'equals()'ObjectEquality
Object.equals(null)ObjectEqualsNull
Octal and decimal integers in same arrayOctalAndDecimalIntegersInSameArray
Octal integerOctalInteger
Overloaded methods with same number of parametersOverloadedMethodsWithSameNumberOfParameters
Overloaded variable argument methodOverloadedVarargsMethod
Overly broad 'catch' blockOverlyBroadCatchBlock
Overly complex anonymous inner classOverlyComplexAnonymousInnerClass
Overly complex arithmetic expressionOverlyComplexArithmeticExpression
Overly complex boolean expressionOverlyComplexBooleanExpression
Overly complex classOverlyComplexClass
Overly complex methodOverlyComplexMethod
Overly coupled classOverlyCoupledClass
Overly coupled methodOverlyCoupledMethod
Overly large initializer for array of primitive typeOverlyLargePrimitiveArrayInitializer
Overly long methodOverlyLongMethod
Overly nested methodOverlyNestedMethod
Overly-strong type castOverlyStrongTypeCast
Overridable method call during object constructionOverridableMethodCallDuringObjectConstruction
Overridden method call during object constructionOverriddenMethodCallDuringObjectConstruction
Package-visible fieldPackageVisibleField
Package-visible inner classPackageVisibleInnerClass
Parameter hides member variableParameterHidesMemberVariable
Parameter name differs from parameter in overridden methodParameterNameDiffersFromOverriddenParameter
Pointless 'indexOf()' comparisonPointlessIndexOfComparison
Pointless arithmetic expressionPointlessArithmeticExpression
Pointless bitwise expressionPointlessBitwiseExpression
Pointless boolean expressionPointlessBooleanExpression
Private member access between outer and inner classesPrivateMemberAccessBetweenOuterAndInnerClass
Private method only used from inner classMethodOnlyUsedFromInnerClass
Prohibited exception caughtProhibitedExceptionCaught
Prohibited exception declaredProhibitedExceptionDeclared
Prohibited exception thrownProhibitedExceptionThrown
Protected fieldProtectedField
Protected inner classProtectedInnerClass
Public fieldPublicField
Public inner classPublicInnerClass
Public method not exposed in interfacePublicMethodNotExposedInInterface
Public method without loggingPublicMethodWithoutLogging
Public static array fieldPublicStaticArrayField
Public static collection fieldPublicStaticCollectionField
Questionable nameQuestionableName
Raw use of parameterized classRawUseOfParameterizedType
RecordStore opened but not safely closedRecordStoreOpenedButNotSafelyClosed
Redundant '.substring(0)'SubstringZero
Redundant 'String.toString()'RedundantStringToString
Redundant 'if' statementRedundantIfStatement
Redundant String constructor callRedundantStringConstructorCall
Redundant conditional expressionRedundantConditionalExpression
Redundant field initializationRedundantFieldInitialization
Redundant importRedundantImport
Redundant interface declarationRedundantInterfaceDeclaration
Redundant local variableUnnecessaryLocalVariable
Redundant no-arg constructorRedundantNoArgConstructor
Reflective access to a source-only annotationReflectionForUnavailableAnnotation
Refused bequestRefusedBequest
Result of method call ignoredResultOfMethodCallIgnored
Result of object allocation ignoredResultOfObjectAllocationIgnored
Return of 'null'ReturnOfNull
Return of 'this'ReturnOfThis
Return of Collection or array fieldReturnOfCollectionOrArrayField
Return of Date or Calendar fieldReturnOfDateField
Reuse of local variableReuseOfLocalVariable
Scope of variable is too broadTooBroadScope
Serializable class in secure contextSerializableClassInSecureContext
Serializable class with unconstructable ancestorSerializableClassWithUnconstructableAncestor
Serializable class without 'readObject()' and 'writeObject()'SerializableHasSerializationMethods
Serializable class without 'serialVersionUID'serial
Serializable non-static inner class with non-Serializable outer classSerializableInnerClassWithNonSerializableOuterClass
Serializable non-static inner class without 'serialVersionUID'SerializableNonStaticInnerClassWithoutSerialVersionUID
Set replaceable by EnumSetSetReplaceableByEnumSet
Shift operation by inappropriate constantShiftOutOfRange
Simplifiable JUnit assertionSimplifiableJUnitAssertion
Single character 'startsWith()' or 'endsWith()'SingleCharacterStartsWith
Single character string concatenationSingleCharacterStringConcatenation
Single character string parameter in 'String.indexOf()' callSingleCharacterStringConcatenation
Single class importSingleClassImport
SingletonSingleton
Socket opened but not safely closedSocketOpenedButNotSafelyClosed
Standard variable namesStandardVariableNames
Statement with empty bodyStatementWithEmptyBody
Static collectionStaticCollection
Static field referenced via subclassStaticFieldReferencedViaSubclass
Static importStaticImport
Static inheritanceStaticInheritance
Static method naming conventionStaticMethodNamingConvention
Static method only used from one other classStaticMethodOnlyUsedInOneClass
Static method referenced via subclassStaticMethodReferencedViaSubclass
Static variable may not be initializedStaticVariableMayNotBeInitialized
Static variable naming conventionStaticVariableNamingConvention
Static variable of concrete classStaticVariableOfConcreteClass
Static variable used before initializationStaticVariableUsedBeforeInitialization
String comparison using '==', instead of 'equals()'StringEquality
String concatenationStringConcatenation
String concatenation in loopStringContatenationInLoop
String concatenation inside 'StringBuffer.append()'StringConcatenationInsideStringBufferAppend
StringBuffer constructor call with 'char' argumentNewStringBufferWithCharArgument
StringBuffer fieldStringBufferField
StringBuffer or StringBuilder without initial capacityStringBufferWithoutInitialCapacity
Subtraction in compareTo()SubtractionInCompareTo
Suspicious 'Collections.toArray()' callSuspiciousToArrayCall
Suspicious 'System.arraycopy()' callSuspiciousSystemArraycopy
Suspicious indentation after control statement without bracesSuspiciousIndentAfterControlStatement
Suspicious test for oddnessBadOddness
Synchronization on 'this'SynchronizeOnThis
Synchronization on a Lock objectSynchroniziationOnLockObject
Synchronization on a non-final fieldSynchronizeOnNonFinalField
Synchronization on an object initialized with a literalSynchronizedOnLiteralObject
TODO commentTodoComment
Tail recursionTailRecursion
Test method with incorrect signatureTestMethodWithIncorrectSignature
Text label in 'switch' statementTextLabelInSwitchStatement
Throwable instance not thrownThrowableInstanceNeverThrown
Transient field in non-serializable classTransientFieldInNonSerializableClass
Transient field is not initialized on deserializationTransientFieldNotInitialized
Type may be weakenedTypeMayBeWeakened
Type parameter explicitly extends 'java.lang.Object'TypeParameterExplicitlyExtendsObject
Type parameter extends final classTypeParameterExtendsFinalClass
Type parameter hides visible typeTypeParameterHidesVisibleType
Type parameter naming conventionTypeParameterNamingConvention
Unary plusUnaryPlus
Unchecked exception classUncheckedExceptionClass
Unconditional 'wait()' callUnconditionalWait
Unconstructable JUnit TestCaseUnconstructableJUnitTestCase
Unnecessarily qualified static usageUnnecessarilyQualifiedStaticUsage
Unnecessary 'continue' statementUnnecessaryContinue
Unnecessary 'default' for enum switch statementUnnecessaryDefault
Unnecessary 'final' for local variableUnnecessaryFinalOnLocalVariable
Unnecessary 'final' for method parameterUnnecessaryFinalForMethodParameter
Unnecessary 'return' statementUnnecessaryReturnStatement
Unnecessary 'this' qualifierUnnecessaryThis
Unnecessary boxingUnnecessaryBoxing
Unnecessary call to 'super()'UnnecessaryCallToSuper
Unnecessary code blockUnnecessaryCodeBlock
Unnecessary enum modifierUnnecessaryEnumModifier
Unnecessary fully qualified nameUnnecessaryFullyQualifiedName
Unnecessary interface modifierUnnecessaryInterfaceModifier
Unnecessary label on 'break' statementUnnecessaryLabelOnBreakStatement
Unnecessary label on 'continue' statementUnnecessaryLabelOnContinueStatement
Unnecessary parenthesesUnnecessaryParentheses
Unnecessary qualifier for 'this'UnnecessaryQualifierForThis
Unnecessary semicolonUnnecessarySemicolon
Unnecessary temporary object in conversion from StringUnnecessaryTemporaryOnConversionFromString
Unnecessary temporary object in conversion to StringUnnecessaryTemporaryOnConversionToString
Unnecessary unary minusUnnecessaryUnaryMinus
Unnecessary unboxingUnnecessaryUnboxing
Unpredictable BigDecimal constructor callUnpredictableBigDecimalConstructorCall
Unqualified instance field accessUnqualifiedFieldAccess
Unqualified static usageUnqualifiedStaticUsage
Unsafe lazy initialization of static fieldNonThreadSafeLazyInitialization
Unused 'catch' parameterUnusedCatchParameter
Unused importUnusedImport
Unused labelUnusedLabel
Use of '$' in identifierDollarSignInName
Use of 'assert' as identifierAssertAsIdentifier
Use of 'enum' as identifierEnumAsIdentifier
Use of AWT peer classUseOfAWTPeerClass
Use of DriverManager to get JDBC connectionCallToDriverManagerGetConnection
Use of Properties object as a HashtableUseOfPropertiesAsHashtable
Use of StringTokenizerUseOfStringTokenizer
Use of System.out or System.errUseOfSystemOutOrSystemErr
Use of archaic system property accessorsUseOfArchaicSystemPropertyAccessors
Use of concrete JDBC driver classUseOfJDBCDriverClass
Use of index 0 in JDBC ResultSetUseOfIndexZeroInJDBCResultSet
Use of java.lang.ProcessBuilder classUseOfProcessBuilder
Use of java.lang.reflectJavaLangReflect
Use of obsolete collection typeUseOfObsoleteCollectionType
Use of sun.* classesUseOfSunClasses
Using 'Random.nextDouble()' to get random integerUsingRandomNextDoubleForRandomInteger
Utility classUtilityClass
Utility class with public constructorUtilityClassWithPublicConstructor
Utility class without private constructorUtilityClassWithoutPrivateConstructor
Value of ++ or -- usedValueOfIncrementOrDecrementUsed
Variable argument methodVariableArgumentMethod
Variables of different types in one declarationVariablesOfDifferentTypesInDeclaration
Volatile array fieldVolatileArrayField
Volatile long or double fieldVolatileLongOrDoubleField
While loop spins on fieldWhileLoopSpinsOnField
Zero-length array allocationZeroLengthArrayAllocation
expression.equals("literal") rather than "literal".equals(expression)LiteralAsArgToStringEquals
java.lang importJavaLangImport
java.lang.Error not rethrownErrorNotRethrown
java.lang.ThreadDeath not rethrownThreadDeathNotRethrown

JPA 2.0 - Early Review

A few days ago, JSR-317, the specification request for JPA 2.0, released an early draft review of the specification. Today I had the chance to skim through it to see what new things it had to offer.

Something I commented on in an earlier blog post was how they might implement deleting orphan members of an association. Would they follow TopLink's private collection mapping, or use Hibernate's additional delete orphan cascade type? It turns out, they chose neither... or rather, they met half way. The EDR specifies the addition of a boolean orphanRemoval attribute on the @OneToMany and @OneToOne annotations. So the terminology follows the Hibernate orphan terminology, however hibernate uses a DELETE_ORPHAN cascade type. The method though is more similar to TopLink, which has private ownership as a property of a collection mapping.

I don't think that I like this way of doing it. I don't think it's consistent, if I want a new entity to be persisted just by adding it to a collection, then I specify cascade=PERSIST. But if I want an existing entity to be deleted just by removing it from a collection, then I specify orphanRemoval=true. Cascading persistence to members added to collections and removing orphaned elements are both similar operations, in that they indirectly change the life cycle state of an entity, that occur at flush time, in a cascade like manner. I think it would be more consistent to say cascade=ORPHAN_REMOVE. The expert group has made a note in the EDR asking for input on which strategy they should use.

JPA 2.0 - New features - Part 1

In my previous blog post I wrote about asking the JPA presenter at the Sun Tech Days if JPA will support collections of basic types. I've now done some research of my own and found that most of the features that I miss most in JPA, compared to Hibernate, will be in JPA.

Let me start by saying that the first version of the Java Persistence API is excellent. Standardising three major competing vendors of ORM technologies, Hibernate, JDO and Toplink, into one standard would have been by no means an easy task. It certainly would have been an easy way out for the expert group to produce an API that included only the features they could agree on, but did not include many of the fundamental features of an ORM framework. I feel however that all the core aspects of ORM concepts have been addressed, the things that were left out were not essential. This is where JPA 2.0 comes in. Now that JPA has gained industry acceptance, it is time to fill the standard out, include all those nice to have features and clean it up around the edges.

The JPA 2.0 specification has yet to be proposed as a JSR, so nothing that I'm about to mention is definitely going to be in there. All of the information I'm providing is based on presentations that I've read given by the people who will be involved in specifying JPA 2.0.

Collections of basic and embedded types

This is the feature that I most miss whenever I use JPA. I can understand why it may not have been seen as essential to be included in JPA initially, collections of basic types is not strictly an object oriented concept, because basic types are not objects. Nevertheless, in practice it is data structure frequently used both in relational databases and in object oriented models.

An example might be tags in a blog. A tag would not be considered an object, it is simply a keyword used to index blog entries. The same tag may be applied to multiple blog entries, but if it were removed from all of these blog entries, it would no longer exist in any form. The concept of a tag is therefore not an object, and hence it would not make sense to map tags as entities in themselves. Rather, each blog entry would have a set of Strings. Using hibernate extensions, this is currently how it's done:

@CollectionOfElements
private Set tags;

This can similarly be mapped in TopLink using @BasicCollection.

Unidirectional one to many

A feature that follows on from collections of basic types, and would also be very helpful in collections of entities, is unidirectional one to many associations. JPA currently only supports one to many associations as the inverse end of a many to one association, so the owning side of the association is the many side, and never the one side. What this means, is if you have a BlogEntry entity, and it has a list of Comment entities, you can't add or remove comments from the blog by operating on the comment list, you have to do it by setting or nullifying the blog property on each comment.

For example, the following code can't be used to add or remove comments from the blog entry:

blogEntry.getComments().add(comment1);
blogEntry.getComments().remove(comment2);

Rather, it has to be done as follows:

comment1.setBlogEntry(blogEntry);
comment2.setBlogEntry(null);

This is definitely the easier feature to map to databases, as relationships in a database are always owned by the many table. The many table will have a column, say blog_entry_id, that is a foreign key to the primary key of the blog_entry table. However, this is not how we like to view things in an object oriented world. A blog entry is composed of, among other things, comments. Without a blog entry instance, a comment instance does not conceptually exist, nor does it make sense to ever use the comment outside of the context of using the blog entry. In this situation, the sensible mapping is to have the blog entry as the owning side, with no reference from the comment to the blog entry. Comments then only ever get accessed through accessing the entry first, and get created and removed from the blog entry via operating on the comment list.

A related question I have that is not clear from my research is whether JPA 2.0 will support Hibernate's delete orphan cascade type, or TopLink's private ownership feature, or something different. Both achieve the same goal but through quite different methods.

Indexed lists

A further feature that follows on from the above two is indexed lists. Seeing as JPA can only support the many side being the owner of an association, the many side also has to be where order is lists is managed. This means adding a property to our Comment entity, we'll call it index. If it's a bidirectional relationship, the order of the returned list can be set by specifying index as the property to order the comments by. Now, if I wanted to move the fifth comment to the top of the comment list, if the one side was the owning side, it would be as simple as:

comments.add(0, comments.remove(4));

However, because the owning side is the many side, it has to be done like this:

for (int i = 0; i < 4; i++)
{
    comments.get(i).setIndex(i + 1);
}
comments.get(4).setIndex(0);
comments.add(0, comments.remove(4));

Note that the last line there is not absolutely necessary, but keeps the comments list is sync with the index properties, should the list want to be continued to be used.

If JPA 2.0 can support unidirectional one to many associations, it will need to also be able to manage the index of the list on the one side, meaning that it will persist any changes made to the list order to the database, so that the first example above will be all that's needed to move the fifth comment to the top of the list.

Sun on JPA

A few weeks ago I attended the Sun Tech Days in Sydney. I was pretty impressed with the breakfast they provided. In fact, all the food was great. The whole place had a really geeky feel to it, and I really liked that too. However, the presentations left me feeling that the whole thing was a waste of time. To be fair, some of the presentations were good. It was excellent to hear James Gosling. But the majority of the presentations I went to were terrible.

I don't think my standards are too high in having this gripe, at one level, Sun provided me with free food and a day where I could mix with like minded people, I shouldn't be complaining. But it just frustrated me so much because Sun could have done so much more for not only me, but also themselves at this event. The whole event is one gigantic sales pitch, and I'm ok with that. I want Sun to get me excited about their products, I want Sun to get me excited about Java development, I want Sun to pitch their best sales pitch at me, do their best to make me want to drop everything that is not Sun, and follow them. Not that I would necessarily do that, but I at least want Sun to sell themselves to me if I go to an event like this. But I think they failed to do that. I'm going to use one particular session, the session on JPA, as an example of how they missed the mark, but most of the sessions I felt were the same.

My expectations from the JPA session were that Sun would sell me JPA. I love Hibernate, I use it for all my database access, I know how it works intimately, and I am very quick to recommend it as the best tool for ORM to every man and his dog. The big problem for me is that part of my reason for loving it is based in my ignorance, I've only briefly touched JPA, and I've never used Toplink or JDO, they could all be miles ahead of Hibernate, but I wouldn't know. This is why I went to JPA session. I wanted to be given reasons why I should try it, I wanted to be given insights into the future of JPA that will make it the best ORM API to use. So when the session ended up being JPA101 - This is how to map an entity with annotations, this is how to do inheritance, this is how to write a query in JPAQL, you can imagine my disappointment.

I'm not sure who Sun is expecting to come to their tech days. But I'll take a guess at the sort of people that do come. For a start, they are people that take an interest in modern Java frameworks, if they weren't, they wouldn't be at the days. They are developers, if they aren't developers then they definitely wouldn't be interested in JPA101 anyway. And they're people that take the initiative to do research and learn about new technologies themselves, otherwise they wouldn't have found out about the tech days to start with. So, I would expect the people described above to have all had experience with at least one of the leading ORM tools. A JPA101 would have been way too simple for almost the entire audience.

In want of more meat on JPA, I asked a question about the future of JPA. "Is it likely that in the future JPA will support collections of basic types, eg Strings, Integers, Dates?". I was shocked by the presenters response. It wasn't that he didn't know the answer to the question, he didn't understand the question. After asking me to repeat the question, he said "well, it does support it". I said no it doesn't, and someone else in the audience piped up and agreed with me, adding that you could map collections of basic types using TopLink or Hibernate extensions. To which the presenter replied "there's your answer". The presenter had no idea. Anyone with real world experience in an ORM tool would have encountered a time when they would have wanted a collection of basic types, and an expert in JPA would know that JPA doesn't support this. I wanted to know why it doesn't support it, maybe there's a good reason, maybe they felt there were difficulties in how to specify it and so left it for the next release. This guy clearly was not an expert in JPA, and clearly had never used JPA in the real world. I doubt if he had any experience beyond learning enough to give the presentation, it's likely that someone else wrote the presentation and he just went off that persons notes.

So I was very disappointed overall with the Sun Tech Days. It was marketed at professionals, but most of the sessions were targeted at newbies. I hope there's someone at Sun that understands JPA, if they can't get that person to give a presentation, maybe they should look into getting local respected industry professionals to give the presentations, that would be a lot more helpful. Until Sun does that, I don't think I'll return.

Facebook authentication in Java

If you're a web developer who likes writing practical, quick, and simple utility applications for yourself and others to use, then Facebook is the dream platform. You don't have to write any user management, sign in, password change pages etc, it comes complete with advanced user management, including friends lists. There's a high chance that your target users already use it, which makes them more likely to adopt your application because they don't need to go and create yet another username/password on yet another website. It already has a theme and style sheets, and will present your application surrounded in menus and title bars. And it provides a large number of ways to publish content and send notifications when events occur. All these features mean that you, the developer, can spend more time doing what you wanted to do, that is, writing your application.

If Java is your chosen platform however, you may encounter some difficulties. Java developers tend to like following well established design patterns. We like to think of things as beans that have distinct purposes. We like to use frameworks such as Spring to manage the beans that contain logic, to glue our application together. We like our data to be stored in beans, and we access that data using getters and setters. The Facebook client API makes this difficult, for the following reasons:

  1. Its instantiation is dependent on parameters in an HTTP request. This means we can't use it as a typical Spring bean.
  2. Depending on which flavour you of client you use, it returns JSON objects, or DOM documents, not the Java beans we like to use.

This article will address the first issue, and in doing so make the second issue less of a worry.

Instantiation of the client

Our first task is to gracefully handle the instantiation of the Facebook client. This will usually be required by most requests, hence, it is an ideal opportunity to use a ServletFilter. The guys at TheLiveWeb have already provided a very good article on how to do this, so I won't go into too much detail. The servlet filter basically intercepts every request, checks to see if there is already a client in the session, and if not, uses the fb_session parameter if it's an embedded fbml app, or auth_token parameter if it's not an fbml app, to instantiate the client. If neither of those parameters exist, it redirects the user to the login page. The ServletFilter I've written is very different to theirs, but both follow the same concept and achieve the same goal.

Using a ThreadLocal to access the client

Our next task is to make the client available to the backend service beans that have no knowledge of the HTTP session. We do this using a ThreadLocal. We start by creating a wrapper class, UserClientBean, to wrap all calls to the client. The client itself is stored in a private static ThreadLocal field:

public class UserClientBean
{
    public static void setClient(FacebookXmlRestClient aClient)
    {
        client.set(aClient);
    }
    public static void remove()
    {
        client.remove();
    }
    private static final ThreadLocal client
        = new ThreadLocal();
    ...
}

So calling UserClientBean.set() will bind a facebook client to the current thread, and UserClientBean.remove() will remove it. We make these calls from the servlet filter:

public void doFilter(ServletRequest aRequest, ServletResponse aResponse,
        FilterChain aChain) throws IOException, ServletException
{
    if (aRequest instanceof HttpServletRequest)
    {
        HttpServletRequest request = (HttpServletRequest) aRequest;
        HttpServletResponse response = (HttpServletResponse) aResponse;
        HttpSession session = request.getSession();
        FacebookXmlRestClient client = (FacebookXmlRestClient) session
                    .getAttribute("facebookClient");
        if (client == null)
        {
            String sessionKey = request
                    .getParameter(FacebookParam.SESSION_KEY.toString());
            String authToken = request.getParameter("auth_token");
            if (sessionKey == null && authToken == null)
            {
                response.sendRedirect(loginPage);
                return;
            }
            else
            {
                try
                {
                    if (sessionKey != null)
                    {
                        client = new FacebookXmlRestClient(apiKey, secret,
                                sessionKey);
                    }
                    else
                    {
                        client = new FacebookXmlRestClient(apiKey, secret);
                        client.auth_getSession(authToken);
                    }
                    client.setIsDesktop(false);
                    session.setAttribute("facebookClient", client);
                }
                catch (FacebookException fe)
                {
                    log.error("Unable to log into facebook", fe);
                }
            }
        }
        // Store the client in the thread
        UserClientBean.setClient(client);
    }
    aChain.doFilter(aRequest, aResponse);
    // Remove the client from the thread
    UserClientBean.remove();
}

If you've never come across ThreadLocal's before, a ThreadLocal is a class that binds the object passed to its set() method to the current thread. Calling the get() method will retrieve that object from the current thread, if it exists. ThreadLocal's are usually declared as static final, so that they can be accessed statically from anywhere in the application.

Using the client

Now that we can access the client as a service, we can add calls as desired to UserClientBean to run on the FaceBookRestClient, and extract the data out in a way that's nice to return to our application. For example:

public List getAppFriends()
{
    List result = new ArrayList();
    try
    {
        Document d = client.get().friends_getAppUsers();
        Node n = d.getFirstChild();
        while (n != null)
        {
            result.add(Long.parseLong(n.getTextContent()));
            n = n.getNextSibling();
        }
    }
    catch (Exception e)
    {
        log.error("Error retrieving logged in facebook user", e);
    }
    return result;
}

public boolean isFriend(long aFriendId)
{
    try
    {
        FacebookXmlRestClient c = client.get();
        long user = c.users_getLoggedInUser();
        if (user == aFriendId)
        {
            return true;
        }
        else
        {
            Document d = c.friends_areFriends((int) user, (int) aFriendId);
            Node n = d.getElementsByTagName("are_friends").item(0);
            if (n.getTextContent().equals("1"))
            {
                return true;
            }
        }
        return false;
    }
    catch (Exception e)
    {
        log.error("Error determining if users are friends", e);
        return false;
    }
}

As you may have noticed, the FaceBookRestClient uses ints, while Facebook recommends user ids being stored as longs. Not very smart huh? At least using this client, our apps will be ready for that bug fix.

About

Hi! My name is James Roper, and I am a software developer with a particular interest in open source development and trying new things. I program in Scala, Java, Go, PHP, Python and Javascript, and I work for Lightbend as the architect of Kalix. I also have a full life outside the world of IT, enjoy playing a variety of musical instruments and sports, and currently I live in Canberra.