Logging to disk in android like circular or ring buffer [closed] - java

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
We don’t allow questions seeking recommendations for books, tools, software libraries, and more. You can edit the question so it can be answered with facts and citations.
Closed 7 years ago.
Improve this question
I need to be able to write logs to disk so that for debugging purposes it can be sent to me by the user. If I use the Log class in android it appears to only have the ability to write to logcat and logcat is not so useful as the relevant logs can disappear after sometime. So for me its important that logs are written to disk and can be recovered later anytime. Therefore I require Logcat like functionality (ring buffer of limited size) but being persisted to disk to persist across App crashes and devices restarts.
Is there any existing class in android that can help me
write Logs to the disk,
Is thread safe,
and implements some kind of ring buffer on disk (so that the log file can not become more than a predefined size and always have most recent logs)
It should also be performant at sizes simliar to the default ring-buffer sizes of the existing Logcat implementation (64kB through to 1MB on higher end devices)
I really do not want to reinvent the wheel and I am happy to use a third-party library for that if I have to , please advise.
Otherwise how could this be implemented with existing Android framework API's and class library?

Have a look at this class DiscourseLogger.We used in many of our projects. You will need to modify it but it is a good starting point.
/*
* Copyright (C) 2010 The Android Open Source Project
*
* 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.
*/
package com.android.email.mail.transport;
import com.android.emailcommon.Logging;
import com.android.mail.utils.LogUtils;
import java.util.ArrayList;
/**
* A class to keep last N of lines sent to the server and responses received from the server.
* They are sent to logcat when {#link #logLastDiscourse} is called.
*
* <p>This class is used to log the recent network activities when a response parser crashes.
*/
public class DiscourseLogger {
private final int mBufferSize;
private String[] mBuffer;
private int mPos;
private final StringBuilder mReceivingLine = new StringBuilder(100);
public DiscourseLogger(int bufferSize) {
mBufferSize = bufferSize;
initBuffer();
}
private void initBuffer() {
mBuffer = new String[mBufferSize];
}
/** Add a single line to {#link #mBuffer}. */
private void addLine(String s) {
mBuffer[mPos] = s;
mPos++;
if (mPos >= mBufferSize) {
mPos = 0;
}
}
private void addReceivingLineToBuffer() {
if (mReceivingLine.length() > 0) {
addLine(mReceivingLine.toString());
mReceivingLine.delete(0, Integer.MAX_VALUE);
}
}
/**
* Store a single byte received from the server in {#link #mReceivingLine}. When LF is
* received, the content of {#link #mReceivingLine} is added to {#link #mBuffer}.
*/
public void addReceivedByte(int b) {
if (0x20 <= b && b <= 0x7e) { // Append only printable ASCII chars.
mReceivingLine.append((char) b);
} else if (b == '\n') { // LF
addReceivingLineToBuffer();
} else if (b == '\r') { // CR
} else {
final String hex = "00" + Integer.toHexString(b);
mReceivingLine.append("\\x" + hex.substring(hex.length() - 2, hex.length()));
}
}
/** Add a line sent to the server to {#link #mBuffer}. */
public void addSentCommand(String command) {
addLine(command);
}
/** #return the contents of {#link #mBuffer} as a String array. */
/* package for testing */ String[] getLines() {
addReceivingLineToBuffer();
ArrayList<String> list = new ArrayList<String>();
final int start = mPos;
int pos = mPos;
do {
String s = mBuffer[pos];
if (s != null) {
list.add(s);
}
pos = (pos + 1) % mBufferSize;
} while (pos != start);
String[] ret = new String[list.size()];
list.toArray(ret);
return ret;
}
/**
* Log the contents of the {#link mBuffer}, and clears it out. (So it's okay to call this
* method successively more than once. There will be no duplicate log.)
*/
public void logLastDiscourse() {
String[] lines = getLines();
if (lines.length == 0) {
return;
}
LogUtils.w(Logging.LOG_TAG, "Last network activities:");
for (String r : getLines()) {
LogUtils.w(Logging.LOG_TAG, "%s", r);
}
initBuffer();
}
}

Related

How to enable linear relaxation outputs

I have a rather complex MILP, but the main problem is the number of continuous variables, not the number of binaries. I just "hard-coded" the linear relaxation to understand its output, and it takes approx. 10-15 minutes to solve (which is not extremely surprising). If I run the MILP with outputs, I don't see anything happening for the first 10 minutes, because it takes those 10 minutes to construct a first integer-feasible solution. So it would help to be able to enable the same outputs I am seeing when solving the linear relaxation "manually" (so something like Iteration: 1 Dual objective = 52322816.412592) within the B&B output.
Is this possible? I googled at bit, but I only found solutions for steering the solution algorithm, or for deriving linear relaxations using callbacks, while I am interested in a "simple" output of the intermediate steps.
It sounds like you are asking for extra detailed logging during the linear relaxation part of the solve during the B&B. Have a look at the CPLEX parameter settings like IloCplex.Param.MIP.Display (try setting this to 5) and also IloCplex.Param.Simplex.Display (try setting to 1 or 2).
within java you could rely on IloConversion objects that will allow you to locally change the type of one or more variables.
See the sample AdMIPex6.java
/* --------------------------------------------------------------------------
* File: AdMIPex6.java
* Version 20.1.0
* --------------------------------------------------------------------------
* Licensed Materials - Property of IBM
* 5725-A06 5725-A29 5724-Y48 5724-Y49 5724-Y54 5724-Y55 5655-Y21
* Copyright IBM Corporation 2001, 2021. All Rights Reserved.
*
* US Government Users Restricted Rights - Use, duplication or
* disclosure restricted by GSA ADP Schedule Contract with
* IBM Corp.
* --------------------------------------------------------------------------
*
* AdMIPex6.java -- Solving a model by passing in a solution for the root node
* and using that in a solve callback
*
* To run this example, command line arguments are required:
* java AdMIPex6 filename
* where
* filename Name of the file, with .mps, .lp, or .sav
* extension, and a possible additional .gz
* extension.
* Example:
* java AdMIPex6 mexample.mps.gz
*/
import ilog.concert.*;
import ilog.cplex.*;
public class AdMIPex6 {
static class Solve extends IloCplex.SolveCallback {
boolean _done = false;
IloNumVar[] _vars;
double[] _x;
Solve(IloNumVar[] vars, double[] x) { _vars = vars; _x = x; }
public void main() throws IloException {
if ( !_done ) {
setStart(_x, _vars, null, null);
_done = true;
}
}
}
public static void main(String[] args) {
try (IloCplex cplex = new IloCplex()) {
cplex.importModel(args[0]);
IloLPMatrix lp = (IloLPMatrix)cplex.LPMatrixIterator().next();
IloConversion relax = cplex.conversion(lp.getNumVars(),
IloNumVarType.Float);
cplex.add(relax);
cplex.solve();
System.out.println("Relaxed solution status = " + cplex.getStatus());
System.out.println("Relaxed solution value = " + cplex.getObjValue());
double[] vals = cplex.getValues(lp.getNumVars());
cplex.use(new Solve(lp.getNumVars(), vals));
cplex.delete(relax);
cplex.setParam(IloCplex.Param.MIP.Strategy.Search,
IloCplex.MIPSearch.Traditional);
if ( cplex.solve() ) {
System.out.println("Solution status = " + cplex.getStatus());
System.out.println("Solution value = " + cplex.getObjValue());
}
}
catch (IloException e) {
System.err.println("Concert exception caught: " + e);
}
}
}
if you use OPL then you could have a look at Relax integrity constraints and dual value
int nbKids=300;
float costBus40=500;
float costBus30=400;
dvar int+ nbBus40;
dvar int+ nbBus30;
minimize
costBus40*nbBus40 +nbBus30*costBus30;
subject to
{
ctKids:40*nbBus40+nbBus30*30>=nbKids;
}
main {
var status = 0;
thisOplModel.generate();
if (cplex.solve()) {
writeln("Integer Model");
writeln("OBJECTIVE: ",cplex.getObjValue());
}
// relax integrity constraint
thisOplModel.convertAllIntVars();
if (cplex.solve()) {
writeln("Relaxed Model");
writeln("OBJECTIVE: ",cplex.getObjValue());
writeln("dual of the kids constraint = ",thisOplModel.ctKids.dual);
}
}

How to verify that all own thrown runtime exceptions are covered in Javadoc?

I throw a bunch of custom runtime exceptions in my code and I want to make sure that in all public methods, I document which runtime exception might be thrown (by myself) and why. This would be very hulpful since I'm maintaining a library which is used by many projects and I want it to be upfront and predictable regarding thrown (runtime) exceptions.
Is there a compiler option, maven plugin, Intellij plugin or custom tool that can help me find missed throws clauses? With checked exceptions it's easy, the compiler will just complain if I missed one, but for runtime exceptions both throws and #throws are not enforced.
One thing I thought of was to temporarily make all my own runtime exceptions checked exceptions (they already share a super class), but that would be a one-off exercise. I would like to verify my code/documentation each time I make changes so I can never forget to document my runtime exceptions.
Another way could be to actually have checked exceptions throughout the code and convert them to runtime only in the public api:
class Foo {
// oops, throws not documented with #throws
public void publicMethod() {
try {
privateMethod1();
} catch (CheckedFooException e) {
throw new RuntimeFooException(e);
}
}
private void privateMethod1() throws CheckedFooException {
privateMethod2();
}
private void privateMethod2() throws CheckedFooException {
throw new CheckedFooException();
}
}
This approach would force me to think about CheckedFooException in all public methods. Then to check if I missed documenting one (ie. #throws RuntimeFooException), I would simply do a regex search on catch.*CheckedFooException and check for missing #throws entries. Rather unwieldy process though (and there's a lot of public api that would get peppered with try...catch statements).
Answer: There is some discussion about whether you should document (your own thrown) runtime exceptions at all (the summary so far: it depends), but as far as a direct answer to my question, the accepted answer answers it adequately; I can take that approach, implement my use case and even make a maven plugin with it, given some time and effort. I uploaded a cleaned up start project for this.
After understanding your question and researching this subject, I finally found what I thought to be one of the best tools to do this job. With this not only you can find each throws instance that you haven't documented, but you can also find where you don't throw anything but accidentally document a throw value.
The idea behind this is to parse the code into an abstract syntax tree. Then look for methods and look for throws statement in the methods. If a method have any throw statement, extract the exception name from those statements. Then get the Javadoc for that method. Check the Javadoc for all the #throw tags and get the name of the exception that been documented. After that, compare the exception throws versus the one that been documented. The last, you kind of have to figure that out on your own depend on your usage circumstance.
The tool I used for this is JavaParser. You can find them on Github at https://github.com/javaparser/javaparser. I downloaded their latest version. Their website is at https://javaparser.org/. They wrote a book on this subject and they mentioned that you can pay $0 dollar for the book. However, I didn't read that as they also have a Javadoc version for their program which can be found at https://www.javadoc.io/doc/com.github.javaparser/javaparser-core/3.15.1.
I wrote a demonstrate code below. In no mean that this code is final. It is just an example. You have to fix it into making it work for your case. I didn't take into consideration of nested classes, nested method, or methods within classes that are within a method. Also, the example code was written for class only and not interface. However, it is easy to adapt the code to change to able to handle interfaces.
For this, you would need to download javaParser, build it, and have their javaparser-core-3.15.1.jar or whichever version in your classpath.
The demonstrated code is below and the test.java is a file from a project that I wrote but you could use any. I also included comments in the example code.
import com.github.javaparser.*;
import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.comments.*;
import com.github.javaparser.ast.stmt.*;
import com.github.javaparser.ast.body.*;
import com.github.javaparser.javadoc.*;
import java.io.IOException;
import java.nio.file.*;
import java.nio.charset.Charset;
import java.util.*;
import java.util.stream.Collectors;
class Main{
public static void main(String[] args) throws IOException {
// Set file path
Path path = Paths.get("test.java");
// Set configuration
ParserConfiguration parseConfig = new ParserConfiguration();
parseConfig.setCharacterEncoding(Charset.forName("UTF-8"));
parseConfig.setTabSize(4);
parseConfig.setLanguageLevel(ParserConfiguration.LanguageLevel.JAVA_8);
// Get the parser
JavaParser jvParser = new JavaParser(parseConfig);
// Parse the result
ParseResult<CompilationUnit> parseResult = jvParser.parse(path);
// Check for problem
if ( !parseResult.isSuccessful() ) {
System.out.print("Parsing java code fail with the following problems:");
List<Problem> problems = parseResult.getProblems();
for ( Problem problem : problems ){
System.out.println(problem.getMessage());
}
return;
}
// Get the compilationUnit
// No optional checking for Optional<CompilationUnit> due to already check above.
CompilationUnit compilationUnit = parseResult.getResult().get();
// Get Classes
List<ClassOrInterfaceDeclaration> classes = compilationUnit.findAll(ClassOrInterfaceDeclaration.class).stream()
.filter(c -> !c.isInterface())
.collect(Collectors.toList());
// Traverse through each class to get method
for ( ClassOrInterfaceDeclaration c : classes ) {
// Get methods
List<MethodDeclaration> methods = c.getMethods();
for ( MethodDeclaration method : methods ) {
// Get the body statement
Optional <BlockStmt> body = method.getBody();
// if no body continue
if ( !body.isPresent() ) continue;
// After getting the body of the method code
// Search for the throw statements.
List<ThrowStmt> throwStatements = body.get().findAll(ThrowStmt.class);
// No throw statements, skip
if ( throwStatements.size() == 0 ) continue;
// Storing name of exceptions thrown into this list.
List<String> exceptionsThrown = new ArrayList<String>();
for ( ThrowStmt stmt : throwStatements ){
// Convert the throw expression to object creation expression and get the type.
String exceptionName = stmt.getExpression().asObjectCreationExpr().getType().toString();
if ( !exceptionsThrown.contains(exceptionName) ) exceptionsThrown.add(exceptionName);
}
/*
* Debug block for up to this point
System.out.println(method.getName());
System.out.println(exceptionsThrown);
System.out.println();
*
**/
// Get The Javadoc
Optional<Javadoc> javadoc = method.getJavadoc();
// To store the throws Tags
List<JavadocBlockTag> throwTags;
// A list of thrown exception that been documented.
List<String> exceptionsDocumented = new ArrayList<String>();
if ( javadoc.isPresent() ) {
throwTags = javadoc.get()
.getBlockTags()
.stream()
.filter(t -> t.getType() == JavadocBlockTag.Type.THROWS)
.collect(Collectors.toList());
for ( JavadocBlockTag tag : throwTags ) {
/*
* This may be buggy as
* the code assumed #throw exception
* to be on its own line. Therefore
* it will just take the first line as the exception name.
*/
String exceptionName = tag.getContent().toText()
.split("\n")[0]; // Use system line separator or change
// line accordingly.
if ( !exceptionsDocumented.contains(exceptionName) )
exceptionsDocumented.add(exceptionName);
}
}
// getBegin can extract the line out. But evaluating the optional would take some more code
// and is just for example so this was done like this without any checking.
System.out.println("Method: " + method.getName() + " at line " + method.getBegin());
System.out.println("Throws Exceptions: ");
System.out.println(exceptionsThrown);
System.out.println("Documented Exceptions:");
System.out.println(exceptionsDocumented);
System.out.println(System.lineSeparator() + System.lineSeparator());
}
}
}
}
test.java content:
package host.fai.lib.faiNumber;
/*
* Copyright 2019 Khang Hoang Nguyen
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files
* (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge,
* publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
**/
/**
* <p>The <code>Base2Util</code> class is a final class that provides
* static methods for converting base 2 numbering system values in
* string representation to a Java's Primitive Data Type.
*
* <p>Currently this class supports converting base 2 numbers values
* in string representation to integer int values and integer
* long values.
*
* <p>This class can parse unsigned base 2 numbers to a supported
* integer signed type as if the integer type is unsigned. However,
* some of the values must be interprete properly to get the correct
* result.
*
* <p>Example for interpreting signed value as unsigned value.
*
* <p>It is possible to store the value of 18446744073709551615L
* into a long(signed) value. However, if that value is stored into a
* signed long integer type and if we were to interprete the value
* normally, we would get a -1L value. However, if the -1L value is
* pass to LongUtil.toStringAsUnsigned, we would get
* 18446744073709551615 in string format.
*
* <p>The following example is to get to -1L. First, we assign a value
* of 9223372036854775807L to an interger long variable, multiply that
* variable to 2L, and add 1L to it.
* <pre>
* long a = 9223372036854775807L * 2L + 1L;
* System.out.println(a);
* System.out.println(LongUtil.toStringAsUnsigned(a));
* </pre>
*
* <p>Example methods for interprete signed type as unsigned type
* in a decimal strings value are
* {#link IntUtil#toStringAsUnsigned(int) IntUtil.toStringAsUnsigned}
* and {#link LongUtil#toStringAsUnsigned(long) LongUtil.toStringAsUnsigned}.
* </p>
*
* #author Khang Hoang Nguyen
*
* #since 1.0.0.f
**/
public final class Base2Util{
private Base2Util(){};
/**
* Parse the input string as signed base 2 digits representation
* into an integer int value.
*
* #param input
* A string to be parsed as signed base 2 number to an
* integer int value.
*
* #return An integer int value of the signed base 2 number
* {#code input} string.
*
* #throws NumberFormatException
* If the {#code input} string contains invalid signed
* base 2 digits, if the {#code input} string contains a
* value that is smaller than the value of Integer.MIN_VALUE(
* {#value java.lang.Integer#MIN_VALUE}),
* or if the {#code input} string contains a value that
* is larger than the value of Integer.MAX_VALUE(
* {#value java.lang.Integer#MAX_VALUE}).
*
* #throws EmptyStringException
* If the {#code input} string is empty.
*
* #since 1.0.0.f
**/
public static final int toInt(final String input){
final int length = input.length();
if ( length == 0 ) throw new EmptyStringException();
final char ch1 = input.charAt(0); int start;
if ( ch1 == '-' || ch1 == '+' ){
if ( length == 1 ) throw new NumberFormatException(input);
start = 1;
} else {
start = 0;
}
int out = 0, c;
while ( start < length && input.charAt(start) == '0' ) start++;
final int runlen = length - start;
if ( runlen > 31 ){
if ( runlen > 32 ) throw new NumberFormatException(input);
if ( ch1 != '-' ) throw new NumberFormatException(input);
if ( input.charAt(start++) != '1') throw new NumberFormatException(input);
for ( ; start < length; start++){
if ( input.charAt(start) != '0' ) throw new NumberFormatException(input);
}
return -2147483648;
}
for ( ; start < length; start++){
c = (input.charAt(start) ^ '0');
if ( c > 1 ) throw new NumberFormatException(input);
out = (out << 1) | c;
}
if ( ch1 == '-' ) return ~out + 1;
return out;
}
/**
* Parse the input string as unsigned base 2 number representation
* into an integer int value as if the integer int is an unsigned
* type. For values that need to be interpreted correctly, see the
* {#link IntUtil#toStringAsUnsigned(int) toStringAsUnsigned} method
* of the {#link IntUtil IntUtil} class.
*
* #param input
* A string to be parsed as unsigned base 2 number to an
* integer int value as if the integer int is an unsigned
* type.
*
* #return An int value that represents an unsigned integer int
* value of the unsigned base 2 number {#code input} string.
*
* #throws NumberFormatException
* If the {#code input} string contains invalid unsigned
* base 2 digits, if the {#code input} string contains a
* value that is beyond the capacity of the integer int
* data type.
*
* #throws EmptyStringException
* If the {#code input} string is empty.
*
* #since 1.0.0.f
**/
public static final int toIntAsUnsigned(final String input){
final int length = input.length();
if ( length == 0 ) throw new EmptyStringException();
int start = 0;
int out = 0, c;
while ( start < length && input.charAt(start) == '0' ) start++;
if ( length - start > 32 ) throw new NumberFormatException(input);
for ( ; start < length; start++){
c = (input.charAt(start) ^ '0');
if ( c > 1 ) throw new NumberFormatException(input);
out = (out << 1) | c;
}
return out;
}
/**
* Parse the input string as signed base 2 number representation
* into an integer long value.
*
* #param input
* A string to be parsed as signed base 2 number to an
* integer long value.
*
* #return An integer long value of the signed base 2 number
* {#code input} string.
*
* #throws NumberFormatException
* If the {#code input} string contains invalid signed
* base 2 digits, if the {#code input} string contains a
* value that is smaller than the value of Long.MIN_VALUE(
* {#value java.lang.Long#MIN_VALUE}), or if
* the {#code input} string contains a value that is larger
* than the value of Long.MAX_VALUE(
* {#value java.lang.Long#MAX_VALUE}).
*
* #throws EmptyStringException
* If the {#code input} string is empty.
*
* #since 1.0.0.f
**/
public static final long toLong(final String input){
final int length = input.length();
if ( length == 0 ) throw new EmptyStringException();
final char ch1 = input.charAt(0); int start = 0;
if ( ch1 == '-' || ch1 == '+' ){
if ( length == 1 ) throw new NumberFormatException(input);
start = 1;
}
long out = 0, c;
while ( start < length && input.charAt(start) == '0' ) start++;
final int runlen = length - start;
if ( runlen > 63 ){
if ( runlen > 64 ) throw new NumberFormatException(input);
if ( ch1 != '-' ) throw new NumberFormatException(input);
if ( input.charAt(start++) != '1') throw new NumberFormatException(input);
for ( ; start < length; start++){
if ( input.charAt(start) != '0' ) throw new NumberFormatException(input);
}
return -9223372036854775808L;
}
for ( ; start < length; start++){
c = (input.charAt(start) ^ '0');
if ( c > 1L ) throw new NumberFormatException(input);
out = (out << 1) | c;
}
if ( ch1 == '-' ) return ~out + 1L;
return out;
}
/**
* Parse the input string as unsigned base 2 number representation
* into an integer long value as if the integer long is an unsigned
* type. For values that need to be interpreted correctly, see the
* {#link LongUtil#toStringAsUnsigned(long) toStringAsUnsigned} method
* of the {#link LongUtil LongUtil} class.
*
* #param input
* A string to be parsed as unsigned base 2 number to an
* integer long value as if the integer long is an unsigned
* type.
*
* #return An integer long value represent the unsigned integer
* long value of the unsigned base 2 number {#code input}
* string.
*
* #throws NumberFormatException
* If the {#code input} string contains invalid unsigned
* base 2 digits, or if the {code input} string
* contains a value that is beyond the capacity of the
* long data type.
*
* #throws EmptyStringException
* If the {#code input} string is empty.
*
* #since 1.0.0.f
**/
public static final long toLongAsUnsigned(final String input){
final int length = input.length();
if ( length == 0 ) throw new EmptyStringException();
int start = 0;
long out = 0, c;
while ( start < length && input.charAt(start) == '0' ) start++;
if ( length - start > 64 ) throw new NumberFormatException(input);
for ( ; start < length; start++){
c = (input.charAt(start) ^ '0');
if ( c > 1L ) throw new NumberFormatException(input);
out = (out << 1) | c;
}
return out;
}
}
If I understand your question correctly, you are violating the purpose of RuntimeException.
As explained in the thread here,
RuntimeException(s) are the one's that are not supposed to be handled by the client. Rather it is a situation, where the client cannot recover. In such case, all he can do is either abandon the application or throw back the error.
If you are adding documentation to cover these exceptions, that means you very well know why this exception is occurring. In such cases it should be checked exception, and not unchecked.
So Technically speaking, no library will provide the functionality you are looking for, as runtime exceptions are not expected to be documented. It's a design smell. so better you correct the design, than adding documentation.
If it is not possible, and you insist to use RuntimeException only, then I would recommend to look at this answer and build your own Findbugs/checkstyle rule that will do the trick.
Let all your exceptions inherit from one exception superclass:
public class MySuperException extends RuntimeException {
}
public class MyException extends MySuperException {
}
To validate, that all exceptions are documented, simply exchange your super class (for example by providing another version of the file at a later position of your classpath):
// temporary class, only for compile time checks
// do not export this into jar
public class MySuperException extends Exception {
}
Please check Semmle code analysis, which has a query "Missing Javadoc for thrown exception"
Semmle has plugins LGTM and QL that could be used from IDE like Eclipse.
or
as an alternative approach please use something similar to Eclipse plugin JAutodoc to complete existing Javadoc.

Not understandable nullpointer exception - java

I have a nullpointer exception I cannot seem to solve, I'd love for you guys to take a look.
This is the exception I'm receiving:
java.lang.NullPointerException
at org.ikov.engine.task.impl.PlayerUpdateTask.execute(PlayerUpdateTask.java:84)
at org.ikov.engine.task.ParallelTask$1.run(ParallelTask.java:44)
at org.ikov.engine.GameEngine$4.run(GameEngine.java:160)
at java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source)
at java.util.concurrent.FutureTask.run(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)
Line 84 of PlayerUpdateTask is:
for (int other : localPlayerList) {
The code up to there is
if(player.getLocalPlayers() == null) {
player.disconnected = true;
return;
}
List<Integer> localPlayerList = new ArrayList<Integer>(player.getLocalPlayers());
/*
* If the map region changed send the new one. We do this immediately as
* the client can begin loading it before the actual packet is received.
*/
if (player.mapRegionDidChange) {
player.getActionSender().sendMapRegion();
}
/*
* The update block packet holds update blocks and is send after the
* main packet.
*/
GamePacketBuilder updateBlock = new GamePacketBuilder();
/*
* The main packet is written in bits instead of bytes and holds
* information about the local list, players to add and remove, movement
* and which updates are required.
*/
GamePacketBuilder packet = new GamePacketBuilder(81,
GamePacket.Type.VARIABLE_SHORT);
packet.startBitAccess();
/*
* Updates this player.
*/
updateThisPlayerMovement(packet);
updatePlayer(updateBlock, player, false, true);
/*
* Write the current size of the player list.
*/
packet.putBits(8, localPlayerList.size());
//Set up a deletion queue
List<Integer> deletionQueue = new ArrayList<Integer>();
/*
* Iterate through the local player list.
*/ - FROM HERE THE NULLPOINTER starts
Please note: the nullpointer does not always happen, it happens once in a while but I'd like to sort it out.
Do you guys have any idea, I do not understand how localPlayerList can be null considering I initialize it earlier in the method. This is for a java game, by the way.
Here's how the local player list is populated:
//We keep track of the amount of players we've added, we want to keep it down a bit as we don't want to loverload people's client
int addedPlayers = 0;
/*
* Loop through every player.
*/
for (Player otherPlayer : PlayerManager.getSingleton().getPlayers()) {
if (otherPlayer == null) {
continue;
}
if (!player.activatedPlayerUpdate) {
break;
}
if (!player.withinDistance(otherPlayer)) {
/*
* Check that the Player is within good distance of the player
* before adding to local list.
*/
continue;
}
/*
* Check if there is room left in the local list.
*/
if (player.getLocalPlayers().size() >= 255 || addedPlayers >= 20) {
/*
* There is no more room left in the local list. We cannot add
* more players, so we just ignore the extra ones. They will be
* added as other players get removed.
*/
break;
}
/*
* Do not add anymore data to the packet if it the packet exceeds
* the maximum packet size as this will cause the client to crash.
*/
if (packet.getLength() + updateBlock.getLength() >= 3072) {
break;
}
/*
* If they should not be added ignore them.
*/
if (otherPlayer == player
|| player.getLocalPlayers()
.contains(otherPlayer.getIndex())
|| !otherPlayer.isVisible()
|| otherPlayer.getMapInstance() != player.getMapInstance()) {
continue;
}
/*
* Add the player to the local list if it is within distance.
*/
player.getLocalPlayers().add(otherPlayer.getIndex());
addedPlayers++;
/*
* Add the player in the packet.
*/
addNewPlayer(packet, otherPlayer);
/*
* Update the player, forcing the appearance flag.
*/
updatePlayer(updateBlock, otherPlayer, true, false);
}
/*
* Check if the update block is not empty.
*/
if (!updateBlock.isEmpty()) {
/*
* Write a magic id indicating an update block follows.
*/
packet.putBits(11, 2047);
packet.finishBitAccess();
/*
* Add the update block at the end of this packet.
*/
packet.put(updateBlock.toPacket().getPayload());
} else {
/*
* Terminate the packet normally.
*/
packet.finishBitAccess();
}
/*
* Write the packet.
*/
player.write(packet.toPacket());
Thanks alot,
David
for (int other : localPlayerList) {
The likely reason is that localPlayerList contains an Integer that is null, and you are getting an NPE during the automatic unboxing to int.
Try to test if "localPlayerList" is null just before the "for". I don't know "getLocalPlayers()" method implementation, but is possible that return to you different results in different times? Is that method thread-safe?

Optimising Code an Array of Strings for a History in Java

I am seeking guidance in the respect of optimising code. The code I have written is for a text-based game in which you type in commands into a command bar. One feature I wished to incorporate into my interface was the ability to scroll through a history of one's last 100 commands entered using the up and down arrow keys so that it would be more convenient for the user to play the game.
I have designed a class in which uses a String[] that will store each new entry in the second position (Array[1]) and move all entries back one position while the first position of the array (Array[0]) is just a blank, empty string. The code initialises the array to have 101 values to compensate for the first position being a blank line.
When a user inputs 0 - 100 in that order, it should then give me the reverse of the order (almost like a last in, first out kind of situation, but storing the last 100 values as opposed to removing them once they are accessed), and since 0 - 100 is 101 values, the last value will be overwritten.
Thus, scrolling up through the history, it would give me 100, 99, 98, ..., 2, 1. If I were to select 50 from the list, it would then be 50, 100, 99, ..., 3, 2. The code indeed does this.
The code is listed below:
public class CommandHistory {
private String[] history;
private final int firstIndex = 1;
private static int currentIndex = 0;
/**
* Default constructor, stores last 100 entries of commands plus the blank
* entry at the first index
*/
public CommandHistory() {
history = new String[101];
}
/**
* Constructor with a capacity, stores the last (capacity) entries of
* commands plus the blank entry at the first index
*
* #param capacity
* Capacity of the commands history list
*/
public CommandHistory(int capacity) {
history = new String[capacity + 1];
}
/**
* Returns the size (length) of the history list
*
* #return The size (length) of the history list
*/
private int size() {
return history.length;
}
/**
* Adds a command to the command history log
*
* #param command
* Command to be added to the history log
*/
public void add(String command) {
history[0] = "";
if (!command.equals("")) {
for (int i = firstIndex; i < size();) {
if (history[i] == null) {
history[i] = command;
break;
} else {
for (int j = size() - 1; j > firstIndex; j--) {
history[j] = history[j - 1];
}
history[firstIndex] = command;
break;
}
}
currentIndex = 0;
}
}
/**
* Gets the previous command in the history list
*
* #return The previous command from the history list
*/
public String previous() {
if (currentIndex > 0) {
currentIndex--;
}
return history[currentIndex];
}
/**
* Gets the next command in the history list
*
* #return The next command from the history list
*/
public String next() {
if (currentIndex >= 0 && (history[currentIndex + 1] != null)) {
currentIndex++;
}
return history[currentIndex];
}
/**
* Clears the command history list
*/
public void clear() {
for (int i = firstIndex; i < size(); i++) {
history[i] = null;
}
currentIndex = 0;
}
/**
* Returns the entire command history log
*/
public String toString() {
String history = "";
for (int i = 0; i < size(); i++) {
history += this.history[i];
}
return history;
}
}
In my interface class, once the user types something into the command bar and hits enter, it will get the text currently stored in the bar, uses the add method to add it to the history, parses the command via another class, and then sets the text in the bar to blank.
Pressing the up arrow calls the next method which scrolls up the list, and the down arrow calls the previous method which scrolls down the list.
It seems to work in every way I wish it to, but I was wondering if there was some way to optimise this code or perhaps even code it in a completely different way. I am making this game to keep myself practiced in Java and also to learn new and more advanced things, so I'd love to hear any suggestions on how to do so.
The comments to your question have already pointed out that you are somehow trying to reinvent the wheel by implementing functionality that the standard Java class library already provides to some extent (see LinkedList/Queue and Arraylist). But since you say you want to keep yourself practiced in Java I guess it is perfectly fine if you try to implement your own command history from scratch.
Here are some of my observations/suggestions:
1) It is not necessary and very counter-intuitive to declare a final first index of 1. It would be easy to start with a default index of 0 and add corresponding checks where necessary.
2) Forget about your private size() method - it is just returning the length of the internal array anyway (i.e. the initial capacity+1). Instead consider adding a public size() method that returns the actual number of added commands and internally update the actual size when adding new commands (see e.g. java.util.ArrayList for reference).
3) At the moment every call to add(String command) will set history[0] = "", which is not necessary. If you want the first index to be "", set it in the constructor. This is also a clear sign, that it would perhaps be better to start with an initial index of 0 instead of 1.
4) A minor issue: "if (!command.equals(""))" during your add method is perhaps OK for such a specialized class but it should definitely be commented in the documentation of the method. Personally I would always let the calling class decide if an empty "" command is considered valid or not. Also this method will throw an undocumented NullPointerException, when null is used as an argument. Consider changing this to "if (!"".equals(command))" or throw an IllegalArgumentException if null is added.
5) "if (history[i] == null)" during the add method is completely unnecessary, if you internally keep a pointer to the actual size of the commands - this is actually a special case that will only be true, when the very first command is added to the command history (i.e. when it's actual size == 0).
6) Having two nested for loops in your add method implementation is also unnecessary, if you keep a pointer to the actual size (see example below)
7) I would reconsider if it is necessary to keep a pointer to the current index in the command history. Personally I would avoid storing such a pointer and leave these details to the calling class - i.e. remove the previous and next methods and either provide a forward/backward Iterator and/or a random access to the index of the available commands. Interestingly, when this functionality is removed from your command history class, it actually comes down to either an implementation of a LinkedList or an ArrayList- whichever way you go. So in the end using one of the built in Java collections would actually be the way to go.
8) Last but nor least I would reconsider if it is useful to insert added commands at the beginning of the list - I believe it would be more natural to append them to the end as e.g. ArrayList does. Adding the commands to the end would make the swapping of all current commands during each call to add() unnecessary...
Here are some of the suggested changes to your class (not really tested...)
public class CommandHistory {
private String[] history;
private int size;
private static int currentIndex = 0;
/**
* Default constructor, stores last 100 entries of commands plus the blank
* entry at the first index
*/
public CommandHistory() {
this(100);
}
/**
* Constructor with a capacity, stores the last (capacity) entries of
* commands plus the blank entry at the first index
*
* #param capacity
* Capacity of the commands history list
*/
public CommandHistory(int capacity) {
history = new String[capacity];
}
/**
* Returns the size (length) of the history list
*
* #return The size (length) of the history list
*/
public int size() {
return size;
}
/**
* Adds a command to the command history log
*
* #param command
* Command to be added to the history log
*/
public void add(String command) {
if (!"".equals(command)) {
if (this.size < history.length) {
this.size++;
}
for (int i = size-1; i >0; i--) {
history[i] = history[i-1];
}
history[0] = command;
currentIndex = 0;
}
}
/**
* Gets the previous command in the history list
*
* #return The previous command from the history list
*/
public String previous() {
if (currentIndex >= 0 && currentIndex < size-1) {
currentIndex++;
}
return history[currentIndex];
}
/**
* Gets the next command in the history list
*
* #return The next command from the history list
*/
public String next() {
if (currentIndex > 0 && currentIndex < size) {
currentIndex--;
}
return history[currentIndex];
}
/**
* Clears the command history list
*/
public void clear() {
for (int i = 0; i < size; i++) {
history[i] = null;
}
currentIndex = 0;
}
/**
* Returns the entire command history log
*/
public String toString() {
String history = "";
for (int i = 0; i < size; i++) {
history += this.history[i] + ", ";
}
return history;
}
}
Well, I guess I have invested far too much time for this, but I learned quite a bit myself on the way - so thanks ;-)
Hope some of this is useful for you.

How do I peek at the first two bytes in an InputStream?

Should be pretty simple: I have an InputStream where I want to peek at (not read) the first two bytes, i.e. I want the "current position" of the InputStream to stil be at 0 after my peeking. What is the best and safest way to do this?
Answer - As I had suspected, the solution was to wrap it in a BufferedInputStream which offers markability. Thanks Rasmus.
For a general InputStream, I would wrap it in a BufferedInputStream and do something like this:
BufferedInputStream bis = new BufferedInputStream(inputStream);
bis.mark(2);
int byte1 = bis.read();
int byte2 = bis.read();
bis.reset();
// note: you must continue using the BufferedInputStream instead of the inputStream
You might find PushbackInputStream to be useful:
http://docs.oracle.com/javase/6/docs/api/java/io/PushbackInputStream.html
When using a BufferedInputStream make sure that the inputStream is not already buffered, double buffering will cause some seriously hard to find bugs.
Also you need to handle Readers differently, converting to a StreamReader and Buffering will cause bytes to be lost if the Reader is Buffered.
Also if you are using a Reader remember that you are not reading bytes but characters in the default encoding (unless an explicit encoding was set).
An example of a buffered input stream, that you may not know is URL url; url.openStream();
I do not have any references for this information, it comes from debugging code.
The main case where the issue occurred for me was in code that read from a file into a compressed stream.
If I remember correctly once you start debugging through the code there are comments in the Java source that certain things do not work correctly always.
I do not remember where the information from using BufferedReader and BufferedInputStream
comes from but I think that fails straight away on even the simplest test.
Remember to test this you need to be marking more than the buffer size (which is different for BufferedReader versus BufferedInputStream), the problems occur when the bytes being read reach the end of the buffer.
Note there is a source code buffer size which can be different to the buffer size you set in the constructor.
It is a while since I did this so my recollections of details may be a little off.
Testing was done using a FilterReader/FilterInputStream, add one to the direct stream and one to the buffered stream to see the difference.
I found an implementation of a PeekableInputStream here:
http://www.heatonresearch.com/articles/147/page2.html
The idea of the implementation shown in the article is that it keeps an array of "peeked" values internally. When you call read, the values are returned first from the peeked array, then from the input stream. When you call peek, the values are read and stored in the "peeked" array.
As the license of the sample code is LGPL, It can be attached to this post:
package com.heatonresearch.httprecipes.html;
import java.io.*;
/**
* The Heaton Research Spider Copyright 2007 by Heaton
* Research, Inc.
*
* HTTP Programming Recipes for Java ISBN: 0-9773206-6-9
* http://www.heatonresearch.com/articles/series/16/
*
* PeekableInputStream: This is a special input stream that
* allows the program to peek one or more characters ahead
* in the file.
*
* This class is released under the:
* GNU Lesser General Public License (LGPL)
* http://www.gnu.org/copyleft/lesser.html
*
* #author Jeff Heaton
* #version 1.1
*/
public class PeekableInputStream extends InputStream
{
/**
* The underlying stream.
*/
private InputStream stream;
/**
* Bytes that have been peeked at.
*/
private byte peekBytes[];
/**
* How many bytes have been peeked at.
*/
private int peekLength;
/**
* The constructor accepts an InputStream to setup the
* object.
*
* #param is
* The InputStream to parse.
*/
public PeekableInputStream(InputStream is)
{
this.stream = is;
this.peekBytes = new byte[10];
this.peekLength = 0;
}
/**
* Peek at the next character from the stream.
*
* #return The next character.
* #throws IOException
* If an I/O exception occurs.
*/
public int peek() throws IOException
{
return peek(0);
}
/**
* Peek at a specified depth.
*
* #param depth
* The depth to check.
* #return The character peeked at.
* #throws IOException
* If an I/O exception occurs.
*/
public int peek(int depth) throws IOException
{
// does the size of the peek buffer need to be extended?
if (this.peekBytes.length <= depth)
{
byte temp[] = new byte[depth + 10];
for (int i = 0; i < this.peekBytes.length; i++)
{
temp[i] = this.peekBytes[i];
}
this.peekBytes = temp;
}
// does more data need to be read?
if (depth >= this.peekLength)
{
int offset = this.peekLength;
int length = (depth - this.peekLength) + 1;
int lengthRead = this.stream.read(this.peekBytes, offset, length);
if (lengthRead == -1)
{
return -1;
}
this.peekLength = depth + 1;
}
return this.peekBytes[depth];
}
/*
* Read a single byte from the stream. #throws IOException
* If an I/O exception occurs. #return The character that
* was read from the stream.
*/
#Override
public int read() throws IOException
{
if (this.peekLength == 0)
{
return this.stream.read();
}
int result = this.peekBytes[0];
this.peekLength--;
for (int i = 0; i < this.peekLength; i++)
{
this.peekBytes[i] = this.peekBytes[i + 1];
}
return result;
}
}

Categories