-
- Introduction
- Code Style
- Indentation
- Braces
- Blank lines
- New lines
- Line Wrapping
- Comments
- Coding rules
- License header
- Packages
- Visibility
- Field initialization
- Variable names
- Control flow
- Documentation
- Exception handling
- Best Practices
- Testing
- Test coverage and tools
- Creating and Publishing Patches
Introduction
If you are planning on writing patches for Smack to add new features, improve existing features or fix bugs these guidelines should help you create patches that can be reviewed fast by the community and then be applied to the source repository.
This document covers guidelines about the code style, some code rules you should be aware of, how to test your code and how to create and publish a patch.
Code Style
Many developers use an IDE like Eclipse which comes with some handy tools to format the code automatically. If the formatting rules of the existing Smack source code and of the IDE used by the developer differ it may lead to patches that contain lots of unnecessary formatting changes which makes the code hard to review.
At the time of writing there are no standard formatting rules defined for Smack. This is a proposal how all Smack source code should be formatted.
A good set of rules is defined by the Java Code Conventions from Sun.
Indentation
4 white spaces should be used as the unit of indentation. Not using tabs has the advantage that the code looks the same on every editor and it avoids mixed indentation with tabs and white spaces.
/** * Indentation */
class Example {
int[] myArray = { 1, 2, 3, 4, 5, 6 };
int theInt = 1;
String someString = "Hello";
double aDouble = 3.0; void foo(int a, int b, int c, int d, int e, int f) {
switch (a) {
case 0:
Other.doFoo();
break;
default:
Other.doBaz();
}
} void bar(List v) {
for (int i = 0; i < 10; i++) {
v.add(new Integer(i));
}
}
} enum MyEnum {
UNDEFINED(0) {
void foo() {
}
}
} @interface MyAnnotation {
int count() default 1;
}
Braces
All opening braces “{” should be on the same line as the declaration statement. The closing brace “}” starts a line by itself and is indented to match its corresponding opening statement.
void bar(List v) {
for (int i = 0; i < 10; i++) {
v.add(new Integer(i));
}
}
Blank lines
Blank lines should appear after each package, import, class, member and method declaration. There should be no successive empty lines.
New lines
There should be no new lines before “do-while”, “try-catch”, “try-finally” and “else if” statements.
“If” statements containing only a single statement should have new line.
/** * If...else */
class Example {
void bar() {
do {
} while (true);
try {
} catch (Exception e) {
} finally {
}
} void foo2() {
if (true) {
return;
}
if (true) {
return;
} else if (false) {
return;
} else {
return;
}
} void foo(int state) {
if (true)
return;
if (true)
return;
else if (false)
return;
else
return;
}
}
Line Wrapping
The maximum line width should be 80 characters to avoid soft wrapping in terminal editors. Lines should be wrapped according to the following rules:
- Break after a comma.
- Break before an operator.
- Prefer higher-level breaks to lower-level breaks.
- Align the new line with the beginning of the expression at the same level on the previous line.
- If the above rules lead to confusing code or to code that’s squished up against the right margin, just indent 8 spaces instead.
Comments
There are three different ways to use comments in Java. The documentation comments (/** … */) and two ways for implementation comments ("/* … */" and “*// … ").
Documentation comments should be used to comment classes, methods and public fields.
Non-public fields and multi-line comments within a method should use the "__/ … */__" implementation comments.
Single-line comments and trailing comments should use the "// … **” implementation comments.
/** * Class Example */
public class Example {
/**
* Foo constant
*/
public static final String THE_FOO = "FOO";
/* foo */
private String foo;
/**
* Constructor
*/
public Example() {
// some initialization code
}
/**
* This method does something
*/
public int doThings() {
/*
* heavy documentation about lots of
* stuff done in this method
*/
...
if (condition) {
return 1; // trailing comment 1
} else {
return -1; // trailing comment -1
}
}
}
Attached is a code style configuration for eclipse which is based on the built-in Java code style with slight modifications.
Coding rules
License header
Each file should have a license header stating that the code is under the Apache License 2.0
/** * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */
Packages
org.jivesoftware.smack
Contains the core classes of Smack to create connections to XMPP servers, to send and receive Packets.
org.jivesoftware.smackx
Contains packages for implementations of the XEP specifications.
Each extension should have its own subpackage named according to the feature it implements (e.g. org.jivesoftware.smackx.nicknames for XEP-0172: User nicknames).
org.jivesoftware.smackx.(feature).packet
In order to implement a feature it is often required to create subclasses of org.jivesoftware.smack.packet.IQ which should be located in a subpackage of the feature package named “packet”.
Visibility
Only classes and and methods that are useful to developers using Smack should be made public.
All fields should be private and only be accessible via getter and setter methods.
Other methods that are only useful for the internal logic of the feature should be made private or protected.
Field initialization
Instance variables should be initialized in their declaration if possible. This improves readability of the code and reduces code in the constructor.
Variable names
Variable names should be a good compromise between being self-explaining and being short. Common local variables such as primitive variables for loop iteration or condition testing may be named with a short name that matches their class name.
int i;
String s;
File f;
float f;
double d;
Iterator<...> it
All other variables should have full names that help understanding their use. In many cases the class name with an initial lower-case letter is a good choice.
ServiceDiscoveryManager serviceDiscoveryManager;
Control flow
Test whether you can return from a method instead of testing whether you should execute a block of code.
Instead of:
public void ... { ...
if (condition){
// long block of code
}
}
write:
public void ... {
...
if (!condition)
return
// long block of code
}
Documentation
At least all public methods should be fully documented including the JavaDoc Tags for the parameters, the return value and possible exceptions.
Potentially long-running and blocking methods should always state in the documentation that they are blocking.
Exception handling
Many methods in Smack can throw an XMPPException. If you want to throw an exception in your code wrap it with an XMPPException and add a message to it.
try {
...
} catch (Exception e) {
throw new XMPPException("something went wrong", e);
}
Best Practices
- To convert a primitive to a String use String.valueOf(…). Don’t use “” + i.
- To convert from a primitive use Integer.parseInt(…) and similiar.
- Don’t use StringBuffer unless you need threadsafety, use StringBuilder instead.
- Similarly don’t use Vector, use ArrayList.
- Never subclass Thread, always pass a Runnable into the constructor instead.
- If equals is implemented in a class it is MANDATORY to implement hashCode as well, otherwise HashSets and other classes which rely on the contract of hashCode will not work. IDEs like Eclipse provide generators to implement equals and hashCode.
- Use enum instead of a bunch of static final int fields. Then you have type safety for related constants and more readable debugging output is possible because you get meaningful names instead of “magic” numbers.
- Avoid the use of static methods and attributes if possible. This is no good object oriented programming style and it also makes testing your code more difficult.
Testing
You should always write unit tests for your code to verify the behavior of your classes and methods and to make it easier for reviewers to understand what your code does and that you thought about some border cases.
For example if you have found a bug in Smack you should write a test that verifies that bug then fix the bug and run the test again to see that it doesn’t fail anymore.
Writing test for all features and bugs verifies the correctness of the code and prevents regressions that might be caused by other changes and fixes.
There are two locations for tests in the Smack repository. The “test-unit” folder contains normal JUnit4 tests that test all classes and methods that don’t depend on any running infrastructure like an XMPP server.
The folder “test” contains test that are running against a local or remote XMPP server. All test cases subclass SmackTestCase which provides a pre-configured execution context. By overwriting getMaxConnections you can specify how many connection you want to use in your test. Invoking getConnection(int index) returns the preconfigured connection.
public class ExampleTest extends SmackTestCase { public ExampleTest(String arg0) {
super(arg0);
} public void testSomething() {
Connection initiator = getConnection(0);
Connection target = getConnection(1);
// send some packets between connections
...
// verify
...
} protected int getMaxConnections() {
return 2;
}
}
The tests can be configured by editing the file “test/config/test-case.xml”. Here you can set the host and port of the XMPP server and a prefix for the usernames of the users that will be automatically created for the tests.
Note: If you want to run the tests from Eclipse make sure that the “smack-config.xml” file is in the class path. Otherwise test may fail because Smack is not initialized correctly. You can do this by configuring the JUnit Run Configuration and add the folder “build/resources” to the classpath or by linking the “build/resources/META-INF” folder in the output folder of the project where the compiled binaries are located.
ln -s ../build/resources/META-INF bin/
Test coverage and tools
There are tools for java that can calculate the code accessed by unit tests like Emma, Cobertura or the Eclipse plugin EclEmma. A good test suite should cover about 80% of the code that you have written.
Another good tool to prevent coding errors is FindBugs which comes as a commandline tool or as Eclipse plugin. It analyzes the code and warns you about potential programming errors.
Creating and Publishing Patches
Before starting to write your patch for Smack you should checkout the latest version of Smack from the SVN repository.
svn co http://svn.igniterealtime.org/svn/repos/smack/trunk Smack
After that you can start coding in that working copy of Smack. This has the advantage that you can create your patch using the SVN tools and don’t have to compare two directories with the “diff” command. Additionally you can always update the working copy to the latest version and check that your code doesn’t conflict with any of the changes committed to the repository in the meantime.
If you are done with your work you should create a patch file.
If your patch contains new files or directories you first have to put them under version control by executing the following line.
svn add --force *
Done that you can create the patch via svn diff command.
svn diff > patch.patch
You can also create patches with Eclipse by right clicking on the project in the Package Explorer -> Team -> Create Patch…
Note that the patch will only contain changes of files that are under version control.
If your patch tends to be very long (more than 1000 lines changed or added) you should probably split your patch in multiple small patches to make reviewing this patch easier.
A good way to do this is to split it in functional units like “Adding method x to core class Y to fix …”, “Implementation of feature X senders side”, “Implementation of feature X receivers side”, “Test cases for feature X” and so on.
Also keep in mind that the patches should not contain any unnecessary formatting changes.
Now the only thing left to do is posting your patch as a discussion in the Smack Developers Board and contact one of the Smack developers about your patch.
You should also check out the “How to contribute code” document.
Happy coding!