I am currently learning to develop in Java and am interested in creating Java classes that other users can import into their program and use. Yes, I know my example class is simple and stupid but I want to learn the concept and start making more complex classes that people can import into their projects.
I created a simple "Logger" class that when called logs both text to the console and to a text file for readability. You can call this class by using the following commands...
Logger Logger = new Logger();
Logger.create();
Logger.log("This text will be logged to the console and log.log");
See below for the Logger class.
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
public class Logger {
FileWriter fw;
BufferedWriter br;
File file = new File("log.log");
boolean fileExists = file.exists();
public void log(String message) {
try {
fw = new FileWriter(file, true);
br = new BufferedWriter(fw);
Calendar cal = Calendar.getInstance();
int hour = cal.get(Calendar.HOUR_OF_DAY);
if(hour > 12)
hour = hour - 12;
int minute = cal.get(Calendar.MINUTE);
int second = cal.get(Calendar.SECOND);
int millis = cal.get(Calendar.MILLISECOND);
int ampm = cal.get(Calendar.AM_PM);
String ampmString;
if(ampm == 1)
ampmString = "PM";
else
ampmString = "AM";
String now = String.format("%02d:%02d:%02d.%03d %s", hour, minute, second, millis, ampmString);
System.out.println(now + " - " + message);
br.write(now + " - " + message);
br.newLine();
br.close();
} catch (Exception err) {
System.out.println("Error");
}
}
public void create() {
try {
fw = new FileWriter(file, true);
br = new BufferedWriter(fw);
SimpleDateFormat sdf = new SimpleDateFormat("MM-dd-YYYY");
String dateString = sdf.format(new Date());
if(file.length() != 0)
br.newLine();
System.out.println("Log: " + file.getAbsolutePath());
br.write("--------------------" + dateString + "--------------------");
br.newLine();
br.close();
} catch (Exception err) {
System.out.println("Error");
}
}
}
The issue I am having is in order to use this class I have to add it to every single project I create and want to use this. Is there a way I can add an import like, mydomain.Logger.*; and be able to access this class and the methods it contains?
My question, What is the best way to allow anyone to import/use my Logger class in the simplest way? What steps do I need to take to allow them to do this?
Compile your class into a JAR file, and then add that jar to the classpath of each project. This is the standard Java approach to reusing classes.
Each class has a package. You declare it on the top of your file. For your class this could be i.e. package com.mydomain;. (Your example does not declare a package so the default is assigned - think "none"). Packages correspond to actual folders e.g. you should place your Logger.java in the path com/mydomain/ in your file system to match a package declaration like the one above.
Now if you create a class in the same package you could use your Logger class by just declaring a variable like in your example. No imports necessary.
If you create the new class in a different package i.e. com.otherdomain (this means that it would actually be in a different folder in the file system) then you would have to use an import statement like this: import com.mydomain.Logger; or import com.mydomain.*; otherwise the compiler would show an error and wouldn't let you use your Logger class.
If you would like to let other people use your class you would probably ship your binary version (i.e. the compiled Logger.class) not the source. And you would probably put that file in a jar (this is a zipped file structure more or less).
If then I wanted to use your class I could just add that jar to the classapth of my project, so the compiler and the runtime system would know to search this file when looking for classes. I would be able then to write an import statement myself inside my class and I could use your logger.
I hope this clears things up
You will have to create a jar file, then people can import that into their projects.
The Scala language lets you import members of arbitrary objects, but Java will only let you import static members. You can try using static imports. However, you'd need to change your Logger class to have a static log method. Since you probably just want a single instance of your logger, I'd recommend using the singleton enum pattern:
// Using "enum" instead of "class"
public enum Logger {
// Create singleton instance
INSTANCE;
// Create static log method for export
public static void log(String message) {
INSTANCE.log(message);
}
// The rest of your class is unchanged
FileWriter fw;
BufferedWriter br;
File file = new File("log.log");
boolean fileExists = file.exists();
public void log(String message) {
// ...
}
public void create() {
// ...
}
}
Now in each file you want to use it you can just do import static mydomain.Logger.log; to get direct access to the log method!
Related
My objective is to look at some lines of codes of an external file and count the number of functions of a class are called then.
For example, if I have the following code:
import java.io.BufferedReader;
import whatever.MyClass;
import java.util.ArrayList;
...
...
public void example(){
InputStreamReader isr = new InputStreamReader (whatever);
MyClass object = new MyClass();
someArrayList.add(whatever2)
someArrayList.add(whatever3)
}
In this case, BufferedReader and MyClass functions were called once, and ArrayList functions were called twice.
My solution for that is get a list of all methods inside the used classes and try to match with some string of my code.
For classes created in my project, I can do the following:
jar -tf jarPath
which returns me the list of classes inside a JAR . And doing:
javap -cp jarPath className
I can get a list of all methods inside a JAR whit a specific class name. However, what can I do to get a external methods names, like add(...) of an "external" class java.util.ArrayList?
I can't access the .jar file of java.util.ArrayList correct? Anyone have another suggestion to reach the objective?
The compiler doesn't put the imports into the object file. It throws them away. Import is just a shorthand to the compiler.(Imports are a compile-time feature ).
first step :
use Qdox https://github.com/paul-hammant/qdox to get all the imports in a class :
String fileFullPath = "Your\\java\\ file \\full\\path";
JavaDocBuilder builder = new JavaDocBuilder();
builder.addSource(new FileReader( fileFullPath ));
JavaSource src = builder.getSources()[0];
String[] imports = src.getImports();
for ( String imp : imports )
{
System.out.println(imp);
}
second step :
inspire from that code , loop through your imports (String array) and apply the same code and you will get the methods .
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class Tes {
public static void main(String[] args) {
Class c;
try {
c = Class.forName("java.util.ArrayList");
Arrays.stream(getAccessibleMethods(c)).
forEach(System.out::println);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
public static Method[] getAccessibleMethods(Class clazz) {
List<Method> result = new ArrayList<Method>();
while (clazz != null) {
for (Method method : clazz.getDeclaredMethods()) {
result.add(method);
}
clazz = clazz.getSuperclass();
}
return result.toArray(new Method[result.size()]);
}
}
Output :
public void java.util.ArrayList.add(int,java.lang.Object)
public boolean java.util.ArrayList.add(java.lang.Object)
public boolean java.util.ArrayList.remove(java.lang.Object)
public java.lang.Object java.util.ArrayList.remove(int)
public java.lang.Object java.util.ArrayList.get(int)
public java.lang.Object java.util.ArrayList.clone()
public int java.util.ArrayList.indexOf(java.lang.Object)
public void java.util.ArrayList.clear()
.
.
.
All the code - one class :
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import com.thoughtworks.qdox.JavaDocBuilder;
import com.thoughtworks.qdox.model.JavaSource;
public class Tester {
public static void main(String[] args) {
// put your .java file path
// CyclicB is a class within another project in my pc
String fileFullPath =
"C:\\Users\\OUSSEMA\\Desktop\\dev\\OCP_Preparation\\src\\w\\CyclicB.java";
JavaDocBuilder builder = new JavaDocBuilder();
try {
builder.addSource(new FileReader( fileFullPath ));
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
JavaSource src = builder.getSources()[0];
String[] imports = src.getImports();
for ( String imp : imports )
{
Class c;
try {
c = Class.forName(imp);
Arrays.stream(getAccessibleMethods(c)).
forEach(System.out::println);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
public static Method[] getAccessibleMethods(Class clazz) {
List<Method> result = new ArrayList<Method>();
while (clazz != null) {
for (Method method : clazz.getDeclaredMethods()) {
result.add(method);
}
clazz = clazz.getSuperclass();
}
return result.toArray(new Method[result.size()]);
}
}
Output all the methods within the classes imported in the file CyclicB.java :
private void java.lang.Throwable.printStackTrace(java.lang.Throwable$PrintStreamOrWriter)
public void java.lang.Throwable.printStackTrace(java.io.PrintStream)
public void java.lang.Throwable.printStackTrace()
public void java.lang.Throwable.printStackTrace(java.io.PrintWriter)
public synchronized java.lang.Throwable java.lang.Throwable.fillInStackTrace()
.
.
.
You may look into OpenJDK project that has a Java compiler. Learn to build the modified versions. Investigate the syntax analysis layer of this compiler and find where the method calls are handled. Put the logging into these locations and now you only need to build your java file with the modified compiler to get the information about the calls.
The build is complex, but you will likely only need a careful editing in a few files. It is not exactly very low hanging fruit but I think it should be possible to discover these files and make changes in them, and still may be a simpler/cleaner approach than to implement the own Java syntax parser (also doable with JavaCC).
If you also need to track calls from the external libraries, build them with the modified compiler as well and you will have the needed records.
GNU Classpath is another open source project where you can do the similar thing, and it may be easier to build. However, unlike OpenJDK, GNU Classpath java system library is not complete.
This approach may not discover some methods called during reflection. But it would discover that reflection framework methods have been called. If it is a security - related project, the simplest would be to agree that reflection is not allowed. It is uncommon to use reflection in a normal Java application that is not a framework.
I have the file allDepartments.json in a subdirectory called fixtures, to which I want to access from the Fixture.java class.
This is my Fixture.java code:
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
public final class Fixture {
private static final String FIXTURES_PATH = "";
private final String fixture;
public Fixture(String fixtureName) throws IOException {
fixture = new String(Files.readAllBytes(Paths.get(FIXTURES_PATH + fixtureName)));
}
public final String getFixture() {
return fixture;
}
}
However every time he tries to access the file I get a java.nio.file.NoSuchFileException: allDepartments.json...
I have heard of the getResource() method and tried every combination possible of it, without success.
I need this to store multi-line strings for my JUnit tests.
What can I do?
The NIO.2 API can't be used to read files that are effectively project resources, i.e. files present on the classpath.
In your situation, you have a Maven project and a resource that you want to read during the unit test of the application. First, this implies that this resources should be placed under src/test/resources so that Maven adds it automatically to the classpath during the tests. Second, this implies that you can't use the Files utility to read it.
You will need to resort to using a traditional BufferedReader:
public Fixture(String fixtureName) throws IOException {
try (BufferedReader br = new BufferedReader(new InputStreamReader(Fixture.class.getResourceAsStream(FIXTURES_PATH + fixtureName)))) {
// do your thing with br.readLine();
}
}
Note the path given to getResourceAsStream is either relative to the current class or absolute. If the resources is located in src/test/resources/folder/allDepartments.json then a valid path would be /folder/allDepartments.json.
Add allDepartments.json to the.classpath file of the project and java should be able to pick it up.
Refer this topic if you want to know how to add a file to class path from eclipse
when you run Fixtures.java the relative path would be
../fixtures/allDepartments.json
try using this path.
Thank you all for helping and suggestions.
Thanks to you I was able to put things working, so here is the trick (which I guess only works for Maven projects):
I moved the allDepartments.json file to the default src/test/resources folder as suggested by you guys. I didn't even had to modify the pom.xml. And now everything works!
So this is my project structure now:
And the final Fixture.java code is:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.stream.Collectors;
public final class Fixture {
private final String fixture;
public Fixture(String fixtureName) throws IOException {
fixture = this.readFile(fixtureName);
}
private String readFile(String fileName) throws IOException {
final InputStream in = this.getClass().getClassLoader().getResource("fixtures/" + fileName).openStream();
final BufferedReader buffer = new BufferedReader(new InputStreamReader(in));
try {
return buffer.lines().collect(Collectors.joining("\n"));
} finally {
buffer.close();
}
}
public final String getFixture() {
return fixture;
}
}
so I'm currently working on reading information from a text file from the first time, and from what I have pieced together, the following code should work and return 100 and 16:
package Utility;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Properties;
public class textReader {
public textReader()
{}
public Object fetchElement(String fileName, String keyName)
{
Properties properties = new Properties();
try {
properties.load(new FileInputStream("P:/Real_Time_Survival/Real_Time_Survivial_Game/assets" + fileName));
} catch (IOException e) {
}
return properties.getProperty("keyName");
}
}
but when called from the main class with
textReader ready = new textReader();
ready.fetchElement("Sprites/ExampleSprite/Default/SpriteData.txt", "FrameDuration");
ready.fetchElement("Sprites/ExampleSprite/Default/SpriteData.txt", "AnimationFrames");
It returns null (have the system printing out those lines, cut it out due to formatting errors). Any idea as to why this won't work?
I'm going to stick my neck out and guess you left off a "/" after "assets"
I um, put the keyName variable as a string.
Yep this is solved, 10/10 best dumb mistakes ever
im trying to put all stopwords on a hashset, i dont want to add it one by one so im trying to put in a txt file and have my scanner scan it. the problem is i think my code does not reach my scanner here is my code:
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.*;
public class StopWords {
public static final Set<String> stopWords = new HashSet<String>();
private static class scan {
public scan()throws IOException {
Scanner s = null;
try{
s = new Scanner(new BufferedReader(new FileReader("stopwords.txt")));
while (s.hasNext()) {
//System.out.println(s.next());
stopWords.add(s.next());
}
}finally{
if (s != null) {
s.close();
}
}
}
}
}
im running my main on other class and im just calling this class. thanks in advance
Make a wrapper for it in enclosing class.
Something like:
public void doScan() {
try {
scan.scan();
catch (IOException e) {};
}
in StopWords class.
This way you could call doScan() on instance of StopWords. You could also make it static.
And I agree that you should follow naming convections of Java language (wikipedia.org).
Just want to add a couple tricks you might consider:
First - you could store your stopwords in a properties file, then use java.util.Properties.load to pull the data in.
Second - you can put your stopwords file on your classpath, and bundle up the stopwords file with the rest of your code in a jar for delivery.
You wind up with something like this:
final Properties stopProps = new java.util.Properties();
stopProps.load( new InputStreamReader( this.class.getClassLoader().getResourceAsStream( "mycode/stopWords.properties", "UTF-8" ) );
...
Good luck!
I'm new to java and trying to learn how to log an exception by example. I found the following example code here:
http://www.kodejava.org/examples/447.html
However, I don't see where the filename for the log file is specified. When I research the question on Google usually people refer to the framework used for programming java to figure out where the log file gets stored. However, I'm not using a framework. I'm just creating my java files using VIM editor from the command line. The java file sits on an Linux CentOS application server and is called from a client's browser.
Question 1: Is it possible to modify the example below to include a file name and path for logging? Or, am I way off base with this question?
Question 2: Even though I log the exception, will it still propagate to the client for the user to view? Hopefully it will, otherwise the user won't know an error has occurred.
package org.kodejava.example.util.logging;
import java.util.logging.Logger;
import java.util.logging.Level;
import java.util.Date;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.text.ParseException;
public class LoggingException {
private static Logger logger = Logger.getLogger(LoggingException.class.getName());
public static void main(String[] args) {
DateFormat df = new SimpleDateFormat("dd/MM/yyyy");
df.setLenient(false);
try {
//
// Try to parsing a wrong date.
//
Date date = df.parse("12/30/1990");
System.out.println("Date = " + date);
} catch (ParseException e) {
//
// Create a Level.SEVERE logging message
//
if (logger.isLoggable(Level.SEVERE)) {
logger.log(Level.SEVERE, "Error parsing date", e);
}
}
}
}
Try this:
try {
FileHandler handler = new FileHandler("myLogFile.log", true);
Logger logger = Logger.getLogger(LoggingException.class.getName());
logger.addHandler(handler);
} catch (IOException e) {
logger.log(Level.SEVERE, "Error parsing date", e);
}
By default, a file handler overwrites the contents of the log file each time it is created. You might also want to append log file so in FileHandler constructor you need to specify true as a second parameter.
Hope this helps.
EDIT:
package org.kodejava.example.util.logging;
import java.util.logging.Logger;
import java.util.logging.Level;
import java.util.Date;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.text.ParseException;
public class LoggingException {
private static Logger logger = Logger.getLogger(LoggingException.class.getName());
public static void main(String[] args) {
DateFormat df = new SimpleDateFormat("dd/MM/yyyy");
df.setLenient(false);
FileHandler handler = new FileHandler("myLogFile.log", true);
logger.addHandler(handler);
try {
//
// Try to parsing a wrong date.
//
Date date = df.parse("12/30/1990");
System.out.println("Date = " + date);
} catch (ParseException e) {
//
// Create a Level.SEVERE logging message
//
if (logger.isLoggable(Level.SEVERE)) {
logger.log(Level.SEVERE, "Error parsing date", e);
}
}
}
}
This should work. I did not test it.
More efficient way would be create a method to initialize logger and add handler to it. But I will mostly recommend you to think about using log4j. It is easy to set up and widely used logging framework.
You need a FileHandler attached to the log, which you can add manually somewhere in your initialization, or configure your logging with a .properties file.
(p.s. the isLoggable call in this example is redundant and only bloats the code)
Add a file handler for the logger in the config.
Link: http://www.crazysquirrel.com/computing/java/logging.jspx
handlers = java.util.logging.ConsoleHandler, java.util.logging.FileHandler
Better go with log4j. It a good logging framework.