I'm having a go at creating a custom Javadoc generator using Doclet, but I'm running into some issues.
I'm following the official documentation and initially had trouble with including the tools.jar file in my project, but I managed to fix this.
My issue now is that after running this command...
javadoc -doclet ListClass -docletpath . MyClass.java
...I am getting the message...
javadoc: error - Cannot find doclet class ListClass
As I said, I've mostly been following the tutorials from the official documentation, but here is my code for reference.
ListClass.java:
import com.sun.javadoc.*;
public class ListClass {
public static boolean start(RootDoc root) {
ClassDoc[] classes = root.classes();
for (int i = 0; i < classes.length; ++i) {
System.out.println(classes[i]);
}
return true;
}
}
And MyClass.java:
/**
* Documentation for my class
*/
public class MyClass {
public static void main(String[] args) {
}
/**
* Documentation for my static void method
*
* #param param This is the parameter it takes in
*/
public static void myStaticVoidMethod(String param) {
}
}
So what I am asking is why I am getting the error I posted above. If someone was able to provide a more comprehensive guide of how Doclet works that would be great as well.
Note: I'm using IntelliJ IDE for my project. Here is my directory structure:
.idea
...
out
...
src
ListClass.java
MyClass.java
JavadocGenerator.iml
You need to compile your ListClass file. Something like:
javac -d . ListClass.java -cp /path/to/tools.jar
Doclet classes are part of the tools jar, so you'll need to include it as your compile-time dependency.
Then your command
javadoc -doclet ListClass -docletpath . MyClass.java
should work.
edit
For you project structure, if you're compiling from the root directory, make sure to reference your files through their subdirectories, and make sure any absolute windows paths are double-quoted:
javac -d . ./src/ListClass.java -cp "C:/Program Files/Java/jdk1.8.0_66/lib/tools.jar"
This would create a compiled ListClass file at the root of the project, and from there use your javadoc command:
javadoc -doclet ListClass -docletpath . ./src/MyClass.java
It would be better to create a classes directory to place your compiled classes, as opposed to in the root of the project, but I'm just working with the structure you've provided. See the documentation for javac and javadoc for more info.
Related
I've the following two source files
File World.java
package planets;
public class World {
public static void main(String[] args) {
Mars.land();
}
}
File Moon.java
package planets;
public class Moon {
public static void land() {
System.out.println("Hello Moon");
}
}
class Mars {
public static void land() {
System.out.println("Hello Mars");
}
}
As we can see, the Moon.java contains two classes: the public Moon class and the nonpublic Mars class.
The files are located inside planets directory, below is showed the directory tree
+current-dir:
+----+planets:
+----+World.java
+----+Moon.java
Now, if I try to compile from Windows command prompt (I'm inside current-dir folder) typing
javac planets\World.java
I receive this error message:
planets\World.java:5: error: cannot find symbol
Mars.land();
^
symbol: variable Mars
location: class World
1 error
It's very strange, because I know that the compiler searches for nonpublic classes inside all the source files of the current package.
Also Cay Horstmann's Core Java Vol 1, 10th ed. at pp. 192-193 says that:
[...]you can import nonpublic classes from the current package. These
classes may be defined in source files with different names. If you
import a class from the current package, the compiler searches all
source files of the current package to see which one defines the
class.
In addition I tried to write these files using Eclipse Oxygen and it compile without problems. But I know that Eclipse use a different compiler.
Why does javac compiler fail?
EDIT: I have not set CLASSPATH variable. So by default compiler looks inside current directory.
you need to type the following commands in order (inside your 'current-dir')
javac planets\Moon.java
javac -cp . planets\World.java
java -cp . planets.World
I'm newer in Java Card Platform so please be patient with me. I'm trying to develop an RMI application for the Java Card 3 Platform. My IDE is Eclipse and my OS is Windows 10. I start by creating a simple interface IContor.java responsible for increasing, decreasing, etc. certain values.
Here is my interface:
package sid;
import java.rmi.Remote;
import java.rmi.RemoteException;
import javacard.framework.UserException;
public interface IContor extends Remote{
public void Incrementer()throws RemoteException,UserException;
public void Decrementer()throws RemoteException,UserException;
public byte GetValue()throws RemoteException,UserException;
public void Init(byte value)throws RemoteException,UserException;
}
Then I provide an implementation for this interface which I named Contor.java:
package sid;
import java.rmi.RemoteException;
import javacard.framework.UserException;
import javacard.framework.service.CardRemoteObject;
public class Contor extends CardRemoteObject implements IContor {
private byte contor = 0;
#Override
public void Incrementer() throws RemoteException, UserException {
++contor;
}
#Override
public void Decrementer() throws RemoteException, UserException {
--contor;
}
#Override
public byte GetValue() throws RemoteException, UserException {
return contor;
}
#Override
public void Init(byte value) throws RemoteException, UserException {
contor = value;
}
}
My Test applet worked ok. Below I wrote that peace of code:
package sid;
import javacard.framework.*;
import javacard.framework.service.Dispatcher;
import javacard.framework.service.RMIService;
public class Test extends Applet {
Dispatcher dispatcher;
public static void install(byte[] bArray, short bOffset, byte bLength) {
new Test().register();
}
protected Test() {
RMIService rmiService = new RMIService(new Contor());
dispatcher = new Dispatcher((short)1);
dispatcher.addService(rmiService,Dispatcher.PROCESS_COMMAND);
}
#Override
public void process(APDU apdu) {
dispatcher.process(apdu);
}
}
This is a standard piece of code. However I want to create a client which uses that interface which implements the interface Remote. So I create a Java application in which I copied the IContor.java interface. Then I opened a command prompt and do the following things:
Go into the directory where the source files of the first project is located (cd bla_bla/Contor/src)
Go a directory up (cd ..)
Go into bin directory (cd bin)
Here I have located the name of the package ( sid) and into package sid I have those three files (Contor.class, IContor.class and Test.class).
Then I typed the following command on the command prompt:
rmic -v1.2 -classpath .;%JC_CLASSIC_HOME%lib/tools.jar -sid/Contor
but I got the following error:
Class javacard.framework.service.CardRemoteObject not found in class sid.Contor.
I replace the tools.jar with api_classic.jar but I still get the same error .
The %JC_CLASSIC_HOME% contains the path to the Java Card 3 development kit. tools.jar contains compiled implementations of packages javacard.framework, javacard.security, javacardx.biometry, javacardx.external and javacardx.framework.tlv . My bout is to generate a client application in bin/sid directory.My %JC_CLASSIC_HOME% value is C:\Program Files (x86)\Oracle\Java Card Development Kit 3.0.5ga\ and I'm using JDK 1.8
Here is my Package Explorer from Eclipse :
What does the error "Class javacard.framework.service.CardRemoteObject not found in class sid.Contor." mean?
This means that you reference the class CardRemoteObject from within your class Contor (as Contor extends CardRemoteObject). However, the class path that you use to compile the RMI stub does not contain this class.
The class javacard.framework.service.CardRemoteObject is located in lib/api_classic.jar. Hence, the correct class path for the Java Card Classic API (which is what you need to generate the stub class for RMI) is lib/api_classic.jar. lib/tools.jar is the Java archive that you later need to compile your client applications against. lib/tools.jar only contains the Java Card related exception classes. However, for compiling the remote interface stub you need the whole Java Card API (at least those classes that are referenced from your interface classes).
How does -classpath work?
The parameter -classpath is a list of directories and/or Java archives (.jar files) that contain all the relevant classes to compile a given Java source file (for javac, the Java compiler) or to compile an RMI interface stub from a given Java class file (for rmic, the Java RMI compiler).
For instance, the parameter -classpath .;"%JC_CLASSIC_HOME%/lib/api_classic.jar" specifies two paths (multiple paths are separated by semicolons (";")):
. specifies the current directory (in your case the bin directory).
"%JC_CLASSIC_HOME%/lib/api_classic.jar" specifiees the Java archive api_classic.jar from your Java Card Development Kit.
Within those paths, classes are organized in directories mapping to the components of the Java package names (e.g. a class sid.Contor would be located in a file sid/Contor.class; a class javacard.framework.service.CardRemoteObject would be located in a file javacard/framework/service/CardRemoteObject.class).
How to compile the RMI stub?
Goto your src directory.
Compile the Java classes of the applet. In your case, you could use something like:
javac -classpath .;"%JC_CLASSIC_HOME%/lib/api_classic.jar" -d ../bin sid/IContor.java sid/Contor.java sid/Test.java
Goto your bin directory: cd ../bin
Compile the Java RMI stub. In your case, you could use something like:
rmic -v1.2 -classpath .;"%JC_CLASSIC_HOME%/lib/api_classic.jar" sid.Contor
The class must be specified without a trailing dash ("-") and using dots (".") instead of slashes ("/"). If the value of the environment variable JC_CLASSIC_HOME contains spaces, you need to surround the whole path of the api_classic.jar file with quotation marks.
I am trying to understand how javac deals with cyclic imports. After few attempts, I got the below code to be compiled:
package com.test.p1;
import com.test.p2.ClassP2;
public class ClassP1 {
public static void main(String[] args) {
System.out.println("ClassP1 loaded");
ClassP2.testP2();
}
}
And
package com.test.p2;
import com.test.p1.ClassP1;
public class ClassP2 {
public static void testP2() {
System.out.println("ClassP2 loaded");
}
}
When I compiled ClassP2.java first, I was expecting it to fail as ClassP1.java was not yet compiled and no class file was generated. Yet, it compiled ok, and generated ClassP2.class and ClassP1.class. Running ClassP1 worked fine as well.
Now my question is how did javac generate class file for ClassP1 when I only compiled ClassP2. And why does this not happen when cyclic reference is not there - i.e. when ClassP2 imports ClassP1, but ClassP1 does not import ClassP2 ?
I am still new to Java and trying to understand how compilation works.
There's nothing cyclic going on here. It sounds like you, along with a lot of other people, confuse import with class loading.
All the import statement does is allow you to use the short name to refer to a Java class in your code:
import java.sql.ResultSet;
public V find() {
ResultSet rs = null; // You can type ResultSet instead of java.sql.ResultSet
}
The import statement won't help you if the short name is ambiguous (e.g. java.util.Date and java.sql.Date in the same class).
The byte code for java.sql.ResultSet is not loaded into the JVM until your code needs it at runtime.
It is not a result of cyclic imports, it is a result of importing ClassP1 in ClassP2.java and javac being able to find the source for ClassP1 based on the arguments you passed to javac and the location from which you executed the command.
See the -sourcepath option in the docs for javac:
-sourcepath sourcepath
Specify the source code path to search for class or interface definitions. As with the user class path, source path entries are separated by semicolons (;) and can be directories, JAR archives, or ZIP archives. If packages are used, the local path name within the directory or archive must reflect the package name.
Note: Classes found through the class path may be subject to automatic recompilation if their sources are also found. See Searching For Types.
I can't get the Service Provider Interface to load an implementation from another JAR in the same directory. It only works when I use -Djava.ext.dirs=. on the command line. Should it not work without?
I have the following interface:
package playground;
public interface TestIface {
public String test();
}
which is implemented here:
package playground;
public class TestImpl implements TestIface {
public String test() {
return "TEST";
}
}
Here I try to load the implementation:
package playground;
import java.util.Iterator;
import java.util.ServiceLoader;
public class Lalala {
public static void main(String[] args) {
ServiceLoader<TestIface> loader = ServiceLoader.load(TestIface.class);
Iterator<TestIface> it = loader.iterator();
while (it.hasNext()) {
TestIface a = it.next();
System.out.println(a.test());
}
System.out.println("DONE");
}
}
The interface and the last class are packaged in main.jar, the implementation in impl.jar.
main.jar has the Main class set and impl.jar has the META-INF/services/playground.TestIface file which contains "playground.TestImpl". Both JARs are in the same directory.
Running
java -jar main.jar
only prints "DONE", the implementation apparently is not found.
If I instead run
java -Djava.ext.dirs=. -jar main.jar
it also prints "TEST" as it should.
What am I doing wrong? Why is the implementation from the other JAR not loaded unless I change the java.ext.dirs setting?
The java.ext.dirs setting automatically adds all jar-files found in the specified directory to the main classloader, which is why the ServiceLoader can find and load TestIface.class (from the Apidocs: "Creates a new service loader for the given service type, using the current thread's context class loader.").
But you should not use java.ext.dirs for this (see here for one of the dangers). And when you use java -jar you cannot use java -cp to set a classpath (you can only use one of them). This leaves you the option to use the URLClassLoader to load additional jars and then call ServiceLoader.load(class from another jar).
Tip: to load other jar-files, use the location of the main.jar file as explained in the answers of this question. Other variables (like startup directory) depend on how and from where Java was started and can easily result in not finding the other jar-files.
When preparing for the SCJP exam, we were going through the following code:
package certificaton;
public class OtherClass
{
public void testIt()
{
System.out.println("otherclass");
}
}
And this:
package somethingElse;
import certification.OtherClass;
public class AccessClass
{
public static void main( String args[])
{
OtherClass o= new OtherClass();
o.testIt();
}
}
I placed both the above files in the following directory: C:\scjp\temp8 ; and the strange thing is that, the .java files are compiling and results in two .class files being created in the same directory. The thing I want to ask, is that, the difference between packages and directory. Isn't it true that the class files could be created in a directory other than the one stated in the package declaration? And the package declaration is something 'virtual', and disregards the windows directory structure. In addition, isn't it also true that, by executing the following command:
javac -d . OtherClass.java
The directories are created conforming to the package declaration, which isn't always mandatory?
The directories are created conforming
to the package declaration, which
isn't always mandatory?
No, the package and directory structures must match. It's mandatory, not optional.