Google App Engine

Согласно Вики: Google App Engine — сервис хостинга сайтов и web-приложений на серверах Google с бесплатным именем <имя_сайта>.appspot.com, либо с собственным именем, задействованным[1] с помощью служб Google

Отличная, оказывается, штука. Установил плагин Google под эклипс, за пять минут создал и задеплоил простейшее приложение: http://coolsoftware-test.appspot.com/.

Update. Немного шаманства с ДНС и приложение доступно на домене coolsoftware.ru: http://testapp.coolsoftware.ru/

===
Перепечатка материалов блога разрешается с обязательной ссылкой на blog.coolsoftware.ru

Пример использования SAX-парсера на Java

В статье про SAX-парсер я рассказал что это такое и пообещал привести примеры не только на Visual Basic, но и на других языках программирования, в частности на Java. Ниже приведен такой пример - класс VXMLChecker, который умеет проверять XML-текст и XML-файл на соответствие XSD-схеме. Для этого у него есть два публичных метода: checkXML и checkFile.

package VXMLParseUtils;
/**

 *
 * @author Vitaly (http://blog.coolsoftware.ru/)
 */

import java.io.*;

import javax.xml.parsers.SAXParserFactory;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.ParserConfigurationException;

import org.xml.sax.helpers.DefaultHandler;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import org.xml.sax.SAXParseException;
import org.xml.sax.Locator;
import org.xml.sax.Attributes;
import org.xml.sax.XMLReader;

public class VXMLChecker extends DefaultHandler {
    boolean m_bOutLog;
    Locator m_Locator;
    String m_sErrorString;
   
    public VXMLChecker() {
        m_bOutLog = true;
        m_Locator = null;
        m_sErrorString = “”;
    }

    ////////////////////////////////////////////////////////////////////
    // content handlers
    ////////////////////////////////////////////////////////////////////

    public void setDocumentLocator (Locator locator)
    {
    m_Locator = locator;
    }

    public void startDocument ()
    throws SAXException
    {
    if (m_bOutLog) System.out.println(“startDocument”);
    }

    public void endDocument ()
    throws SAXException
    {
    if (m_bOutLog) System.out.println(“endDocument”);
    }

    public void endPrefixMapping (String prefix)
    throws SAXException
    {
    if (m_bOutLog) System.out.println(“endPrefixMapping: “+prefix);
    }

    public void startElement (String uri, String localName,
                  String qName, Attributes attributes)
    throws SAXException
    {
    if (m_bOutLog) System.out.println(“startElement: “+uri+“,”+localName+“,”+qName);
    }

    public void endElement (String uri, String localName, String qName)
    throws SAXException
    {
    if (m_bOutLog) System.out.println(“endElement: “+uri+“,”+localName+“,”+qName);
    }

    public void characters (char ch[], int start, int length)
    throws SAXException
    {
    if (m_bOutLog) System.out.println(“characters: “+new String(ch, start, length));
    }

    public void ignorableWhitespace (char ch[], int start, int length)
    throws SAXException
    {
    if (m_bOutLog) System.out.println(“ignorableWhitespace: “+new String(ch, start, length));
    }

    public void processingInstruction (String target, String data)
    throws SAXException
    {
    if (m_bOutLog) System.out.println(“processingInstruction: “+target+“,”+data);
    }

    public void skippedEntity (String name)
    throws SAXException
    {
    if (m_bOutLog) System.out.println(“skippedEntity: “+name);
    }

    ////////////////////////////////////////////////////////////////////
    // error handlers
    ////////////////////////////////////////////////////////////////////
    public void warning (SAXParseException e)
        throws SAXException
    {
        if (m_bOutLog) System.out.println(“warning: “+e.getMessage());
    }

    public void error (SAXParseException e)
        throws SAXException
    {
        if (m_bOutLog) System.out.println(“error: “+e.getMessage());
        String sAddErrorString = “[XMLChecker2.error] SAXParseException thrown:\n+ e.getMessage();
        m_sErrorString = m_sErrorString +\n+ sAddErrorString;
    }

    public void fatalError (SAXParseException e)
        throws SAXException
    {
        if (m_bOutLog) System.out.println(“fatal error: “+e.getMessage());
        String sAddErrorString = “[XMLChecker2.fatalError] SAXParseException thrown:\n+ e.getMessage();
        m_sErrorString = m_sErrorString +\n+ sAddErrorString;
        throw e;
    }
    ////////////////////////////////////////////////////////////////////
   
    public boolean parseXML(Reader xmlReader, Reader xsdReader)
        throws IOException, SAXException, ParserConfigurationException
    {
        boolean bRes = false;

        m_sErrorString = “”;

        SAXParserFactory saxFactory = SAXParserFactory.newInstance();
        SAXParser parser = saxFactory.newSAXParser();
        XMLReader reader = parser.getXMLReader();

        reader.setContentHandler(this);
        reader.setErrorHandler(this);

        if (xsdReader != null)
        {
            reader.setProperty(
                http://java.sun.com/xml/jaxp/properties/schemaLanguage",
                http://www.w3.org/2001/XMLSchema");
            reader.setProperty(
                http://java.sun.com/xml/jaxp/properties/schemaSource",
                new InputSource(xsdReader));
            reader.setFeature(http://xml.org/sax/features/validation", true);
        }
        try
        {
            reader.parse(new InputSource(xmlReader));
        }
        catch (Exception e)
        {
            if (m_bOutLog) System.out.println(“parse failed: “+e.getMessage());
            if (m_sErrorString.length() <= 0) m_sErrorString = e.getMessage();
        }
        bRes = (m_sErrorString.length() == 0);

        return bRes;
    }

    public static String checkXML(String XmlText, String SchemaFileName)
        throws ClassNotFoundException, IOException, SAXException
    {
        VXMLChecker checker = new VXMLChecker();
        Reader xmlReader = null;
        Reader xsdReader = null;
        try
        {
            xmlReader = new StringReader(XmlText);
            xsdReader = new FileReader(SchemaFileName);
            checker.parseXML(xmlReader, xsdReader);
        }
        catch(Exception e)
        {
            if (xmlReader != null) xmlReader.close();
            if (xsdReader != null) xsdReader.close();
            return e.getMessage();
        }
        if (xmlReader != null) xmlReader.close();
        if (xsdReader != null) xsdReader.close();
        return checker.m_sErrorString;
    }

    public static String checkFile(String XmlFileName, String SchemaFileName)
        throws ClassNotFoundException, IOException, SAXException
    {
        VXMLChecker checker = new VXMLChecker();
        Reader xmlReader = null;
        Reader xsdReader = null;
        try
        {
            xmlReader = new FileReader(XmlFileName);
            xsdReader = new FileReader(SchemaFileName);
            checker.parseXML(xmlReader, xsdReader);
        }
        catch(Exception e)
        {
            if (xmlReader != null) xmlReader.close();
            if (xsdReader != null) xsdReader.close();
            return e.getMessage();
        }
        if (xmlReader != null) xmlReader.close();
        if (xsdReader != null) xsdReader.close();
        return checker.m_sErrorString;
    }
}

===
Перепечатка материалов блога разрешается с обязательной ссылкой на blog.coolsoftware.ru

SAX-парсер

В заметке Schematron и XSD я вскользь упомянул про SAX-парсер и то, что с его помощью можно проверить XML-файл на соответствие XSD-схеме. Здесь я постараюсь описать это подробнее на примере SAX-парсера от Microsoft.

Итак, способ разбора XML-файла с помощью SAX-парсера является событийно-ориентированным. Это означает, что приложение, которое  использует SAX-парсер, получает от него уведомления о начале и об окончании XML-элементов, атрибутах, тексте внутри XML-узлов, ошибках разбора и т.д. - в том порядке, в котором SAX-парсер встретил их в XML-файле. При этом, SAX-парсер не сохраняет в памяти разобранные элементы, в отличие от интерфейса DOM,  для которого строится и хранится в памяти объектная модель всего XML. Это делает SAX-парсер пригодным для потоковой обработки (например, для проверки или для загрузки куда-либо) XML-файлов любого размера.

Посмотрим на пример использования SAX-парсера в Visual Basic 6.0 (примеры на Java, C# обязательно будут в след. статьях).

Для начала в нашем проекте на VB нам необходимо подключить Reference “Microsoft XML, v6.0”.

Затем нам необходимо создать ClassModule - класс объекта, который будет принимать уведомления от SAX-парсера. Назовем его MySAXReader. Он должен реализовывать интерфейсы MSXML2.IVBSAXContentHandler и MSXML2.IVBSAXErrorHandler.

Implements MSXML2.IVBSAXContentHandler
Implements MSXML2.IVBSAXErrorHandler

Private Sub IVBSAXContentHandler_endElement(strNamespaceURI As String, _

strLocalName As String, strQName As String)

    Debug.Print “endElement: “ & strLocalName

End Sub

Private Sub IVBSAXContentHandler_startElement(strNamespaceURI As String, _

strLocalName As String, strQName As String, ByVal oAttributes As MSXML2.IVBSAXAttributes)

    Debug.Print “startElement: “ & strLocalName

End Sub

Private Sub IVBSAXErrorHandler_error(ByVal oLocator As MSXML2.IVBSAXLocator, _
strErrorMessage As String, ByVal nErrorCode As Long)

    Debug.Assert “error: “ & strErrorMessage

End Sub

Методы IVBSAXContentHandler_startElement и IVBSAXContentHandler_endElement будут вызываться SAX-парсером в начале и в конце XML-элемента соответственно. В этих методах, а также в других методах интерфейсов MSXML2.IVBSAXContentHandler и MSXML2.IVBSAXErrorHandler, нужно реализовывать логику приложения. Это может быть, например, загрузка значений в базу данных. Или запись ошибок в лог.

Теперь сделаем у нашего класса публичный метод, который будет осуществлять обработку XML-файла, а заодно и проверку его по XSD-схеме. Вот код:

Public Sub VerifyFile(ByVal sXmlFileName As String, ByVal sSchemaFileName As String)
    Dim oSaxReader As MSXML2.SAXXMLReader60
    Dim oSC As MSXML2.XMLSchemaCache60
    Set oSaxReader = CreateObject(“MSXML2.SAXXMLReader.6.0”) ‘создаем объект SAXXMLReader
    Set oSaxReader.contentHandler = Me ‘Обработчик событий SAX-паресера -
                                                               ‘объект, реализующий интерфейс IVBSAXContentHandler
    Set oSaxReader.errorHandler = Me ‘Обработчик ошибок  -
                                                            ‘объект, реализующий интерфейс IVBSAXErrorHandler
    Set oSC = CreateObject(“MSXML2.XMLSchemaCache.6.0”) ‘Кэш XSD-схем
    oSC.Add “”, sSchemaFileName ‘Добавляем в кэш имя файла нашей XSD-схемы
    oSaxReader.putFeature “schema-validation”, True ‘Включаем проверку по схеме
    oSaxReader.putFeature “exhaustive-errors”, True ‘Включаем проверку всех ошибок
    oSaxReader.putProperty “schemas”, oSC ‘Связываем SAXXMLReader и XMLSchemaCache
    oSaxReader.parseURL sXmlFileName ‘Запускаем SAX-парсер
End Sub

===
Перепечатка материалов блога разрешается с обязательной ссылкой на blog.coolsoftware.ru

Failed to start Qt Designer

Столкнулся с проблемой - в NetBeans после обновления перестал запускаться Qt Designer с формулировкой: “Failed to start Qt Designer. Make sure it is installed and added to PATH”.

Лечится в терминале командой:

export PATH=$PATH:/Developer/Applications/Qt/Designer.app/Contents/MacOS

===
Перепечатка материалов блога разрешается с обязательной ссылкой на blog.coolsoftware.ru

Schematron и XSD

Коротко про схематрон я написал в предыдущей статье. Здесь же речь пойдет об объединении схематрон-схемы и схемы XSD.

XSD-схема замечательно работает, когда необходимо провести форматный контроль XML файла. В нем есть все необходимые инструменты, чтобы проверить структуру XML-файла и соответствие данных определенным типам. Однако логический и арифметический контроль с помощью XSD-схемы осуществить нельзя. Для этого лучше всего подходит схематрон.

Однако, хранить две отдельных схемы (XSD и Schematron) в разных файлах неудобно. К тому же, существующие механизмы обработки XML-файлов (DOM- и SAX-парсеры) не умеют работать со схемами Schematron, а дважды проверять один и тот же XML по разным схемам не рационально (потребуется дважды “отпарсить” весь XML-файл).

Предлагается задавать правила проверки на языке Schematron в подэлементах xs:appinfo элементов xs:annotation XSD-схемы.

Пример:

&#60;?xml version=”1.0”?&#62;
<xs:schema  xmlns:xs=”http://www.w3.org/2001/XMLSchema" xmlns:sch=”http://www.ascc.net/xml/schematron">
  <xs:element name=”Root”>
    xs:annotation
      xs:appinfo
        <sch:pattern name=”DocumentChecker”>
          <sch:rule context=”Person”>
            <sch:assert test=”@birthYear<1990”>Birth year should be less than 1990.
          
        
      
    
  

В этом примере мы видим утверждение на языке Schematron <sch:assert test=”@birthYear<1990”>Birth year should be less than 1990., которое должно проверяться в контексте XML-узла Person (<sch:rule context=”Person”>).

===
Перепечатка материалов блога разрешается с обязательной ссылкой на blog.coolsoftware.ru

Схематрон (Schematron)

Схематрон - это язык для проверки правильности утверждений о данных в формате XML.

Разработан Rick Jelliffe из Academia Sinica Computing Centre (http://xml.ascc.net/resource/schematron/schematron.html).

Валидация XML-файла по схематрон-схеме подобна трансформации этого XML-файла в соответствии с преобразованием на языке XSLT (eXtensible Stylesheet Language Transformations). На выходе получается текст, который по-сути является протоколом проверки.

На самом деле, любую схематрон-схему можно преобразовать в XSLT. Это свойство схематрона как раз и является самым главным его достоинством.
Пример схематрон-схемы приведен ниже:
<?xml version="1.0" encoding="US-ASCII"?>
 Example Schematron Schema
 
  
    A 'dog' element should contain two 'ear' elements.
    This dog has a bone.
  
  
Давайте разберем, что здесь что.

Тег sch:schema - схема, корневой тег схематрона.

Внутри тега схемы располагаются  название схемы - тег sch:title, и шаблоны - теги sch:pattern. Атрибут шаблона name задает имя шаблона (в нашем примере: “dog_pattern”).

Шаблон состоит из правил - тег sch:rule. Каждое правило применяется к узлу проверяемого по схематрон-схеме XML-файла, путь к которому (XPath) задается атрибутом context.

Правило состоит из утверждений (sch:assert) и сообщений (sch:report), которые имеют атрибут test - условие, формулируемое на языке XPath. Разница между ними в том, что если условие в сообщении истинно (в примере: test=”bone”), то в выходной файл пишется текст сообщения (This dog has a bone.), а текст утверждения (A ‘dog’ element should contain two ‘ear’ elements.) пишется в выходной файл только если условие НЕ выполнено (test=”count(ear) = 2”).

===

Перепечатка материалов блога разрешается с обязательной ссылкой на blog.coolsoftware.ru

Oracle PL/SQL: Обработка ошибок

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

  create or replace package body VTESTPKG is

    procedure Test1

    is

    begin

      raise NO_DATA_FOUND;

    end Test1;

  end VTESTPKG;

Теперь, вызовем VTESTPKG.Test1 и запишем текст исключения в output:

  begin

    VTESTPKG.Test1;

  exception

    when others then

      dbms_output.put_line(sqlerrm);

  end;

Результат будет выглядеть так:

ORA-01403: данные не найдены

Код и текст ошибки в SQLERRM есть, однако нам бы очень хотелось увидеть место возникновения ошибки - в идеале номер строки и стек вызовов. Ниже приведен код функции ERR_WHENCE, которая возвращает место возникновения ошибки, и стандартный обработчик ошибок RaiseError, который я использую при обработке всех ошибок в моем коде на PL/SQL.

  function ERR_WHENCE return varchar2

  is

    l_back_trace varchar2(4096) default DBMS_UTILITY.FORMAT_ERROR_BACKTRACE;

    l_pos integer;

  begin

    if substr(l_back_trace, length(l_back_trace), 1) = chr(10)

    then

      l_back_trace := substr(l_back_trace, 1, length(l_back_trace)-1);

    end if;

    l_pos := instr(l_back_trace, chr(10), -1);

    if l_pos > 0 then

      l_back_trace := substr(l_back_trace, l_pos+1);

    end if;

    return l_back_trace || chr(10);

  end ERR_WHENCE;

  procedure RaiseError(

    error_source varchar2,

    error_message varchar2 default ERR_WHENCE || sqlerrm,

    error_code number default -20001

  )

  is

  begin    

    if not error_source is null then

      raise_application_error(

        error_code, ‘error in ‘ || error_source || chr(10) || error_message);

    else

      raise_application_error(

        error_code, error_message);

    end if;

  end RaiseError;

Для демонстрации использования создадим процедуры Test2 и Test3, в которых используется обработчик ошибок RaiseError:

  procedure Test2

  is

  begin

    raise NO_DATA_FOUND;

  exception

    when others then

      RaiseError(‘Test2’);

  end Test2;

  procedure Test3

  is

  begin    

    Test2;

  exception

    when others then

      RaiseError(‘Test3’);

  end Test3;

Результат (в SQLERRM) вызова Test2:

ORA-20001: error in Test2

ORA-06512: на  “AIS.VTESTPKG”, line 44

ORA-01403: данные не найдены

Результат (в SQLERRM) вызова Test3:

ORA-20001: error in Test3

ORA-06512: на  “AIS.VTESTPKG”, line 53

ORA-20001: error in Test2

ORA-06512: на  “AIS.VTESTPKG”, line 44

ORA-01403: данные не найдены

===

Перепечатка материалов блога разрешается с обязательной ссылкой на blog.coolsoftware.ru

Oracle SQL: INSERT WHEN

В Oracle SQL есть интересная конструкция “INSERT WHEN…”, о которой не все знают.

INSERT
WHEN ([Condition]) THEN
INTO [TableName] ([ColumnName])
VALUES ([VALUES])
ELSE
INTO [TableName] ([ColumnName])
VALUES ([VALUES])
SELECT [ColumnName] FROM [TableName];

Во первых, эта конструкция позволяет с помощью одной каманды INSERT вставлять данные в разные таблицы.

Во вторых, попробуем решить следующую задачу: пусть имеется таблица VTEST с полем A VARCHAR2(10) и нужно  написать команду INSERT, которая будет вставлять значение ‘AAA’, отсутствующее в этой таблице (если такое значение уже есть, то новую запись создавать не нужно). Решение этой задачи может выглядеть примерно так:

INSERT INTO VTEST (A)
  SELECT ‘AAA’ FROM dual
    WHERE NOT EXISTS (SELECT * FROM VTEST WHERE A=’AAA’)

Такая конструкция, конечно, работает. Но плохо в ней то, что значение ‘AAA’ фигурирует дважды: в SELECT ‘AAA’ FROM dual и в SELECT * FROM VTEST WHERE A=’AAA’.

А вот так выглядит та же операция, но с использованием INSERT WHEN:

INSERT
  WHEN NOT EXISTS (SELECT * FROM VTEST WHERE A=A0)
   THEN INTO VTEST (A) VALUES (A0)
  SELECT ‘AAA’ A0 FROM dual

Здесь ‘AAA’ присутствует только 1 раз.

===

Перепечатка материалов блога разрешается с обязательной ссылкой на blog.coolsoftware.ru

Howto: запустить приложение на node.js в виде сервиса (linux)

1. Создаем файл ~/myapp.service:

[Unit]

Description=My App

After=network-online.target

[Service]

Restart=on-failure

WorkingDirectory=/var/mayapp

ExecStart=/usr/bin/node /var/myapp/index.js

[Install]

WantedBy=multi-user.target

2. cp ~/myapp.service /etc/systemd/system

3. systemctl start myapp.service

4. systemctl enable myapp.service

5. journalctl -u myapp

===
Перепечатка материалов блога разрешается с обязательной ссылкой на blog.coolsoftware.ru