Использование Jython в Showcase

Материал из Course Orchestra
Перейти к: навигация, поиск
Showcase

Создание решений на КУРС:Showcase

Использование Jython

Общие сведения

Jython – это реализация языка Python на языке Java. Для того чтобы выполнить какое-то действие с помощью Jyhthon, в описании элемента в информационной панели или навигаторе должно быть указано имя jython-скрипта с расширением py!

Jython скрипты хранятся в каталоге userdata в папке scripts\jython. В данной папке могут быть подкаталоги, которые Jython рассматривает как пакеты. Поэтому, в каждом подкаталоге должен быть пустой файл со следующим именем:

__init__.py

На файлы из подкаталогов можно ссылаться из Showcase, используя для разделения отдельных частей пути прямые слэши (/). Обращаем внимание: ссылки на каталоги и файлы чувствительны к регистру символов. При запуске скрипта из Showcase доступны только файлы из текущей userdata.

Каждый скрипт должен реализовывать класс, название которого должно совпадать с названием файла. Так как Jython, как и любой другой язык устанавливает некоторые ограничения на названия типов, то следует запомнить, что имя файла (и класса) должно начинаться с букв и содержать только буквы и цифры латинского алфавита и подчеркивания. Основной класс должен наследоваться от определенного интерфейса Showcase. Таким образом, скрипт должен иметь следующий вид:

from ru.curs.showcase.core.jython import JythonProc;

class JythonFileName(JythonProc):     
    def xxx(self, params...):

где JythonFileName - имя файла Jython, а xxx и params зависят от назначения Jython скрипта:

  • execute(self, context) - для server activity;
  • getRawData(self, context) - для навигатора и инф. панели;
  • getRawData(self, context, elementId) - для веб-текста и xforms;
  • getRawData(self, context, elementId, conn) - для графиков (а в будущем для карты и грида);
  • handle(self, request) - для веб-сервисов;
  • save(self, context, elementId, data) - сохранение данных;
  • transform(self, context, data) - преобразование данных (возвращает преобразованную строку с данными).

Общее правило: если возвращаемые данные представляют собой одну строку, они возвращаются в виде строки. Это правило работает для навигатора, информационной панели и веб-сервисов. Если нужно вернуть две строки, как для элементов информационной панели, то возвращается объект JythonDTO, который можно создать так:

from ru.curs.showcase.core.jython import JythonDTO

return JythonDTO(data, settings)

где в первом параметре передаются данные, а во втором - настройки.

До версии 2.0.9 пакет ru.curs.showcase.core назывался ru.curs.showcase.model

Предоставлять данные с помощью Jython для графика, карты и грида тоже можно, но по-другому. Для того чтобы Showcase смог построить данные элементы, ему нужен набор записей - RecordSet. Поэтому Showcase ожидает от Jython процедуры объект JythonDTO, в котором в параметре data содержится SQL-запрос, которые возвращает данные, а в параметре settings - строка с настройками. После выполнения Jython-процедуры Showcase выполняет полученный из процедуры SQL запрос, который и возвращает требуемый RecordSet. Обратите внимание, что функция getRawData для данных элементов получает 3 параметра, не считая self. Последний из них - conn - это уже подготовленное соединение с БД, в котором нужно создавать все временные таблицы, на которые ссылается возвращаемый SQL-запрос. Данное соединение использует параметры из файла app.properties. Как правильно обратиться к БД из других функций, читайте ниже.

Примеры jython-скриптов приведены в файле userdata.zip, входящем в дистрибутив Showcase.

Контекст, приходящий в большинство процедур, имеет следующий вид:

public interface AbstractCompositeContext {

	String getSession();

	void setSession(String aSession);

	String getMain();

	void setMain(String aMain);

	String getAdditional();

	void setAdditional(String aAdditional);

	String getFilter();

	void setFilter(String aFilter);
}


В jython-коде доступны все классы Java и Showcase, а также все классы Jython, которые расположены в каталоге ${webapp}\WEB-INF\libJython. Специально для разработки решений на Python мы включили в дистрибутив следующие сторонние библиотеки:

  1. Suds - Python-библиотека для работы с веб-сервисами;
  2. Apache POI - Java-библиотека для создания Microsoft Office документов (как старого, так и нового форматов);
  3. iReport - Java библиотека для создания отчетов, в частности она позволяет легко создавать PDF документы.

Примеры работы с Apache POI и iReport приведены ниже.

Требуемые для разработки решений сторонние библиотеки рекомендуется распаковывать прямо в каталог для jython в userdata. Если библиотека используется в нескольких userdata или нескольких решениях можно включить ее в дистрибутив. При наличии одноименных классов, вначале идет поиск в Java, потом поиск в каталоге скриптов папки userdata данного решения, а затем поиск в общих jython-классах.

Кодировка строк

В начале любого Jython-скрипта должна быть указана его кодировка - UTF-8.

# coding=UTF-8

Возвращать строки из скрипта нужно в формате unisyntaxhighlight. Это относится и к данным, и к сообщениям об ошибке. Для того, чтобы преобразовать обычную строку в unisyntaxhighlight, нужно ввести следующую строку:

unisyntaxhighlight("русский и english", "utf-8")

В скрипт из Java строки приходят также в формате unisyntaxhighlight вне зависимости от того, переданы ли они как строковый параметр или являются результатом вызова функции.

Начиная с версии 2.0.7 перед вызовом скрипта Jython кодировка utf-8 устанавливается как кодировка по умолчанию, используемая в тех случаях, когда кодировка не указана. Например:

s = unisyntaxhighlightStr.ensyntaxhighlight()

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

Возврат ошибок

Вернуть ошибку из скрипта можно теми же 2-мя способами, что и в хранимых процедурах:

  1. генерацией исключения,
  2. возвратом кода ошибки.

Пример генерации правильного исключения:

        if (not context.getMain()):       
            raise Exception(u"не нравится мне этот контекст!")

Пример возврата произвольной ошибки:

from ru.curs.showcase.app.api import UserMessage;

return UserMessage("1", u"ошибка из Jython")

Пример возврата произвольного сообщения:

from ru.curs.showcase.app.api import MessageType;
from ru.curs.showcase.app.api import UserMessage;

return UserMessage("1", u"Сообщение из Jython", MessageType.INFO)

Пример возврата типичного сообщения для пользователя (не обязательно ошибки), заданного в файле user.messages.xml:

from ru.curs.showcase.app.api import UserMessage;

return UserMessage(u"ID из файла user.messages.xml", None)

Возврат "ok"-сообщений (INFO, WARNING)

Вернуть такое сообщение можно при помощи возврата кода ошибки. Создается объект класса UserMessage (с типом INFO или WARNING), который передается параметром в JythonDTO. Например,

    res = JythonDTO(data, settings, UserMessage("1", u"Сообщение из Jython", MessageType.INFO))
    return res

Внимание! Модуль UserMessage перенесен из пакета ru.curs.showcase.core в пакет ru.curs.showcase.app.api. Необходимо подправить секцию from...import в файлах решения

Аналогично выдача "ok"-сообщений происходит и в Челесте.

Шаблоны

Для удобной отладки Jython-скриптов подготовлен универсальный шаблон. Шаблон включает следующие операции::

  1. Объявление всех процедур, необходимых для работы с Showcase;
  2. Сохранение данных, пришедших из Showcasе, в глобальные переменные;
  3. Вызов функции mainProc, которая должна содержать обработку данных.

В случае вызова не из Showcase, а из Eclipse или командной строки, сразу вызывается mainProc. Таким образом, код можно и отлаживать, и выполнять из Showcase без каких-либо дополнительных правок.

Все пришедшие из Showcase строки конвертируются из unisyntaxhighlight в обычные, потому что оператор print в Jython не понимает unisyntaxhighlight! Если ваш файл используется только для определенного действия (например, для генерации xforms), то можно отредактировать шаблон, удалив из него лишние объявления.

Шаблон (abstractjythonproc.zip) входит в состав дистрибутива showcase.

Еще полезные шаблоны:

  1. saxjythonproc.zip - для разбора XML-документа с помощью SAX-парсера; .
  2. todb.zip - для обращения к БД;
  3. xmldom.zip - для работы с XML-документом с помощью DOM.

Пример работы с SAX- и DOM-парсерами http://www.java-samples.com/showtutorial.php?tutorialid=152.

Примеры кода

Обратите внимание: при импорте Java-модулей, вообще говоря, можно использовать символ *, например:

from net.sf.jasperreports.engine import *

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

from net.sf.jasperreports.engine import JasperFillManager, JREmptyDataSource, JRExporterParameter


Перекодировать байтовую строку

s = inputStr.desyntaxhighlight(from_encoding).ensyntaxhighlight(to_encoding)

Подключить внешний каталог со скриптами

import sys.path
sys.path.append('путь к каталогу')

Установить кодировку по умолчанию

from org.python.core import syntaxhighlightcs
syntaxhighlightcs.setDefaultEncoding('utf-8')

Открыть текстовый файл в определенной кодировке

import syntaxhighlightcs
syntaxhighlightcs.open('имя файла', 'r', 'utf-8')

где r - режим открытия файла "только для чтения".

Преобразовать строку в текст, который можно отобразить на HTML-странице или передать в URL

s = unisyntaxhighlightStr.ensyntaxhighlight('ascii', 'xmlcharrefreplace')

В результате русские буквы и спецсимволы будут заменены их кодами, например, Ӓ

Узнать текущий каталог для userdata

from ru.curs.showcase.runtime import AppInfoSingleton 

root = AppInfoSingleton.getAppInfo().getCurUserData().getPath()

Преобразовать строку в DOM-документ

from ru.curs.showcase.util.xml import XMLUtils

doc = XMLUtils.stringToDocument(string);

Преобразовать строку в InputStream для дальнейшей передачи в Java-функцию

from ru.curs.showcase.util import TextUtils

stream = TextUtils.stringToStream(string)

Работа с БД

from ru.curs.showcase.runtime import ConnectionFactory

# пример работы с PyConnection
pyConn = ConnectionFactory.getPyConnection();
try:
    cur = pyConn.cursor()
    cur.execute("select top 1 name from geo3")             
    print cur.fetchone()[0]
finally:     
    pyConn.close()

Обратите внимание, задавать вручную строку соединения в этом случае не надо! Дублирования данных из app.properties не происходит!

Работа с Apache POI - создание простого файла xslx

from org.apache.poi.ss.usermodel import Cell
from org.apache.poi.ss.usermodel import Row
from org.apache.poi.ss.usermodel import Sheet
from org.apache.poi.ss.usermodel import Workbook
from org.apache.poi.ss.util import CellReference
from org.apache.poi.xssf.streaming import SXSSFWorkbook
from java.io import FileOutputStream

def mainproc():
    iMemoryRows = 100
    wb = SXSSFWorkbook(iMemoryRows);
    sh = wb.createSheet();
    for rownum in xrange(1000):
        row = sh.createRow(rownum);
        for cellnum in xrange(10):
            cell = row.createCell(cellnum);
            address = CellReference(cell).formatAsString();
            cell.setCellValue(address);                    
    out = FileOutputStream(outputFile);
    wb.write(out);
    out.close();

Работа с iReport - генерация простого PDF-файла на основе ранее созданного jasper-отчета

import os.path
from course import JasperReportProducer

def mainproc():
    JasperReportProducer.produce(os.path.dirname(__file__) + "/" + inputFile, outputFile)

где inputFile - файл с шаблоном отчета, лежащий в том же каталоге, что и скрипт, а outputFile - полный путь к выходному файлу.