Сейчас уже никто не создает программы в консоли. Используя любимую IDE, разработчик чувствует себя неуютно за чужим компьютером, где её нет.
Решив разобраться в работе Ant и Maven, я поймал себя на том, что не смогу собрать приложение без них в консоли.
В данной статье я постарался уместить все этапы проектирования демонстрационного приложения, чтобы не искать справку по каждой команде на просторах Интернета.

От простого к ...

Каждая программа обычно содержится в отдельном каталоге. Я придерживаюсь правила создавать в этом каталоге по крайней мере две папки: src и bin. В первой содержатся исходные коды, во второй — результат компиляции. В данных папках будет структура каталогов, зависящая от пакетов.

Один файл

Можно сделать и без лишних папок.
Берем сам файл.

publicclassHelloWorld
{
    publicstaticvoidmain(String[] args)
    {
        System.out.println("Hello World!");
        Calculator calc=newCalculator();
        System.out.println("2+2="+calc.sum(2,2));
    }
}

Переходим в каталог, где лежит данный файл, и выполняем команды.

    javac HelloWorld.java

В данной папке появится файл HelloWorld.class. Значит программа скомпилирована. Чтобы запустить

    java -classpath . HelloWorld

 

Отделяем бинарные файлы от исходников


Теперь сделаем тоже самое, но с каталогами. Создадим каталог HelloWorld и в нем две папки src и bin.
Компилируем

    javac -d bin src/HelloWorld.java

Здесь мы указали, что бинарные файлы будут сохраняться в отдельную папку bin и не путаться с исходниками.

Запускаем

    java -classpath ./bin HelloWorld

 

Используем пакеты


А то, вдруг, программа перестанет быть просто HelloWorld-ом. Пакетам лучше давать понятное и уникальное имя. Это позволит добавить данную программу в другой проект без конфликта имен. Прочитав некоторые статьи, можно подумать, что для имени пакета обязательно нужен домен. Это не так. Домены — это удобный способ добиться уникальности. Если своего домена нет, воспользуйтесь аккаунтом на сайте (например, ru.habrahabr.mylogin). Он будет уникальным. Учтите, что имена пакетов должны быть в нижнем регистре. И избегайте использования спецсимволов. Проблемы возникают из-за разных платформ и файловых систем.

Поместим наш класс в пакет с именем com.qwertovsky.helloworld. Для этого добавим в начало файла строчку

    packagecom.qwertovsky.helloworld;

В каталоге src создадим дополнительные каталоги, чтобы путь к файлу выглядел так: src/com/qwertovsky/helloworld/HelloWorld.java.
Компилируем

    javac -d bin src/com/qwertovsky/helloworld/HelloWorld.java

В каталоге bin автоматически создастся структура каталогов как и в src.

    HelloWorld
    '---bin
    '   '---com
    '       '---qwertovsky
    '          '---helloworld
    '             '---HelloWorld.class
    '---src
        '---com
            '---qwertovsky
                '---helloworld
                    '---HelloWorld.java

Запускаем

    java -classpath ./bin com.qwertovsky.helloworld.HelloWorld

 

Если в программе несколько файлов


Изменим программу. Не обращайте внимание на логику. Её нет.

HelloWorld.java
packagecom.qwertovsky.helloworld;
publicclassHelloWorld
{
    publicstaticvoidmain(String[] args)
    {
        inta=2;
        intb=3;
        Calculator calc=newCalculator();
        System.out.println("Hello World!");
        System.out.println(a+"+"+b+"="+calc.sum(a,b));
    }
}
Calculator.java
packagecom.qwertovsky.helloworld;

importcom.qwertovsky.helloworld.operation.Adder;
publicclassCalculator
{
    publicintsum(int... a)
    {
        Adder adder=newAdder();
        for(inti:a)
        {
            adder.add(i);
        }
        returnadder.getSum();
    }
}
Adder.java
packagecom.qwertovsky.helloworld.operation;

publicclassAdder
{
    privateintsum;

    publicAdder()
    {
        sum=0;
    }

    publicAdder(inta)
    {
        this.sum=a;
    }

    publicvoidadd(intb)
    {
        sum+=b;
    }
    publicintgetSum()
    {
        returnsum;
    }
}

Компилируем

    javac  -d bin src/com/qwertovsky/helloworld/HelloWorld.java
    srccomqwertovskyhelloworldHelloWorld.java:9: cannot find symbol
    symbol  : class Calculator
    location: class com.qwertovsky.helloworld.HelloWorld
                Calculator calc=new Calculator();
                ^
    srccomqwertovskyhelloworldHelloWorld.java:9: cannot find symbol
    symbol  : class Calculator
    location: class com.qwertovsky.helloworld.HelloWorld
                Calculator calc=new Calculator();
                                    ^
    2 errors

Ошибка возникла из-за того, что для компиляции нужны файлы с исходными кодами классов, которые используются (класс Calculator). Надо указать компилятору каталог с файлами с помощью ключа -sourcepath.
Компилируем

    javac -sourcepath ./src -d bin src/com/qwertovsky/helloworld/HelloWorld.java

Запускаем

    java -classpath ./bin com.qwertovsky.helloworld.HelloWorld
    Hello Word
    2+3=5

 

Если удивляет результат


Есть возможность запустить отладчик. Для этого существует jdb.
Сначала компилируем с ключом -g, чтобы у отладчика была информация.

    javac -g -sourcepath ./src -d bin src/com/qwertovsky/helloworld/HelloWorld.java

Запускаем отладчик

    jdb -classpath bin -sourcepath src com.qwertovsky.helloworld.HelloWorld
    Initializing jdb ...
    >

Отладчик запускает свой внутренний терминал для ввода команд. Справку по последним можно вывести с помощью команды help.
Указываем точку прерывания на 9 строке в классе Calculator

    > stop at com.qwertovsky.helloworld.Calculator:9
    Deferring breakpoint com.qwertovsky.helloworld.Calculator:9.
    It will be set after the class is loaded.

Запускаем на выполнение.

    > run
    run com.qwertovsky.helloworld.HelloWorld
    Set uncaught java.lang.Throwable
    Set deferred uncaught java.lang.Throwable
    >
    VM Started: Set deferred breakpoint com.qwertovsky.helloworld.Calculator:9
    Hello World!
    Breakpoint hit: "thread=main", com.qwertovsky.helloworld.Calculator.sum(), line=9 bci=0
    9               Adder adder=new Adder();

Чтобы соориентироваться можно вывести кусок исходного кода, где в данный момент находится курссор.

    main[1] list
    5    public class Calculator
    6    {
    7       public int sum(int... a)
    8       {
    9 =>            Adder adder=new Adder();
    10              for(int i:a)
    11              {
    12                      adder.add(i);
    13              }
    14              return adder.getSum();

Узнаем, что из себя представляет переменная а.

    main[1] print a
     a = instance of int[2] (id=340)
    main[1] dump a
     a = {
    2, 3
    }
    main[1] stop at com.qwertovsky.helloworld.operation.Adder:19
    Deferring breakpoint com.qwertovsky.helloworld.operation.Adder:19.
    It will be set after the class is loaded.

Продолжим исполнение.

    main[1] cont
    > Set deferred breakpoint com.qwertovsky.helloworld.operation.Adder:19

    Breakpoint hit: "thread=main", com.qwertovsky.helloworld.operation.Adder.add(), line=19 bci=0
    19              sum+=b;
    main[1] list
    15      }
    16
    17      public void add(int b)
    18      {
    19 =>           sum+=b;
    20      }
    21
    22      public int getSum()
    23      {
    24              return sum;
    main[1] print sum
     sum = 0
    main[1] print b
     b = 2

Выполним код в текущей строке и увидим, что sum стала равняться 2.

    main[1] step
    >
    Step completed: "thread=main", com.qwertovsky.helloworld.operation.Adder.add(), line=20 bci=10
    20      }
    main[1] print sum
     sum = 2

Поднимемся из класса Adder в вызвавший его класс Calculator.

    main[1] step up
    >
    Step completed: "thread=main", com.qwertovsky.helloworld.Calculator.sum(), line=10 bci=36
    10              for(int i:a)

Удаляем точку прерывания

    main[1] clear com.qwertovsky.helloworld.operation.Adder:19
    Removed: breakpoint com.qwertovsky.helloworld.operation.Adder:19
    main[1] step
    >
    Step completed: "thread=main", com.qwertovsky.helloworld.Calculator.sum(), line=12 bci=30
    12                      adder.add(i);

Можно избежать захода в методы, используя команду next.

    main[1] next
    >
    Step completed: "thread=main", com.qwertovsky.helloworld.Calculator.sum(), line=10 bci=36
    10              for(int i:a)
    main[1] next
    >
    Step completed: "thread=main", com.qwertovsky.helloworld.Calculator.sum(), line=14 bci=42
    14              return adder.getSum();

Проверяем значение выражения и завершаем выполнение.

    main[1] eval adder.getSum()
     adder.getSum() = 5
    main[1] cont
    > 2+3=5
    The application exited

 

Хорошо бы протестировать

Используем JUnit.

packagecom.qwertovsky.helloworld;

importstaticorg.junit.Assert.*;

importjava.util.Arrays;
importjava.util.Collection;

importorg.junit.Test;
importorg.junit.runner.RunWith;
importorg.junit.runners.Parameterized.Parameters;

@RunWith(value=org.junit.runners.Parameterized.class)
publicclassTestCalculator
{
    intexpected;
    int[] arg;

    @Parameters
    publicstaticCollection<int[][]> parameters()
    {

        returnArrays.asList(newint[][][]{
                {{4}, {2, 2}}
                ,{{-1},{4, -5}}
                ,{{0},{0,0,0}}
                ,{{0},{}}
                });
    }

    publicTestCalculator(int[] expected, int[] arg)
    {
        this.expected=expected[0];
        this.arg=arg;
    }
    @Test
    publicvoidtestSum()
    {
        Calculator c=newCalculator();
        assertEquals(expected,c.sum(arg));
    }
}

Компилируем

    mkdir test_bin
    javac  -classpath lib/path/junit-4.8.2.jar  -sourcepath ./src -d test_bin test/com/qwertovsky/helloworld/TestCalculator.java

Запускаем. В качестве разделителя нескольких путей в classpath в Windows используется ';', в Linux — ':'. В консоли Cygwin не работают оба разделителя. Возможно, должен работать ';', но он воспринимается как разделитель команд.

    java  -classpath lib/path/junit-4.8.2.jar:./test_bin  org.junit.runner.JUnitCore com.qwertovsky.helloworld.TestCalculator
    JUnit version 4.8.2
    ....
    Time: 0,031
    OK (4 tests)

 

Создадим библиотеку

Класс Calculator оказался полезным и может быть использован во многих проектах. Перенесем всё, что касается класса Calculator в отдельный проект.

    HelloWorld
    '---bin
    '---src
        '---com
            '---qwertovsky
                '---helloworld
                    '---HelloWorld.java
    Сalculator
    '---bin
    '---src
    '   '---com
    '       '---qwertovsky
    '           '---calculator
    '               '---Calculator.java
    '               '---operation
    '                   '---Adder.java
    '---test
        '---com
            '---qwertovsky
                '---calculator
                    '---TestCalculator.java
    

Измените также назавания пакетов в исходных текстах. В HelloWorld.java нужно будет добавить строку

    importcom.qwertovsky.calculator.Calculator;

Компилируем.

    cd Calculator
    javac -sourcepath src -d bin src/com/qwertovsky/calculator/Calculator.java

Делаем архив jar

    jar cvf calculator.jar -C bin .
    added manifest
    adding: com/(in = 0) (out= 0)(stored 0%)
    adding: com/qwertovsky/(in = 0) (out= 0)(stored 0%)
    adding: com/qwertovsky/calculator/(in = 0) (out= 0)(stored 0%)
    adding: com/qwertovsky/calculator/Calculator.class(in = 497) (out= 373)(deflated 24%)
    adding: com/qwertovsky/calculator/operation/(in = 0) (out= 0)(stored 0%)
    adding: com/qwertovsky/calculator/operation/Adder.class(in = 441) (out= 299)(deflated 32%)

С помощью ключа -C мы запустили программу в каталоге bin.

Надо узнать, что у библиотеки внутри


Можно распаковать архив zip-распаковщиком и посмотреть, какие классы есть в библиотеке.
Информацию о любом классе можно получить с помощью дизассемблера javap.

    javap -c -classpath calculator.jar com.qwertovsky.calculator.Calculator
    Compiled from "Calculator.java"
    public class com.qwertovsky.calculator.Calculator extends java.lang.Object{
    public com.qwertovsky.calculator.Calculator();
      Code:
       0:	aload_0
       1:	invokespecial	#1; //Method java/lang/Object."<init>":()V
       4:	return

    public int sum(int[]);
      Code:
       0:	new	#2; //class com/qwertovsky/calculator/operation/Adder
       3:	dup
       4:	invokespecial	#3; //Method com/qwertovsky/calculator/operation/Adder."<init>":()V
       7:	astore_2
       8:	aload_1
       9:	astore_3
       10:	aload_3
       11:	arraylength
       12:	istore	4
       14:	iconst_0
       15:	istore	5
       17:	iload	5
       19:	iload	4
       21:	if_icmpge	42
       24:	aload_3
       25:	iload	5
       27:	iaload
       28:	istore	6
       30:	aload_2
       31:	iload	6
       33:	invokevirtual	#4; //Method com/qwertovsky/calculator/operation/Adder.add:(I)V
       36:	iinc	5, 1
       39:	goto	17
       42:	aload_2
       43:	invokevirtual	#5; //Method com/qwertovsky/calculator/operation/Adder.getSum:()I
       46:	ireturn
    }

Из результата видно, что класс содержит кроме пустого конструктора, ещё один метод sum, внутри которого в цикле вызывается метод add класса Adder. По завершении метода sum, вызывается Adder.getSum ().
Без ключа -c программа выдаст только список переменных и методов (если использовать -private, то всех).

    javap -private  -classpath calculator.jar com.qwertovsky.calculator.operation.Adder
    Compiled from "Adder.java"
    public class com.qwertovsky.calculator.operation.Adder extends java.lang.Object{
        private int sum;
        public com.qwertovsky.calculator.operation.Adder();
        public com.qwertovsky.calculator.operation.Adder(int);
        public void add(int);
        public int getSum();
    }

 

Лучше снабдить библиотеку документацией

Изменим для этого класс калькулятора.

packagecom.qwertovsky.calculator;

importcom.qwertovsky.calculator.operation.Adder;

/**
 * Калькулятор, который умеет складывать
 * @authorQwertovsky
 *
 */
publicclassCalculator
{
    /**
     * Определение суммы слагаемых
     * @parama массив слагаемых
     * @returnсумма
     */
    publicintsum(int... a)
    {
        Adder adder=newAdder();
        for(inti:a)
        {
            adder.add(i);
        }
        returnadder.getSum();
    }
}

Документацию можно создать следующей командой. При ошибке программа выдаст список возможных опций.

    mkdir doc
    javadoc -d doc -charset utf-8  -sourcepath src -author -subpackages com.qwertovsky.calculator

В результате получиться следующее
Calculator_388_292

Можно подписать jar-архив

Если требуется подписать свою библиотеку цифровой подписью, на помощь придут keytool и jarsigner.
Генерируем подпись.

    keytool -genkey -keyalg rsa -keysize 2048 -alias qwertokey -keystore path/to/qwerto.keystore
    Enter keystore password:
    Re-enter new password:
    What is your first and last name?
      [Unknown]:  Valery Qwertovsky
    What is the name of your organizational unit?
      [Unknown]:  Qwertovsky
    What is the name of your organization?
      [Unknown]:  Qwertovsky
    What is the name of your City or Locality?
      [Unknown]:  Tver
    What is the name of your State or Province?
      [Unknown]:  Tverskaya obl.
    What is the two-letter country code for this unit?
      [Unknown]:  RU
    Is CN=Valery Qwertovsky, OU=Qwertovsky, O=Qwertovsky, L=Tver, ST=Tverskaya	obl., C=RU correct?
      [no]:  y
    Enter key password for <qwertokey>
            (RETURN if same as keystore password):
    Re-enter new password:


Генерируем Certificate Signing Request (CSR)

    keytool -certreq -file path/to/qwertokey.crt -alias qwertokey -keystore path/to/qwerto.keystore

Содержимое полученного файла отправляем в центр сертификации. От центра сертификации получаем сертификат. Сохраняем его в файле (например, qwertokey.cer) и импортируем в хранилище

    keytool -import -trustcacerts -keystore path/to/qwert.keystore -alias qwertokey -file path/to/qwertokey.cer

Подписываем jar-архив

    jarsigner -keystore path/to/qwerto.keystore calculator.jar qwertokey

Файл qwertokey.cer отправляем всем, кто хочет проверить архив. Проверяется он так

    jarsigner -verify -verbose -certs -keystore path/to/qwerto.keystore calculator.jar

 

Использование библиотеки


Есть программа HelloWorld, которая использует библиотечный класс Calculator. Чтобы скомпилировать и запустить программу, нужно присоединить библиотеку.
Компилируем

    cd HelloWorld
    javac -sourcepath src -d bin -classpath path/to/calculator.jar src/com/qwertovsky/helloworld/HelloWorld.java

Запускаем

    java -classpath bin:path/to/calculator.jar com.qwertovsky.helloworld.HelloWorld

 

Собираем программу

Это можно сделать по-разному.

Первый способ

 

    cd HelloWorld
    echo main-class: com.qwertovsky.helloworld.HelloWorld>manifest.mf
    echo class-path: lib/calculator.jar >>manifest.mf
    mkdir lib
    cp path/to/calculator.jar lib/calculator.jar
    jar -cmf manifest.mf helloworld.jar  -C bin .

Здесь есть тонкости.
В строке

    main-class: com.qwertovsky.helloworld.HelloWorld

не должно быть пробелов в конце.
Вторая тонкость описана в [3]: в этой же строке должен стоять перенос на следующую строку. Это если манифест помещается в архив сторонним архиватором.
Программа jar не включит в манифест последнюю строку из манифеста, если в конце не стоит перенос строки.
Ещё момент: в манифесте не должно быть пустых строк между строками. Будет выдана ошибка «java.io.IOException: invalid manifest format».

При использовании команды echo надо следить только за пробелом в конце строки с main-class.

Второй способ

 

    cd HelloWorld
    echo class-path: lib/calculator.jar >manifest.mf
    mkdir lib
    cp path/to/calculator.jar lib/calculator.jar
    jar -cmef manifest.mf com.qwertovsky.helloworld.HelloWorld  helloworld.jar  -C bin .

В данном способе избегаем ошибки с пробелом в main-class.

Третий способ

 

    cd HelloWorld
    mkdir lib
    cd lib
    jar -xvf path/to/calculator.jar com/
      created: com/
      created: com/qwertovsky/
      created: com/qwertovsky/calculator/
     inflated: com/qwertovsky/calculator/Calculator.class
      created: com/qwertovsky/calculator/operation/
     inflated: com/qwertovsky/calculator/operation/Adder.class
    cd ..
    cp  -r bin/* lib/
    jar -cef com.qwertovsky.helloworld.HelloWorld  helloworld.jar  -C lib .
    rm -r lib

Включили код нужной библиотеки в исполняемый файл.

Запуск исполняемого jar-файла

Файл calculator.jar исполняемым не является. А вот helloworld.jar можно запустить.
Если архив был создан первыми двумя способами, то рядом с ним в одном каталоге должна находится папка lib с файлом calculator.jar. Такие ограничения из-за того, что в манифесте в class-path указан путь относительно исполняемого файла.

    cd Calculator
    ls ../HelloWorld/lib
    calculator.jar
    java -jar ../HelloWorld/helloworld.jar

При использовании третьего способа нужные библиотеки включаются в исполняемый файл. Держать рядом нужные библиотеки не требуется. Запускается аналогично.

    java -jar ../HelloWorld/helloworld.jar

 

Как быть с приложениями JavaEE

Аналогично. Только библиотеки для компиляции нужно брать у сервера приложений, который используется. Если я использую JBoss, то для компиляции сервлета мне нужно будет выполнить примерно следующее

     javac -classpath path/to/jboss/common/lib/jboss-servlet*.jar  -d ./classes src/com/qwertovsky/app/servlets/MenuSt.java


Структура архива JavaEE-приложения должна соответствовать определенному формату. Например

    my.ear
    `---META-INF
    |   `---manifest.mf
    `---lib
    |   `---mylib.jar
    `---my.war
    |   `---META-INF
    |   |   `---manifest.mf
    |   `---WEB-INF
    |   |   `---lib
    |   |   |   `---myweblib.jar
    |   |   `---classes
    |   |   |   `---com
    |   |   |       `---...
    |   |   `---web.xml
    |   `---index.html
    |   `---<остальноевеб-содержимое (страницы, изображения)>
    `---myejb.jar

Способы запуска приложения на самом сервере с помощью командной строки для каждого сервера различны.

Надеюсь, данная статья станет для кого-нибудь шпаргалкой для работы с Java в командной строке. Данные навыки помогут понять содержание и смысл Ant-скриптов и ответить на собеседовании на более каверзные вопросы, чем «Какая IDE Вам больше нравится?».

Настройка доступа извне к определенному ip и определенному порту или как настроить port-mapping на Huawei HG530?
На днях нужно было настроить проброс порта для сервера видеоконференций. У клиента Интернет раздавал модем Huawei HG530.

  1. Заходим в админ-панель устройства (http://192.168.0.1), дефалтовый логин: admin, пароль: admin
  2. Basic > NAT Settings
  3. Выбираем наш задействованный Virtual Circuit, в моем случае он — PVC2
  4. Появляется надстройка Virtual Server
  5. В поле Application указываем имя нашего сервиса
  6. Выбираем протокол, в моем случае TCP
  7. Задаем внешний порт, сразу же под протоколом, Strat Port Number — End Port Number, у меня 443 порт
  8. Указываем внутренний IP сервера или устройства к которому нужно предоставить доступ, у меня он на 192.168.0.10
  9. Задаем внутренний порт устройства к которому необходимо подключение (доступ извне), Strat Port Number — End Port Number, у меня 443-порт
  10. Жмем Submit и перезагружаем модем
  11. Радуемся

pic_1

pic_2

pic_3

Скопировать /etc/passwd, /etc/group, /etc/shadow и создать директории.
+ /etc/gshadow, пробовал это, но оно каким то образом живёт на /etc/passwd- , /etc/group- , /etc/shadow- , /etc/gshadow- . Ребут нечо не решает. как его заставить подхватить то что я ему пихаю? Или как заставить useradd создавать юзера одной строчкой включая создание пароля? Тогда можно было бы одним куском вбить килограм юзверей.

 

Или как заставить useradd создавать юзера одной строчкой включая создание пароля?
Можно так

Код
useradd username
echo userpasswd | passwd --stdin username

Или так:

Код
useradd -p $(mkpasswd -Hmd5 userpasswd) username

Или так:

Код

useradd username
echo username:userpasswd | chpasswd

Две невероятно полезные команды системного администратора для анализа узких мест системы:

Проверка текущей нагрузки на дисковую подсистему

iostat -x 1

Для подробного анализа производительности, например, диска dm-108 запускается команда. Ключ -m переводит все в мегабайты

iostat -x -m dm-1081

Проверка текущей загрузки сети

iftop -n

iftop недоступен по-умолчанию, для этого следует установить пакет

aptitude install iftop

Управление программным RAID-массивом в Linux выполняется с помощью программы mdadm.

У программы mdadm есть несколько режимов работы.
Continue Reading