...

суббота, 10 мая 2014 г.

Стратегия (Перевод с английского главы «Strategy» из книги «Pro Objective-C Design Patterns for iOS» Carlo Chung)

Помните ли вы, когда вы в последний раз начиняли блок кода множеством разных алгоритмов и использовали спагетти из условий if-else / switch-case, чтобы определить, какой именно из них использовать. Алгоритмы могли представлять собой набор функций/методов похожих классов, которые решают схожие проблемы. К примеру, у вас есть процедура для проверки входных данных. Сами данные могут быть любых типов (например, CGFloat, NSString, NSInteger и прочее). Каждый из типов данных требует различных алгоритмов проверки. Если бы вы могли инкапсулировать каждый алгоритм в виде объекта, то можно было бы не использовать группу операторов if-else / switch-case для проверки данных и определения, какой из алгоритмов нужен.

В объектно-ориентированном программировании вы можете выделить связанные алгоритмы в различные классы стратегий. Паттерн проектирования, который применяется в таких случаях, называется Стратегия. В этой главе мы обсудим концепции и ключевые возможности паттерна Стратегия. Мы также спроектируем и реализуем несколько классов для проверки данных в виде стратегий для валидации ввода объекта текстового поля UITextField позже в этой главе.


Что собой представляет паттерн Стратегия?




Одну из ключевых ролей в этом паттерне играет класс стратегии, который объявляет общий интерфейс для всех поддерживаемых алгоритмов. Есть также конкретные классы стратегий, которые реализуют алгоритмы, используя интерфейс стратегии. Объект контекста конфигурируется с помощью экземпляра конкретного объекта стратегии. Объект контекста использует интерфейс стратегии для вызова алгоритма, определенного в конкретном классе стратегии. Их отношения проиллюстрированы на диаграмме классов на рисунке 19–1.

image

Рисунок 19–1. Структура классов паттерна Стратегия


Группа или иерархия связанных алгоритмов в форме классов ConcreteStrategy (A, B и C) разделяют общий algorithmInterface, поэтому Context может получить доступ к разным вариантам алгоритмов с помощью одного и того же интерфейса.


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


Исходное определение, данное в книге «Паттерны проектирования» GoF (Addison-Wesley, 1994).


Экземпляр Context может быть сконфигурирован с помощью различных объектов ConcreteStrategy во время выполнения. Можно это рассматривать как изменение «внутренностей» объекта Context, так как изменения происходят изнутри. Декораторы (смотри главу 16, паттерн Декоратор и мою предыдущую статью), в противовес, изменяют «шкуру» объекта, так как модификации пристыковываются извне. Пожалуйста, обращайтесь к разделу «Изменение «шкуры» объекта в сравнении с изменением «внутренностей»» в главе 16 (предыдущая статья) за более детальной информацией о различиях.


Паттерн Стратегия в Модель-Вид-Контроллер


В паттерне Модель-Вид-Контроллер контроллер определяет, когда и как виду отображать данные, содержащиеся в модели. Сам вид знает, как отобразить что-то, но не знает, что, пока контроллер ему не укажет. Работая с другим контроллером, но при том же виде, формат выводимых данных может быть тем же, но типы данных могут быть другими в соответствии с другим выводами от нового контроллера. Контроллер в этом случае является как бы стратегией для объекта вида. Как мы упоминали в предыдущих главах, отношения между контроллером и видом основаны на паттерне Стратегия.


Когда уместно использование паттерна Стратегия?




Использование этого паттерна целесообразно в следующих случаях:

  • В логике класса используется множество условных операторов для выбора нужного поведения. Вы можете переместить условный код в отдельный класс стратегии.

  • Вам нужны разные варианты алгоритма.

  • Вам бы не хотелось выставлять наружу сложные и узко специфичные структуры данных (клиентам).




Применение стратегий проверки данных на примере класса UITextField




Давайте создадим простой пример реализации паттерна Стратегия в приложении. Предположим, что нам нужен некий объект UITextField в нашем приложении, который принимает ввод пользователя; мы будем использовать результаты ввода в нашем приложении позже. У нас есть поле текстового ввода, которое принимает только буквы, то есть a–z или A–Z, а также у нас есть поле, которое принимает только числовые данные, то есть 0–9. Чтобы убедиться, что ввод в полях верен, каждому из них нужно иметь какую-то процедуру проверки данных на месте, запускаемую после того, как пользователь заканчивает редактирование.

Мы можем поместить необходимую проверку данных в метод объекта делегата UITextField, textFieldDidEndEditing:. Экземпляр UITextField вызывает этот метод каждый раз, когда теряет фокус. В этом методе мы можем убедиться в том, что в цифровом поле введены только цифры, а в буквенном — только буквы. Этот метод принимает на входе ссылку на текущий объект поля ввода (в виде параметра textField), но какой именно это из двух объектов?


Без паттерна Страгии мы бы пришли к коду, подобному показанному в листинге 19–1.


Листинг 19–1. Типичный сценарий проверки содержимого UITextField в методе делегата textFieldDidEndEditing



- (void)textFieldDidEndEditing:(UITextField *)textField
{
if (textField == numericTextField)
{
// проверяем [textField text] и убеждаемся,
// что значение цифровое
}
else if (textField == alphaTextField)
{
// проверяем [textField text] и убеждаемся,
// что значение содержит только буквы
}
}


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


Совет: Если в вашем коде много условных операторов, то это может означать, что их нужно отрефакторить и выделить в отдельные объекты Стратегии.


Теперь наша цель — взяться за этот код проверки и раскидать его по различным классам Стратегий, чтобы можно было его повторно использовать в делегате и других методах. Каждый из наших классов берет строку из поля ввода, затем проверяет его, основываясь на требуемой стратегии, и в конце возвращает значение типа BOOL и экземпляр NSError, если проверка провалилась. Возвращенный объект NSError поможет определить, из-за чего именно проверка не была успешна. Поскольку проверка и цифрового, и буквенного ввода связаны друг с другом (у них одинаковые типа на входе и выходе), их можно объединить одним интерфейсом. Наш набор классов показан на диаграмме классов на рисунке 19–2.


image

Рисунок 19–2. Диаграмма классов показывает отношения между CustomTextField и связанными с ним стратегиями


Мы объявим этот интерфейс не в виде протокола, а в виде абстрактного базового класса. Абстрактный базовый класс более удобен в данном случае, потому что проще рефакторить общее для всех конкретных классов стратегий поведение. Наш абстрактный базовый класс будет выглядеть, как показано в листинге 19–2.


Листинг 19–2. Объявление класса InputValidator в InputValidator.h



@interface InputValidator : NSObject
{
}
// Заглушка для любой стратегии проверки
- (BOOL) validateInput:(UITextField *)input error:(NSError **) error;

@end


Метод validateInput: error: принимает ссылку на UITextField в качестве входного параметра, поэтому он может проверить все, что находится в поле ввода, и возвращает значение BOOL как результат проверки. Метод также принимает ссылку на указатель на NSError. Когда произошла какая-то ошибка (то есть метод не смог проверить правильность ввода), метод создаст экземпляр NSError и присвоит его указателю, поэтому, в каком бы контексте не использовался класс проверки, всегда есть возможность получить более детальную информацию об ошибке из этого объекта.


Реализация этого метода по умолчанию только лишь устанавливает указатель на ошибку в nil и возвращает NO, как показано в листинге 19–3.


Листинг 19–3. Реализация по умолчанию класса InputValidator в InputValidator.m



#import "InputValidator.h"

@implementation InputValidator

// Заглушка для любой стратегии проверки
- (BOOL) validateInput:(UITextField *)input error:(NSError **) error
{
if (error)
{
*error = nil;
}
return NO;
}
@end


Почему мы не использовали NSString в качестве входного параметра? В этом случае любое действие внутри объекта стратегии будет односторонним. Это значит, что валидатор просто сделает проверку и вернет результат без модификации исходного значения. С входным параметром типа UITextField мы можем объединить два подхода. Наши объекты проверки будут иметь возможность изменить исходное значение текстового поля (например, удалив неправильные символы) или просто просмотреть значение без его изменения.


Другой вопрос – почему бы нам просто не бросить исключение NSException, если проверка провалилась? Это потому, что выброс собственного исключения и перехват его в блоке try-catch во фреймворке Cocoa Touch является очень ресурсоемкой операцией и не рекомендуется (но try-catch системные исключения – это совсем другое дело). Относительно дешевле вернуть объект NSError, что рекомендовано в Cocoa Touch Developer’s Guide. Если мы посмотрим на документацию фреймворка Cocoa Touch, мы заметим, что есть множество API, которые возвращают экземпляр NSError, когда возникает какая-то ненормальная ситуация. Распространенный пример – это один из методов NSFileManager, (BOOL)moveItemAtPath:(NSString *)srcPath toPath:(NSString *)dstPath error:(NSError **)error. Если возникает ошибка, когда NSFileManager пытается переместить файл из одного места в другое, он создаст новый экземпляр NSError, который описывает проблему. Вызывающий метод может использовать информацию, содержащуюся в возвращенном объекте NSError для дальнейшей обработки ошибок. Таким образом, цель объекта NSError в нашем методе – это обеспечение информации об отказе в работе.


Теперь мы определили, как должен вести себя хороший класс проверки ввода. Сейчас мы можем заняться созданием настоящего проверяющего. Давайте создадим сначала тот, что для ввода чисел, как показано в листинге 19–4.


Листинг 19–4. Объявление класса NumericInputValidator в NumericInputValidator.h



#import "InputValidator.h"

@interface NumericInputValidator : InputValidator
{
}

// Метод проверки, который убеждается, что ввод содержит только
// цифры, то есть 0-9
- (BOOL) validateInput:(UITextField *)input error:(NSError **) error;

@end


NumericInputValidator наследует от абстрактного базового класса InputValidator и переопределяет его метод validateInput: error:. Мы объявляем метод заново, чтобы подчеркнуть, что данный подкласс реализует или переопределяет его. Это не обязательно, но является хорошей практикой.


Реализация метода дана в листинге 19–5.


Листинг 19–5. Реализация класса NumericInputValidator в NumericInputValidator.m



#import "NumericInputValidator.h"

@implementation NumericInputValidator

- (BOOL) validateInput:(UITextField *)input error:(NSError**) error
{
NSError *regError = nil;
NSRegularExpression *regex = [NSRegularExpression
regularExpressionWithPattern:@"^[0-9]*$"
options:NSRegularExpressionAnchorsMatchLines
error:&regError];
NSUInteger numberOfMatches = [regex
numberOfMatchesInString:[input text]
options:NSMatchingAnchored
range:NSMakeRange(0, [[input text] length])];

// если нет совпадений,
// то возвращаем ошибку и NO
if (numberOfMatches == 0)
{
if (error != nil)
{
NSString *description = NSLocalizedString(@"Input Validation Failed", @"");
NSString *reason = NSLocalizedString(@"The input can contain only numerical
values", @"");
NSArray *objArray = [NSArray arrayWithObjects:description, reason, nil];
NSArray *keyArray = [NSArray arrayWithObjects:NSLocalizedDescriptionKey,
NSLocalizedFailureReasonErrorKey, nil];
NSDictionary *userInfo = [NSDictionary dictionaryWithObjects:objArray
forKeys:keyArray];
*error = [NSError errorWithDomain:InputValidationErrorDomain
code:1001
userInfo:userInfo];
}
return NO;
}
return YES;
}
@end


Реализация метода validateInput:error: фокусируется главным образом на двух аспектах:



  1. Он проверяет количество совпадений численных данных в поле ввода с предварительно созданным объектом NSRegularExpression. Регулярное выражение, которое мы использовали, — это «^[0–9]*$». Он означает, что с начала всей строки (обозначено «^») и конца (обозначено «$»), должно быть 0 и более символов (обозначено «*») из набора, который содержит только цифры (обозначено «[0–9]»).

  2. Если совпадений нет вообще, то он создает новый объект NSError, который содержит сообщение «The input can contain only numerical values» и присваивает его входному указателю на NSError. Затем он наконец возвращает значение типа BOOL, указывающее на успех или неуспех операции. Ошибка ассоциирована с особым кодом 1001 и особым значением домена ошибки, определенным в заголовочном файле класса InputValidator примерно так, как показано ниже:

    static NSString * const InputValidationErrorDomain = @"InputValidationErrorDomain";





Брат класса NumericInputValidator, который проверяет наличие только букв во вводе, называемый AlphaInputValidator, содержит похожий алгоритм для проверки контента поля ввода. AlphaInputValidator переопределяет тот же метод, что и NumericInputValidator. Очевидно, что этот алгоритм проверяет, что входная строка содержит только буквы, как показано в листинге 19–6.

Листинг 19–6. Реализация класса AlphaInputValidator в AlphaInputValidator.m



#import "AlphaInputValidator.h"

@implementation AlphaInputValidator

- (BOOL) validateInput:(UITextField *)input error:(NSError**) error
{
NSError *regError = nil;
NSRegularExpression *regex = [NSRegularExpression
regularExpressionWithPattern:@"^[a-zA-Z]*$"
options:NSRegularExpressionAnchorsMatchLines
error:&regError];
NSUInteger numberOfMatches = [regex
numberOfMatchesInString:[input text]
options:NSMatchingAnchored
range:NSMakeRange(0, [[input text] length])];
// если нет совпадений,
// то возвращаем ошибку и NO
if (numberOfMatches == 0)
{
if (error != nil)
{
NSString *description = NSLocalizedString(@"Input Validation Failed", @"");
NSString *reason = NSLocalizedString(@"The input can contain only letters", @"");
NSArray *objArray = [NSArray arrayWithObjects:description, reason, nil];
NSArray *keyArray = [NSArray arrayWithObjects:NSLocalizedDescriptionKey,
NSLocalizedFailureReasonErrorKey, nil];
NSDictionary *userInfo = [NSDictionary dictionaryWithObjects:objArray
forKeys:keyArray];
*error = [NSError errorWithDomain:InputValidationErrorDomain
code:1002
userInfo:userInfo];
}
return NO;
}
return YES;
}
@end


Наш класс AlphaInputValidator также является разновидностью InputValidator и реализует метод validateInput:. Он имеет похожие на брата, NumericInputValidator, структуру кода и алгоритм, за исключением того, что использует другое регулярное выражение в объекте NSRegularExpression, и код ошибки и сообщение специфичны для буквенной проверки. Регулярное выражение, которое мы используем для проверки букв, — «^[a-zA-Z]*$». Оно похоже на выражение для его собрата по числовой проверке, кроме того, что набор допустимых символов содержит буквы и нижнего, и верхнего регистра. Как мы видим, в обеих версиях много дублирующегося кода. У обоих алгоритмов похожая структура; вы можете отрефакторить структуру в шаблонный метод (смотри главу 18) в абстрактный базовый класс. Конкретные подклассы InputValidator могут переопределить примитивные операции, определенные в InputValidator, чтобы вернуть уникальную информацию шаблонному алгоритму – например, регулярное выражение и различные атрибуты конструирования объекта NSError и т. д. Я оставлю вам это в качестве упражнения.


Сейчас у нас уже есть классы проверки, готовые к использованию в приложении. Однако UITextField не знает о них, поэтому нам нужна собственная версия UITextField, которая все понимает. Мы создадим подкласс UITextField, который содержит ссылку на InputValidator и метод validate, это показано в листинге 19–7.


Листинг 19–7. Объявление класса CustomTextField в CustomTextField.h



#import "InputValidator.h"

@interface CustomTextField : UITextField
{
@private
InputValidator *inputValidator_;
}

@property (nonatomic, retain) IBOutlet InputValidator *inputValidator;

- (BOOL) validate;

@end


CustomTextField содержит свойство, которое удерживает (retain) ссылку на InputValidator. Когда вызывается его метод validate, он использует ссылку на InputValidator, чтобы начать проверку. Мы можем увидеть это в реализации, показанной в листинге 19–8.


Листинг 19–8. Реализация класса CustomTextField в CustomTextField.m



#import "CustomTextField.h"

@implementation CustomTextField

@synthesize inputValidator=inputValidator_;

- (BOOL) validate
{
NSError *error = nil;
BOOL validationResult = [inputValidator_ validateInput:self error:&error];
if (!validationResult)
{
UIAlertView *alertView = [[UIAlertView alloc]
initWithTitle:[error localizedDescription]
message:[error localizedFailureReason]
delegate:nil
cancelButtonTitle:NSLocalizedString(@"OK", @"")
otherButtonTitles:nil];
[alertView show];
[alertView release];
}
return validationResult;
}

- (void) dealloc
{
[inputValidator_ release];
[super dealloc];
}

@end


В методе validate посылается сообщение [inputValidator_ validateInput:self error:&error] ссылке inputValidator_. Красота паттерна в том, что CustomTextField-у не нужно знать, какого типа InputValidator он использует или какие-либо детали алгоритма. Поэтому если в будущем мы добавим какой-то новый InputValidator, объект CustomTextField будет использовать новый InputValidator так же.


Итак, все подготовительные работы сделаны. Допустим, клиентом является UIViewController, который реализует протокол UITextFieldDelegate и содержит два IBOutlets типа CustomTextField, как показано в листинге 19–9.


Листинг 19–9. Объявление класса StrategyViewController в StrategyViewController.h



#import "NumericInputValidator.h"
#import "AlphaInputValidator.h"
#import "CustomTextField.h"

@interface StrategyViewController : UIViewController <UITextFieldDelegate>
{
@private
CustomTextField *numericTextField_;
CustomTextField *alphaTextField_;
}

@property (nonatomic, retain) IBOutlet CustomTextField *numericTextField;
@property (nonatomic, retain) IBOutlet CustomTextField *alphaTextField;

@end


Мы решили позволить контроллеру реализовывать метод делегата (void)textFieldDidEndEditing:(UITextField *)textField и поместить проверку туда. Этот метод будет вызываться каждый раз, когда значение в поле ввода будет изменяться, а фокус будет потерян. Когда пользователь закончит ввод, наш класс CustomTextField вызовет этот метод делегата, проиллюстрировано в листинге 19–10.


Листинг 19–10. Клиентский код, определенный в методе делегата textFieldDidEndEditing:, который проверяет экземпляр CustomTextField с помощью объекта стратегии (InputValidator)




@implementation StrategyViewController

@synthesize numericTextField, alphaTextField;

// ...
// другие методы вьюконтроллера
// ...
#pragma mark -
#pragma mark UITextFieldDelegate methods

- (void)textFieldDidEndEditing:(UITextField *)textField
{
if ([textField isKindOfClass:[CustomTextField class]])
{
[(CustomTextField*)textField validate];
}
}
@end


При вызове textFieldDidEndEditing:, когда редактирование в одном из полей закончено, метод проверяет, что объект textField принадлежит к классу CustomTextField. Если так, то он посылает сообщение validate ему для запуска процесса проверки введенного текста. Как мы можем видеть, нам больше не нужны эти условные операторы. Вместо этого у нас есть гораздо более простой код для тех же целей. За исключением дополнительной проверки того, что объект textField является типом CustomTextField, больше ничего сложного нет.


Но подождите минутку. Кое-что выглядит не очень хорошо. Как бы мы могли присвоить корректные экземпляры InputValidator numericTextField_ и alphaTextField_, определенным в StrategyViewController? Оба поля ввода объявлены как IBOutlet в листинге 19–9. Мы можем подцепить их во вьюконтроллере Interface Builder через IBOutlet-ы, как мы делаем с другими кнопками и прочим. Аналогично в объявлении класса CustomTextField в листинге 19–7, его свойство inputValidator также IBOutlet, что означает, что мы можем присвоить экземпляр InputValidator объекту *TextField тоже в Interface Builder. Таким образом, все может быть сконструировано посредством использования ссылочных соединений Interface Builder, если вы объявите определенные свойства класса как IBOutlet. Для более детального обсуждения, как использовать кастомные объекты Interface Builder, обращайтесь к «Использование CoordinatingController в Interface Builder» в главе 11, где говорится о паттерне Медиатор.


Заключение




В этой главе мы обсудили концепции паттерна Стратегия и как можно задействовать этот паттерн для использования клиентами различных связанных алгоритмов. Пример реализации проверок ввода для кастомного UITextField показывает, как различные классы проверки могут изменить «внутренности» объекта. Паттерн Стратегия чем-то похож на паттерн Декоратор (глава 16 и моя предыдущая статья). Декораторы расширяют поведение объекта извне в то время, как различные стратегии инкапсулируются внутри объекта. Как говорят, декораторы изменяют «шкуру» объекта, а стратегии – «внутренности».

В следующей главе мы увидим другой паттерн, который тоже связан с инкапсуляцией алгоритмов. Инкапсулированный алгоритм в основном используется для отложенного выполнения команды в виде отдельного объекта.


This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


Выбираем Linux-дистрибутив для VPS

Привет, хабрасообщество! Сегодня я был озадачен проблемой выбора дистрибутива для своего дроплета на DigitalOcean и провел небольшое тестирование потребления оперативной памяти, которое может быть вам также полезно. Немного отойдя от темы, хочу сказать, что я давно искал недорогой VPS для собственных нужд и уже было хотел купить его у Hetzner, однако мне посчастливилось наткнуться на DigitalOcean, который предлагает VPS вкупе с шустрым SSD за 5$/месяц.

На данный момент сервис предоставляет на выбор пять дистрибутивов — Ubuntu, CentOS, Debian, Arch Linux, Fedora. Не так давно Arch Linux получил depricated-статус и больше не рекомендуется для создания новых дроплетов. Fedora по понятным причинам отпадает, по крайней мере для меня, как основа для веб-сервера. В итоге мы получаем на выбор три дистрибутива, которые, кстати говоря, являются самыми используемыми на веб-серверах.


Впервые попав на страницу создания дроплета полгода назад, я особо не раздумывал и выбрал CentOS 6.5 x64. Этот выбор был обоснован тем, что AWS (Amazon Web Services) отдает предпочтение CentOS и это мнение является для меня авторитетным. В течении полугода я использовал свой дроплет исключительно как LAMP-хостинг, но подошел момент, когда мне понадобилось запустить стек для Ruby On Rails, которому я отдал свое предпочтение для разработки нового стартапа. Во время установки пакетов система просто ушла в даун, сославшись на недостаток оперативной памяти. Конечно, эту проблему можно было обойти путем подключения swap-файла, но это не наш метод. Я решил сравнить потребление памяти последних версий дистрибутивов на чистых дроплетах с одной открытой ssh-сессией и попробовать переосмыслисть свой выбор для VPS в условиях весьма ограниченного объема ОЗУ (512 MB в моем случае).


Итак, что из этого вышло, смотрите под катом.


CentOS 6.5 x32



[root@centos32 ~]# uname -a
Linux centos32 2.6.32-431.1.2.0.1.el6.i686 #1 SMP Fri Dec 13 11:45:23 UTC 2013 i686 i686 i386 GNU/Linux
[root@centos32 ~]# free
total used free shared buffers cached
Mem: 510888 52104 458784 0 4868 27644
-/+ buffers/cache: 19592 491296
Swap: 0 0 0


CentOS 6.5 x64



[root@centos64 ~]# uname -a
Linux centos64 2.6.32-431.1.2.0.1.el6.x86_64 #1 SMP Fri Dec 13 13:06:13 UTC 2013 x86_64 x86_64 x86_64 GNU/Linux
[root@centos64 ~]# free
total used free shared buffers cached
Mem: 502260 75488 426772 0 4464 29224
-/+ buffers/cache: 41800 460460
Swap: 0 0 0


Debian 7.0 x32



root@debian32:~# uname -a
Linux debian32 3.2.0-4-686-pae #1 SMP Debian 3.2.54-2 i686 GNU/Linux
root@debian32:~# free
total used free shared buffers cached
Mem: 514436 53284 461152 0 6024 29796
-/+ buffers/cache: 17464 496972
Swap: 0 0 0


Debian 7.0 x64



root@debian64:~# uname -a
Linux debian64 3.2.0-4-amd64 #1 SMP Debian 3.2.54-2 x86_64 GNU/Linux
root@debian64:~# free
total used free shared buffers cached
Mem: 508936 62376 446560 0 6320 31408
-/+ buffers/cache: 24648 484288
Swap: 0 0 0


Ubuntu 14.04 x32



root@ubuntu32:~# uname -a
Linux ubuntu32 3.13.0-24-generic #46-Ubuntu SMP Thu Apr 10 19:08:14 UTC 2014 i686 i686 i686 GNU/Linux
root@ubuntu32:~# free
total used free shared buffers cached
Mem: 507292 193632 313660 312 17808 152596
-/+ buffers/cache: 23228 484064
Swap: 0 0 0
root@ubuntu32:~#


Ubuntu 14.04 x64



root@ubuntu64:~# uname -a
Linux ubuntu64 3.13.0-24-generic #46-Ubuntu SMP Thu Apr 10 19:11:08 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux
root@ubuntu64:~# free
total used free shared buffers cached
Mem: 501868 279348 222520 312 15428 225604
-/+ buffers/cache: 38316 463552
Swap: 0 0 0


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


1. Ubuntu 14.04 является очень прожорливой в плане потребления ОЗУ и поэтому становится сомнительным выбором для дроплета с малым объемом памяти.

2. Версии для x64 потребляют больше памяти на 20% в случае Debian и на 45% в случае CentOS и Ubuntu.


Для себя на этот раз я сделал выбор в пользу 32-битной версии Debian, однако, если рассматривать вариант с более мощным дроплетом, то мой выбор упал бы на Ubuntu x64.


Надеюсь, это информация будет вам полезна, когда в следующий раз вам нужно будет выбирать между дистрибутивами для VPS.


This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


Парадокс Симпсона в мобильной аналитике

Аналитика в мобильном маркетинге не так проста, как может показаться на первый взгляд. Некоторые метрики не всегда отражают реальное положение вещей, поэтому к ним стоит относиться очень внимательно. Сегодня мы расскажем о парадоксе Симпсона или парадоксе объединения. Этот статистический феномен описывает условия, когда в нескольких группах данных может быть общая тенденция, но при рассмотрении их в отдельности ситуация меняется — тенденции становятся противоположными.

Рассмотрим это на гипотетическом примере, близком к мобильному маркетингу. Допустим, что есть группа пользователей, из которых 5000 — пользователи iOS, а 10000 — Android. Средняя конверсия составляет 5%: 4% для iOS и 5,5% для Android. Согласитесь, что менеджер по продукту на основе этих данных может принять вполне конкретные решения …и совершить ошибку. Рассмотрим данные подробнее — с разбивкой по устройствам. Из пяти тысяч пользователей Apple в группе оказывается 1500 iPad (конверсия 6,67%) и 3500 iPhone (2,86%), а в группе платформы Google 8000 планшетов (6,25%) и 2000 смартфонов (2,5%). То есть выясняется, что конверсия в обеих группах устройств Apple оказывается выше, если рассматривать их в отдельности.


image


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


Чтобы избежать таких ошибок, не объединяйте разнородные группы. Ниже список критериев, которые нужно использовать в мобильном маркетинге для разделения данных на группы:

— регионы или страны;

— типы и платформы устройств;

— источники получения информации;

— поведенческие сигналы;

— дата установки (в случае изучения сезонности спроса).


Источник: mobiledevmemo.com


http://ift.tt/1kelqlk


This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


Дайджест интересных материалов из мира веб-разработки и IT за последнюю неделю №108 (4 — 10 мая 2014)

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.




Метки лучше разделять запятой. Например: общение, социальные сети, myspace.com, подростки, мердок


или закрыть

This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


Психология роботов и умные компьютеры: как это работает и где этому научиться. Лекция Максима Мусина в Яндексе

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

Если вспомнить фильм «Териминатор» и технологии, которыми по сюжету пользовались киборги, то можно будет выделить и нейронные сети, и возможность беспроводной связи с внешним источником интеллекта (Skynet), и компьютерное зрение, и распознавание звука, понимание различных языков. На момент выхода фильма на экраны все это было абсолютной фантастикой, технологиями далекого будущего. Но сегодня большая часть этих технологий реализована в том или ином виде. Попробуем разобраться, что же из всего перечисленного уже используется.




На картинке выше можно увидеть результат работы полностью автономного алгоритма распознавания. Практически все объекты на картинке распознаны верно. Что касается распознавания голоса, то с ними в наше время сталкивался почти каждый обладатель смартфона: голосовой ввод поисковых запросов и голосовые команды GPS-навигатору уже не кажутся чем-то необычным. Технологии машинного перевода благодаря переходу на статистическую модель также за последние пару десятилетий продвинулись очень далеко и плотно вошли в нашу жизнь. В большинстве случаев перечисленные технологии применяют передачу данных на сервер, который распознает, переводит или еще каким-либо образом обрабатывает данные. Так что и беспроводная связь с внешним интеллектом уже реализована и широко применяется.


Но отойдем от «Терминатора» и попробуем определить, что же понимается под искуственным интеллектом, и в какой момент мы сможем с уверенностью сказать, что машина сравнялась по интеллекту с человеком. На ум сразу приходит всем известный тест Тьюринга, в котором человеку нужно определить, с кем он общается: с человеком или машиной.


Как работают умные компьютеры




Человеческое мышление построено на основе сетей нейронов головного мозга. Эти нейроны вдохновили ученых на создание искусственного нейрона. Искуственный нейрон получает на вход несколько значений с различными весами, зависящими от важности входящей связи. Нейрон проводит взвешенное суммирование и выдает предсказанное значение и принимает некоторое решение:


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



Если сигнал оказывается справа от прямой, он становится синим, а если слева – красным. Нейрон раскрашивает часть плоскости в плюс, а часть плоскости – в минус. Таким образом, у нас появляется способ тренировать искусственные нейроны. Предположим, у нас есть некоторое количество точек, про которые мы знаем, плюсовые они или минусовые. Мы берем наш нейрон и за счет манипуляции с весами мы получаем каждый раз новое уравнение плоскости. И мы подбираем это уравнение плоскости таким образом, чтобы все синие точки были по одну сторону прямой, а все красные – по другую.



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



Если мы возьмем yi за правильные значения, а xi за входящие значения, то мы можем получить вот такой квадрат:



Эту функцию мы можем дифференцировать, а значит мы можем найти ее минимум, так как это и есть ошибка – отклонение настоящих значений от предсказанных. Так можно натренировать нейрон, который достаточно хорошо разделяет точки.


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



Однако так все это работает только до тех пор, пока буквы стоят в сетке ровно. Из-за слишком большого количества вариантов расположения букв, алгоритм уже не справляется распознаванием. Как можно решить эту проблему? Первое, что приходит в голову – попытаться вместо сетки использовать для букв набор каких-нибудь отличительных признаков. Будем смотреть на букву не как на 25 пикселей, а как на набор характеристик, которые не зависят от поворотов. Например, рассмотрим ее как граф, вытащим оттуда вершины первой степени, второй степени, третьей степени, вытащим циклы.



Нейронная сеть





Если мы возьмем три значения и каждое из них отправим на три нейрона первого слоя, каждый из них посчитает взвешенную сумму, посчитает сигмоиду и переправит ее на следующий слой, в итоге все они соберутся на выходной нейрон, который построит предсказанное значение. Зачем это нужно? Примерно так организован наш мозг: нейроны связаны друг с другом, у них есть входные и выходные ответвления. Обучается такая есть примерно следующим образом:



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


This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


Два проекта массового онлайн-сотрудничества

На дворе 21 век. По предсказаниям писателей-фантастов 20 века мы должны жить с летающими автомобилями за окном и роботами, которые делают все за нас, а то и вообще уже лететь к другим звездным системам. К сожалению это не так. Но кое-что фантасты предсказать не сумели, точнее не смогли предсказать влияние, которое оказал интернет на общество и цивилизацию в целом. Я не смогу описать влияние интернета на общество, для этого потребуется не одна сотня «хабрапостов», а то и больше, но я выделю интересное для меня направление, а именно краудсорсинг.

Начну я пожалуй с рассказа о проекте CAPTCHA. Этот проект знаком не только специалистам IT, но и каждому пользователю интернета. Он всех раздражает, но без него наша жизнь была бы ужасна. Кол-во спама было бы в разы больше. Но именно такой раздражающий всех проект как CAPTCHA подтолкнул Луиса фон Ана из университета Карнеги — Меллон к созданию проекта reCAPTCHA. Этот проект также нес в себе функцию защиты от ботов. Добавилась одна составляющая, невидимая для обычных пользователей — помощь в оцифровке текстов книг. Теперь при вводе текста с картинки, пользователю предлагается ввести два слова. Первое уже известно системе, а второе слово системе неизвестно и она не способна его распознать программой распознавания текста. Второе слово берется из источника, требующего распознавания (например, книги). Проверка и прохождение «капчи» осуществляется по тому слову, которое известно системе. Неизвестное слово вводить необязательно, так как результат закрепляется на основе статистики, а не ответа одного пользователя, что позволяет выбрать верный вариант. В сентябре 2009 года reCAPTCHA была приобретена компанией Google. А весной 2012 года Google запустил эксперимент по распознаванию изображений из Google Maps и Google Street View с помощью сервиса reCAPTCHA.



Продолжение читайте под катом.


Нашел еще один древний пост на хабре: Как работает reCAPTCHA? (он весьма информативен).


Все это и много другое Луис фон Ан рассказывает в своем выступлении на конференции TED:



В этом выступлении Луис начинает рассказывать о проекте Duolingo, о нем ранее писал black_bunny. Не буду пересказывать, то что и так уже написано, добавлю лишь, что на данный момент этот проект продвинулся далеко вперед, а эти ребята, эксплуатирующие сотни миллионов человек по всему миру, стали моими кумирами. Это демонстрация того, как идеи меняют мир, а самое главное, что теперь изменить мир под силу даже небольшой группе людей, которая создаст сервис для онлайн-взаимодействия и сотрудничества людей по всему миру.


This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


Кросскомпиляция библиотек под iOS, делаем это правильно

Во время разработки большого проекта возникает такой момент, когда надо встроить в приложение библиотеку из мира open source с подходящей лицензией. Например, вам захотелось ускорить декодирование картинок, или понадобился sqlite3 с fts4, или нужны какие-то плюшки из libicu, которых нету в системной libicucore.

Для этого библиотеку, которая понадобилась, нужно будет собрать для 5 архитектур: armv7, armv7s, arm64, i386, x86_64. С кросскомпиляцией есть много подводных камней, на которые не хотелось бы наткнуться, когда есть уже проверенные решения. В этом коротком посте я расскажу об автоматизации сборки библиотек на примере protobuf и sqlite3.



Прежде чем что-то делать, нам надо определиться с тем, что же нам надо на выходе.



  • Автоматизация процесса сборки нескольких библиотек

  • Удобство добавления новых библиотек

  • Распространение решения внутри репозитория

  • Сохранение заголовочных файлов для всех архитектур




Исходя из этих требований получилось следующее решение. Makefile, который управляет скачиванием, распаковкой, патчингом и сборкой библиотек. Код его не большой, так что можно привести тут и обсудить. (Или скачать c github и запустить, а потом читать дальше.)

Определяем путь к самому новому SDK в системе и в build_arches: запускаем этот же Makefile до arch: с заполнеными переменными ARCH и IOS_PLATFORM. После того как build_arches: отработает, запустится скрипт, который соберет для каждой из библиотек жирную fat версию.



XCODE_DEVELOPER = $(shell xcode-select --print-path)
IOS_PLATFORM ?= iPhoneOS

# Pick latest SDK in the directory
IOS_PLATFORM_DEVELOPER = ${XCODE_DEVELOPER}/Platforms/${IOS_PLATFORM}.platform/Developer
IOS_SDK = ${IOS_PLATFORM_DEVELOPER}/SDKs/$(shell ls ${IOS_PLATFORM_DEVELOPER}/SDKs | sort -r | head -n1)

all: build_arches
mkdir -p lib

# Make fat libraries for all architectures
for file in build/armv7/lib/*.a; \
do name=`basename $$file .a`; \
${IOS_PLATFORM_DEVELOPER}/usr/bin/lipo -create \
-arch armv7 build/armv7/lib/$$name.a \
-arch armv7s build/armv7s/lib/$$name.a \
-arch arm64 build/arm64/lib/$$name.a \
-arch i386 build/i386/lib/$$name.a \
-arch x86_64 build/x86_64/lib/$$name.a \
-output lib/$$name.a \
; \
done;
echo "Making fat libs"

# Build separate architectures
build_arches:
${MAKE} arch ARCH=armv7 IOS_PLATFORM=iPhoneOS
${MAKE} arch ARCH=armv7s IOS_PLATFORM=iPhoneOS
${MAKE} arch ARCH=arm64 IOS_PLATFORM=iPhoneOS
${MAKE} arch ARCH=i386 IOS_PLATFORM=iPhoneSimulator
${MAKE} arch ARCH=x86_64 IOS_PLATFORM=iPhoneSimulator




Когда make будет работать над зависимостями указанными в цели arch: переменные окружения будут проинициализированы для той архитектуры, которая собирается в настоящий момент. Обратите внимание, что мы заполнили PREFIX и make install библиотек установит результат сборки в папки ./build/armv7, ./build/armv7s и т.д.

Цель arch: указывает на цели от которых она зависит. В нашем случае это библиотеки, которые мы собираем. При добавлении новых библиотек — их цели надо будет добавить в зависимости arch: иначе они не соберутся.



PREFIX = ${CURDIR}/build/${ARCH}
LIBDIR = ${PREFIX}/lib
INCLUDEDIR = ${PREFIX}/include

CXX = ${XCODE_DEVELOPER}/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang++
CC = ${XCODE_DEVELOPER}/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang
CFLAGS = -isysroot ${IOS_SDK} -I${IOS_SDK}/usr/include -arch ${ARCH} -miphoneos-version-min=5.0
CXXFLAGS = -stdlib=libc++ -isysroot ${IOS_SDK} -I${IOS_SDK}/usr/include -arch ${ARCH} -miphoneos-version-min=5.0
LDFLAGS = -stdlib=libc++ -isysroot ${IOS_SDK} -L${LIBDIR} -L${IOS_SDK}/usr/lib -arch ${ARCH} -miphoneos-version-min=5.0
LIBTOOLFLAGS = -arch_only ${ARCH}

arch: ${LIBDIR}/libsqlite3.a ${LIBDIR}/libprotobuf.a




В последней части осталось самое простое. Цели сборки библиотек, которые зависят от целей скачивания исходников. Именно тут можно указать кастомные ключи для ./configure или добавить поддержку arm64 в protobuf.

${LIBDIR}/libsqlite3.a: ${CURDIR}/sqlite3
cd sqlite3 && env CXX=${CXX} CC=${CC} CFLAGS="${CFLAGS}" \
CXXFLAGS="${CXXFLAGS} -DSQLITE_ENABLE_RTREE -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_FTS4_UNICODE61" \
LDFLAGS="${LDFLAGS}" ./configure --host=arm-apple-darwin --disable-shared --prefix=${PREFIX} && ${MAKE} clean install

${LIBDIR}/libprotobuf.a: ${CURDIR}/protobuf
cd protobuf && env CXX=${CXX} CC=${CC} CFLAGS="${CFLAGS}" CXXFLAGS="${CXXFLAGS}" LDFLAGS="${LDFLAGS}" \
./configure --host=arm-apple-darwin --disable-shared --with-protoc=/usr/local/bin/protoc --prefix=${PREFIX} && ${MAKE} clean install

${CURDIR}/sqlite3:
curl http://ift.tt/1fWrKAM > sqlite3.tar.gz
tar xzvf sqlite3.tar.gz
rm sqlite3.tar.gz
mv sqlite-autoconf-3080403 sqlite3
touch sqlite3

${CURDIR}/protobuf:
curl http://ift.tt/1lbUhRR > protobuf.tar.gz
tar xzvf protobuf.tar.gz
rm protobuf.tar.gz
mv protobuf-2.5.0 protobuf
# add arm64 support http://ift.tt/1fWrMZq
patch -p0 <protobuf_arm64.patch
touch protobuf

clean:
rm -rf build lib sqlite3 protobuf




На выходе: в папке /lib лежат fat версии библиотек, а в build/{$ARCH}/include заголовочные файлы, которые могут пригодиться в работе.

Заголовочные файлы для каждой архитектуры раздельно нужны не всегда, но встречаются библиотеки, которые на этапе ./configure в явном виде сохраняют размеры системных типов в заголовочный файл, например в config.h. Когда мы используем такой файл для arm64 и armv7 одновременно, есть риск, что что-то пойдет не так на каком-то этапе работы. И именно чтобы не гадать поломается что-то в логике работы библиотеки или или нет и не включать в проект дополнительное тестирование библиотеки на всех архитектурах в поисках проблем совместимости, я для всех fat библиотек использую раздельные версии заголовочных файлов. Сделать это просто, в Header Search Path нужно добавить "$(SRCROOT)/../../libs/build/$(arch)/include". Где /../../libs/build/ путь к папке build относительно файла xcodeproj.


Этот способ сборки я подсмотрел в github репозитории сборки растрового рендера mapnik, там же можно посмотреть более сложный вариант Makefile, когда одна библиотека зависит от нескольких других.


Файлы этого поста можно скачать с github и полюбоваться на бегущие строчки кросскомпиляции. Достаточно набрать make.


This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


Popcorn Time для Android


сегодня в 10:35




После закрытия проекта Popcorn Time появилось множество форков (благо проект был Open Source), которые тоже стали неожиданно закрываться. Но не все.



И вот, буквально вчера, вышла Open Source версия для Android (4.2 и выше). Правда, почти сразу же после публикации, она была удалена из Google Play Store. Но, автор выложил исходники., так что любой сможет собрать свой .apk. Или скачать уже готовый .apk файл.


Я попробовал и получил следующую картинку:





Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.


This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


[Перевод] Статус «Кровоточащего сердца»: обновление до «Разбитого»


Для сведения: Во многих упоминаниях данной статьи авторы ошибочно называют меня сотрудником Opera Software. На самом деле я ушёл из Opera больше года назад и сегодня работаю в новой компании — Vivaldi Technologies AS


Предыстория


Как я уже рассказывал в моей предыдущей статье, несколько недель назад в библиотеке OpenSSL была обнаружена уязвимость (CVE-2014-0160), получившая громкое название "Heartbleed". Данная уязвимость позволяла злоумышленникам добывать такую важную информацию, как, например, пользовательские пароли или закрытые ключи шифрования сайтов, проникая на уязвимые «защищённые» веб-серверы (поясняющий комикс).


В результате, все затронутые данной напастью веб-сайты должны были пропатчить свои серверы, а также выполнить другие действия, чтобы обезопасить своих пользователей. Стоит отметить, что уровень опасности значительно возрос после того, как информация об уязвимости разлетелась по сети (было зафиксировано несколько серьёзных происшествий и как минимум один человек, пытавшийся использовать Heartbleed в корыстных целях, оказался под арестом).



Замедление активности использования патча


В течении нескольких недель со дня обнаружения уязвимости я отслеживал активность применения исправляющего патча с помощью собственного тестера TLS Prober. В настоящий момент тестер сканирует примерно 500 000 отдельных серверов, используя изменения имён хостов в различных доменах (общим числом 23 миллиона) с выборкой около миллиона веб-сайтов из Alexa Top.


Согласно шести сканированиям, проведённым мной в период с 11 апреля, число уязвимых серверов резко сократилось с 5,36% от числа всех проверяемых серверов до 2,33% на этой неделе. Около 20% просканированных серверов поддерживают Heartbeat TLS Extension, это говорит о том, что до 75% уязвимых серверов было пропатчено в течении первых четырёх дней после обнаружения уязвимости, т.е. до моего первого сканирования.


Однако, если за первые две недели сканирования число уязвимых серверов сократилось вдвое — до 2,77%, в последующие пару недель эта цифра снизилась только до 2,33%, что указывает на почти полную остановку процесса «лечения» уязвимых серверов.


image


Вообще, сканирование показывает, что серверы наиболее популярных веб-сайтов были пропатчены. Около 73% сканируемых веб-сайтов используют сертификаты от Certificate Authority, распознаваемые браузерами, и только 30% уязвимых веб-сайтов используют подобные сертификаты. Всё это немного сокращает важность проблемы в целом, но даже если какие-то серверы выглядят малозначимыми в масштабах всего интернета, реальным пользователям данных серверов от этого не легче.


Есть и не менее серьёзная проблема: многие сертификаты, использовавшиеся уязвимыми серверами, продолжают использоваться и на исправленных серверах. Фактически, если предположить, что все серверы, поддерживавшие Heartbeat при первом сканировании, были перед этим уязвимы, то 2/3 сертификатов не были обновлены после исправления серверов с помощью патча (т.к. они оказались теми же самыми, что и до исправления серверов). Учитывая, что каждый сервер, пропатченный после 7 апреля, предположительно обладал скомпрометированным закрытым ключом сертификата (ведь злоумышленники могли использовать Heartbleed на этих серверах), можно говорить о серьёзной проблеме, до сих пор угрожающей пользователям подобных веб-сайтов.


Кроме того, копание в цифрах выявило ещё две проблемы.


Число уязвимых серверов F5 BigIP остаётся стабильным


Первая проблема заключается в том, что абсолютное число серверов F5 BigIP (мощные ускорители SSL/TLS) с определённой конфигурацией, использующей уязвимые версии OpenSSL, остаётся неизменным. Выявить BigIP серверы сегодня очень легко, т.к. они имеют проблемы с определёнными видами квитирования TLS.


image


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


image


Причина того, что абсолютное число уязвимых серверов BigIP оставалось неизменным, кроется в следующем: число серверов BigIP включая те, что используют OpenSSL версии 1.0.1 (с поддержкой Heartbeat), было удвоено за последний месяц (после медленного снижения за последние пару лет), и многие новоиспечённые серверы используют уязвимую версию библиотеки OpenSSL.


image


image


Я предполагаю, что частично проблема создана новыми владельцами серверов BigIP: возможно, они просто забыли обновить оригинальное серверное ПО после его установки.


Учитывая, что серверы BigIP как правило используются для обслуживания большого числа пользователей, можно предположить, что все они имеют довольно серьёзные проблемы с безопасностью.


Обновление до «Разбитого сердца»


Вторая проблема также связана с числом уязвимых серверов BigIP, но выглядит гораздо хуже: при моём последнем сканировании было обнаружено 20% уязвимых на текущий момент серверов (выборкой по IP), и 32% (!) уязвимых серверов BigIP, которые не были уязвимы во время предыдущих проверок. Это означает, что тысячи сайтов, ранее не имевших проблем с Heartbleed, теперь эти проблемы получили!


image


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


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


Для исправления данной проблемы владельцам уязвимых серверов придётся вновь пройти всю процедуру наложения патча, что приведёт к денежным затратам, которых в действительности можно было избежать. Ориентировочно, наложение патча, замена сертификата и проведение соответствующего тестирования потребует работы трёх системных администраторов в течении четырёх часов. При стоимости каждого админочаса в $40, приблизительный общий размер лишних расходов на исправление 2500 «заболевших» серверов (в моём примере) может составить около $1,2 млн. А учитывая, что моё тестирование охватывает лишь около 10% всех серверов в сети, общий объём непредвиденных расходов составит $12 млн.


Heartbleed является очень серьёзной проблемой и исправление данной уязвимости необходимо сделать всем затронутым данным «несчастьем» серверам, но я начинаю думать, что активность тематической прессы, страдающей массой неточностей и дающей преждевременные советы (таких, как, например, «замените свой пароль немедленно!» не дожидаясь обновления ПО сервера или «аннулирование сертификатов в связи с Heartbleed значительно замедлит скорость работы в сети»), является, в какой-то степени, контрпродуктивным.


Вышеупомянутые данные о серверах, которые были «обновлены» и таким образом стали уязвимыми для проблемы, которая раньше их не касалась, могут быть результатом искажённого освещения проблемы в прессе.


Мои рекомендации остаются неизменными: сначала устанавливаем исправляющий патч, затем обновляем сертификаты, и только потом меняем пароли (именно в такой последовательности). Тематические журналисты должны сконцентрироваться именно на этом, а все панические настроения нужно исключить полностью. Соблюдайте фактическую точность при обсуждении проблемы, предлагайте читателям способы проверки (могут ли они стать жертвой уязвимости), например, расскажите о существующих программных инструментах, таких, как Тестер серверов от SSL Labs, и не забывайте рассказать, как можно избавиться от проблемы.


This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


Автризация twitter или как убить сутки с библиотекой STTwitter

Предисловие




Убить сутки — реально, особенно учитывая наикорявейшую авторизацию twitter по сравнению например с тем же facebook.

Для сравнения алгоритм получения авторизации:

twitter

Получает token -> Использую этот токен заходим на страницу логина -> Ждем авторизации пользователя и получаем новый токен по callback_url -> обмениваем токен на токен сикрет.


facebook

Открываем facebook страницу логина с параметром calback_url и другими опциями -> ждем авторизиции а на callback получаем уже токен сикрет который можно использовать.


Да не спорю, есть SDK от обоих соц сетей, есть встроенные записи, но мне необходимо было реализовать именно данный метод авторизации. Потому что другие уже достаточно красиво реализованы и есть множество библиотек. Но что делать если встроенные записи не активированы, или были деактивированны. Не заставлять же пользователя лезь в настройки. Пока он будет ползать может забыть о приложении. Поэтому сел я за разработку логина через web для facebook и twitter.



Но поговорить я хотел именно о twitter, потому что как я сказал выше — twitter обладает просто колоссальным количеством подводных камней по сравнению с другими социалками предоставляющими oAuth1/2


И так погнали. Для разработки я решил использовать pod библиотеку STTwitter потому что я не хотел запорачиваться и писать шафраторы (ах да для oauth 1 если вдруг кто не знает нужно посылать контрольную сумму всех параметров заголовка запроса что делать мне лично было лень). Надо сказать на удивление хорошая бибилиотека, но документации для нее нети никакой (собственно поэтому и решил запостить свое решение). В остальном же- все стандартно.


Шаг 1 совместно со 2




Первым делом необходимо получить первый токен, который вообще не понятно зачем нужен. Ну да ладно, против системы не попрешь. Тут все просто, необходимо послать в post запросе авторизацию по данным приложения (api keys) и контрольные суммы.

Забегая немного вперед: используя полученный токен мы должны составить ссылку, по которой отправим пользователя. Чтобы не терять пользователя (не давать ему выходить из проложения) можно было бы сделать достаточно просто: открыть сафари, а затем по callback_url и кастомного url schemes для приложения (например myappscheme://parseresult) вернуться в приложение и заспарсить переданные твиттером параметры. И вот он первых подводный камень- twitter не дает зарегистрировать кастомные схемы url. Из-за этого возврат в приложение по такой схеме становится невозможным. Как быть? Прийдется делать кастомный UIWebView и простыл делегатным методом webView:shouldStartLoadWithRequest: перехватывать и проверять загружаемые ссылки нашего UIWebView. Теперь осталось сделать callback какойнибудь нереальный домен (который всеравно не загрузится, либо уже поменять на реальный, на всякий случай, когда приложение заимеет сайт) типа getmetothewounderlandandfeedmewitharainbownearpinkunicorns.com

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

У меня вся логика по обращению к библиотеке STT вынесена в отдельные классы, думаю будет понятно что где поэтому акцентировать что откуда не буду, темболее что пишу достаточно подробно.

Быблиотекой STTwitter это делается просто:

Сразу ленивая инициализация, чему я научился от бешенного старика из сенфорда :D (кто знает тот поймет). Вырезал всю лишнюю шелуху типа проверки на внутреннюю учетку в системе и тд.



// api и oauth - сильные публичные свойства класса twitterEngine
-(STTwitterOAuth *)oauth {
if (!_oauth) {
_oauth = [STTwitterOAuth twitterOAuthWithConsumerName:@"APP_NAME" consumerKey:TWI_API_KEY consumerSecret:TWI_API_SECREET];
}
return _oauth;
}

-(STTwitterAPI *)api {
if (!_api) {
if ([twitterEngine isAuthorized]) {
NSLog(@"Full access account");
_api = [STTwitterAPI twitterAPIWithOAuthConsumerKey:TWI_API_KEY consumerSecret:TWI_API_SECREET oauthToken:[[NSUserDefaults standardUserDefaults] valueForKey:TWI_STORE_AUTH_TOKEN] oauthTokenSecret:[[NSUserDefaults standardUserDefaults] valueForKey:TWI_STORE_AUTH_SECRET]];
} else {
NSLog(@"App only client");
_api = [STTwitterAPI twitterAPIAppOnlyWithConsumerName:IN_APP_NAME consumerKey:TWI_API_KEY consumerSecret:TWI_API_SECREET];
}
}
return _api;
}


Немного контроллера:



- (IBAction)loinTwitterButtonClicked:(id)sender {
twitter = [twitterEngine new]; // twitterEngine мой класс для работы со всеми внешними средствами с twitter
if ([twitterEngine isAuthorized]) {
// handling authorized
} else {
twitter.delegate = self;
[twitter sendRequestToken];
}
}


Класса:



-(void)sendRequestToken
{
[self.oauth postTokenRequest:^(NSURL *url, NSString *oauthToken) {
[self.delegate openWebViewWithUrl:url]; // простейший протокол передачи полученного url для загрузки в UIWebVew
} oauthCallback:@"http://ift.tt/1la99Ao" errorBlock:^(NSError *error) {
// handling error
}];
}


И снова контроллера (тут метод протокола, и делегат от UIWebView который перехватывает загружаемые URL



-(void) openWebViewWithUrl:(NSURL *)url
{
// Если не задать размер за минусом статус бара браузер будет коряво накладываться на часы/батарейку/оператора
UIWebView *browser = [[UIWebView alloc] initWithFrame:CGRectMake(0, 20, self.view.frame.size.width, self.view.frame.size.height - 20)];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[browser loadRequest:request];
[browser setDelegate:self];
[self.view addSubview:browser];
}

-(BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
NSURL *url = [request URL];
NSLog(@"Loading URL: %@", [url absoluteString]);
if ([[url host] isEqualToString:@"getmetothewounderlandandfeedmewitharainbownearpinkunicorns.com"]) {
[webView removeFromSuperview]; // Удаляем себя за не надобностью
NSDictionary *params = [twitterEngine parseURLToParams:url];
[twitter sendAccessToken:[params objectForKey:@"oauth_verifier"]]; // это и есть прямой выход на ШАГ 3 и обмен токена на сикрет.
return NO; // если возвращаем NO то браузер просто не будет загружать эту ссылку, но параметры мы уже получили.
}
return YES;
}


Шаг 3




И походу последний. На изучение библиотеки STT на эту тему (последний шаг) я убил больше всего времени а решение оказалось в пару строчек. Но, как я и хотел я избежал главного, возни с контрольной суммой и соединения все в заголовки. Так же хочу заметить, что по тупости oAuth1 от twitter обмен токена oauth_verifier не посредством заголовка (!!!) как это делалось в предыдущих шагах, а именно а теле самого POST запроса. Вот тут STT мне воткнула палку в колесо. Как оказалось STT не имеет прямого инструмента работы через схему авторизации которую я выбрал, а именно не имеет метода обмена oauth_verifier на oauth_token_secret. Пришлось полезть глубже и использовать очень глубокие методы суперклассов, которые по сути были чуть ли не простым системным методом отсылки. Немного отступая — http протокол знаю, умею, но мне хватило в свое время повозиться с сокетами чтобы я возненавидел такую низкоуровневую работу с протоколами. Мне ближе если можно так сказать программирование средних уровней (не UI но и не прямые протоколы). Но пути назад уже небыло, и я уже приготовился писать свой дополнительный метод, который реализует обмен токена. Боже копаться в чужом коде, да еще и дописывать его- для меня это сущий кошмар, хотя бы потому, что его писал не я. Но деваться было некуда… как друг я заметил знакомую сроковую переменную @«oauth_verifier» и тут же начал изучать метод где она была замечена. Вообще, почему я не догадался воспользоваться поиском по этому ключу досих пор не знаю. Сэкономил бы несколько часов.

Что оказалось: есть в твиттере авторизация по pin коду. Схема похожая, но реализована она как раз для приложений которые не могу сделать callback внутрь и тем самым получить токен на обмен. По сути PIN в этой схеме есть упрощенный (ну блин, запомните с сайта 8 цифр и введете их в приложении?) токен для обмена. И так как я ни разу не изучал схему авторизации по пину (да и всеравно она задокументирована двумя абзацами даже в базе знаний твиттера) я не знал, что механизм у них один. Благо я решил назвать свой метод похожим на

postAccessTokenRequestWithPIN:successBlock:errorBlock:


Это тот самы метод библиотеки STT который по сути и реализововал то, что нужно было мне. Пару нажатий на клавиши и мы получаем последний метод класса twitterEngine реализующий обмен токена на токен сикрет и его запись в карман для последующего восстановления:



-(void)sendAccessToken:(NSString *)oauth_verifier
{
[self.oauth postAccessTokenRequestWithPIN:oauth_verifier successBlock:^(NSString *oauthToken, NSString *oauthTokenSecret, NSString *userID, NSString *screenName) {
[[NSUserDefaults standardUserDefaults] setObject:oauthToken forKey:TWI_STORE_AUTH_TOKEN];
[[NSUserDefaults standardUserDefaults] setObject:oauthTokenSecret forKey:TWI_STORE_AUTH_SECRET];
[[NSUserDefaults standardUserDefaults] synchronize];
} errorBlock:^(NSError *error) {
// handling error
}];


Заключение




По сути все. Далее работа лежала над проверкой внутренней записи в телефоне что STT умеет делать по сути прямо при реализации. Если у вас есть какието мысли как улучшить данное решение будет интересно послушать. Тем более что по своей природе я как iOS разработчик склонен злоупотреблять протоколами и обзервингом, уж больно они мне нравятся.

This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


Функция reduce

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

Конкретно JavaScript обладает целым огородом камней. Подводным огородом.

На практике, подводные камни встречаются не так часто, напротив, хороший код склонен быть описанным в рамках здорового подмножества языка. Это также является и причиной, почему запомнить все заковырки языка достаточно сложно: они не являются необходимыми для каждодневной практики. Тем не менее, разнообразные граничные случаи использования языковых конструкций это отличная разминка для ума, а также стимул узнать язык немного лучше. Сегодняшний экземпляр попался мне на глаза в процессе прохождения JavaScript Puzzlers.


Меня заинтересовал вопрос номер 3:

Каков результат этого выражения (или нескольких)?



[ [3,2,1].reduce(Math.pow), [].reduce(Math.pow) ]




В качестве ответа авторами, на выбор, даются следующие варианты:

* ошибка

* [9, 0]

* [9, NaN]

* [9, undefined]

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


Несмотря на то, что пример достаточно отстранённый, аппликация функций и частично определённых функций к коллекциям это распространённая практика для JS, и, при здравом использовании, она способна сделать код чище, как в плане исполнения — избавить его от лишних замыканий, так и в визуальном плане — меньше скобочного мусора (вопрос использования препроцессоров оставим для другой статьи).


А в этой статье вы найдёте:

* Разбор задачки.

* JavaScript reduce с чисто практической точки зрения.

* Несколько акробатических этюдов с reduce (reduce с академической точки зрения).

* Репозиторий с плюшками к статье.

* Несколько других reduce.



Разбор задачки




Чтож, для начала разберёмся с задачей в начале статьи. А вариантов здесь хватает.

reduce (здесь и далее имеется ввиду Array.prototype.reduce), вместе с другими функциями из прототипа Array: filter, map, forEach, some, every, является функцией высшего порядка, то есть она принимает на вход другую функцию (будем называть эту передаваемую функцию f*). Функция f* будет вызвана с некоторыми агрументами для каждого элемента коллекции.

Конкретно reduce, используется для генерации некоторого агрегирующего значения на основе коллекции. Она последовательно применяет f* к каждому элементу, передавая ей текущее значение переменной, в которой накапливается результат (аккумулятора) и текущий обрабатываемый элемент. Также, в reduce можно передать начальное значение аккумулятора. Причём, (!) поведение reduce будет различаться в зависимости от того, передано это значение или нет.


Функция Math.pow производит возведение в степень, то есть её поведение различается в зависимости от переданной степени: это может быть квадрат, куб, или квадратный корень или любая другая вещественная степень.


При этом остаются открытыми следующие вопросы:

* Как ведёт себя reduce, если вызвать её на пустом массиве?

* Как ведёт себя Math.pow, если недодать ей степень?


Для стандартных функций JS нет общей политики обработки ошибок. Некоторые функции могут действовать строго: бросать исключения, если что-то не так в переданных данных, некоторые будут возвращать всяческие пустые значения: null, undefined, NaN, а прочие будут работать пермиссивно: попытаются что-то сделать даже с не совсем корректными данными.


Как много вопросов затронул всего один пример.


А теперь правильный ответ: мы получим TypeError, в котором виновато второе подвыражение. Функция reduce на пустом массиве И без переданного начального значения бросает TypeError.


Почему так? Вчитываемся в спецификацию reduce




Чтож, давайте почитаем что пишет MDN o Array.prototype.reduce. Выясняются следующие тонкости работы функции:

Если initialValue передано, то на первой итерации функция будет вызвана с этим значением и значением первого элемента массива. Если же, initialValue не передано, то функция будет вызвана со значениями первого и второго элементов массива. Отсюда также следует, что если начальное значение не передано, то функция вызывается на один раз меньше, иначе ровно столько раз, сколько элементов в массиве.

Можно представлять форму с initialValue вот так:



array.reduce(fn, initialValue) ⇔ [ initialValue ].concat(array).reduce(fn);




Вторым интересным аспектом является обработка пустого массива. Если массив пустой, и передано начальное значение, то оно является результатом работы функции, а результат f* игнорируется. Если же массив пуст, а начальное значение не передано, то выбрасывается TypeError.

[].reduce(fn, initialValue) ⇔ [ initialValue ].reduce(fn) ⇒ initialValue;
[].reduce(fn) ⇒ TypeError;




На самом деле поведение функции достаточно логично: она пытается вызвать f* со значениями из входных данных. Если начальное значение передано, то оно является элементом данных идущим перед первым элементом. Если не передано ничего (нет элементов и начального значения), то функция не имеет данных для генерации агрегата, и она выбрасывает исключение. Так или иначе, поведение немножко сложное и может стать подводным камнем. reduce, по сути, перегружается для одного агрумента и для двух, и перегруженные варианты имеют разное поведение на пустом массиве.

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

* Джедайский: исполнить код в уме, зная о том как работают reduce и Math.pow.

* Ковбойский: вбить в REPL этот код и попытаться подвести рассуждения под результат.


Также, можно ознакомиться с моим примером, который должен помочь понять задачку:

StreetStrider/habrahabr-javascript-reduce:tests/puzzler.js. Он является jasmine-тестом.


Магия и шарм reduce




reduce примечателен тем, что он может быть использован для того, чтобы описать все остальные функции высшего порядка объекта Array: forEach, filter, map, some, every.

Это станет понятным, если избавиться от мысли, что reduce обязан аккумулировать значение того же типа, что и значения в массиве. Действительно, логичным кажется мыслить, что если мы берём массив чисел и суммируем их, то получаем также число. Если мы берём массив строк и конкатенируем их, то также получаем строку. Это естественно, но reduce также способен возвращать массивы и объекты. Причём передача будет происходить из итерации в итерацию благодаря аккумулятору. Это позволяет строить на reduce функции любой сложности.


Для примера, давайте построим map:



function map$viaReduce (array, fn)
{
return array.reduce(function (memo, item, index, array)
{
return memo.concat([ fn(item, index, array) ]);
}, []);
};




Здесь через аккумулятор передаётся накапливающийся массив. Он будет того же размера, что и исходный, но со значениями, пропущенными через функцию-трасформатор fn. Также здесь не забыто, то fn принимает не только элемент, но индекс и массив последующими параметрами. Параметр функции concat обёрнут в массив, чтобы избежать «развёртки» значения, если fn вернёт массив. В качестве начального значения передан пустой массив.

Этот код есть в репозитории, а ещё для него есть тесты.


Тем, кто заинтересовался, предлагаю в качестве упражнения реализовать функции filter, и одну из кванторных: some либо every. Вы заметите, что везде используется возврат накапливаемого массива.


Ещё один нетривиальный пример, который приходит на ум, это реализация функции uniq. Как известно, JavaScript страдает от отсутствия в стандартной либе многих нужных вещей. В частности, нет функции, которая устраняет дубликаты в массиве, и разработчики используют разные кастомные реализации (лично я советую использовать _.uniq из LoDash/Underscore).



function uniq$viaReduce (array)
{
return array.reduce(function (memo, item)
{
return (~ memo.indexOf(item) ? null : memo.push(item)), memo;
}, []);
};




Эта реализация использует немного JS-ного джедаизма, для лаконичности. В принципе, этот код не планируется когда-либо менять, поэтому это не наносит существенного ущерба. Здесь используется сайд-эффект внутри тернарного оператора, а именно, мы проталкиваем элемент в массив, если он не найден на текущем куске. Оператор тильда используется для сравнения с -1. Всё выражение завёрнуто в оператор запятую, который на каждом шаге (после всех действий) возвращает memo. Примечательно, что эта реализация также сохраняет порядок в массиве.

Код и тесты есть в репозитории.


Ладно, не «немного», этот код был сильно джедайский, меня оправдывает только наличие тестов и то, что это библиотечная функция, поведение которой не будет меняться.


В качестве разминки, я рекомендую реализовать, например, функцию zipObject суть её в том, что она принимает на вход массив пар (массивов), где нулевой элемент это ключ, а первый — значение, и возвращает сконструированный Object с соответствующими ключами/значениями.


Подробнее о репозитории.




Репозиторий с примерами является npm-пакетом. Его можно поставить, используя адрес на гитхабе:

npm install StreetStrider/habrahabr-javascript-reduce




В src/ лежат примеры функций, в tests/ — jasmine-тесты. Прогнать все тесты можно с помощью npm test.

Другие reduce




* В JavaScript у reduce есть злой брат-близнец правосторонний аналог: reduceRight. Он нужен, чтобы агрегировать массивы справа-налево, без необходимости в дорогостоящем reverse.

* LoDash/Underscore есть _.reduce, _.reduceRight. Они обладают рядом дополнительных возможностей.

* В Python есть reduce. Да. Но он официально не рекомендуется к использованию. Вместо него предлагается использовать списковые выражения и конструкции for-in. Кода получается больше, но он становится намного более читаемым. Это соответствует Дао языка.

* В некоторых языках reduce/reduceRight называются foldl/foldr.

В SQL есть пять стандартных агрегирующих функций: COUNT, SUM, AVG, MAX, MIN. Эти функции используются, чтобы свести результирующую выборку к одному кортежу. Аналогичные функции можно реализовать на JavaScript (тоже на reduce).


Кстати, четыре из пяти агрегирующих функций SQL (не считая COUNT) возвращают NULL, если выборка пустая (COUNT возвращает определённое значение: 0). Это полностью аналогично JS-ному TypeError на пустом списке.



postgres=# SELECT SUM(x) FROM (VALUES (1), (2), (3)) AS R(x);
sum
-----
6
(1 row)



postgres=# SELECT SUM(x) IS NULL AS sum FROM (VALUES (1), (2), (3)) AS R(x) WHERE FALSE;
sum
-----
t
(1 row)





Ссылки



  1. JavaScript Puzzlers.

  2. MDN: Array.prototype.reduce().

  3. github:StreetStrider/habrahabr-javascript-reduce.

  4. JavaScript.ru: Массив: Перебирающие методы.


Благодарности




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

Спасибо всем, кто напишет мне в личные сообщения об ошибках и недочётах в статье, а также в репозитории.

Спасибо за внимание.


This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


пятница, 9 мая 2014 г.

[Перевод] Oracle выиграла суд против Google


сегодня в 23:44




Корпорация Oracle одержала победу в ключевом деле против Google. Аппеляционный суд США постановил: Oracle обладает авторским правом на части языка программирования Java, использованные Google при разработке ОС Android. «Мы пришли к выводу, что набор команд, заставляющий компьютер выполнить желаемые операции, может содержать выражения, подвергаемые авторскому праву», — комментирует судья федерального округа Кэтлин О'Мэлли.

В 2010 Oracle подала в суд на Google за нарушение авторских прав на части Java для Android. Oracle оценила ущерб в один миллиард долларов.


Oracle чрезвычайно удовлетворена: «решение федерального округа — большая победа для Oracle и всей индустрии программного обеспечения, продиктованное защитой авторского права для стимулирования инноваций и уверенности, что разработчики будут вознаграждены за свои достижения».


Компания Google не прокомментировала ситуацию.




Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.


This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


[recovery mode] Что подразумевают под APT?


сегодня в 23:00


APT, которые часто переводят на русский как целенаправленные атаки, стали популярной темой страшилок от ИБ. Под APT подразумевался вирус Stuxnet, под APT подразумевались атаки на RSA и Sony, под APT подразумевалась атака на Gmail под кодовым названием «Аврора». Последнюю, правда, иногда расшифровывали как Asia Pacific Threat. Очевидно, что каждая компания под APT подразумевает что-то своё, поэтому было бы интересно, что именно каждый вкладывает в это словосочетание. Попытаемся классифицировать определения целевых атак, которые используют различные компании.

Итак, я предлагаю определить следующие квалификационные признаки APT:



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

  • Сложность кодов. В некоторых случаях под целевыми атаками подразумевают сложные коды, которые с лёгкостью проходят существующие в конкретной компании средства защиты. Такие атаки, как правило, действительно целенаправлены — коды разрабатываются под заказ для проникновения в корпоративную сеть конкретной компании, предварительно изучив, например, с помощью методов конкурентной разведки используемые в компании средства ИТ и сопутствующие им защитные механизмы. Такой атакой вполне могла быть атака на RSA и Sony.

  • Скрытность. В некоторых случаях целенаправленной считают атаку, которая закрепляется в информационной системе и хакеры долго контролируют внедренные при атаке вредоносные коды. Такие скрытые атаки позволяют украсть много ценных данных, хотя и организовать их значительно сложнее. Хакерам приходится постоянно менять коды так, чтобы их невозможно было обнаружить, использовать скрытые каналы взаимодействия с внедренными агентами и оставлять в захваченной системе много троянских кодов, которые позволят восстановить контроль над системой в случае обнаружения вторжения. В качестве примера подобной атаки можно привести атаку на сеть магазинов Target, где злоумышленникам удалось достаточно долго оставаться незамеченными, что и позволило украсть значительный объём данных.


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




Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.


This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


AniJS – библиотека для декларативного описания CSS-анимации

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


По-моему это прекрасное решение, описывать анимацию в духе:



<div data-anijs="if: click, do: flipInY, to: .container-box"></div>




Всё максимально интуитивно: при клике на указанном div, делаем анимацию flipInY на элементе .container-box.

Упомяну также о возможности создания очереди событий, например:



<div class="element1" data-anijs="if: click, do: wobble, to: .element2">Нажми меня!</div>
<div class="element2">Здесь будет Wobble!</div>
<div class="element3" data-anijs="if: animationend, on: .element2, do: hinge">А потом я упаду</div>




То есть при клике на .element1, будет применён эффект «потрясывания» (wobble) для .element2, по завершению которого к .element3 будет применён эффект выпадения за нижнюю границу браузера.

В общем много интересного!

Заинтересовавшихся прошу ознакомиться.


This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


Аналитика мобильных приложений. Запись вебинара


сегодня в 21:58


Обучающий материал на тему «Аналитика мобильных приложений».

Ведущий: Анар Бабаева

Из передачи вы узнаете:

— какое приложение принесет деньги;

— как спрогнозировать доходность приложения;

— как построить внутреннюю систему аналитики, основанную на важных метриках (LTV, churn rate, retention rate и т. д.);

— как подсмотреть за конкурентами в замочную скважину;

— как настраивать аналитику при экспансии в Москву, регионы РФ, страны Азии и Южной Америки, развивающиеся и специфические страны;

— какие инструменты использовать для проведения анализа внутри приложения;

— как анализировать эффективность мобильных рекламных кампаний;

— и многое другое.


Видео тут: http://ift.tt/1saExjc




Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.


This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


Подключение Macbook Pro к 10G Ethernet сети

В предыдущей статье рассматривалась не очень дорогая возможность запустить 10G Ehternet сеть для задач каталогизации и обработки фото и видео материалов.

А вот подключить к такой сети современный Macbook Pro не очень просто. С интерфейсами у этих ноутбуков дело обстоит, скажем так, специфично.

К обычной гигабитной сети Macbook можно подключить через USB3 — Gigabit Ethernet переходник. Если этой производительности достаточно, получаем хорошее бюджетное решение. Но не в нашем случае.

Остается единственный скоростной интерфейс этого ноутбука — Thunderbolt.

Готовых переходников ThunderBolt — 10G Ethernet вроде бы нет. Но несколькими производителями выпускаются так называемые Thunderbolt Expansion Chassis. Коробочки с парой Thunderbolt интерфейсов и одним или двумя PCIe слотами.

А в слоты эти можно поставить много чего. В том числе подходящую 10G карту.

Ключевое слово — подходящую. Далеко не всякая 10G карты Thunderbolt совместима. Производители этих «коробочек» такие карты тестируют и публикуют список совместимых.

Плюсом можно считать тот факт, что если карта хотя бы у одного производителя Expansion Chassis указана как совместимая, она скорее всего будет работать в продукции любого производителя «коробочек».

ThunderBolt-20140508207-3.JPG



Получается довольно элегантное решение. Expansion Chassis вместе с картой (в моем случае была 2х портовая карта от Myricom) ставим на некотором отдалении от ноутбука, чтобы не мешалось. В него включаем по возможности длинный Thunderbolt кабель (до 3м можно использовать обычные кабели, более длинные — только оптические), а другой конец кабеля — в Macbook.


ThunderBolt-20140508208-6.JPG


Expansion Chassis у некоторых производителей включается только тогда, когда получает сигнал от Thunderbolt кабеля. Это удобно. А вот то, что некоторые модели имеют довольно шумный вентилятор, мало кого порадует.


На Macbook надо будет поставить драйверы 10G карты. И если все сделано правильно, в Mac OSX у нас появляются 10G порты.


Screen Shot 2014-05-08 at 21.15.45.JPG


Дальше начинается самое интересное — добиться от нашей конструкции максимальной производительности.

Те, кто имел дело с 10G картами знают, что для оптимальной работы их хорошо бы настроить. У драйвера есть не мало «ручек», за которые можно покрутить. Смысл настройки в том, чтобы сбалансировать вычислительные ресурсы компьютера, требуемые на обслуживание высокопроизводительной сетевой карты с ресурсами, которых требуют выполняемые на машине задачи.

Как это сделать, обычно подробно написано в документации. Иногда этой информации не достаточно, но служба поддержки производителя карт приходит на помощь. Я когда свою первую 10G сеть настраивал, несколько дней с техподдержкой общался.

Первое, что можно включить — это включить обработку TCP пакетов процессором платы (TCP Large Receive Offload) и подобрать для него оптимальное значение.

Затем настроить частоту прерываний, генерируемый картой. От этого зависит, сколько процессорной мощности будет отводиться на работу с картой.

Далее — полезно увеличить socket buffer size.

И наконец аккуратно оттестировать систему.


В завершении — о стоимости.

Thunderbolt Expansion Chassis обходится где-то от $350.

10G Ethernet карта — где-то от $200.

Thunderbolt кабель на 3м — порядка $40.

Все это — при заказе либо на сайтах производителей, либо на ebay.


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


This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.