пятница, августа 03, 2012

Как сгенерировать SHA-1 дайджест по строке (в том числе и в Android)



public class SecurityUtils {
public static String getSHA1Digest(String str) throws NoSuchAlgorithmException {
if (str == null || str.trim().length() == 0) {
throw new InvalidParameterException("Не указано значение  строки");
}
MessageDigest sha = MessageDigest.getInstance("SHA-1");
byte[] bytes = str.getBytes();
sha.update(bytes);
byte[] digest = sha.digest();

StringBuilder sb = new StringBuilder(40);
for (int i = 0; i < digest.length; i++) {
String hexVal = String.format("%02x", Integer.valueOf((digest[i] + 256) & 0xFF));
sb.append(hexVal);
}

return sb.toString();
}
}

Проверяем:


public class SHA1Test extends TestCase {

public void testSHA1() {
   String sourceString = "12345";
   String expectedResult = "8cb2237d0679ca88db6464eac60da96345513964";
   String result = "";
 
   try {
result = SecurityUtils.getSHA1Digest(sourceString);
} catch (NoSuchAlgorithmException e) {
assertTrue(e.getMessage(), false);
}
 
   assertEquals(expectedResult, result);  
}




вторник, июля 03, 2012

Наглядный пример :)


четверг, мая 03, 2012

create database link и ошибка SQL Error: ORA-00933: SQL command not properly ended

Понадобилось создать линк к удалённой БД.
Смотрю пример в документации:


CREATE DATABASE LINK local 
   CONNECT TO hr IDENTIFIED BY hr
   USING 'local';


и выполняю
create database link mylink connect to schema_name identified by 123456 using 'orcl'

В результате получаю сообщение об ошибке:

SQL Error: ORA-00933: SQL command not properly ended
00933. 00000 -  "SQL command not properly ended"

Разницы от того, выполняется ли запрос из SQL Developer или из sqlplus, нет никакой.

Как оказалось, значение пароля (123456) нужно указать в двойных кавычках - да и другие параметры тоже лучше в них, т. е. вот так:

create database link "mylink" connect to "schema_name" identified by "123456" using 'orcl'


При этом orcl - именно в одинарых.

среда, апреля 18, 2012

TextView, ellipsize, maxLines и странности

Есть у меня такой TextView:

    [TextView
        android:id="@+id/news_item_text"
        style="@style/text_plain_embossed"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:ellipsize="end"
        android:maxLines="4"
        android:text="Здесь текст новости, который может быть очень очень очень длинным и не помещаться на одну строку. Шла Маша по шоссе и сосала сушку. Чем дальше в лес, тем толще партизаны. Странный аттрактор не имеет выхода."/]

Всё, что я от него хочу, это:

  • чтобы длинный текст отображался бы в 4 строки (android:maxLines="4" )
  • чтобы в конце текста, если он не помещается целиком, добавлялось бы многоточие ( android:ellipsize="end")
И вот тут начинается самое интересное. В разных версиях (я тут вначале опечатался и набрал слово "весрия", но иногда хочется именно так и оставить) Андроида результат отображения этого представления получается весьма разный. Изображения сняты из эмулятора, но на устройствах они получаются такими же (проверялось на HTC Desire (2.3), Samsung Galaxy Tab 7.7 (3.2) и на Samsung Galaxy S II (4.0)).

Android 2.2

Android 3.2


Android 4.0

Видно, что:
Android 2.2 - количество строк - плохо, многоточие - хорошо.
Android 3.2 - количество строк - хорошо, многоточие - плохо
Android 4.0 - количество строк - хорошо, многоточие - хорошо.

При этом, в если убрать android:ellipsize="end", то в 2.2 получается вот так:
Android 2.2, без ellipsize

И как с этим бороться?!



пятница, апреля 06, 2012

Отформатировать копейки в рубли с разделителями групп и с точкой

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


private static DecimalFormat currencyFormat;
static {
     currencyFormat = new DecimalFormat();
     DecimalFormatSymbols dfs = currencyFormat.getDecimalFormatSymbols(); 
     dfs.setDecimalSeparator('.'); 
     currencyFormat.setDecimalFormatSymbols(dfs);
     currencyFormat.setMinimumFractionDigits(2); 
     currencyFormat.setMaximumFractionDigits(2); 
     currencyFormat.setGroupingSize(3);
     currencyFormat.setGroupingUsed(true);
}

int amount = ...
double roubles = (double) amount / 100;
return currencyFormat.format( roubles );

среда, марта 21, 2012

Как программно добавить строки с колонками одинаковой ширины в TableLayout

Если коротко - для каждой колонки (т. е. для содержимого TableRow) нужно задать
layout_width="0dp"
layout_weight="1"

Вот пример кода
// Важно импортировать именно этот класс LayoutParams - иначе
// строки просто не покажутся
import android.widget.TableRow.LayoutParams;

import static android.view.ViewGroup.LayoutParams.FILL_PARENT;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;



@ViewById(R.id.datatable)
TableLayout datatable;
...   ...   ...


for (ParamValue param : params) {
TableRow row = new TableRow(this);
row.setLayoutParams(new LayoutParams(FILL_PARENT, WRAP_CONTENT));

TextView paramName = new TextView(this);
paramName.setText(param.parameter);
paramName.setLayoutParams(new LayoutParams(0, WRAP_CONTENT, 1));

TextView paramValue = new TextView(this);
paramValue.setText(param.value);
paramValue.setLayoutParams(new LayoutParams(0, WRAP_CONTENT, 1));

row.addView(paramName);
row.addView(paramValue);

datatable.addView(row, new LayoutParams(FILL_PARENT, WRAP_CONTENT));
}





пятница, марта 09, 2012

FrameLayout и layout_marginTop

Не знаю, баг это или фича, но в случае, если view находится внутри frameLayout, то установка этому view свойства layout_marginTop ни на что не влияет - view по-прежнему располагается без отступа сверху.

Чтобы этот отступ сверху появился бы, нужно дополнительно задать этому view значение layout_gravity="top".

пятница, февраля 10, 2012

Пользователи phpbb3 не могут отправлять личные сообщения

Установил phpbb 3.0.10, создал пользователя и обнаружил, что ему не доступна возможность отправки личных сообщений:
Вам не разрешено использовать данную возможность. Возможно, вы недавно зарегистрировались на конференции или вам необходимо проявить больше активности на ней, чтобы получить такое право.
Обнаружил, что, начиная с 3.0.x (не помню с какой именно), появилась группа "Новые пользователи". Регистрируемые пользователи форума автоматически включаются в состав этой группы до тех пор, пока они не отправят заданное количество сообщений. Члены этой группы ограничены в возможностях отправки личных сообщений.

Количество сообщений задаётся в настройках: "Общие - Регистрация пользователей", пункт "Лимит сообщений для новых пользователей". По-умолчанию это значение установлено в 3. Если изменить его на 0, то новые пользователи в эту группу включаться не будут.



phpbb - Как настроить cookies для компьютера без имени домена

Понадобилось поставить phpbb3 на комп, для которого отсутствует доменное имя.
В общем случае phpbb в настройках кук такое не ожидает увидеть и в результате использует для отслеживания сессий не куки, а SID. Автоматический вход на форум в таком случае не работает. 

Вот здесь нашёл такое решение:
Пусть адрес форума имеет вид: http://mycomp/phpbb

В файле /includes/session.php нужно найти метод set_cookie и заменить в этом методе строку 'localhost' на имя компьютера 'mycomp'.

После этого выйти из браузера, запустить снова и проверить авторизацию - если SID к адресу прицепляться перестал, то всё хорошо. Но проверять лучше не на админовской учётной записи, потому что в моём случае SID к ней продолжает прикрепляться по-прежнему. 

пятница, декабря 09, 2011

Oracle, как выбрать подмножество строк


Выбрать строки 10..20 из HR.departments

select * 
  from (select d.*, rownum rnum
          from HR.departments d
         where rownum <= 20)
where rnum >= 10

Если при этом набор данных нужно отсортировать, как чаще всего и бывает, то это выражение можно записать в виде

select * 
  from (select d.*, rownum rnum
          from HR.departments d
         where rownum <= 20
         order by d.department_name)
where rnum >= 10


но работать будет не правильно - значения rownum определяются на этапе выборки данных и при выполнении order by порядок будет утерян. 

Поэтому в таком случае можно обернуть выборку и сортировку в ещё один select:

select * 
from (
  select data.*, rownum rnum 
  from ( 
    select d.*
    from HR.departments d
    order by d.department_name ) data
  where rownum <= 20 )
where rnum >= 10


четверг, декабря 08, 2011

Cursor и порядок

Как (внезапно) выяснилось, порядок, в котором столбцы выбираются в курсор, имеет важное значение.

Пусть есть таблица:
CREATE TABLE A( F1 NUMBER(1), F2 FLOAT);

с такими данными:
INSERT INTO A VALUES(1, 11.1);  
INSERT INTO A VALUES(2, 22.22);
INSERT INTO A VALUES(3, 33.333);

И пусть мне захотелось сделать курсор, который бы содержал данные из этой таблицы:
DECLARE
  TYPE a_cursor_type IS REF CURSOR RETURN A%ROWTYPE;
  a_cursor a_cursor_type;
  
  a_record A%ROWTYPE;
BEGIN
  OPEN a_cursor FOR SELECT f1, f2 FROM A;
  
  LOOP
    FETCH a_cursor INTO a_record;
    EXIT WHEN a_cursor%NOTFOUND;
    
    dbms_output.put_line('F1: ' || a_record.f1 || ', F2: ' || a_record.f2);
  END LOOP;
END;

В выражении SELECT f1, f2 FROM A важно не перепутать порядок выбираемых полей таблицы - хотя в обычной жизни на такие вещи не обращаешь внимания. А вот с курсором обращать приходится, в противном случае вместо ожидаемого результата
F1: 1, F2: 11.1
F1: 2, F2: 22.22
F1: 3, F2: 33.333

легко получить
Error report:
ORA-06502: PL/SQL: numeric or value error: number precision too large
ORA-06512: at line 10
06502. 00000 -  "PL/SQL: numeric or value error%s"

При этом повезёт, если типы столбцов или содержащиеся в них значения окажутся не совместимые и сразу получим сообщение об ошибке. Иначе можно совсем долго разбираться, отчего в нужных столбцах показываются неправильные данные.




понедельник, декабря 05, 2011

Как подключиться из ERwin к Oracle

Предположим, я хочу выполнить Reverse Engineer для какой-то схемы базы. Для этого я вызываю Tools -> Reverse Engineer... Появляется диалог, в котором я указываю тип СУБД - Oracle


Нажимаю Next.

Изменяю или оставляю заданные значения опций импорта на появившемся окне:


и снова нажимаю Next.

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

Здесь я указываю тип аутентификации - у меня используется Database Authentification.
Указываю значения username и пароля. 

После этого необходимо указать значение строки соединения (connection string). Значение этой строки можно добыть из tnsnames.ora.

В tnsnames.ora выбираю определение

XE =
  (DESCRIPTION =
    (ADDRESS = (PROTOCOL = TCP)(HOST = desktop-1)(PORT = 1521))
    (CONNECT_DATA =
      (SERVER = DEDICATED)
      (SERVICE_NAME = XE)
    )
  )

Это выражение нужно привести к одной строке, убрав переводы строк, получив вот такое:

(DESCRIPTION =  (ADDRESS = (PROTOCOL = TCP)(HOST = desktop-1)(PORT = 1521)) (CONNECT_DATA =  (SERVER = DEDICATED) (SERVICE_NAME = XE)))

Копирую эту строку в поле ввода Connection String - и можно нажимать Connect. 



воскресенье, декабря 04, 2011

Поле для хранения GUID в Oracle, использование SYS_GUID

Тип данных для хранения значения - RAW(16).
Для автогенерации значения можно сделать триггер, использовать хранимую процедуру, а можно и вот так:

CREATE TABLE TEST(ID RAW(16) DEFAULT SYS_GUID(), ...);

С другой стороны, если хотим получить значение созданного идентификатора, как обычно и бывает, можно вот так

DECLARE guid RAW(16);
BEGIN
  guid := SYS_GUID();
  INSERT INTO TEST2(ID, NAME) VALUES(guid, 'ПЖиВ И медведи');
END;

среда, ноября 16, 2011

JMeter, MS SQL Server и JDBC-драйвер

Отчего-то не получилось сделать так, чтобы JMeter заработал бы с JDBC-драйвером от Microsoft. Пробовал с sqljdbc4.jar текущей версии (sqljdbc_3.0.1301.101_rus).

Вроде, на вид, всё хорошо - сделал совсем простой Thread Group, добавил JDBC Connection Configuration, указал нужные параметры... Добавил один JDBC Request с совсем простым запросом. Ну и, конечно, View Results Tree.

И что получилсь? Да ничего.

Нажимаю Run - Start, и сценарий сразу же молча завершается. В View Results Tree нет вообще ничего. В логе JMeter появляется только одна строка - "Running the test!". Пункт меню Run - Start снова горит как Enabled.

Для интереса пробую:

Изменяю значение jdbc connection. Было вот так: jdbc:sqlserver://localhost:1433;databaseName=MyDatabase

Добавляю в начале произвольный символ. Запускаю. В View Results Tree вижу корректное сообщение об ошибке, а в логе JMeter - соответствующий Exception.

Изменяю название базы данных на заведомо неправильное. И снова, ожидаемое сообщение об ошибке в View Results Tree, ожидаемое исключение в логе.


Изменяю название class name на заведомо неправильное, например, на такое com.microsoft.sqlserver.jdbc.SQLServerDriver123. Снова корректное сообщение об ошибке.


Указываю заведомо неправильное имя пользователя или неправильный пароль. Опять вижу ожидаемую ошибку.

Но стоит вернуть всё к правильным значениям - опять ничего не происходит.

Решил попробовать использовать другой драйвер. Скачал и поставил текущую сборку jTDS. Разумеется, что поменял название класса - стало net.sourceforge.jtds.jdbc.Driver и connection string - теперь она сделалось такой - jdbc:jtds:sqlserver://localhost:1433/MyDatabase.

И всё сразу получилось.

Если честно, так и не понял, чем драйвер от MS так не подошёл к JMeter.

понедельник, ноября 07, 2011

Ссылки на чтение про JMeter

Понадобилось поразбираться  в теме нагрузочного тестирования, поэтому решил пособирать ссылки на эту тему.

Простой нагрузочный тест с Apache JMeter
Описывается использование Access Log Sampler для создания сценария тестирования, средств отображения View Results in Table, Graph Results, Aggregate Report и подходы к интерпретации получаемых результатов.

Рецепт нагрузочного тестирования на JMeter
Так же на основе Access Log Sampler, но более уже более продвинутая логика тестирования.

Быстрое создание нагрузочных тестов на JMeter для web-сайтов
Описывается создание сценариев путём записи действий пользователя при помощи Recording Controller и HTTP Proxy Server. Так же показано использование CSV Data Set Config для получения логинов и паролей из CSV-файла для имитации авторизации разных пользователей.

Отладка тест-плана JMeter
Показывается отладка тестового плана, использование HTTP Request Defaults, Regular Expression Extractor, If Controller.

Apache JMeter
Ссылка на сайт проекта JMeter

SQL query performance test with JMeter
Конфигурация тестирования MS SQL Server

Regular Expressions in JMeter


Хорошая статья, очень хороша для начала работы. 


А вот тут, кстати, живёт набор плагинов для JMeter - jmeter-plugins

Easy scripting of JSON applications with Apache JMeter

четверг, октября 13, 2011

Как включить показ номеров строк в редакторе SQL Server Management Studio

Чтобы не отвечать на вопрос "А чем ты занимался вчера три часа" ответом "Искал строчку 983 в редакторе" удобно включить отображение номеров строк в редакторе.

Включить можно вот тут: выбрать Сервис - Параметры - Текстовый редактор - Все языки - Общее и поставить крыжик "Номера строк".

В англ. варианте путь этот - Tools - Options - TextEditor - All Languages - General.

среда, октября 12, 2011

Word 2010 - Макрос не найден. Возможно он отключен по соображениям безопасности

Запускаю свежеустановленный MS Office 2010 - и при запуске три раза появляется сообщение "Макрос не найден. Возможно он отключен по соображениям безопасности".



У меня Windows 7 64bit и такой же, 64-bit-ный, Office.

На компьютере установлен Lingvo X3, вот он-то и портит нам картину этим сообщением.
Что характерно, Office ставился уже после того, как был установлен Lingvo.

Чтобы убрать это надоедливое сообщение (пока в ABBYY не смогут пофиксить проблему, правда, зная сколько они делают Lingvo для Андроида, возможно этого не случится никогда), нужно:

зайти в %APPDATA%\Microsoft\Word\Startup и переименовать файл Lingvo14.dotm. Делать это нужно при не запущенном офисе.

понедельник, октября 10, 2011

ProgressDialog, Back и handler


У меня есть activity. На ней имеется кнопка, инициализирующая обращение к какому-то объекту, выполняющему длительную обработку запроса. Перед началом обработки activity показывает progressDialog.
В качестве одного из параметров метода вызываемого объекта передаётся handler. После окончания обработки запроса сервер отправит этому handler сообщение, уведомляя о завершении работы, и передавая, возможно, результат при помощи Bundle.

handler, получив сообщение, спрячет progressDialog и покажет alertDialog, который и сообщит о завершении обработки запроса.

Пусть логика взаимодействия подразумевает возможность не дожидаться завершения запроса. progressDialog в таком случае логично сделать сancelable.

Теперь нужно сделать так, чтобы при нажатии пользователем на Back во время отображения progressDialog, не только прятался бы сам progressDialog, но и завершалась бы текущая activity.  В противном случае получается так, что progressDialog больше не показывается, но по-прежнему продолжает показываться та же activity. Что смутительно.

Для этого нужно диалогу сказать, что делать в случае его отмены:


@Click
public void callServerButton() {
ProgressDialog dialog = new ProgressDialog(this);
dialog.setTitle("Подождите");
dialog.setMessage("Запрос обрабатывается...");
dialog.setCancelable(true);
dialog.setOnKeyListener(new DialogInterface.OnKeyListener() {

@Override
public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
dialog.cancel();
finish();
return true;
}
return false;
}
});

activeDialog = dialog;
activeDialog.show();

Server server = new Server();
server.doIt(serverHandler);
}

Теперь во время отображения progressDialog в случае нажатия на Back закроется не только сам диалог, но и activity, чего и добивались.

Теперь возникает другой вопрос - пусть пользователь, не дождавшись завершения работы сервера, нажал Back, в результате activity скрылась. Пользователь продолжает работать с приложением. А в это время сервер закончил обработку запроса и отправил сообщение handler.

handler получает сообщение, пытается создать и отобразить alertDialog. Но диалог использует контекст той activity, которой уже нет - в результате получаем завершение работы приложения и stacktrace для дальнейшего изучения.

Чтобы избежать такой проблемы добавляем какой-нибудь признак в activity. Доопределяем onStop() так, чтобы при выполнении этого метода устанавливалось бы значение этого признака. После этого говорим handler - показывай AlertDialog только в том случае, если значение признака не установлено.

Разумеется, при таком решении пользователь лишается возможности узнать, чем же закончилось выполнение запроса. Для не критичных случаев - это нормально. Например, пользователь захотел посмотреть значения курсов валют, но, не дождавшись, передумал.

Если же происходит что-то, требующее уведомления пользователя, то в данном решении нужно посмотреть в сторону Toast или Notifications.