Sidslog

Март 11, 2010

Авторизация и подтверждение регистрации в rails-2.3. Authlogic, recaptcha, tlsmail

Filed under: rails, Uncategorized — Метки: , , , , , — sidslog @ 9:06 пп

Добавление authlogic в rails-2.3 проект

1) Добавляем gem’ы authlogic, tlsmail и recaptcha

config.gem ‘authlogic’
config.gem ‘tlsmail’
config.gem ‘recaptcha’

2) Создаем миграцию, в которой добавляем необходимые колонки в таблицу пользователей

add_column :users, :login, :string, :default => », :null => false
add_column :users, :email, :string, :default => », :null => false
add_column :users, :crypted_password, :string
add_column :users, :password_salt, :string
add_column :users, :persistence_token, :string, :default => », :null => false
add_column :users, :single_access_token, :string, :default => », :null => false
add_column :users, :perishable_token, :string, :default => », :null => false
add_column :users, :login_count, :integer, :default => 0, :null => false
add_column :users, :failed_login_count, :integer, :default => 0, :null => false
add_column :users, :last_request_at, :datetime
add_column :users, :last_login_at, :datetime
add_column :users, :current_login_ip, :string
add_column :users, :last_login_ip, :string
add_column :users, :active, :boolean, :default => false, :null => false
add_column :users, :openid_identifier, :string

3) Создаем модель user_session

script/generate session user_session

4) Создаем контроллер user_sessions

script/generate controller user_sessions

в контроллер помещаем код:

class UserSessionsController < ApplicationController
  before_filter :require_no_user, :only => [:new, :create]
  before_filter :require_user, :only => :destroy

  def new
    @user_session = UserSession.new
  end

  def create
    @user_session = UserSession.new(params[:user_session])
    if @user_session.save
      flash[:notice] = "Login successful!"
      redirect_back_or_default account_url
    else
      render :action => :new
    end
  end

  def destroy
    current_user_session.destroy
    flash[:notice] = "Logout successful!"
    redirect_back_or_default new_user_session_url
  end
end

5) Редактируем ApplicationController

class ApplicationController < ActionController::Base
  before_filter :require_user
  helper :all # include all helpers, all the time

  helper_method :current_user_session, :current_user
  filter_parameter_logging :password, :password_confirmation

  private
    def current_user_session
      return @current_user_session if defined?(@current_user_session)
      @current_user_session = UserSession.find
    end

    def current_user
      return @current_user if defined?(@current_user)
      @current_user = current_user_session && current_user_session.record
    end

    def require_user
      unless current_user
        store_location
        flash[:notice] = "You must be logged in to access this page"
        redirect_to new_user_session_url
        return false
      end
    end

    def require_no_user
      if current_user
        store_location
        flash[:notice] = "You must be logged out to access this page"
        redirect_to account_url
        return false
      end
    end

    def store_location
      session[:return_to] = request.request_uri
    end

    def redirect_back_or_default(default)
      redirect_to(session[:return_to] || default)
      session[:return_to] = nil
    end
end

6) Создаем контроллер для Users

script/generate controller users

  skip_before_filter :require_user # Override application wide filter
  before_filter :require_no_user, :only => [:new, :create]
  before_filter :require_user, :only => [:show, :edit, :update, :visit_first, :visit_next, :save_after_visit, :select_company]

  def new
    @user = User.new
  end

  def create
    @user = User.new
    if @user.signup!(params)
      @user.deliver_activation_instructions!
      flash[:notice] = "Your account has been created. Please check your e-mail for your account activation instructions!"

      redirect_to new_user_session_url
    else
      render :action => :new
    end
  end

  def show
    @user = @current_user
    result = get_json
    @lat = result[0]
    @lng = result[1]
  end

  def edit
    @user = @current_user
  end

  def update
    @user = @current_user # makes our views "cleaner" and more consistent
    if @user.update_attributes(params[:user])
      flash[:notice] = "Account updated!"
      redirect_to account_url
    else
      render :action => :edit
    end
  end

В routes.rb добавляем

# config/routes.rb
map.resource :account, :controller => «users»
map.resources :users

7) Создаем контроллер activations

script/generate controller activations new create

В контроллер activations добавляем

  before_filter :require_no_user, :only => [:new, :create]

  def new
    @user = User.find_using_perishable_token(params[:activation_code], 1.week) || (raise Exception)
    raise Exception if @user.active?
  end

  def create
    @user = User.find(params[:id])

    raise Exception if @user.active?

    if @user.activate!(params)
      @user.deliver_activation_confirmation!
      redirect_to account_url
    else
      render :action => :new
    end
  end

8) Добавляем в user.rb

 attr_accessible :login, :email, :password, :password_confirmation, :openid_identifier
 def active?
     active
 end 

  acts_as_authentic do |c|
    c.validates_length_of_password_field_options = {:on => :update, :minimum => 4, :if => :has_no_credentials?}
    c.validates_length_of_password_confirmation_field_options = {:on => :update, :minimum => 4, :if => :has_no_credentials?}
  end # block optional

  def has_no_credentials?
    self.crypted_password.blank? && self.openid_identifier.blank?
  end

  def signup!(params)
    self.login = params[:user][:login]
    self.email = params[:user][:email]
#    self.password = params[:user][:password]
    save_without_session_maintenance
  end

 def activate!(params)
    self.active = true
    self.password = params[:user][:password]
    self.password_confirmation = params[:user][:password_confirmation]
    self.openid_identifier = params[:user][:openid_identifier]

    save
  end

  def deliver_activation_instructions!
    reset_perishable_token!
    Notifier.deliver_activation_instructions(self)
  end

  def deliver_activation_confirmation!
    reset_perishable_token!
    Notifier.deliver_activation_confirmation(self)
  end

9) Создаем Mailer Notifier

class Notifier < ActionMailer::Base
  def activation_instructions(user)
     subject       "Activation Instructions"
     from          "test@company.com"
     recipients    user.email
     sent_on       Time.now
     body          :account_activation_url => register_url(user.perishable_token)
  end

  def activation_confirmation(user)
    subject       "Activation Complete"
    from          "test@company.com"
    recipients    user.email
    sent_on       Time.now
    body          :root_url => root_url
  end

end

10) В routes.rb добавляем

map.register ‘/register/:activation_code’, :controller => ‘activations’, :action => ‘new’
map.activate ‘/activate/:id’, :controller => ‘activations’, :action => ‘create’

11) В activations/new.html.erb добавляем код:

Activate your account

<% form_for @user, :url => activate_path(@user.id), :html => { :method => :post } do |form| %>
    <%= form.error_messages %>
    <%= render :partial => "form", :locals => { :form => form }%>
    <%= form.submit "Activate" %>
<% end %>

В _form.html.erb добавляем:

<%= form.label :password, "Set your password" %>

<%= form.password_field :password %>

<br />
<%= form.label :password_confirmation %>

<%= form.password_field :password_confirmation %>

<br />
<%= recaptcha_tags  :display => {:theme => 'white'} %>

12) Создаем initializars/mail.rb

if RAILS_ENV != 'test'
#  email_settings = YAML::load(File.open("#{RAILS_ROOT}/config/email.yml"))
#  ActionMailer::Base.smtp_settings = email_settings[RAILS_ENV] unless email_settings[RAILS_ENV].nil?
  require 'tlsmail'

  Net::SMTP.enable_tls(OpenSSL::SSL::VERIFY_NONE)

  ActionMailer::Base.smtp_settings = {
    :address => 'smtp.example.com',
    :port => 587,
    :enable_starttls_auto => true,
    :domain => 'test.com',
    :authentication => :plain,
    :user_name => 'test@example.com',
    :password => 'password'
  }

  ActionMailer::Base.delivery_method = :smtp
  ActionMailer::Base.default_url_options[:host] = "127.0.0.1"
  ActionMailer::Base.default_url_options[:port] = "3000"
  ActionMailer::Base.raise_delivery_errors = true

end

13) Создаем user_sessions/new.html.erb

<h1>Login

<% form_for @user_session, :url => user_session_path do |f| %>
  <%= f.error_messages %>
  <%= f.label :login %><br />
  <%= f.text_field :login %><br />
  <br />
  <%= f.label :password %><br />
  <%= f.password_field :password %><br />
  <br />
  <%= f.check_box :remember_me %><br />
  <br />
  <%= f.submit "Login" %>
<% end %>

14) Создаем notifier/activation_confirmation.html.erb

<!-- new file app/views/notifier/activation_confirmation.erb -->
Your account has been activated.

<%= @root_url %>

15) Создаем notifier/activation_instructions.html.erb

Thank you for creating an account! Click the url below to activate your account!

<%= @account_activation_url %>

If the above URL does not work try copying and pasting it into your browser. If you continue to have problem, please feel free to contact us.

16) В environment.rb добавляем

ENV['RECAPTCHA_PUBLIC_KEY'] = ’6Lf2cQsAAAAAAINPdVA2lVQ73Wtjfa15z2wbiihc’
ENV['RECAPTCHA_PRIVATE_KEY'] = ’6Lf2cQsAAAAAAISRfXwI3atESGkpeihOSiiB-m0H’

Февраль 28, 2010

Интернационализация rails-2.3 приложения с использованием плагина Translate routes

Filed under: Uncategorized — Метки: , , , , , — sidslog @ 8:31 пп

Про встроенный модуль i18n можно почитать здесь, на rails-i18n.org или по-русски здесь

Я бы хотел обратить ваше внимание на плагин Translate Routes, который позволяет без лишних телодвижений настроить модуль интернационализации rails приложения.Сам плагин и описание к нему можно найти здесь – http://github.com/raul/translate_routes. На страничке http://github.com/raul/translate_routes_demo можно посмотреть пример приложения, использующего этот плагин.

Установка:

Вариант 1.
1) Скачиваем плагин в папку /vendor/plugins.
2) В папке /config/locales создаем YAML файлы с требуемыми локалями. Например, ru.yml.
3) Добавляем в конец файла /config/environmet.rb строку вида ActionController::Routing::Translator.i18n.
4) В application_controller.rb добавляем фильтр before_filter :set_locale_from_url

Вариант 2.
1) Скачиваем плагин в папку /vendor/plugins
2) В папке /config/locales создаем YAML файл i18n-routes.yml. В нем храним локали для всех языков.
3) В конец файла /config/environmet.rb строку ActionController::Routing::Translator.translate_from_file('config','i18n-routes.yml')
4) В application_controller.rb добавляем фильтр before_filter :set_locale_from_url

Теперь по ссылкам вида domen.com/ru/controller... будем доступна русская локаль, а по domen.com/controller... локаль по умолчанию. Локаль по умолчанию – en, поменять ее можно в файле environment.rb (config.i18n.default_locale).

Код для получения текущего значения локали и переключения для view:

<div id=«locale-switch»>

<strong><%= I18n.t ‘current_locale’ %>: <%= I18n.locale.to_s %></strong> |

<%= I18n.locale == ‘es’ ? link_to(‘en’, :locale => ‘en’) : link_to(‘es’, :locale => ‘es’) %>

</div>

Февраль 17, 2009

Работа с 1С с помощью jacob

Filed under: Uncategorized — Метки: , , — sidslog @ 11:44 пп

Основная задача состояла в синхронизации нашего сервиса с 1С(списком контрагентов, номенклатуры и т.д.)
При этом 1С могла быть как на dbf-файлах, так и на MSSQL, поэтому было решено испольнозовать COM для связи сервиса с 1С. Конечно, если бы 1С работала на SQL, гораздо проще было бы общаться с ней с помощью jdbc драйвера, но так как варианты конфигурации могли быть самые разные, использование сom решало все проблемы.
JACOB идеально подошел нашим требованиям. Дальше задача выглядела тривиально: переписать код 1С через com.

Инициализация и авторизация в 1С:

try {
    sC = new ActiveXComponent(_1CUtils.V7_APPLICATION);
    sControl = (Dispatch)sC.getObject();
} catch (Exception e) {
    e.printStackTrace();
    throw new _1CException(_1CException._1C_NOT_AVAILABLE) ;
}
int rmTradeId ;
try {
    rmTradeId = Dispatch.getIDOfName(sC, _1CUtils.V7_RM_TRADE) ;
} catch (Exception e) {
    e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
    throw new _1CException(_1CException.RM_TRADE_NOT_AVAILABLE);
}
Variant rmTradeVar = Dispatch.invoke(sControl, rmTradeId, Dispatch.Get, new Object[]{}, new     int[]{rmTradeId}) ;
int id = Dispatch.getIDOfName(sControl, _1CUtils.V7_INITIALIZE) ;

Object[] args = new Object[3] ;
args[0] = rmTradeVar.getInt() ;
args[1] = "/D" + ResourceUtils.getResource("path") + " /N" + ResourceUtils.getResource("user") + " /P" +   ResourceUtils.getResource("password")  ;
args[2] = new Variant("NO_SPLASH_SHOW").getString() ;
Variant result = Dispatch.invoke(sControl, _1CUtils.V7_INITIALIZE, Dispatch.Get, args, new int[]{id}) ;
if (!result.getBoolean())  {
    throw new _1CException(_1CException.NOT_AUTHORISED);
}

В переменных _1CUtils.V7_APPLICATION, _1CUtils.V7_RM_TRADE, _1CUtils.V7_INITIALIZE хранятся соответсвующие значения:

  • V77.Application
  • RMTrade
  • Initialize

Для авторизации используется второй параметр функции Initialize – «/DПуть /NПользователь /PПароль».

Далее, используя стандартные функции 1С, мы можем получить доступ ко всем данным.

  • Для чтения свойства справочника воспользоваться функцией GetAttrib, для записи – SetAttrib.
  • Для создания объекта – CreateObject
  • Для поиска – FindByCode, FindByDescr
  • Для выбора из перечисления функциями – ValueByIdentifier
  • Для создания нового объекта – New
  • Для получения текщуго значения – CurrentItem, выбора значений -SelecвtItems, выбора текущего значения – GetItem
  • Для записи – Write
  • и т.д.

Прекрасный справочник по работе с функциями 1С можно скачать здесь

Весь функционал работы с 1С был вынесен на отдельный сервер под Windows)), общение с которым осуществлялось псредством веб-сервисов(использовалась связка tomcat+metro).

Не забудьте положить jacob.dll в classpath(или Windows/system32/)

Июль 9, 2008

Hibernate + MySQL5 (MappingException: JDBC type = -1)

Filed under: Uncategorized — Метки: , , , , — sidslog @ 11:16 дп

Проблема возникала при использовании Native Query в Jboss-4.2.2. В качестве БД использовалась MySQL 5.*. То есть при запуске запросов вида :

Query query =getEntityManager().createNativeQuery(«SELECT agr.AGREEMENTID , enc.NAME\n» +
«\tFROM GEL$DOC$AGREEMENT agr INNER JOIN GEL$DOC$AGR$ENCLOSURE enc ON (enc.AGREEMENTID = agr.AGREEMEN
TID)\n» +
«UNION\n» +
«SELECT template.TEMPLATEID, tenc.NAME\n» +
«\tFROM GEL$DOC$TEMPLATE template INNER JOIN GEL$DOC$TPL$PARAGRAPH tenc ON (tenc.TEMPLATEID = templat
e.TEMPLATEID)») ;
List
obj = query.getResultList() ;
)

падала ошибка:

ERROR [STDERR] javax.ejb.EJBException: javax.persistence.Persistenc
eException: org.hibernate.MappingException:
No Dialect mapping for JDBC type: -1

Проблема оказалась в использовании полей типа text, которые не мапятся в hibernate.
Решить проблему удалось, пересобрав hibernate, изменив в Mysql5Dialect метод registerVarcharTypes и добавив туда строки

registerColumnType(Types.LONGVARCHAR, 65535, «text»);
registerHibernateType(Types.LONGVARCHAR, 65535, «text»);

Май 27, 2008

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

Filed under: Uncategorized — Метки: , , , , , — sidslog @ 2:39 пп

Были ошибки в JS, написанном в предыдущем посте. Те, что нашел, исправил. Также исправил яваскрипт для компоненты, срабатывающей по фокусу. Код самой компоненты появится через пару дней))

Вот исправленный JS:

var searchValue = «» ;

var onloadFunctions = new Array() ;
var onAfterLoad = new Array() ;

var FindHandler = new Object() ;

var searchElements = new Array() ;
var searchStrings = new Array() ;

FindHandler.testClass = function(tagClass, className) {
    var classN = new RegExp(«(^|\\s)» + className + «(\\s|$)»);
    return classN.test(tagClass) ;
}

FindHandler.getParentByClass = function(className, tag) {
    while (tag.parentNode) {
        if (tag.className && this.testClass(tag.className, className)) {
            return tag ;
        } else {
            tag = tag.parentNode ;
        }
    }
    return null ;
}

FindHandler.getElementsByClassName = function(className, tag) {
    var tag = tag || document ;

    var returnElements = new Array() ;
    for (var i = 0 ; i < tag.childNodes.length; i ++) {
        if (tag.childNodes[i].className && this.testClass(tag.childNodes[i].className, className)) {
            returnElements.push(tag.childNodes[i]) ;
        }
    }

    for (var i = 0 ; i < tag.childNodes.length; i ++) {
        var ret = this.getElementsByClassName(className, tag.childNodes[i]) ;
        if (ret) {
            for (var j = 0 ; j < ret.length; j ++) {
                returnElements.push(ret[j]) ;
            }
        }
    }

    return returnElements.length > 0 ? returnElements : null ;
}

var KeyCode = new Object() ;
KeyCode.VK_ENTER = 13 ;

var f1 =  function() {
    var form = document.forms[0] ;
    if (form) {
        form.onkeypress = function(e) {
            var key;
            if (window.event)
                 key = window.event.keyCode;     //IE
            else
                 key = e.which;     //firefox

            if (key == KeyCode.VK_ENTER) {

                var target ;
                if (window.event) {
                    target = window.event.srcElement ;
                } else {
                    target = e.target ;
                }
                var parent = FindHandler.getParentByClass(«useonenter-container», target) ;
                if (parent) {
                    var controls = FindHandler.getElementsByClassName(«useonenter-control», parent) ;
                    if (controls && controls[0]) {
                        if (controls[0].tagName == «INPUT» || controls[0].tagName == «BUTTON») {
                            controls[0].click() ;
                        } else {
                            controls[0].onclick() ;
                        }
                    }
                }
                return false ;
            }

            return true ;
        }

    }
}

function focusSearch() {
    var target ;
    if (window.event) {
        target = window.event.srcElement ;
    } else {
        target = this ;
    }
    var index = searchElements.indexOf(target) ;
    if (target.value == searchStrings[index]) {
        target.value = «» ;
    }
}
function blurSearch() {
    var target ;
    if (window.event) {
        target = window.event.srcElement ;
    } else {
        target = this ;
    }
    var index = searchElements.indexOf(target) ;
    if (target.value == «») {
        target.value = searchStrings[index] ;
    }
}
var f2 = function() {
    var allElements = FindHandler.getElementsByClassName(‘search-value-container’, null) ;

    var el = new Array() ;
    if (allElements) {
        for (var i = 0; i < allElements.length; i ++) {
            el[i] = allElements[i] ;
            if (el[i]) {
                el[i].onfocus = focusSearch ;
                el[i].onblur = blurSearch ;
            }
        }
    }
}

var setFocusEvents = function() {
    if (searchElements && searchStrings) {
        if (searchElements.length == searchStrings.length) {
            for (var i = 0; i < searchElements.length; i ++) {
//                alert(searchElements[i]) ;
                searchElements[i].onfocus = focusSearch ;
                searchElements[i].onblur = blurSearch ;
                searchElements[i].onfocus ;
                searchElements[i].onblur ;
                if (searchElements[i].value == null || searchElements[i].value == «») {
                    searchElements[i].value = searchStrings[i] ;
                }
            }
        }
    }
}

var changeBeforeSubmit = function() {
    var form = document.forms[0] ;
    if (form) {
        form.onsubmit = function() {
            for (var i = 0; i < searchElements.length;  i ++) {
                if (searchElements[i].value == searchStrings[i]) {
                    searchElements[i].value = «» ;
                }
            }
        }
    }
}

onloadFunctions.push(f1) ;
onloadFunctions.push(f2) ;
onloadFunctions.push(changeBeforeSubmit) ;
onAfterLoad.push(setFocusEvents) ;

window.onload = function() {
    for (var i = 0 ; i < onloadFunctions.length; i ++) {
        onloadFunctions[i]() ;
    }
    for (var i = 0 ; i < onAfterLoad.length; i ++) {
        onAfterLoad[i]() ;
    }
}

Май 25, 2008

Отслеживаем нажатие ENTER внутри формы

Filed under: Uncategorized — Метки: , , , , , , , — sidslog @ 12:21 пп

На форме jsf-страницы может быть расположено несколько UICommand компонент. Необходима возможность указать, какая компонента должна срабатывать при нажатии ENTER в одном из полей ввода.

Для этого будем обрабатывать onkeypressed внутри формы. То, какая компонента будет срабатывать и в каком контейнере, будем указывать с помощью классов «control» и «container».

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

<h:form id=»default»>
    <t:panelGrid id=»cont» >
        <t:inputText value=»#{testInfo.name}»/>
        <t:commandButton type=»button» action=»#{testInfo.act1}» value=»button» />
        <t:commandLink action=»#{testInfo.act2}» value=»link» >
            <jsfaces:test forId=»cont»/>
        </t:commandLink>
    </t:panelGrid>
    <t:panelGrid id=»cont2″>
        <t:inputText value=»#{testInfo.name}»/>
        <t:commandButton action=»#{testInfo.act1}» value=»button»>
            <jsfaces:test forId=»cont2″ />
        </t:commandButton>
        <t:commandLink action=»#{testInfo.act2}» value=»link» />
    </t:panelGrid>
</h:form>

Внутри сont все нажатия ENTER должны обрабатыватся с помощью CommandLink, внутри cont2 – CommandButton.

Код JS для обработки нажатия ENTER:

var onloadFunctions = new Array() ;

var FindHandler = new Object() ;

FindHandler.testClass = function(className) {
    var classN = new RegExp(«(^|\\s)» + className + «(\\s|$)»);
    return classN.test(className) ;
}

FindHandler.getParentByClass = function(className, tag) {
    while (tag.parentNode) {
        if (tag.className && this.testClass(tag.className)) {
            return tag ;
        } else {
            tag = tag.parentNode ;
        }
    }
    return null ;
}

FindHandler.getElementsByClassName = function(className, tag) {
    var returnElements = new Array() ;
    for (var i = 0 ; i < tag.childNodes.length; i ++) {
        if (tag.childNodes[i].className && this.testClass(tag.childNodes[i].className)) {
            returnElements.push(tag.childNodes[i]) ;
        }
    }

    for (var i = 0 ; i < tag.childNodes.length; i ++) {
        var ret = this.getElementsByClassName(className, tag.childNodes[i]) ;
        if (ret) {
            for (var j = 0 ; j 0 ? returnElements : null ;
}

var KeyCode = new Object() ;
KeyCode.VK_ENTER = 13 ;

var f1 = function() {
    var form = document.forms[0] ;
    if (form) {
        form.onkeypress = function(e) {
            var key;
            if (window.event)
                key = window.event.keyCode; //IE
            else
                key = e.which; //firefox

            if (key == KeyCode.VK_ENTER) {

                var target ;
                if (window.event) {
                    target = window.event.srcElement ;
                } else {
                    target = e.target ;
                }
                var parent = FindHandler.getParentByClass(«container», target) ;
// alert(parent) ;
                if (parent) {
                    var controls = FindHandler.getElementsByClassName(«control», parent) ;
                    if (controls && controls[0]) {
// alert(controls[0].tagName) ;
                        if (controls[0].tagName == «INPUT» || controls[0].tagName == «BUTTON») {
                            controls[0].click() ;
                        } else {
                            controls[0].onclick() ;
                        }
                    }
                }
            }

            return false ;
        }
    }
}

onloadFunctions.push(f1) ;

window.onload = function() {
    for (var i = 0 ; i < onloadFunctions.length; i ++) {
        onloadFunctions[i]() ;
    }
}

Классы компоненты:

UITest.java:

public class UITest extends UIComponentBase implements NamingContainer {

    public void restoreState(FacesContext facesContext, Object object) {
        if (object instanceof Object[]) {
            Object[] state = (Object[]) object ;
            super.restoreState(facesContext, state[0]);
            forId = (String) state[1] ;
        } else {
            super.restoreState(facesContext, object); //To change body of overridden methods use File | Settings | File Templates.
        }
    }

    public Object saveState(FacesContext facesContext) {
        Object[] state = new Object[2] ;
        state[0] = super.saveState(facesContext) ;
        state[1] = forId ;
        return state ; //To change body of overridden methods use File | Settings | File Templates.
    }

    public static final String COMPONENT_TYPE = «ru.js.faces.component.test.Test» ;
    public static final String COMPONENT_FAMILY = «ru.js.faces.component.test.Test» ;
    public static final String RENDERER_TYPE = «ru.js.faces.component.test.TestRenderer» ;

    public UITest() {
        super() ;
        setRendererType(RENDERER_TYPE) ;
    }

    public String getFamily() {
        return COMPONENT_FAMILY ; //To change body of implemented methods use File | Settings | File Templates.
    }

    private String forId ;

    public String getForId() {
        return forId;
    }

    public void setForId(String forId) {
        this.forId = forId;
    }
}

TestTag.java:

public class TestTag extends UIComponentTag {

    public String getComponentType() {
        return UITest.COMPONENT_TYPE; //To change body of implemented methods use File | Settings | File Templates.
    }

    public String getRendererType() {
        return UITest.RENDERER_TYPE; //To change body of implemented methods use File | Settings | File Templates.
    }

    public String forId;

    public String getForId() {
        return forId;
    }

    public void setForId(String forId) {
        this.forId = forId;
    }

    protected void setProperties(UIComponent uiComponent) {
        super.setProperties(uiComponent); //To change body of overridden methods use File | Settings | File Templates.
        if (null != getForId()) {
            ((UITest)uiComponent).setForId(getForId());
        }
    }
}

TestRenderer.java:

public class TestRenderer extends Renderer {

    @Override
    public void encodeBegin(FacesContext facesContext, UIComponent uiComponent) throws IOException {
        super.encodeBegin(facesContext, uiComponent); //To change body of overridden methods use File | Settings | File Templates.
        UITest testComponent = (UITest) uiComponent ;
        ResponseWriter writer = facesContext.getResponseWriter();

        writer.startElement(«script», testComponent);
        writer.writeAttribute(«type», «text/javascript», null);

        String containerId = ((UITest)uiComponent).getForId() ;
        UIComponent parent = uiComponent.getParent() ;
        String controlId = parent.getId() ;

//JavaScript в данном случае добавляет к фунциям, которые выполнятся при загрузке еще одну. В ней элементам контейнера и контрола будут присваиваться соответствующие классы
        writer.writeText(«var «+ controlId +»UseOnEnter = function () { var container = document.getElementById(document.forms[0].id + ‘:’ + ‘»+ containerId + «‘) ;\n» +
        »var control = document.getElementById(document.forms[0].id + ‘:’ + ‘» + controlId + «‘); \n» +
        »container.className = container.className + ‘ container’ ;\n» +
        »control.className = control.className + ‘ control’; }\n» +
        »onloadFunctions.push(«+ controlId +»UseOnEnter)», null) ;

        writer.endElement(«script»);
    }

}

Описание компоненты и рендерера в faces-config.xml:

<component>
    <component-type>ru.js.faces.component.test.Test</component-type>
    <component-class>ru.js.faces.component.test.UITest</component-class>

    <property>
        <property-name>value</property-name>
        <property-class>java.lang.String</property-class>
    </property>

</component>

<render-kit>
    <renderer>
        <component-family>ru.js.faces.component.test.Test</component-family>
        <renderer-type>ru.js.faces.component.test.TestRenderer</renderer-type>
        <renderer-class>ru.js.faces.component.test.TestRenderer</renderer-class>
    </renderer>
</render-kit>

Описание тега в components.tld:

<tag>
    <name>test</name>
    <tag-class>ru.js.faces.component.test.TestTag</tag-class>
    <body-content>empty</body-content>
    <attribute>
        <name>forId</name>
        <type>java.lang.String</type>
        <required>true</required>
        <rtexprvalue>false</rtexprvalue>
    </attribute>
    <attribute>
        <name>id</name>
        <type>java.lang.String</type>
        <required>false</required>
        <rtexprvalue>false</rtexprvalue>
    </attribute>
</tag>

Подключение components.tld в web.xml:

<jsp-config>
    <taglib>
        <taglib-uri>http://jsfaces</taglib-uri>
        <taglib-location>/WEB-INF/components.tld</taglib-location>
    </taglib>
</jsp-config>

Май 23, 2008

Myfaces Tomahawk и Javascript изменение надписи при наведении фокуса

Filed under: Uncategorized — Метки: , , , , — sidslog @ 12:09 пп

На странице расположена таблица с некоторыми данными. Над ней фильтр с полем ввода и кнопкой найти. Изначально в поле ввода значение «Поиск…», при наведении фокуса значение пропадает. На то, какое поле ввода будет обрабатываться, будет указывать с помощью класса.

Код jsf:

<h:inputText value=»#{searchInfo.searchName}» styleClass=»search-value-container» />

<h:commandLink value=» #{resources['command.FIND']} » action=»#{searchInfo.find}» />

Класс search-value-container указывает на то, что наведение и уход фокуса будут обрабатываться у inputText.

В бине :

public String getSearchName() {
    if (getName() == null || getName().equals(«»)) {
        return Util.getResource(Messages.SEARCH_CODE) ;
    }
        return getName();
}

public void setSearchName(String name) {
    if (name == null || name.equals(Util.getResource(Messages.SEARCH_CODE)) || name.equals(«»)) {
        setName(null);
    } else {
        setName(name.trim()) ;
    }
}

public String getName() {
    return name;
}

public void setName(String name) {
    this.name = name;
}

Код JS для обработки фокуса:

var el = new Array() ;
var searchValue = «» ;

function getElementsByClassName(className, tag, elm){
     var testClass = new RegExp(«(^|\\s)» + className + «(\\s|$)»);
     var tag = tag || «*» ;
     var elm = elm || document ;
     var elements = (tag == «*» && elm.all) ? elm.all : elm.getElementsByTagName(tag) ;
     var returnElements = [] ;
     var current ;
     var length = elements.length ;
     for ( var i = 0; i < length; i ++) {
         current = elements[i] ;
         if (testClass.test(current.className)) {
             returnElements.push(current) ;
         }
     }
     return returnElements ;
}

function focusSearch() {
     if (this.value == searchValue) {
         this.value = «» ;
     }
}

function blurSearch() {
     if (this.value == «») {
         this.value = searchValue ;
     }
}

function load() {

     var allElements = getElementsByClassName(‘search-value-container’, null, null) ;

     for (var i = 0; i < allElements.length; i ++) {
         el[i] = allElements[i] ;
         if (el[i]) {
             el[i].onfocus = focusSearch ;
             el[i].onblur = blurSearch ;
         }
     }

}

window.onload = load ;

Для того, чтобы указать, какое значение должно быть в поле ввода, необходимо перед inputText’ом поместить

<t:jsValueSet name=»searchValue» value=»#{resources['message.SEARCH_CODE']}» />

Май 17, 2008

Решение PermGen OutOfMemoryError

Filed under: Uncategorized — Метки: , , , — sidslog @ 6:01 пп

Теория про поколеняи и сборщики мусора

Ошибка PermGen происходит при переполнении памяти в постоянном (permanent) поколении.

Решение:

Использовать синхронизованый сборщик мусора

-XX:+UseConcMarkSweepGC

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

-XX:+CMSPermGenSweepingEnabled

Указать сборщику на выгрузку классов

-XX:+CMSClassUnloadingEnabled

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

-XX:MaxPermSize=128m

Ссылка на оригинал

Май 16, 2008

JBOSSQL ManyToOne OUTER JOIN

Filed under: Uncategorized — Метки: , , — sidslog @ 2:30 пп

Класс карточки

public class Card implements Serializable {
private List contributors;
@OneToMany(mappedBy = «card»)
public List getContributors() {
return contributors != null ? contributors : (contributors = new ArrayList());
}
}

Класс контрибутора:

public class Contributor implements Serializable {
private Card card ;
@ManyToOne(targetEntity = Card.class)
@JoinColumn(name = «CARDID»)
public Card getCard() {
return card;
}
}

При выборке карточек для поиска по контрибуторам можно использовать запрос:

getEntityManager().createQuery(«SELECT DISTINCT card FROM catalog.Card card LEFT OUTER JOIN card.contributors contributor WHERE contributor….») ;

Март 2, 2008

richFaces tutorial

Filed under: Uncategorized — Метки: , — sidslog @ 6:26 пп

http://labs.jboss.com/file-access/default/members/jbossrichfaces/freezone/docs/devguide/en/html/index.html

Theme: Silver is the New Black. Блог на WordPress.com.

Follow

Get every new post delivered to your Inbox.