# Copyright: Copyright 2009 Topic Maps Lab, University of Leipzig. # License: Apache License, Version 2.0 unless Object.const_defined?("Gem") && rtmgem = Gem.loaded_specs["rtm-ontopia"] javatmapi_path = File.expand_path(File.join(File.dirname(__FILE__), "../../../rtm-javatmapi/lib")) $LOAD_PATH.unshift javatmapi_path if File.directory?(javatmapi_path) end require 'rtm/javatmapi' Dir[File.join(File.dirname(__FILE__), 'ontopia/javalibs/*.jar')].each {|file| require file } require File.join(File.dirname(__FILE__), '/ontopia/io/to_cxtm') module RTM class Ontopia < JavaTMAPI identifier :ontopia def initialize(*args) super tmsf = Java::NetOntopiaTopicmapsImplTmapi2::TopicMapSystemFactory.new set_tmsf(tmsf) set_properties(@params[:properties]) set_features(@params[:features]) create_system end def self.connect(*args) params = args.first # redirect to create an rdbms connection if an adapter is given and we did not already com from there if params && params[:adapter] && self != OntopiaRdbms OntopiaRdbms.connect(*args) else # adapter was not given or we already handled it and came back here through super super end end end class OntopiaRdbms < Ontopia require File.join(File.dirname(__FILE__), '/ontopia/rdbms/properties') require File.join(File.dirname(__FILE__), '/ontopia/rdbms/store') identifier :ontopia_rdbms def initialize(params={}) require 'active_record' unless defined?(ActiveRecord) require 'jdbc_adapter' unless defined?(ActiveRecord::ConnectionAdapters::Jdbc) params2 = params || {} # XXX hack: maybe setting params in Engine#initialize is not the best idea # enhance all ontopia properties from rails-style config ontopia_properties = RTM::Ontopia::Rdbms::Properties.rails2ontopia(config(params2)) # FIXME: this works only if properties was not specified as file params2[:properties] ||= {} ontopia_properties.each do |k,v| params2[:properties][k] = v.to_s unless params2[:properties].key?(k) end super @params.merge(params2) end def config(params = @params) # if no config-option is given, use params directly, but clean it up a bit params[:config] || params.reject{|k,v| k == :identifier || k == :backend || k == :implementation} end # Migrate the Database to contain the Ontopia RDBMS schema. Uses the connection data provided with connect def migrate_database self.class.migrate_database(config) end alias migrate migrate_database # Drops the Ontopia schema from the database. This corresponds to migrating the database down. def migrate_database_down self.class.migrate_database_down(config) end alias migrate_down migrate_database_down def migrate_database_force_down self.class.migrate_database_force_down(config) end alias migrate_force_down migrate_database_force_down def migrate_database_custom(name) self.class.migrate_database_custom(name, config) end alias migrate_custom migrate_database_custom # Migrate the Database to contain the Ontopia RDBMS schema. # Uses a Rails-style database configuration hash to connect to the database. # # Additional hash-keys to override the defaults: # :connection => an existing connection to be used instead of creating using given parameters # :schema_sql => directly supply sql to execute instead of :schema_file or autodetect # :schema_file => directly supply sql file to execute instead of autodetect # :adapter_schema_name => directly supply the schema name (generic, h2, mysql, oracle8, oracle9i, oracle10g, postresql, sqlserver) # :direction => :up|:down create or drop schema (defaults to :up) # def self.migrate_database(config) res = nil connect_with(config) do |connection| # see if we were provided with a schema directly or get it from file schema_sql = config[:schema_sql] unless schema_sql # should we migrate up or down? direction = config[:direction] || :up create_or_drop = case direction.to_s when "up" "create" when "down" "drop" when "force_down" "force_drop" else direction.to_s end # get the adapter name for ontopia from the config adapter_schema_name = config[:adapter_schema_name] || config[:adapter].sub(/^jdbc/, '') # check if it is one of the available adapters available_schemas = %w[generic h2 mysql oracle8 oracle9i oracle10g postresql sqlserver] adapter_schema_name = "generic" unless available_schemas.include?(adapter_schema_name) # find the corresponding schema file schema_file = config[:schema_file] || File.join(File.dirname(__FILE__), "/../../res/rdbms/setup/#{adapter_schema_name}.#{create_or_drop}.sql") raise "Could not find schema definition file #{File.expand_path(schema_file)}." unless File.exist?(schema_file) # read the schema schema_sql = File.read(schema_file) end # # execute the schema # res = connection.execute(schema_sql) # execute the schema, statement by statement statement_list = schema_sql.split(/;/).map(&:strip).reject{|s| s.blank?} statement_list.each do |statement| connection.execute(statement) end end res end # Drops the Ontopia schema from the database. This corresponds to migrating the database down. def self.migrate_database_down(params={}) migrate_database(params.merge(:direction => :down)) end def self.migrate_database_force_down(params={}) migrate_database(params.merge(:direction => :force_down)) end def self.migrate_database_custom(name, params={}) migrate_database(params.merge(:direction => name)) end # Provides a given block with a (temporary) connection using the given # configuration. Either an existing connection is used (and kept open) or # a new connection is opened, yielded to the block and then closed again. # # This is needed for migrations and for the connection check. # Within the connection check it loads the needed JDBC connectors via # ActiveRecord. def self.connect_with(config) require 'active_record' unless defined?(ActiveRecord) require 'jdbc_adapter' unless defined?(ActiveRecord::ConnectionAdapters::Jdbc) # prepare variable for separate connection class (if needed) anonymous_active_record_base = nil # use a supplied connection or create one using the config connection = config[:connection] unless connection # create a new anonymous class extending ActiveRecord::Base for a separate connection anonymous_active_record_base = Class.new(ActiveRecord::Base) # connect to backend using the anonymous base class anonymous_active_record_base.establish_connection(config) connection = anonymous_active_record_base.connection end begin yield connection if block_given? ensure # close connection if we created a new one anonymous_active_record_base.remove_connection if anonymous_active_record_base end end def check check_ok = false self.class.connect_with(config) do |connection| check_ok = true if connection.table_exists?('tm_topic_map') || connection.table_exists?('TM_TOPIC_MAP') # MySQL needs ALLCAPS, but e.g. H2 needs small letters. Dunno why. end check_ok end end end Java::OrgTmapiCore::TopicMap.register_java_implementation Java::NetOntopiaTopicmapsImplTmapi2::TopicMapImpl module Java::OrgTmapiCore::Topic register_java_implementation Java::NetOntopiaTopicmapsImplTmapi2::TopicImpl end module Java::OrgTmapiCore::Association register_java_implementation Java::NetOntopiaTopicmapsImplTmapi2::AssociationImpl end module Java::OrgTmapiCore::Name register_java_implementation Java::NetOntopiaTopicmapsImplTmapi2::NameImpl end module Java::OrgTmapiCore::Occurrence register_java_implementation Java::NetOntopiaTopicmapsImplTmapi2::OccurrenceImpl end module Java::OrgTmapiCore::Variant register_java_implementation Java::NetOntopiaTopicmapsImplTmapi2::VariantImpl end module Java::OrgTmapiCore::Role register_java_implementation Java::NetOntopiaTopicmapsImplTmapi2::RoleImpl end module Java::OrgTmapiCore::Scoped register_java_implementation Java::NetOntopiaTopicmapsImplTmapi2::ScopedImpl register_java_implementation Java::NetOntopiaTopicmapsImplTmapi2::AssociationImpl register_java_implementation Java::NetOntopiaTopicmapsImplTmapi2::NameImpl register_java_implementation Java::NetOntopiaTopicmapsImplTmapi2::OccurrenceImpl register_java_implementation Java::NetOntopiaTopicmapsImplTmapi2::VariantImpl end module Java::OrgTmapiCore::Typed register_java_implementation Java::NetOntopiaTopicmapsImplTmapi2::AssociationImpl register_java_implementation Java::NetOntopiaTopicmapsImplTmapi2::NameImpl register_java_implementation Java::NetOntopiaTopicmapsImplTmapi2::OccurrenceImpl register_java_implementation Java::NetOntopiaTopicmapsImplTmapi2::RoleImpl end module Java::OrgTmapiCore::Construct register_java_implementation Java::NetOntopiaTopicmapsImplTmapi2::ConstructImpl register_java_implementation Java::NetOntopiaTopicmapsImplTmapi2::TopicMapImpl register_java_implementation Java::NetOntopiaTopicmapsImplTmapi2::TopicImpl register_java_implementation Java::NetOntopiaTopicmapsImplTmapi2::AssociationImpl register_java_implementation Java::NetOntopiaTopicmapsImplTmapi2::NameImpl register_java_implementation Java::NetOntopiaTopicmapsImplTmapi2::OccurrenceImpl register_java_implementation Java::NetOntopiaTopicmapsImplTmapi2::VariantImpl register_java_implementation Java::NetOntopiaTopicmapsImplTmapi2::RoleImpl end module Java::OrgTmapiCore::Reifiable register_java_implementation Java::NetOntopiaTopicmapsImplTmapi2::ReifiableImpl register_java_implementation Java::NetOntopiaTopicmapsImplTmapi2::TopicMapImpl register_java_implementation Java::NetOntopiaTopicmapsImplTmapi2::AssociationImpl register_java_implementation Java::NetOntopiaTopicmapsImplTmapi2::NameImpl register_java_implementation Java::NetOntopiaTopicmapsImplTmapi2::OccurrenceImpl register_java_implementation Java::NetOntopiaTopicmapsImplTmapi2::VariantImpl register_java_implementation Java::NetOntopiaTopicmapsImplTmapi2::RoleImpl end module Java::OrgTmapiCore::Locator register_java_implementation Java::NetOntopiaTopicmapsImplTmapi2::LocatorImpl end module Java::OrgTmapiCore::DatatypeAware register_java_implementation Java::NetOntopiaTopicmapsImplTmapi2::DatatypeAwareImpl register_java_implementation Java::NetOntopiaTopicmapsImplTmapi2::VariantImpl register_java_implementation Java::NetOntopiaTopicmapsImplTmapi2::OccurrenceImpl end