This handout describes how to perform common Java development tasks in the Allen Center software labs with IntelliJ and the command line.
Contents:
A few CSE 331 tasks require connecting to a Linux machine and running commands from the command line. To understand the instructions may require some basic understanding of how to use a shell to perform command-line actions. This material is covered (in more depth than we need) in CSE391, which has materials posted online.
You should have already performed the initial setup. If you are working on your own computer or a CSE Linux Home VM, and you are working through the handouts in the recommended order, you should have installed IntelliJ. Else, it is installed on the department machines.
When importing up your project on IntelliJ, use the Version Control instructions to setup your project. You should pick Gradle as the external model you are importing the project from.
You may be able to click and icon or search for IntelliJ in your program launcher.
Otherwise, go to the directory where IntelliJ is installed and then go into the
bin
subdirectory. Then type the following command:
./idea.sh
IntelliJ will start up, display a splash screen, and then potentially show a settings import dialog:
IntelliJ is asking you whether to import settings. If you have used IntelliJ before, pick the first option and locate the settings. Otherwise, pick the second option.
From the start menu, search for IntelliJ.
IntelliJ will start up, display a splash screen, and then potentially show a settings import dialog:
IntelliJ is asking you whether to import settings. If you have used IntelliJ before, pick the first option and locate the settings. Otherwise, pick the second option.
We expect your code to not have any generics-related problems. For example, the following code is unacceptable:
List myList = new ArrayList(); myList.add("foo");
The generic type List
of myList
should be parametrized, for
instance, to String
by replacing the first line with
List<String> myList = new ArrayList<String>();
Note that
List<String> myList = new ArrayList();
is also incorrect.
By default, IntelliJ does not show generics problems as errors. You can run a generify tool to ensure that your code contains acceptable generics.
To make this configuration, go to Refactor » Generify..., update the preferences as required and click on Preview. Use this to ensure that all the changes being made are correct, and click Do Refactor.
Open the Project View in IntelliJ if you are not already using it (View » Tool Windows » Project).
You can open multiple files in IntelliJ by double-clicking each of them from the Project View pane. Once you have multiple files open, you can switch between them quickly by holding down Ctrl and hitting Tab to bring up a dropdown box of open files, and then using the arrow keys to select the file whose editor you wish to bring into focus. You can also navigate through different files using the tabs on top of the editor pane.
To create a new Java source file (with the .java
extension),
right click on the package you want to add the file to New »
Java Class. A window will pop up, asking you for the name of the class.
Choose a name for your class (e.g. MyClass
) Type this name in the
"Name" field and click Finish.
(If you want your new class to be executable, it will need a main
method.)
There is a similar procedure for creating new non-Java files such as text files.
Right click on the directory you want to add the file to
New » File. In the resulting dialog box, type the desired filename.
If you want to create a new directory, you can do so by appending the directory
name in front of your desired filename. For example, if you want to create a file
problem0.txt
in the directory hw1/answers
, but the
answers
directory does not yet exist, you can choose hw1
as
the parent directory, and then type answers/problem0.txt
as the
file name, and IntelliJ will create the new directory and file for you.
Here are some useful actions that you can perform when editing Java code:
Autocompletion is the ability of an editor to guess what you are typing after you type out only part of a word. Using autocompletion will reduce the amount of typing that you have to do as well as the number of spelling mistakes, thereby increasing your efficiency.
IntelliJ continuously parses your Java files as you are editing, so it is aware of the names of variables, methods, etc... that you have declared thus far.
CTRL+Space can be used to autocomplete most
things inside the IntelliJ Java editor. For example, if you have declared
a variable named spanishGreeting
in the current class,
and have typed the letters spanishGree
in a subsequent line,
IntelliJ can infer that you mean to type spanishGreeting
.
To use this feature, press CTRL+Space while your cursor is at the right of the incomplete
name. You
should see spanishGree
expand to spanishGreeting
.
IntelliJ can also help you autocomplete method names.
Suppose you have a variable myList
of type List
, and
you want to call the method clear
. Begin typing "myList.
"
— at this point, a pop-up dialog will display
a list of
available methods for the type List
, and you can select the
appropriate method with the arrow keys. You can force the popup to appear
with CTRL+Space.
You can press
CTRL+ALT+o to
organize your imports in a Java file.
IntelliJ will remove extraneous import
statements and try to
infer correct ones for types that you refer to in your code but
have not yet been imported.
(If the name of the class that needs to be imported is
ambiguous – for example, there is a java.util.List
as
well as a java.awt.List
– then IntelliJ will prompt you
to choose which one to import.)
Although you can directly browse the Java 11 API and other documentation at the Oracle website, it is often useful to be able to cross-reference parts of your code with the appropriate documentation from within your editor.
Note that you need to generate the API documentation locally before you can view docs for classes in the assignment. Use the javadoc task on gradle to do the same.
IntelliJ provides multiple ways to look at documentation of code directly from the IDE. Their website contains documentation on how to do this.
If you are adding documentation of code that is written by you, you will
need to prepend the file location with file:///
.
Gradle is a build system, which automates development tasks.
To invoke Gradle, run the command ./gradlew
in directory
~/cse331-19sp-YourCSENetID
.
The most common command is ./gradlew build
.
It runs the "build" task, which checks your program for common errors, compiles your program, runs tests, and generates documentation.
Using a build system like gradle is better than you having to remember to run each of those tasks separately.
Run ./gradlew tasks
to see a list of other tasks you can use.
To run Gradle from the command line:
cd ~/cse331-19sp-YourCSENetID ./gradlew target
In the above, replace target with the name of the task you want gradle to run.
If you use Windows, change ./gradlew
to gradlew.bat
.
A timeout error such as "read timed out", or "Could not resolve all artifacts",
indicates
a network connection problem. Try your command again.
If that does not work, then add the --offline
command-line option to your ./gradlew
command.
If you get
Could not create service of type ScriptPluginFactory using BuildScopeServices.createScriptPluginFactory().
and rerunning the command gives the same error, then email support@cs, pasting in the exact command you ran (including the directory you ran it from) and the exact output.
If you get
Execution failed for task ':clean'. > Unable to delete file: ...
then run ./gradlew cleanByRenaming
If Gradle hangs and produces no output for a long time, run the following command
kill -9 $(lsof -t +D ~/.gradle) && rm -rf ~/.gradle
(which itself may take many seconds, but generally less than a minute, to run) and try again. If the command fails, you can run this instead:
mv ~/.gradle ~/.gradle-DELETEME
To run Gradle in IntelliJ there are 2 different tool windows you could run it from:
View > Tool Windows > Gradle
)
You can select the task you want to run under each folder. For example,
the command ./gradlew build
on the command line
is equivalent to selecting build > build
in the tool window.
For this class, most of the tasks you are able to run are under
build
, validation
, and other
.
You can also run the task you want by clicking on the icon, and typing in the name of the task you would like to execute.
You will be able to run most ./gradlew
comannd line targets on
this terminal.
You must compile your source code before running it. The javac
compiler is used to transform Java programs into bytecode form, contained
in a class file. Class files are recognized by their .class
extension. The bytecode in class files can be executed by the java
interpreter.
To compile all source files:
cd ~/cse331-19sp-YourCSENetID ./gradlew build
This will run a Gradle script to compile
all the .java
files into corresponding
.class
files. Note that if one or more of your files do not
compile, you will receive error messages and no .class
files will be generated for the files that do not compile properly.
IntelliJ is set up by default to automatically recompile your code every time you save. Classes with compile errors are marked in the Project View with a red underline.
If your file is saved and IntelliJ says that it does not compile but you believe that it should, make sure that all of the files on which your file depends are saved and compiled. If that does not work, try refreshing your project or using Build » Rebuild Project to force IntelliJ to recognize the latest versions of everything.
Once you have compiled your source code into class files, you can execute it with the Java interpreter.
Typically, to run a program you will just type ./gradlew
on the command
line, possibly with a more specific target: ./gradlew target
.
To run a program, right click on the Java source file containing the
main()
method and choose Run 'FILENAME' where
FILENAME is the file with the main()
method.
There is also a button near the top-right-hand side of the IntelliJ window that looks like which will re-run the last application (or JUnit test, see below) that you ran.
JUnit is the testing framework that you will use for writing and running tests.
For more information, visit:
You can run JUnit from several different environments:
To compile your code and run tests:
./gradlew build
JUnit is normally integrated with IntelliJ, but since we are using Gradle, we must tell IntelliJ to delegate work to Gradle.
File > Settings
for Windows and Linux or
IntelliJ IDEA > Preferences
for macOS
Build, Execution, Deployment > Build Tools > Gradle >
Runner
Now, if you run any tests, it will use Gradle to run it. To run any specific test file:
You can also run a specific test file from the command line using
the --tests
flag, such as in the following commands:
./gradlew test --tests packageName.SomeTestClass.someTestMethod ./gradlew test --tests *SomeTestClass.someTestMethod ./gradlew test --tests *SomeTestClass
The first two commands above will run a specific test method in a specific test class that you choose. The last one will run all the tests in a particular file.
To run all of the tests in your test suit, select Verification > test
in the IntelliJ Gradle tool window.
A test result window should pop up near the bottom of the screen
momentarily and run all the tests. Once the tests are done, you'll see a report
of which tests failed and why:
On the left, you can see a list of all tests classes run (in this case,
only setup.FibonacciTest
), as well as all test methods run within that
class (in this case, there are four methods in that class). You may need to toggle the two
buttons in the top left (marked with a blue box in the image) if you can't see all the tests -
these buttons control which tests will be shown in the list below.
At the top-center of the window (indicated in the image by a red box) you can see a summary
of all the tests run and how many succeeded/failed.
You can see the status of each individual test on the left. In this example, the tests
testThrowsIllegalArgumentException
and testInductiveCase
failed normally (the yellow "X") - that is, they failed because
one or more of the requirements of the test were not met. By contrast,
testBaseCase
failed abnormally (the red "!"). This usually happens when
an uncaught exception is thrown during the test, as is the case for this example.
expectedIllegalArgumentException
was the only test that passed. You can click once
on any of the tests to see more details about why a test failed (and any System.out.print
or similar output generated by the test). If a test failed abnormally, you can also see any relevant
stack traces. These details show up in the right area of the window. In this example,
testThrowsIllegalArgumentException
is selected and we can see (highlighted by a red box
in the image) a better description of what went wrong during the test.
You can also double-click on any of the tests in the left area to jump to that part of the testing code. Click the play button in the top left of the testing window to re-run all the tests shown, or the button right below it (play button on top of a red exclamation point) to re-run just the tests that failed.
Because your JUnit tests will likely have different class and method names
than those of your classmates, there needs to be a standardized way
of accessing every student's tests. Thus, each assignment comes with
the JUnit test classes SpecificationTests
and
ImplementationTests
. You will load all
the JUnit tests you wrote in one of these two test suites.
SpecificationTests
, as it name suggests, should
contain only specification tests — that is, those tests that
check only for features implied by the specification. Consequently,
your specification tests should be valid tests for any other person's code
that claims to satisfy the same specification, even if that implementation
is inherently very different.
Conversely, ImplementationTests
should contain
implementation tests — that is, those tests that test only
details that are specific to your implementation.
As an example, suppose you were implementing the following specification:
/** Frobs the blarghnik. * @spec.requires b != null */ public void frob(Blarghnik b);
A specification test should never pass in a null parameter to this method — this would violate the specified pre-condition. However, your particular implementation might check for the null parameter and throw a NullPointerException. Your implementation test can safely exercise this case by passing in null.
Similarly, an iterator specification which does not specify the order in which elements are returned indicates that no specific order should be assumed in a specification test. Your implementation may happen to keep elements in a sorted list, and so your implementation test may wish to check that the elements returned by the iterator are sorted.
Before your submit each assignment, you should run tests and validate
your assignment with ./gradlew build
.
The javadoc tool produces API documentation (in HTML form) from source code annotated with special comments.
To run the Javadoc tool, issue this command:
./gradlew javadocYou can find the generated API documentation, in HTML form, under
build/docs/javadoc
.
If IntelliJ fails due to unknown tags, you will need to add the tags to IntelliJ.
Your Java code comments contain "tags", which are introduced by an at-sign (@). The Javadoc tool formats these specially in the HTML output.
We have extended the javadoc tool to recognize additional CSE 331 tags, in adition to the standard tags. These additional tags declare specification fields for classes and requires, modifies, and effects clauses for methods. Note that all Javadoc tags must appear after all non-tag comments for classes and methods.
@spec.specfield name : T // text | Indicates that name is a abstract specification field of type T for the class, adding text as a comment if present |
@spec.derivedfield name : T // text | Same as specfield, except that this also adds the property "derived" to the output information |
Derived fields can be viewed as functions on preexisting state; thus
if a class had a specfield @spec.specfield n : integer
we could define a
derived field:
@spec.derivedfield pos : boolean // pos = true iff n > 0
Derived fields are not allowed to hold any information that could not be already calculated from the already existing state in the object. Thus, you use specfields to introduce new state variables and derived fields to introduce functions on those state variables.
Derived fields are not strictly needed in specifications, but they may reduce complexity and redundancy.
@spec.requires X | Declares X to be a precondition for the method |
@spec.modifies Y | Declares that nothing besides Y will be modified by the method (as long as X holds when it is invoked) |
@spec.effects Z | Declares that Z will hold at exit from the method (as long as X holds when it is invoked) |
You may need te specify the path to the git
executable:
In the Settings/Preferences dialog (Ctrl+Alt+S), select Version Control | Git in the left pane.
It is bad style to have tab characters in code files, because they display differently for different people.
(Pressing the Tab key while using your editor is great, but that key should should insert spaces.)
In IntelliJ IDEA, make sure that the
Editor >> Code Style >> Default Indent Options : Use tab character
checkbox is not selected.
IntelliJ will warn you about unrecognized the 331 Javadoc tags (@spec.*
).
There are two ways to eliminate the warnings.
File > Settings
for Windows and Linux or
IntelliJ IDEA > Preferences
for macOS
Editor > Inspections > Java > Javadoc
and uncheck the
box next to "Declaration has Javadoc problems".
Using the keyboard shortcut:
Alt + Enter
.
Using the settings panel
Options >
Additional Javadoc Tags
and add all of the extended tags.
After you have done one of the above, the warnings for the extended tags will go away.
Optionally, you could install the Google Java Format plugin to IntelliJ that will reformat your code using the google-java-format tool.
File > Settings
for Windows and Linux or
IntelliJ IDEA > Preferences
for macOS
Plugins
If the staff provides a new version of a library, IntelliJ will not automatically know about the library's new symbols (classes and methods). As a result, IntelliJ will not do completion and may issue warnings about them. To resolve the problem:
git pull
./gradlew build