1

I'm trying to import a self made package into a Java project. I got it to work once with some test class. So, when I tried to change it to an official, approved class name, the compiling stopped working. I can't explain why it worked, nor why the changes made it stop working.

This is super annoying. So, I've been digging for a few days.

I've search over a dozen posts here, plus many other sites, and cannot find an explanation of how this is supposed to be structured.

Finally I gutted everything and put together this example that should work according to everything I've searched, but it doesn't. It is a stripped down version of something that should import my own package and call a function. Minuscule code showing what I am trying to do, and what is failing.

In this minuscule example, I am building a package, as "com.company.functions" with a MyFunctions.java. All of one function inside to demonstrate it.

I do not have a classpath set up in my environment. Only a path to the JDK binaries. I do that so I can keep control and understanding at the command line level.

The "package" is located in folder JavaPackage. The folders are:

JavaPackage\
    com\
        company\
            functions\  (the MyFunctions.java is here)
    classes\

I compile it fine.

C:\JavaPackages\JavaPackage>javac -d classes -classpath classes com\company\functions\*.java

I create the JAR file fine

C:\JavaPackages\JavaPackage>jar cvf mypackage.jar  classes\com\company\functions\
added manifest
adding: classes/com/company/functions/(in = 0) (out= 0)(stored 0%)
adding: classes/com/company/functions/MyFunctions.class(in = 285) (out= 220)(deflated 22%)

I look in the JAR with 7-Zip,and everything looks fine. (I compared this 7-Zip autopsy to the package that was working, as mentioned at the beginning of this post, and the class names all line up correctly, from what I can understand. Everything looks correct)

Now, I am creating a test program. Called, for lack of a better name, Java_Test.

Java_Test\
    TestProgram\  (source files here)
    classes\

I move my jar file to Java_Test\classes

C:\JavaPackages\Java_Test>dir classes
11/11/2022  10:16 AM             1,325 mypackage.jar

I have two files in Java_Test\TestProgram: start.java and Test.java. Start is just the location of the static main, and it invokes the Test class. That's not any issue. It's the following compile failure.

I attempt to compile with

C:\JavaPackages\Java_Test>javac -d classes -cp classes  TestProgram\*.java

Which should specify that the output *.class files go into the folder "classes", and that the class path to import things is in the (same) folder "classes"

I get the following error

TestProgram\Test.java:3: error: package com.company.functions does not exist
import com.company.functions.*;
^

Well, it does exist. I can see it right there in the classes folder.

Maybe it's got something to do with the JAR name. Who knows? I can't find a good explanation of how this is supposed to work, so I even rebuilt the JAR file using the main name of the class: "functions"

So, now I have two JAR files of different names, but their contents are exactly the same. I figure the compiler should find one of them. The one it needs.

C:\JavaPackages\Java_Test>dir classes
11/11/2022  10:28 AM             1,325 functions.jar
11/11/2022  10:16 AM             1,325 mypackage.jar

However, the Java compiler still refuses to see it.

Can some please explain what is going on? This is frustrating, and making no sense. Since I come from the C/C++ world, linking to a lib is easy. Yet this package concept in Java is a confusing nightmare.

The full source files are below, not that it makes any difference, because it's the package that can't be found.

This is what is in MyFunctions.java for the package file

package com.company.functions;
public class MyFunctions {
    public int SomthingToDo() {
    int x = 1;
    return 0;
    }
}

Test program that should call the function from the package. Except fails at line 2

package TestProgram;
import com.company.functions.*; 
import java.io.*;

public class Test  {        
    public void Run() {
        m_functions = new MyFunctions();
        m_functions.SomthingToDo();
        System.out.println("Exiting");
    }
    private MyFunctions m_functions;
}

For your reading enjoyment, this is start.java, which is not significant to my issue:

package TestProgram;
public class start {
    public static void main(String args[]) {
        m_test = new Test();
        m_test.RunScanner();
    }
static private Test m_test;
}
Mark Rotteveel
  • 100,966
  • 191
  • 140
  • 197
SpacemanScott
  • 953
  • 9
  • 21
  • "Any advice is appreciated." - use Apache Maven or Gradle. Honestly. Btw.: package names are expected to be lowercase. – Christoph Dahlen Nov 11 '22 at 17:37
  • @ChristophDahlen Use a tool that obfuscates and doesn't let you understand: Not good advice from my experience. I've had enough difficulty with Android Studio, CCS, Eclipse, and others hiding what's going on. I intend to understand this. Also, the "package" is lowercase, I made sure of that. The classes in the package aren't. The test shell (test.java) isn't going to in package so I blew that off as irrelevant. And the same error occurs even without the package declaration in the test shell. – SpacemanScott Nov 11 '22 at 18:00
  • 1
    Full marks for posting text and not images. I can therefore copy and paste and correct `C:\JavaPackages\JavaPackage>jar cvf mypackage.jar -C classes com\company\functions` You could put the main class in there and make an executable jar with `cvfe`. e.g. `C:\JavaPackages\JavaPackage>jar cvfe mypackage.jar com.company.functions.Main -C classes com\company\functions` – g00se Nov 11 '22 at 18:11
  • You probably don't have a main class, but imagine you did have one ;) – g00se Nov 11 '22 at 18:17
  • @g00se Thx. However, the final goal is we have to provide this package to an outside customer. It's hundreds of lines of code. So it's their executable that has to "link" to it (to use the C vernacular) – SpacemanScott Nov 11 '22 at 18:18
  • That's fine. Just ignore the bit about the main class – g00se Nov 11 '22 at 18:28
  • @g00se If the additional command line arg `-C` was supposed to be significant, it didn't help. I used it to regenerate hte package, moved it, and I am still getting a **package does not exist error** – SpacemanScott Nov 11 '22 at 18:41
  • Got to leave now but you could usefully post jar tf ... – g00se Nov 11 '22 at 18:43
  • 1
    The JAR is not on the classpath. The JAR file itself must be on the classpath, Having it within a directory that is on the classpath is not sufficient. – Mark Rotteveel Nov 12 '22 at 10:14

2 Answers2

3

I create the JAR file fine

C:\JavaPackages\JavaPackage>jar cvf mypackage.jar  classes\com\company\functions\
added manifest
adding: classes/com/company/functions/(in = 0) (out= 0)(stored 0%)
adding: classes/com/company/functions/MyFunctions.class(in = 285) (out= 220)(deflated 22%)

It looks like it will place the files inside the JAR file in a sub directory classes/. The package path should begin at the root directory of the JAR file, not inside other directories like classes/. The ClassLoader will not be able to find the classes if you do. Create the JAR file in a way, that the .class files are in directories like com/company/functions/MyFunctions.class. Then the ClassLoader will be able to find the class com.company.functions.MyFunctions by checking the JAR file directory com/company/functions/.

I attempt to compile with

C:\JavaPackages\Java_Test>javac -d classes -cp classes  TestProgram\*.java

You have not specified the JAR file in your classpath. Java will not look randomly inside any JAR file. See How do I add jar files to the Classpath? on how to add a JAR file to the classpath. You might want to use something like

C:\JavaPackages\Java_Test>javac -d classes -cp classes\mypackage.jar  TestProgram\*.java

(with the fixed issue about the content of the JAR file above)

But you might need to add the current directory . to your classpath as well to find your other java files or classes, which are not in the JAR file.

Progman
  • 16,827
  • 6
  • 33
  • 48
  • Yes! Thanks. First time it worked for me on the big project, I created the package using the same classes folder. It appears I got away without specifying the package name because all the classes were in there too (doh!) In this next case, using a different classpath and it couldn't find it. Also, creating the JAR, I found I can use the -C option to change into the classes folder. Live and learn... A LOT! – SpacemanScott Nov 13 '22 at 04:11
1
goose@t410:/tmp/src$ find
.
./start.java
./MyFunctions.java
./classes
./Test.java
===========================================================================================
goose@t410:/tmp/src$ cat start.java 
package testprogram;

public class start {
    public static void main(String args[]) {
        m_test = new Test();
        //m_test.RunScanner();
        m_test.Run();
    }

    static private Test m_test;
}

===========================================================================================
goose@t410:/tmp/src$ head -n 1 *.java
==> MyFunctions.java <==
package com.company.functions;

==> start.java <==
package testprogram;

==> Test.java <==
package testprogram;
===========================================================================================
goose@t410:/tmp/src$ javac -d classes *.java
===========================================================================================
goose@t410:/tmp/src$ find
.
./start.java
./MyFunctions.java
./classes
./classes/testprogram
./classes/testprogram/Test.class
./classes/testprogram/start.class
./classes/com
./classes/com/company
./classes/com/company/functions
./classes/com/company/functions/MyFunctions.class
./Test.java
===========================================================================================
goose@t410:/tmp/src$ jar cvf functions.jar -C classes com/company/functions
added manifest
adding: com/company/functions/(in = 0) (out= 0)(stored 0%)
adding: com/company/functions/MyFunctions.class(in = 285) (out= 219)(deflated 23%)
goose@t410:/tmp/src$ 
===========================================================================================
goose@t410:/tmp/src$ jar cvfe mypackage.jar testprogram.start -C classes testprogram
added manifest
adding: testprogram/(in = 0) (out= 0)(stored 0%)
adding: testprogram/Test.class(in = 567) (out= 380)(deflated 32%)
adding: testprogram/start.class(in = 382) (out= 273)(deflated 28%)
===========================================================================================
goose@t410:/tmp/src$ java -cp mypackage.jar:functions.jar testprogram.start
Exiting

You can ignore any commands that are not java,javac or jar when you run them yourself. Other commands are informational. NB, you need to get your naming right. Two points: a) you'll notice I had to correct an error preventing the main class from running, and b) you'll need to use a semicolon instead of a colon for the java command on Windows. You'll notice that the whole classpath was needed for the java command. If you want to be able to use the -jar parameter, you'll need to use your own manifest. e.g.:

jar cvf functions.jar -C classes com/company/functions
================================================================================
(Creation of manifest - Unixes:)
================================================================================
echo -e "Main-Class: testprogram.start\nClass-Path: functions.jar" >manifest.txt
cat manifest.txt
================================================================================
(Creation of manifest - Windows: [untested])
================================================================================
echo  "Main-Class: testprogram.start" & echo "Class-Path: functions.jar" >manifest.txt
type manifest.txt
================================================================================
jar cvfm mypackage.jar manifest.txt -C classes testprogram
================================================================================
java -jar mypackage.jar
g00se
  • 3,207
  • 2
  • 5
  • 9