Creating a Package
To create a package, you choose a name for the package (naming conventions are discussed in the next section) and put a package statement with that name at the top of every source file that contains the types (classes, interfaces, enumerations, and annotation types) that you want to include in the package.
The package statement (for example, package graphics; ) must be the first line in the source file. There can be only one package statement in each source file, and it applies to all types in the file.
Note: If you put multiple types in a single source file, only one can be public , and it must have the same name as the source file. For example, you can define public class Circle in the file Circle.java , define public interface Draggable in the file Draggable.java , define public enum Day in the file Day.java , and so forth.
You can include non-public types in the same file as a public type (this is strongly discouraged, unless the non-public types are small and closely related to the public type), but only the public type will be accessible from outside of the package. All the top-level, non-public types will be package private.
If you put the graphics interface and classes listed in the preceding section in a package called graphics , you would need six source files, like this:
If you do not use a package statement, your type ends up in an unnamed package. Generally speaking, an unnamed package is only for small or temporary applications or when you are just beginning the development process. Otherwise, classes and interfaces belong in named packages.
Пакеты Java – назначение и использование
Пакеты Java – это механизм для группировки классов, которые связаны друг с другом, в одну и ту же «группу» (пакет). Когда проект становится больше, например, приложение или API, полезно разделить код на несколько классов, а классы – на несколько пакетов. Тогда становится легче выяснить, где находится определенный класс, который вы ищете.
Пакет подобен каталогу в файловой системе. На самом деле на диске он является каталогом. Все исходные файлы и файлы классов, принадлежащих одному и тому же пакету, находятся в одном каталоге.
Могут содержать подпакеты. Таким образом, могут составлять так называемую структуру пакета, похожую на структуру каталогов. Это дерево пакетов, подпакетов и классов внутри этих классов. Организована как каталоги на вашем жестком диске или как каталоги внутри zip-файла (JAR-файлы).
Вот скриншот примера структуры:

Вверху вы видите каталог с именем “src”. Это исходный корневой каталог. Это не сам пакет. Внутри этого каталога все подкаталоги соответствуют пакетам. Таким образом, «коллекции», «com», «параллелизм» и т. д. – это все пакеты (которые также являются каталогами на диске). На снимке экрана выше они показаны значком папки.
Расширено два пакета подуровня, чтобы вы могли видеть классы внутри. Классы проиллюстрированы с помощью маленького синего круга с буквой C внутри, на скриншоте выше.
Полный путь к подпакету – это его имя со всеми именами пакетов-предков, разделенных точками. Например, полный путь к «навигационному» подпакету:
Точно так же полное имя класса включает имя его пакета. Например, полное имя класса «Page»:
Создание структуры
Чтобы создать пакет, вы должны сначала создать корневой каталог на вашем жестком диске. Он сам по себе не является частью структуры пакета. Содержит все исходные коды, которые должны войти в структуру.
Создав исходный корневой каталог, вы можете начать добавлять в него подкаталоги. Каждый подкаталог соответствует пакету. Вы можете добавить подкаталоги в подкаталоги, чтобы создать более глубокую структуру.
Добавление классов
Чтобы добавить классы, вы должны сделать две вещи:
- Поместите исходный файл в каталог, соответствующий пакету, в который вы хотите поместить класс.
- Объявите этот класс как часть пакета.
Первый пункт довольно прост. Создайте корневой каталог источника и внутри него создайте каталоги для каждого пакета и подпакета рекурсивно. Поместите файлы классов в каталог, соответствующий пакету, в который вы хотите добавить его.
Когда вы поместили свой исходный файл в правильный каталог (соответствующий пакету, к которому должен принадлежать класс), вы должны объявить внутри этого файла класса, что он принадлежит этому пакету:
Первая строка в приведенном выше коде – это то, что объявляет класс Page принадлежащим к com.blog.navigation.
Соглашения об именах
Пакеты всегда пишутся строчными буквами. В отличие от классов, где первая буква обычно является заглавной.
Чтобы избежать создания пакетов с такими же именами, как у других общедоступных, рекомендуется начинать иерархию с обратного доменного имени вашей компании. Например, поскольку доменное имя компании – blog.com, надо начать со структуры с именем com.blog. Другими словами, пакет верхнего уровня с именем com с подпакетом внутри называется blog.
Импорт
Если класс A должен использовать класс B, вы должны ссылаться на класс B внутри класса A. Если классы A и B находятся в одном и том же пакете, компилятор будет принимать ссылки между двумя классами:
Если классы A и B находятся в одном и том же пакете, проблем с кодом выше нет. Однако, если класс A и B находятся в разных, класс A должен импортировать класс B, чтобы использовать его:
Это первая строка в примере, которая импортирует класс B. В примере предполагается, что класс B находится в пакете с именем anotherpackage.
Если бы класс B находился в подпакете другого пакета, вам пришлось бы перечислить полный путь пакета и подпакета к классу B. Например, если бы класс B находился в пакете anotherpackage.util, то оператор import выглядел бы так:
Импорт всех классов из другого пакета
Если вам нужно использовать много классов из определенного пакета, их импорт по одному приводит к большому количеству операторов импорта. Можно импортировать все классы, используя символ * вместо имени класса:
Использование классов через определенное имя
Можно использовать класс из другого пакета, не импортируя его с помощью оператора импорта. Вы можете написать полное имя его, а не просто имя самого класса. Полное имя класса состоит из полного пути пакета до подкласса, содержащего класс, а также самого имени класса. Полное имя класса – это то, что вы написали бы в операторе импорта. Например:
Вы можете использовать это полное имя класса для ссылки на класс TimeUtil внутри другого класса, например так:
Пакетное разделение
Официального стандарта для этого нет, но есть два широко используемых метода.
Разделить на слои
Первый метод состоит в том, чтобы разделить классы после определения, к какому «слою» приложения они принадлежат. Например, ваше приложение может иметь слой базы данных. Тогда вы создадите пакет базы данных. Все классы, участвующие в обмене данными с базой данных, будут расположены в нем.
Разделить по функциональности приложения
Второй метод – разделить ваши классы в зависимости от того, к какой части функциональности приложения они принадлежат. Таким образом, если ваше приложение имеет функциональную область, которая рассчитывает пенсии, вы можете создать пакет с именем pension. Все классы, так или иначе участвующие в пенсионных расчетах, будут включены в него (или подпакеты, если число классов в пенсии станет большим).
В сочетании с доменным именем структура для пенсионного пакета будет:
Всего три пакета, два вложенных в другие.
Метод «деления по функциональности приложения» имеет тенденцию работать лучше, чем «деление по слоям», поскольку в вашем приложении растет число классов.
Вместо того, чтобы иметь фиксированное количество пакетов слоев, число которых продолжает расти, вы получаете растущее число пакетов функциональности приложения с меньшим количеством классов внутри.
Встроенные
Платформа поставляется с множеством встроенных пакетов. Они содержат классы для самых разных целей, которые часто нужны программистам, например, чтение и запись файлов с локального жесткого диска, отправка и получение данных по сетям и Интернету, подключение к базам данных и многое, многое другое.
Packages in Java: How to Create and Use Packages in Java?
One of the most innovative Packages in Java are a way to encapsulate a group of classes, interfaces, enumerations, annotations, and sub-packages. Conceptually, you can think of java packages as being similar to different folders on your computer. In this tutorial, we will cover the basics of packages in features of Java is the concept of packages. Java.
Listed below are the topics covered in this article:
- What is Package in Java?
2. Built-in Packages
3. User Defined Packages
- Creating a Package in Java
- Including a Class in Java Package
- Creating a class inside package while importing another package
- Using fully qualified name while importing a class
4. Static Import in Java
5. Access Protection in Java Packages
6. Points to Remember
What is Package in Java?
Java package is a mechanism of grouping similar types of classes, interfaces, and sub-classes collectively based on functionality. When software is written in the Java programming language, it can be composed of hundreds or even thousands of individual classes. It makes sense to keep things organized by placing related classes and interfaces into packages.
Using packages while coding offers a lot of advantages like:
- Re-usability: The classes contained in the packages of another program can be easily reused
- help us to uniquely identify a class, for example, we can have Name Conflicts: Packages company.sales.Employee and company.marketing.Employee classes
- Controlled Access: Offers access protection such as protected classes, default classes, and private class
- Data Encapsulation: They provide a way to hide classes, preventing other programs from accessing classes that are meant for internal use only
- Maintainance: With packages, you can organize your project better and easily locate related classes
It’s a good practice to use packages while coding in Java. As a programmer, you can easily figure out the classes, interfaces, enumerations, and annotations that are related. We have two types of packages in java.
Types of Packages in Java
Based on whether the package is defined by the user or not, packages are divided into two categories:
- Built-in Packages
- User-Defined Packages
Built-in Packages
Built-in packages or predefined packages are those that come along as a part of JDK (Java Development Kit) to simplify the task of Java programmer. They consist of a huge number of predefined classes and interfaces that are a part of Java API’s. Some of the commonly used built-in packages are java.lang, java.io, java.util, java.applet, etc. Here’s a simple program using a built-in package.
Output:
The ArrayList class belongs to java.util package. To use it, we have to import the package using the import statement. The first line of the code import java.util.ArrayList imports the java.util package and uses ArrayList class which is present in the subpackage util.
User-Defined Packages
User-defined packages are those which are developed by users in order to group related classes, interfaces, and sub-packages. With the help of an example program, let’s see how to create packages, compile Java programs inside the packages, and execute them.
Creating a Package in Java
Creating a package in Java is a very easy task. Choose a name for the package and include a package command as the first statement in the Java source file. The java source file can contain the classes, interfaces, enumerations, and annotation types that you want to include in the package. For example, the following statement creates a package named MyPackage.
The package statement simply specifies to which package the classes defined belongs to.
Note: If you omit the package statement, the class names are put into the default package, which has no name. Though the default package is fine for short programs, it is inadequate for real applications.
Including a Class in Java Package
To create a class inside a package, you should declare the package name as the first statement of your program. Then include the class as part of the package. But, remember that, a class can have only one package declaration. Here’s a simple program to understand the concept.
As you can see, I have declared a package named MyPackage and created a class Compare inside that package. Java uses file system directories to store packages. So, this program would be saved in a file as and will be stored in the directory named MyPackage. When the file gets compiled, Java will create a Compare.java .class file and store it in the same directory. Remember that name of the package must be same as the directory under which this file is saved.
You might be wondering how to use this Compare class from a class in another package?
Creating a class inside a package while importing another package
Well, it’s quite simple. You just need to import it. Once it is imported, you can access it by its name. Here’s a sample program demonstrating the concept.
Output:
I have first declared the package Edureka, then imported the class Compare from the package MyPackage. So, the order when we are creating a class inside a package while importing another package is,
- Package Declaration
- Package Import
Well, if you do not want to use the import statement, there is another alternative to access a class file of the package from another package. You can just use fully qualified name while importing a class.
Using fully qualified name while importing a class
Here’s an example to understand the concept. I am going to use the same package that I have declared earlier in the blog, MyPackage.
Output:
In the Demo class, instead of importing the package, I have used a fully qualified name such as MyPackage.Compare to create the object of it. Since we are talking about importing packages, you might as well check out the concept of static import in Java.
Static Import in Java
Static import feature was introduced in Java from version 5. It facilitates the Java programmer to access any static member of a class directly without using the fully qualified name.
Though using static import involves less coding, overusing it might make the program unreadable and unmaintainable. Now let’s move on to the next topic, access control in packages.
Access Protection in Java Packages
You might be aware of various aspects of Java’s access control mechanism and its access specifiers. Packages in Java add another dimension to access control. Both classes and packages are a means of data encapsulation. While packages act as containers for classes and other subordinate packages, classes act as containers for data and code. Because of this interplay between packages and classes, Java packages addresses four categories of visibility for class members:
- Sub-classes in the same package
- Non-subclasses in the same package
- Sub-classes in different packages
- Classes that are neither in the same package nor sub-classes
The table below gives a real picture of which type access is possible and which is not when using packages in Java:
We can simplify the data in the above table as follows:
- Anything declared public can be accessed from anywhere
- Anything declared private can be seen only within that class
- If access specifier is not mentioned, an element is visible to subclasses as well as to other classes in the same package
- Lastly, anything declared protected element can be seen outside your current package, but only to classes that subclass your class directly
This way, Java packages provide access control to the classes. Well, this wraps up the concept of packages in Java. Here are some points that you should keep in mind when using packages in Java.
Points to Remember
- Every class is part of some package. If you omit the package statement, the class names are put into the default package
- A class can have only one package statement but it can have more than one import package statements
- The name of the package must be the same as the directory under which the file is saved
- When importing another package, package declaration must be the first statement, followed by package import
Well, this brings us to the end of this ‘Packages in Java’ article. We learned what a package is and why we should use them. There is no doubt that the Java package is one of the most important parts for efficient java programmers. It not only upgrades the programmer’s coding style but also reduces a lot of additional work. If you wish to check out more articles on the market’s most trending technologies like Artificial Intelligence, DevOps, Ethical Hacking, then you can refer to Edureka’s official site.
Do look out for other articles in this series which will explain the various other aspects of Java.
Java Packaging HOWTO
Before you start reading, please make sure that you’re reading the version corresponding to the oldest Fedora release you want to package for. We don’t generally backport new features to versions available in stable Fedora.
Current version: 24
Authors and contributing
The authors of this document are:
Mikolaj Izdebski, Red Hat
Nicolas Mailhot, JPackage Project
Stanislav Ochotnicky, Red Hat
Ville Skyttä, JPackage Project
Michal Srb, Red Hat
This document is free software; see the license for terms of use, distribution and/or modification.
The source code for this document is available in git repository. Instructions for building it from sources are available in README file.
This document is developed as part of Javapackages community project, which welcomes new contributors. Requests and comments related to this document should be reported at Red Hat Bugzilla.
Before contributing please read the README file, which among other things includes basic rules which should be followed when working on this document. You can send patches to Red Hat Bugzilla. They should be in git format and should be prepared against master branch in git. Alternatively you can also send pull requests at Github repository.
Abstract
This document aims to help developers create and maintain Java packages in Fedora. It does not supersede or replace Java Packaging Guidelines, but rather aims to document tools and techniques used for packaging Java software on Fedora.
1. Introduction
Clean Java packaging has historically been a daunting task. Lack of any standard addressing the physical location of files on the system combined with the common use of licensing terms that only allow free redistribution of key components as a part of a greater ensemble has let to the systematic release of self-sufficient applications with built-in copies of external components.
As a consequence applications are only tested with the versions of the components they bundle, a complete Java system suffers from endless duplication of the same modules, and integrating multiple parts can be a nightmare since they are bound to depend on the same elements — only with different and subtly incompatible versions (different requirements, different bugs). Any security or compatibility upgrade must be performed for each of those duplicated elements.
This problem is compounded by the current practice of folding extensions in the JVM itself after a while; an element that could safely be embedded in a application will suddenly conflict with a JVM part and cause subtle failures.
It is not surprising then that complex Java systems tend to fossilize very quickly, with the cost of maintaining dependencies current growing too high so fast people basically give up on it.
This situation is incompatible with typical fast-evolving Linux platform. To attain the aim of user- and administrator-friendly RPM packaging of Java applications a custom infrastructure and strict packaging rules had to be evolved.
1.1. Basic introduction to packaging, reasons, problems, rationale
This section includes basic introduction to Java packaging world to people coming from different backgrounds. Goal is to understand language of all groups involved. If you are a Java developer coming into contact with RPM packaging for the first time start reading Java developer section. On the other hand if you are coming from RPM packaging background an introduction to Java world is probably a better starting point.
It should be noted that especially in this section we might sacrifice correctness for simplicity.
1.2. For Packagers
Java is a programming language which is usually compiled into bytecode for JVM (Java Virtual Machine). For more details about the JVM and bytecode specification see JVM documentation.
1.2.1. Example Java Project
To better illustrate various parts of Java packaging we will dissect simple Java “Hello world” application. Java sources are usually organized using directory hierarchies. Shared directory hierarchy creates a namespace called package in Java terminology. To understand naming mechanisms of Java packages see Java package naming conventions.
Let’s create a simple hello world application that will execute following steps when run:
Print out “Hello World from” and the name from previous step
To illustrate certain points we artificially complicate things by creating:
Input class used only for input of text from terminal
Output class used only for output on terminal
HelloWorldApp class used as main application
In this project all packages are under src/ directory hierarchy.
Although the directory structure of our package is hierarchical, there is no real parent-child relationship between packages. Each package is therefore seen as independent. Above example makes use of three separate packages:
Environment setup consists of two main parts:
Telling JVM which Java class contains main() method
Adding required JAR files on JVM classpath
The sample project can be compiled to a bytecode by Java compiler. Java compiler can be typically invoked from command line by command javac .
For every .java file corresponding .class file will be created. The .class files contain Java bytecode which is meant to be executed on JVM.
One could put invocation of javac to Makefile and simplify the compilation a bit. It might be sufficient for such a simple project, but it would quickly become hard to build more complex projects with this approach. Java world knows several high-level build systems which can highly simplify building of Java projects. Among the others, probably most known are Apache Maven, Apache Ant and Gradle.
See also Maven and Ant sections.
Having our application split across many .class files wouldn’t be very practical, so those .class files are assembled into ZIP files with specific layout called JAR files. Most commonly these special ZIP files have .jar suffix, but other variations exist ( .war , .ear ). They contain:
Compiled bytecode of our project
Additional metadata stored in META-INF/MANIFEST.MF file
Resource files such as images or localisation data
Optionaly the source code of our project (called source JAR then)
They can also contain additional bundled software which is something we don’t want to have in packages. You can inspect the contents of given JAR file by extracting it. That can be done with following command:
The detailed description of JAR file format is in the JAR File Specification.
The classpath is a way of telling JVM where to look for user classes and 3rd party libraries. By default, only current directory is searched, all other locations need to be specified explicitly by setting up CLASSPATH environment variable, or via -cp ( -classpath ) option of a Java Virtual Machine.
Please note that two JAR files are separated by colon in a classpath definition.
See official documentation for more information about classpath.
Classic compiled applications use dynamic linker to find dependencies (linked libraries), Python interpreter has predefined directories where it searches for imported modules. JVM itself has no embedded knowledge of installation paths and thus no automatic way to resolve dependencies of Java projects. This means that all Java applications have to use wrapper shell scripts to setup the environment before invoking the JVM and running the application itself. Note that this is not necessary for libraries.
1.2.2. Build System Identification
Build system used by upstream can be usually identified by looking at their configuration files, which reside in project directory structure, usually in its root or in specialized directories with names such as build or make .
Build managed by Apache Maven is configured by an XML file that is by default named pom.xml . In its simpler form it usually looks like this:
It describes project’s build process in a declarative way, without explicitly specifying exact steps needed to compile sources and assemble pieces together. It also specifies project’s dependencies which are usually the main point of interest for packagers. Other important feature of Maven that packagers should know about are plugins. Plugins extend Maven with some particular functionality, but unfortunately some of them may get in the way of packaging and need to be altered or removed. There are RPM macros provided to facilitate modifying Maven dependencies and plugins.
Apache Ant is also configured by an XML file. It is by convention named build.xml and in its simple form it looks like this:
Ant build file consists mostly of targets, which are collections of steps needed to accomplish intended task. They usually depend on each other and are generally similar to Makefile targets. Available targets can be listed by invoking ant -p in project directory containing build.xml file. If the file is named differently than build.xml you have to tell Ant which file should be used by using -f option with the name of the actual build file.
Some projects that use Apache Ant also use Apache Ivy to simplify dependency handling. Ivy is capable of resolving and downloading artifacts from Maven repositories which are declaratively described in XML. Project usually contains one or more ivy.xml files specifying the module Maven coordinates and its dependecies. Ivy can also be used directly from Ant build files. To detect whether the project you wish to package is using Apache Ivy, look for files named ivy.xml or nodes in the ivy namespace in project’s build file.
Gradle is configured by a script written in Groovy (dynamic language built on top of JVM and extending Java’s syntax). It’s configuration file is build.gradle .
While unlikely, it’s still possible that you encounter a project whose build is managed by plain old Makefiles. They contain a list of targets which consist of commands (marked with tab at the begining of line) and are invoked by make target or simply make to run the default target.
1.2.3. Quiz for Packagers
At this point you should have enough knowledge about Java to start packaging. If you are not able to answer following questions return back to previous sections or ask experienced packagers for different explanations of given topics.
What is the difference between JVM and Java?
What is a CLASSPATH environment variable and how can you use it?
Name two typical Java build systems and how you can identify which one is being used
What is the difference between java and javac comands?
What are contents of a typical JAR file?
What is a pom.xml file and what information it contains?
How would you handle packaging software that contains lib/junit4.jar inside source tarball?
Name at least three methods for bundling code in Java projects
1.3. For Java Developers
Packaging Java software has specifics which we will try to cover in this section aimed at Java developers who are already familiar with Java language, JVM, classpath handling, Maven, pom.xml file structure and dependencies.
Instead we will focus on basic packaging tools and relationships between Java and RPM world. One of the most important questions is: What is the reason to package software in RPM (or other distribution-specific formats). There are several reasons for it, among others:
Unified way of installation of software for users of distribution regardless of upstream projects
Verification of authenticity of software packages by signing them
Simplified software updates
Automatic handling of dependencies for users
Common filesystem layout across distribution enforced by packaging standards
Ability to administer, monitor and query packages installed on several machines through unified interfaces
Distribution of additional metadata with the software itself such as licenses used, homepage for the project, changelogs and other information that users or administrators can find useful
1.3.1. Example RPM Project
RPM uses spec files as recipes for building software packages. We will use it to package example project created in previous section. If you didn’t read it you don’t need to; the file listing is available here and the rest is not necessary for this section.
We packed the project directory into file helloworld.tar.gz .
RPM spec files contain several basic sections:
Package metadata such as its name, version, release, license…
A Summary with basic one-line summary of package contents
Package source URLs denoted with Source0 to SourceN directives
Source files can then be referenced by %SOURCE0 to %SOURCEn , which expand to actual paths to given files
In practice, the source URL shouldn’t point to a file in our filesystem, but to an upstream release on the web
Patches — using Patch0 to PatchN
Build dependencies specified by BuildRequires , which need to be determined manually.
Run time dependencies will be detected automatically. If it fails, you have to specify them with Requires .
More information on this topic can be found in Dependency handling section.
Few sentences about the project and its uses. It will be displayed by package management software.
Unpacks the sources using setup -q or manually if needed
If a source file doesn’t need to be extracted, it can be copied to build directory by cp -p %SOURCE0 .
Apply patches with %patchX , where X is the number of patch you want to apply. (You usually need the patch index, so it would be: %patch0 -p1 )
Contains project compilation instructions. Usually consists of calling the projects build system such as Ant, Maven or Make.
Runs projects integration tests. Unit test are usually run in %build section, so if there are no integration tests available, this section is omitted
Copies built files that are supposed to be installed into %
Lists all files, that should be installed from %
Documentation files are prefixed by %doc and are taken from build directory instead of buildroot
Directories that this package should own are prefixed with %dir
Contains changes to this spec file (not upstream)
Has prescribed format. To prevent mistakes in format, it is advised to use tool such as rpmdev-bumpspec from package rpmdevtools to append new changelog entries instead of editing it by hand
To build RPM from this spec file save it in your current directory and run rpmbuild :
If everything worked OK, this should produce RPM file
/rpmbuild/RPMS/x86_64/helloworld-1.0-1.fc18.x86_64.rpm . You can use rpm -i or yum localinstall commands to install this package and it will add /usr/share/java/helloworld.jar and /usr/bin/helloworld wrapper script to your system. Please note that this specfile is simplified and lacks some additional parts, such as license installation.
Paths and filenames might be slightly different depending on your architecture and distribution. Output of the commands will tell you exact paths
As you can see to build RPM files you can use rpmbuild command. It has several other options, which we will cover later on.
Except building binary RPMs ( -bb ), rpmbuild can also be told to:
build only source RPMs (SRPMs), the packages containing source files which can be later build to RPMs (option -bs )
build all, both binary and source RPMs (option -ba )
See rpmbuild ‘s manual page for all available options.
1.3.2. Querying repositories
Fedora comes with several useful tools which can provide great assistance in getting information from RPM repositories.
repoquery is a tool for querying information from YUM repositories. Maintainer of Java packages might typically query the repository for information like «which package contains Maven artifact groupId:artifactId «.
The example above shows that one can get to commons-io:commons-io artifact by installing apache-commons-io package.
By default, repoquery uses all enabled repositories in YUM configuration, but it is possible to explicitly specify any other repository. For example following command will query only Rawhide repository:
Sometimes it may be useful to just list all the artifacts provided by given package:
Output above means that package apache-commons-io provides two Maven artifacts: previously mentioned commons-io:commons-io and org.apache.commons:commons-io . In this case the second one is just an alias for same JAR file. See section about artifact aliases for more information on this topic.
Another useful tool is rpm . It can do a lot of stuff, but most importantly it can replace repoquery if one only needs to query local RPM database. Only installed packages, or local .rpm files, can be queried with this tool.
Common use case could be checking which Maven artifacts provide locally built packages.
1.3.3. Quiz for Java Developers
How would you build a binary RPM if you were given a source RPM?
What is most common content of Source0 spec file tag?
What is the difference between Version and Release tags?
How would you apply a patch in RPM?
Where on filesystem should JAR files go?
What is the format of RPM changelog or what tool would you use to produce it?
How would you install an application that needs certain layout (think ANT_HOME) while honoring distribution filesystem layout guidelines?
How would you generate script for running a application with main class org.project.MainClass which depends on commons-lang jar?
2. Java Specifics in Fedora for Users and Developers
This section contains information about default Java implementation in Fedora, switching between different Java runtime environments and about few useful tools which can be used during packaging/development.
2.1. Java implementation in Fedora
Fedora ships with reference implementation of Java Standard Edition 7 called OpenJDK. OpenJDK provides Java Runtime Environment for Java applications and set of development tools for Java developers.
From users point of view, java command is probably most interesting. It’s a Java application launcher which spawns Java Virtual Machine (JVM), loads specified .class file and executes its main method.
Here is an example how to run sample Java project from section 1.1.1:
OpenJDK provides a lot of interesting tools for Java developers:
javac is a Java compiler which translates source files to Java bytecode, which can be later interpreted by JVM.
jdb is a simple command-line debugger for Java applications.
javadoc is a tool for generating Javadoc documentation.
javap can be used for disassembling Java class files.
2.1.1. Switching between different Java implementations
Users and developers may want to have multiple Java environments installed at the same time. It is possible in Fedora, but only one of them can be default Java environment in system. Fedora uses alternatives for switching between different installed JREs/JDKs.
Example above shows how to chose default Java environment. java command will then point to the Java implementation provided by given JRE.
See man alternatives for more information on how to use alternatives .
Developers may want to use Java compiler from different JDK. This can be achieved with alternatives —config javac .
2.2. Building classpath with build-classpath
Most of the Java application needs to specify classpath in order to work correctly. Fedora contains several tools which make working with classpaths easier.
build-classpath — this tool takes JAR filenames or artifact coordinates as arguments and translates them to classpath-like string. See following example:
log4j corresponds to log4j.jar stored in % <_javadir>. If the JAR file is stored in subdirectory under % <_javadir>, it’s neccessary to pass subdirectory/jarname as an argument to build-classpath . Example:
2.3. Building JAR repository with build-jar-repository
Another tool is build-jar-repository . It can fill specified directory with symbolic/hard links to specified JAR files.Similarly to build-classpath , JARs can be identified by their names or artifact coordintes.
Similar command rebuild-jar-repository can be used to rebuild JAR repository previously built by build-jar-repository . See man rebuild-jar-repository for more information.
build-classpath-directory is a small tool which can be used to build classpath string from specified directory.
3. Java Specifics in Fedora for Packagers
WORK IN PROGRESS mizdebsk, 25 Oct 2013
3.1. Directory Layout
This section describes most of directories used for Java packaging. Each directory is named in RPM macro form, which shows how it should be used in RPM spec files. Symbolic name is followed by usual macro expansion (i.e. physical directory location in the file system) and short description.
Directory that holds all JAR files that do not contain or use native code and do not depend on a particular Java standard version. JAR files can either be placed directly in this directory or one of its subdirectories. Often packages create their own subdirectories there, in this case subdirectory name should match package name.
Directory where architecture-specific JAR files are installed. In particular JAR files containing or using native code (Java Native Interface, NI) should be installed.
Root directory where all Java API documentation (Javadoc) is installed. Each source package usually creates a single subdirectory containing aggregated Javadocs for all binary packages it produces.
Directory where Project Object Model (POM) files used by Apache Maven are installed. Each POM must have name that strictly corresponds to JAR file in % <_javadir>or % <_jnidir>.
Root directory where different Java Virtual Machines (JVM) are installed. Each JVM creates a subdirectory, possibly with several alternative names implemented with symbolic links. Directories prefixed with java contain Java Development Kit (JDK), while directories which names start with jre hold Java Runtime Environment (JRE).
Directories containing configuration files for Java Virtual Machines (JVM).
% — /usr/lib/jvm-exports % — /usr/lib/jvm-private % — /usr/lib/jvm % — /usr/share/jvm % — /usr/lib/jvm-common % — /usr/share/jvm-common
Directories containing implementation files of Java Virtual Machines (JVM). Describing them in detail is out of scope for this document. Purpose of each directory is commented briefly in macros.jpackage file in /etc/rpm . More detailed description can be found in JPackage policy.
Directory containing Java configuration files. In particular it contains main Java configuration file — java.conf .
These directories are rarely used and are retained mostly for compatibility with JPackage project. They are meant to contain JAR files that depend on particular Java standard version. Note that such JARs can (and usually are) placed in % <_javadir>or % <_jnidir>.
3.2. JAR File Identification
Complex Java applications usually consist of multiple components. Each component can have multiple implementations, called artifacts. Artifacts in Java context are usually JAR files, but can also be WAR files or any other kind of file.
There are multiple incompatible ways of identifying (naming) Java artifacts and each build system often encourages usage of specific naming scheme. This means that Linux distributions also need to allow each artifact to be located using several different identifiers, possible using different schemes. On the other hand it is virtually impossible to every naming scheme, so there are some simplifications.
This chapter describes artifact different ways to identify and locate artifacts in system repository.
3.2.1. Relative paths
JAR artifacts are installed in one of standard directory trees. Usually this is either % <_javadir>( /usr/share/java ) or % <_jnidir>( /usr/lib/java ).
The simplest way of identifying artifacts is using their relative path from one of standard locations. All artifact can be identified this way because each artifacts has a unique file name. Each path identifying artifact will be called artifact path in this document.
To keep artifact paths simpler and more readable, extension can be omitted if it is equal to jar . For non-JAR artifacts extension cannot be omitted and must be retained.
Additionally, if artifact path points to a directory then it represents all artifacts contained in this directory. This allows a whole set of related artifacts to be referenced easily by specifying directory name containing all of them.
If the same artifact path has valid expansions in two different root directories then it is unspecified which artifacts will be located.
3.2.2. Artifact specification
As noted in previous section, every artifact can be uniquely identified by its file path. However this is not always the preferred way of artifact identification.
Modern Java build systems provide a way of identifying artifacts with an abstract identifier, or more often a pair of identifiers. The first if usually called group ID or organization ID while the second is just artifact ID. This pair of identifiers will be called artifact coordinates in this document. Besides group ID and artifact ID, artifact coordinates may also include other (optional) information about artifact, such as extension, classifier or version.
In Linux distributions it’s important to stay close to upstreams providing software being packaged, so the ability to identify artifacts in the same way as upstream does is very important from the point of view of packaging. Every artifact can optionally be identified by artifact coordinates assigned during package build. Packages built with Maven automatically use this feature, but all other packages, even these built with pure javac , can use this feature too (see description of %mvn_artifact and %add_maven_depmap macros).
3.2.3. Aliases
Aliases working in two ways:
Symlinks for paths
Additional mappings for artifact specifications
WORK IN PROGRESS msimacek, 31 March 2015
In the real world the same project can appear under different names as it was evolving or released differently. Therefore other projects may refer to those alternative names instead of using the name currently prefered by upstream.
Artifact aliases
XMvn provides a way to attach multiple artifact coordinates to a single artifact. Dependent projects that use alternative coordinates can then be built without the need to patch their POMs or alter the build by other means. It will also generate virtual provides for the alias, so it can be also used in Requires and BuildRequires. Creating an alias is achieved by %mvn_alias macro.
3.2.4. Compatibility versions
Handling of compatibility packages, versioned jars etc.
WORK IN PROGRESS msimacek, 2015-04-02
In Fedora we prefer to always have only the latest version of a given project. Unfortunately, this is not always possible as some projects change too much and it would be too hard to port dependent packages to the current version. It’s not possible to just update the package and keep the old version around as the names, file paths and dependency provides would clash. The recommended practice is to update the current package to the new version and create new package representing the old version (called compat package). The compat package needs to have the version number (usually only the major number, unless further distinction is necessary) appended to the name, thus effectivelly having different name from RPM’s point of view. Such compat package needs to perform some additional steps to ensure that it can be installed and used along the non-compat one.
You should always evaluate whether creating a compat package is really necessary. Porting dependent projects to new versions of dependencies may be a compicated task, but your effort would be appreciated and it’s likely that the patch will be accepted upstream at some point in time. If the upstream is already inactive and the package is not required by anything, you should also consider retiring it.
Maven Compat Versions
XMvn supports marking particular artifact as compat, performing the necessary steps to avoid clashes with the non-compat version. An artifact can be marked as compat by %mvn_compat_version . It accepts an artifact argument which will determine which artifact will be compat. The format for specifying artifact coordinates is the same as with %mvn_alias . In the common case you will want to mark all artifacts as compat. You can specify multiple compat versions at a time.
When XMvn performs dependency resolution for a dependency artifact in a project, it checks the dependency version and compares it against all versions of the artifact installed in the buildroot. If none of the compat artifacts matches it will resolve the artifact to the non-compat one. This has a few implications:
The versions are compared for exact match. The compat package should provide all applicable versions that are present in packages that are supposed to be used with this version.
The dependent packages need to have correct BuildRequires on the compat package as the virtual provides is also different (see below).
In order to prevent file name clashes, compat artifacts have the first specified compat version appended to the filename. Virtual provides for compat artifacts also contain the version as the last part of the coordinates. There are multiple provides for each specified compat version. Non-compat artifact don’t have any version in the virtual provides.
3.3. Dependency Handling
WORK IN PROGRESS msimacek, 2015-04-09
Describe RPM Provides and Requires, their interactions especially with relation to JAR file identification, explain mvn(gId:aId…) strings.
RPM has multiple types of metadata to describe dependency relationships between packages. The two basic types are Requires and Provides. Requires denote that a package needs something to be present at runtime to work correctly and the package manager is supposed to ensure that requires are met. A single Requires item can specify a package or a virtual provide. RPM Provides are a way to express that a package provides certain capability that other packages might need. In case of Maven packages, the Provides are used to denote that a package contains certain Maven artifact. They add more flexibility to the dependency management as single package can have any number of provides, and they can be moved across different packages without breaking other packages’ requires. Provides are usually generated by automatic tools based on the information from the built binaries or package source.
The Java packaging tooling on Fedora provides automatic Requires and Provides generation for packages built using XMvn. The Provides are based on Maven artifact coordinates of artifacts that were installed by the currenlty being built. They are generated for each subpackage separately. They follow a general format mvn(groupId:artifactId:extension:classifier:version) , where the extension is omitted if it’s jar and classifier is omitted if empty. Version is present only for compat artifacts, but the trailing colon has to be present unless it’s a Jar artifact with no classifier.
The generated Requires are based on dependencies specified in Maven POMs in the project. Only dependencies with scope set to either compile , runtime or not set at all are used for Requires generation. Requires don’t rely on package names and instead always use virtual provides that were described above, in exactly the same format, in order to be satisfieable by the already existing provides. For packages consisting of multiple subpackages, Requires are generated separately for each subpackage. Additionally, Requires that point to an artifact in a different subpackage of the same source package are generated with exact versions to prevent version mismatches between artifacts belonging to the same project.
The requires generator also always generates Requires on java-headless and javapackages-tools .
If the package is built built using different tool than Apache Maven, but still ships Maven POM(s), the you will still get automatic provides generation if you install the POM using %mvn_artifact and %mvn_install . The requires generation will also be executed but the outcome largely depends on whether the POM contains accurate dependency insformation. If it contains dependency information, you should double check that it’s correct and up-to-date. Otherwise you need to add Requires tags manually as described in the next section.
For packages without POMs it’s necessary to specify Requires tags manually. In order to build the package you needed to specify BuildRequires tags. You Requires tags will therefore likely be a subset of your BuildRequires , excluding build tools and test only dependencies.
The generated Requires and Provides of built packages can be queried using rpm :