Skip to content
This repository has been archived by the owner on Aug 27, 2024. It is now read-only.
Mateusz Kubuszok edited this page Jun 20, 2013 · 8 revisions

JAppJUp is a project of a Client and Server applications able to download and install updates for some software.

Assumptions

  • project consist of Client and Server,
  • Server should be created with Spring Framework and Hibernate technologies,
  • Client is responsible for connecting to Server, obtaining information about available updates, their download and installation,
  • Client should be accessible from tray icon. Its menu should make it possible to install all updates or update-and-run some specified program,
  • information about updates is modeled as follows:
    • Programs - top level aggregation by programs for which updates are prepared,
    • Packages - aggregates specified part of a Program - submodule, library, subprogram etc.,
    • Updates - updates for each Package - each has information about whether it's intended for "development" or "release" version, as well as version number and changes description,
  • Programs should also have information about known bugs.

Basic setup

This section covers basic setup for building and development. For configuration see Client configuration and Server configuration. For usage see Client usage and Server usage.

Checkout

Currently source code of AutoUpdater is located at SVN.

Initial configuration

Building require presence of Gradle at version 1.0. Before first build You should make sure that proxy definition in /build.gradle is configured properly and that internet connection is available:

  • open build.gradle in Updater's main directory,
  • at the beginning of file there will be lines like:
    • System.properties['http.proxyHost'] = 'something'
    • System.properties['http.proxyPort'] = 'something'
  • if they are commented out, uncomment them and change "something" to the right values of host and port for Your configuration.

Configuration of Servier is located in /AutoUpdaterServer/src/main/webapp/WEB-INF/classes. Hibernate.properties and JDBC.properties are used for database configuration and should be review before building and deploying WAR file to server. ServerRepoApplication.properties contains informaton about places where Updates are stored on server. messages.properties and ValidationMessages.properties stores messages displayed in browser. Before building it for deployment make sure that configuration files contain proper values.

More details:

  • Server configuration - how to configure server, so that WAR file built by Gradle will be ready to be deployed.

Building project

All Gradle commands have to be executed in Updater's main directory.

Both Client and Server can be build with gradle build command. If You only want to build one of them, use:

  • gradle buildClient (shortcut for :AutoUpdaterGuiClient:build)
  • gradle buildServer (shortcut for :AutoUpdaterServer:build)

respectively.

Results will be stored in /Dist directory. It should be noted that currently builder only creates .jar files (for Client) and WAR file (for Server). While WAR file can be immediately deployed to any J2EE server (e.g. Tomcat via its AppManager), Client is only a runnable JAR.

For ensuring that Client can be run without heavy reliance on Java installation, it can be wrapped with launch4j. Prepared configuration is available in /Misc/Launch4j directory - it contains configuration for Client.jar -> Client.exe and Installer.jar -> Installer.exe, as well as manifest and icon. They should be edited to run executable JARs with portable JRE stored in ./jre directory. Launch4j configuration can be edited via executable provided with launch4j package.

In future prepared Client instance can be distributed with Inno Setup installer.

More details:

Eclipse configuration

To import projects to Eclipse, run gradle eclipse task, and then import all projects into workspace. Settings used during development can be found in /Misc/EclipseSettings:

  1. build projects for Eclipse with gradle eclipse,
  2. open Eclipse and select option "import existing projects into workspace",
  3. if You feel like it, create "Client" and "Server" working sets and place projects there accordingly,
  4. select option "import preferences" and import preferences from /Misc/EclipseSettings/settings.ebf.

Notice that sometimes there might appear empty warning - it can be ignored, as it Eclipse validator error.

Tests

Tests can be found inside /test/unit or /test/functional directories of a project, and have "Test" word asa class name prefix. They use JUnit4 as well as Mockito and FESTAssertions libraries.

To run test we can simply use default Eclipse configuration ("Run as/JUnit Test"), or make use of Gradle. Command gradle test runs all tests, while gradle :ProjectName:test only tests for a specified project.

More details:

By default all building commands (gradle build, gradle buildClient, gradle buildServer) runs tests. To build everything without testing You should use gradle build -x test.

Unfortunately not everything is yet covered by tests.

!!! Important !!! While testing application from under Eclipse in settings.xml we should change path to Installer.exe to absolute path. Since path are resolved in relation to current position of a Client.exe when project is run from Eclipse it will fail to find it in the same directory as *.class binaries. Setting path to absolute will solve that problem. In release version though path should be set back to relative (.) to ensure that no matter where Updater will be placed, it will manage to find Installer.exe.

Other helpful Gradle commands

Same as with building these commands have to be executed in Updater's main directory:

  • gradle tasks - displays task that we can build for Updater,
  • gradle tasks --all - displays all task - including task per project (e.g. AutoUpdaterServer:test),
  • gradle javadoc - generates documentation basing on Javadoc block comments (besides GUI project, majority of classes and methods is documented),
  • gradle dependencies - displays dependencies. While root project doesn't have any, gradle :ProjectName:dependencies shows all required libraries and projects.

Client configuration

This section covers only configuration of a Client. For building see Building project. For usage see Client usage.

Client's settings are stored in AutoUpdater withing local AppData directory (e.g. user_directory/AppData/Local/AutoUpdater on Windows 7). Information is divided into to files:

  • settings.xml - containing information about Proxy, location of Installer.exe, and programs that will be updated,
  • installationData.xml - contains information about current installations: what packages are installed for each of programs defined in settings.xml, and what are their versions.

settings.xml is created during startup of program if program cannot find it, while installationData.xml after first successful installation (after each successful installation information in file is updated).

Simple editor of settings can be started with Client.exe --config command, though its currently very simple, as it can only add new program (neither edition nor deletion works). One might also try to edit settings.xml manually, though it's not recommended.

Prepared settings.xml can be initiated by some installer to ensure that end user wouldn't need to enter data manually.

Exemplary settings.xml file with explanation

<?xml version="1.0" encoding="UTF-8"?>
<configuration> <!-- (1) -->
    <client <!-- (2) -->
        name="AutoUpdater" <!-- (3) -->
        executable="Client.jar"> <!-- (4) -->
        <locations <!-- (5) -->
            clientDir="." <!-- (6) -->
            console="java -jar .\Client.jar" <!-- (7) -->
            installer=".\Installer.exe" /> <!-- (8) -->
    </client>
    <programs> <!-- (9) -->
        <program <!-- (10) -->
            name="Program_9_2" <!-- (11) -->
            executableName="C:\Program Files (x86)\Nokia Siemens Networks\Program\9.2\Program.exe" <!-- (12) -->
            programDir="C:\Program Files (x86)\Nokia Siemens Networks\Program\9.2" <!-- (13) -->
            console="&quot;C:\Program Files (x86)\Nokia Siemens Networks\Program\9.2\Program.exe&quot;" <!-- (14) -->
            serverURL="http://10.154.46.119:8080/Program_repo" <!-- (15) -->
            developmentVersion="false" /> <!-- (16) -->
    </programs>
</configuration>
  • (1) configuration is root of a settings.xml file. It contains two direct descendants: (2) client and (9) programs,
    • (2) client contains general information about update process and the updater itself. It doesn't have any information program-specific,
      • (3) name was intended to contains Client's name recognized on repository. It would have been during update of the program itself, but that functionality wasn't introduced yet. Currently it isn't used anywhere besides tests,
      • (4) executable was intended to contain name by which spawned process would kill Updater via JSDPU. Same situation as above,
      • (5) locations stores information about some locations used by updater,
        • (6) clientDir stores information about relative/absolute path to the client directory. By default it point to . (current directory), but for test purpose it can be hardcoded to point at some specific directory,
        • (7) console contains information about command that can be used to startup Client after it was updated. Same as (2) and (3),
        • (8) installer points to location of the Installer.jar/Installer.exe file. During normal use it can be simply set to ., but for test purpose (running Updater under Eclipse) it should point to absolute location of an existing Installer.exe, since it cannot resolve its location another way,
    • (9) programs contain information specific to each managed program,
      • (10) program node with contains all settings for a single program,
        • (11) name stores program's name identified by server,
        • (12) executableName stores location of a program, that can be used during its termination by JSDPU (substring inside initialization command of a process that we intend to terminate. Exact executable name is usually enough to identify process, and differ it from other processes with the same name.exe),
        • (13) programDir contains absolute path to a program. All relative paths during update will be calculated in relation to this directory,
        • (14) console contains exact command that would be typed to console to startup program, including arguments and/or launchers. It should be noted that if some part of that command would have to be wrapped in quotation marks (e.g. path to program containing spaces), it has to be wrapped here as well (&amp;quot; for XML),
        • (15) serverURL address of a repository from which information and updates should be obtained. Since used procedures cannot resolve redirecting there should be typed final URL and not shortened,
        • (16) developmentVersion if true updates should be downloaded for "development version", if "false" they will be obtained for "release version".

Exemplary installationData.xml file with explanation

<?xml version="1.0" encoding="UTF-8"?>
<installed> <!-- (1) -->
    <program <!-- (2) -->
        name="Program_trunk" <!-- (3) -->
        pathToDirectory="C:\Program Files (x86)\Nokia Siemens Networks\Program\trunk" <!-- (4) -->
        serverAddress="http://10.154.46.119:8080/Program_repo"> <!-- (5) -->
        <package <!-- (6) -->
            name="Installation" <!-- (7) -->
            version="9.2.4.10317" /> <!-- (8) -->
    </program>
</installed>
  • (1) installed is a root containing all programs that have installed at least one package,
    • (2) program contains information about installed packages for a specified program,
      • (3) name contains program name on server - first of 3 properties uniquely identifying program for Updater. Has to match program/name property in settings.xml,
      • (4) pathToDirectory contains path to program's directory
        • second of 3 properties uniquely identifying program for Updater. Has to match program/programDir property in settings.xml,
      • (5) serverAddress contains address of a program's repository - third of 3 properties uniquely identifying program for Updater. Has to match program/serverURL property in settings.xml,
      • (6) package contains information about single package of a program,
        • (7) name contains name of an installed package. Has to match package name on server,
        • (8) version contains version number of current installation of a package.

Program uniqueness definition

Notice that program's name, installation directory and repository uniquely identify program. It means that:

  • at the same directory we CAN keep 2 installations from 2 different repositories or with 2 different name on 1 repository (e.g. one providing main app, while the other plugins),
  • we CAN keep several installations of the same program (e.g. one being development while the other being release version),
  • we CANNOT have installed development and release version of the same program in one directory.

Server configuration

This section covers only configuration of a Server. For building see Building project. For installation and usage see Server usage.

Servers configuration is stored within src/main/webapp/WEB-INF/classes directory:

  • repository-specific properties not used by dependent libraries:
    • ServerRepoApplication.properties - contains settings mostly related to storing Updates on disc and mapping of resources used by site (e.g. CSS and JS files),
  • properties used by libraries managing DB connection and ORM:
    • JDBC.properties - contains settings related to handling connection to DB by JDBC drivers,
    • Hibernate.properties - contains settings related to Hibernate (logging, event handling),
  • messages used for displaying information to user:
    • messages.properties - contains messages displayed to used, that are obtained by thei identifier,
    • ValidationMessages.properties - contains messages resolved and displayed by Spring validators,
  • logging:
    • log4j.properties - contains settings about loggins used by different packages.

ServerRepoApplication.properties explanation

# Resources properties

# Address to which resources should be mapped
resources.mapping=/resources/** # (1)
# Locations where resources should be searched
resources.locations=/resources/ # (2)
# Cache period of resources
resources.cachePeriod=10000 # (3)


# Storage properties

# Mapping of directory storing uploaded files
# Available prefixes:
# "home:" => System.getProperty("user.home")
# "run:"  => System.getProperty("user.dir")
storage.mapping=C:\\UploadStorage # (4)


# MulitpartResolver properties

# Maximal size of uploaded file in bytes
multipartResolver.maxUploadSize=268435456 # (5)
  • (1) resources.mapping defines which URIs should be mapped to local resources. It shouldn't be changed,
  • (2) resources.locations defines local directories containing mapped resources. It shouldn't be changed,
  • (3) resources.cachePeriod defines length of a cache period for resources,
  • (4) storage.mapping defines local directory that will store updates. After migration to another server, content of this folder should be copied and mapping updated to a new location,
  • (5) multipartResolver.maxUploadSize maximal allowed size of file. Anything above that will result in failure.

JDBC.properties explanation

# Defines driver used by JDBC
jdbc.driver=org.postgresql.Driver # (1)
# Defines URL connectiong to DB, usually jdbc:dbtype://address/schemaname
jdbc.url=jdbc:postgresql://localhost:5432/updater # (2)
# Defines username and password of an user connecting to DB
jdbc.user=[DB username] # (3)
jdbc.password=[DB password] # (4)
  • (1) jdbc.driver full name of a driver used to connecting to DB,
  • (2) jdbc.url URL in JDBC format (jdbc:[DB type]://[server address]/[name of schema in DB]),
  • (3) jdbc.user contains username used during connection to DB,
  • (4) jdbc.password contains password used during connection to DB.

Hibernate.properties explanation

# Hibernate properties

# Defines drivers used to connect to DB
hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect # (1)
# Defines whether or not update DB on SessionFactory creation,
# Possible values:
#  - validate - validates the schema of DB,  
#  - update - updates the schema of existing DB,  
#  - create - destroys old and creates new schema,
#  - create-drop - drop schema at the end of session,
hibernate.hbm2ddl.auto=update # (2)
# Defines BLOB treating in Hibernate
hibernate.jdbc.use_streams_for_binary=true # (3)
hibernate.connection.SetBigStringTryClob=true # (4)
# Maximal allowed JDBC batches
hibernate.jdbc.batch_size=0 # (5)
# Defines options regarding logging
hibernate.show_sql=true # (6)
hibernate.format_sql=true # (7)
hibernate.use_sql_comments=true # (8)


# Javax Persistance properties

# Defines validation mode
javax.persistence.validation.mode=NONE # (9)
  • (1) hibernate.dialect defines dialect used by Hibernate to communicate with DB. It has to be checked after each change of DB,
  • (2) hibernate.hbm2ddl.auto defines behavior on SessionFactory creation - it can be used to ensure that DB schema match mapping defined by model,
  • (3) hibernate.jdbc.use_streams_for_binary whether to use streams while uploading binary data to DB,
  • (4) hibernate.connection.SetBigStringTryClob whether use CLOB to handle big streams,
  • (5) hibernate.jdbc.batch_size defined the batch size. It is used to merge several queries into one to improve performance,
  • (6) hibernate.show_sql whether or not show SQL in logs (it is safe since dump omits values used to parametrize query),
  • (7) hibernate.format_sql whether SQL dump should be formatted to be more readable,
  • (8) hibernate.use_sql_comments whether SQL dumps should have comments,
  • (9) javax.persistence.validation.mode - Spring Framework binding provides model validation. Since they aren't modified after receiving from user it is not needed to validate them again. Hibernate 4.1 doesn't have event listeners that would have been used to initiate services injected into validators, so in case validator has to connect to DB, action would result in NullPointerException when attempting to use uninitialized service (Checking with DB is the only way to ensure uniqueness of some field without handling runtime SQLException).

Messages

Messages are obtained by their ID via <spring:message code="message_id" /> from messages.properties.

Validators use their own properties file ValidationMessages.properies. They are obtained via constraint annotations, by message() property: String message() default "{name_of_validation_message}";.

log4j

See log4j.properties to obtain more information (it is nicely documented were necessary).

Client usage

This section covers actual usage of the Client. For building see Building project. For configuration see Client configuration.

After building in directory /Dist/Client we have several JAR files (each for one project) and ./libraries catalog containing required dependencies.

Starting Client

File Client.jar contains manifest.mf that makes it possible to use it as a runnable JAR. Since some of System properties depend on place from which we startup VM, it is required to either run in directly from directory, or ensure that shourtcut has "Start in" path same to directory containing Updater. If we start program from command line it is javaw -jar Client.jar (if we use "java" instead of "javaw" console window appear besides GUI window).

launch4j make it possible to set up current directory to the JAR's one, and then it can be run safely from any location.

Basics

Right after startup client will appear in tray as a blue icon. Single left click will open Updater's management window, while single right click open tray menu.

From tray menu we are able to update-and-start one of programs defined in settings.xml, fetch update information again, or start/cancel all available updates installation.

Inside management window we can fetch updates information, start/cancel installation of all updates and check current status of each managed program by selecting tab with its name (first two tabs though are used to manage Updater).

Each program's tab make it possible to check:

  • version number of each package,
  • changelogs,
  • known bugs.

Logs

Logging is managed by properties client.logger.properties and installer.logger.properties. There level of logging can be defined as in net.jsdpu.logger.Level for each class/package.

Results will be written to client.log and installer.log.

Server usage

This section covers actual usage of the Server. For building see Building project. For configuration see Server configuration.

!!! Notice !!! - IE in compatibility mode messes up with page's CSS - make sure to disable it for repository, if You browse it with IE.

Installation

  1. check what kind of DB is used, its location, schema, and credentials of the that will connect to it,
  2. if necessary modify JDBC.properties and Hibernate.properties accordingly,
  3. select directory that will store updates on server and set it ServerRepoApplication.properties,
  4. build WAR with gradle buildServer (run command in Updater's main directory),
  5. deploy it to the server,
    1. in Tomcat's case open server's main page,
    2. select "Manage app" and log in,
    3. undeploy /Program_repo if it is there,
    4. deploy Program_repo.war by form below - make sure that WAR has exactly that name, since Tomcat doesn't allow to specify name of subsite deployed by file, other way that by WAR's name,
  6. application should initiate existing DB schema with required tables,
  7. since at least 1 administrator is required to manage it, we need to initiate tables with one. Authentication manager uses BCrypt, so we need to ensure that password is in supported format - it can be done with help of AutoUpdaterServer/sql files - they initiate all the tables, but we can easily extract the part that only creates "root" user,
  8. check if You can log with default credentials ([root], [password]),
  9. change password to something else. Link to the form can be found on the top bar.

Manual management

Logged user can manage repository according to his privileges:

  • user without any can view projects, known bugs, packages and updates; he can download updates; he cannot display other users list,
  • user with "Admin" privilege can display and manage other users - create and delete them, and change their privileges; he cannot though change password of other users,
  • user with "Repo Admin" privilege can add, edit and delete programs, known bugs, packages and updates.

Quick file upload

Not logged users can use quick file upload form. It makes possible to:

  1. check credentials,
  2. check privileges,
  3. validate all data, in one go. It is required though that program and package for which we are adding update exists, update cannot be already added to repository and user must exist and have privileged to add update.

It is used by script /Misc/PythonUploadScript/uploader.py as a public upload API.

TO DO

Improvement desirable

  • refactoring and improvement of JSDPU library,
  • replacing manual parsing of XML with Jaxb schemes,
  • moving hardcoded messages into properties,
  • more clean notifications,
  • replacing of custom logger with log4j or similar,
  • improvement of interface in repository.

Known bugs

  • Updater runs program as child process, resulting in VM waiting with exit to ending all programs started by it. Since only one instance of Updater is allowed, it results in not being able to run Updater after closing it, till all its children are terminated. Possible solution would be to find a way to run programs as detached processes.