2017-08-14 3 views
1

Windows 10 Professional 64ビット版でRDF4J 2.2.1を使用しています。私は日時に敏感ないくつかのSPINコンストラクタルールを持っています。たとえば、xsd:dateTimeデータ型のプロパティを含むトリプルと、SPARQLの組み込みのnow()関数の出力を比較したい場合があります。この機能をデバッグするには、システムクロックを操作するのではなく、何らかの形でRDF4Jの日時の認識を操作すると便利です。私は、一般的な商用ソフトウェア(Solution Softの「Time Machine」など)があり、これは一般にWindowsプロセスの時間の認識を操作できることを認識しています。しかし、このソフトウェアは、概念実証プロジェクトではあまりにも高価すぎるようです。私が行うことができるようにしたいのですがどのようなRDF4Jでデバッグ用の日付/時刻を操作する

  • 設定RDF4Jの日付/時間のいくつかの任意の日付/時刻値に。
  • RDF4Jの日付/時刻は、リアルタイムの速度またはデバッグ中にプログラム可能なより高速な速度で進みます。

誰もRDF4Jのこのような日時の操作方法の提案はありますか?それは、時間に敏感なSPINルールのデバッグをより効率的にするでしょう。私は多くの他のものがそれに依存しているので、私のPCのシステム時計と戦わないことを好むでしょう。仮想PC全体を実行し、仮想PCでデバッグすることも別の選択肢だと思いますが、よりシンプルな方法が必要です。

ありがとうございました。

答えて

1

RDF4JのカスタムSPARQL関数のさらなる例として他の人を助けてくれることを期待して、自分の解決策を私の質問に投稿しています。私はこれをエレガントな解決策として(テスト条件の設定のせいで)保持していませんが、それはうまく動作し、私の要求を満たしています。このソリューションは、私は今、3または引数なしのどちらかを取ることができsoo:spectrumOpsDateTime()と呼ばれる関数としてPREFIX soo: <http://www.disa.mil/dso/a2i/ontologies/PBSM/Sharing/SpectrumOperationsOntology#>によって定義された名前空間で実装されたカスタムを持っているhttp://docs.rdf4j.org/custom-sparql-functions/ ...

に基づいて@jeen_broekstraから答えを拡張します。 3つの引数の場合、スケーリングされた日付時刻を次のように設定できます。

  • まず引数:xsd:boolean ...使用のシステム・クロックの場合trueまたはfalse
  • 第二引数場合スケールクロックを使用します。xsd:dateTime ...スケーリングされたクロックの開始日/時間(最初の引数がtrueの場合は無視されます)操作
  • 第三引数:xsd:double(最初の引数がtrueの場合は無視されます)...スケールクロック・レート(例えば2.0がスケーリングクロックが二回リアルタイムで、速く走る意味)

何が存在しない場合引数であるsoo:spectrumOpsDateTime()は、Javaコードで指定された初期値または最後に指定された3引数の呼び出しに応じて、スケーリングされた日付/時刻またはシステムの日付/時刻を返します。テスト対象のSPARQLおよびSPINコードは引数のないバージョンのみを使用します。テストセットアップクエリは、特定のテストの時間条件を設定します。ここで

は今朝始まる2倍の速度を設定する例SPARQLのセットアップクエリです:

PREFIX soo: <http://www.disa.mil/dso/a2i/ontologies/PBSM/Sharing/SpectrumOperationsOntology#> 

SELECT DISTINCT * 
WHERE { 
    BIND(soo:spectrumOpsDateTime("false"^^xsd:boolean, "2017-08-22T10:49:21.019-05:00"^^xsd:dateTime, "2.0"^^xsd:double) AS ?testDateTime) . 
} 

ここでスケーリングした日付/時刻を取得する例SPARQLクエリです:

PREFIX soo: <http://www.disa.mil/dso/a2i/ontologies/PBSM/Sharing/SpectrumOperationsOntology#> 

SELECT DISTINCT * 
WHERE { 
    BIND(soo:spectrumOpsDateTime() AS ?testDateTime) . 
} 

単一のクラスこのカスタム関数を実装するために使用されます:

/** 
* 
*/ 

package mil.disa.dso.spo.a2i.nsc.sharing2025.scaledDateTime; 

import java.time.ZonedDateTime; 
import java.time.format.DateTimeFormatter; 
import java.time.temporal.ChronoUnit; 

import org.eclipse.rdf4j.model.IRI; 
import org.eclipse.rdf4j.model.Literal; 
import org.eclipse.rdf4j.model.Value; 
import org.eclipse.rdf4j.model.ValueFactory; 
import org.eclipse.rdf4j.model.impl.SimpleValueFactory; 
import org.eclipse.rdf4j.query.algebra.evaluation.ValueExprEvaluationException; 
import org.eclipse.rdf4j.query.algebra.evaluation.function.Function; 

/** 
* Class for generating a configurable date/time clock that can either be a pass-through of the 
* system clock or a scaled clock starting at a specified date/time running at a specified 
* rate from that specified time (first call). 
* @author Greg Cox of Roberson and Associates &copy Copyright 2017 Roberson and Associates, All Right Reserved 
* 
*/ 
public class DateTimeGenerator implements Function { 
    private static final String thisClassName = "RDF4JCustomSPARQLFunction." + DateTimeGenerator.class.getSimpleName(); 
    private static final String thisClassFullName = DateTimeGenerator.class.getName(); 
    private static final boolean errorMessages = true; 
    private static final boolean verboseMessages = true; 

    private double clockPace = 2.0;      // the speed of the clock, 1.0 is real time, 2.0 is 2x real time (double speed) 
    private boolean useSystemClock = false;    // flag to indicate whether to use scaled clock or pass through the system clock 

    private ZonedDateTime startingRealDateTime = null; // the real time stamp at the first call to the evaluate function 
    private ZonedDateTime startingScaledDateTime =  // the scaled time stamp (starting scaled time) at the first call to the evaluate function 
      ZonedDateTime.parse("2016-08-21T17:29:37.568-05:00"); 

    // define a constant for the namespace of custom function 
    private static String NAMESPACE = "http://www.disa.mil/dso/a2i/ontologies/PBSM/Sharing/SpectrumOperationsOntology#"; // defined as soo: elsewhere 





    // this is the evaluate function needed to implement the RDF4J Function interface 
    // it can take 0 or 3 arguments 
    // 0 - get the current scaled time (starting by first call) 
    // 3 - useSystemClock flag (true/false), starting date/time (xsd:dateTime), clock pace (non-negative real w/ 1.0 meaning 1sec = 1sec) 
    @SuppressWarnings("unused") 
    @Override 
    public Value evaluate(ValueFactory valueFactory, Value... args) throws ValueExprEvaluationException { 
     String thisMethodMessagePrefix = ""; 

     if (errorMessages || verboseMessages) { 
      String thisMethodName = ".evaluate: "; 
      thisMethodMessagePrefix = thisClassName + thisMethodName; 
     } 


     if (args.length == 3) { 
      // Three arguments --> attempting to set mode/parameters, so attempt to parse/check them 
      if (verboseMessages) System.out.println(thisMethodMessagePrefix + "attempting to set scaled clock mode/parameters"); 

      boolean argErrFlag = false; 
      boolean newUseSystemClock = false; 
      String argErrMessage = ""; 

      // first argument should be true/false on whether to use system clock (true) or scaled clock (false) 
      if (!(args[0] instanceof Literal)) { 
       argErrFlag = true; 
       argErrMessage += "first argument must be a literal true/false value... "; 
      } else { 
       String useSystemClockString = args[0].stringValue(); 
       if (useSystemClockString.equalsIgnoreCase("true")) { 
        if (verboseMessages) System.out.println(thisMethodMessagePrefix + "use system clock specified"); 
        newUseSystemClock = true; 
       } else if (useSystemClockString.equalsIgnoreCase("false")) { 
        if (verboseMessages) System.out.println(thisMethodMessagePrefix + "use scaled clock specified"); 
        newUseSystemClock = false; 
       } 
       else { 
        argErrFlag = true; 
        argErrMessage += "first argument must be a literal true/false value... "; 
       } 
      } 

      // second argument should be starting date/time for scaled clock (ignore if using system clock) 
      ZonedDateTime startTime = null; 
      if (!newUseSystemClock) { 
       if (!(args[1] instanceof Literal)) { 
        argErrFlag = true; 
        argErrMessage += "second argument must be literal xsd:dateTime value for start of scaled date/time... "; 
       } else { 
        String startDateTimeString = args[1].stringValue(); 
        try { 
         startTime = ZonedDateTime.parse(startDateTimeString); 
        } catch (Exception e) { 
         argErrFlag = true; 
         argErrMessage += "could not parse starting date/time... " + e.getMessage() + "... "; 
        } 
       } 
      } 

      // third argument should be clock pace for scaled clock (ignore if using system clock) 
      Double newClockPace = null; 
      if (!newUseSystemClock) { 
       if (!(args[2] instanceof Literal)) { 
        argErrFlag = true; 
        argErrMessage += "third argument must be literal xsd:double value for clock pace... "; 
       } else { 
        String clockPaceString = args[2].stringValue(); 
        try { 
         newClockPace = Double.parseDouble(clockPaceString); 
        } catch (Exception e) { 
         argErrFlag = true; 
         argErrMessage += "could not parse clock pace which should be a positive xsd:double... "; 
        } 
        if ((newClockPace != null) && (newClockPace <= 0.0)) { 
         argErrFlag = true; 
         argErrMessage += "clock pace must be positive, got " + newClockPace + "... "; 
        } 
       } 
      } 

      // check for errors and set up the generator if no errors... 
      if (argErrFlag) { 
       if (errorMessages) System.err.println(thisMethodMessagePrefix + "ERROR - " + argErrMessage); 
       if (errorMessages) System.err.println(thisMethodMessagePrefix + "throwing exception..."); 
       throw new ValueExprEvaluationException(
         "spectrum operations time function soo:spectrumOpsDateTime() encountered errors in function arguments... " + 
           argErrMessage); 
      } else if (newUseSystemClock) { 
       if (verboseMessages) System.out.println(thisMethodMessagePrefix + "using unscaled system clock"); 
       useSystemClock = newUseSystemClock; 
      } else if (!newUseSystemClock) { 
       if (verboseMessages) System.out.println(thisMethodMessagePrefix + "using scaled time"); 
       useSystemClock = newUseSystemClock; 
       startingRealDateTime = ZonedDateTime.now(); 
       if (verboseMessages) System.out.println(thisMethodMessagePrefix + "setting starting real time to " + startingRealDateTime.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME)); 
       if (verboseMessages) System.out.println(thisMethodMessagePrefix + "setting start time to " + startTime.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME)); 
       startingScaledDateTime = startTime; 
       if (verboseMessages) System.out.println(thisMethodMessagePrefix + "setting clock pace to " + String.format("%5.2f", newClockPace * 100.0) + "%"); 
       clockPace = newClockPace; 
      } 

     } else if (args.length != 0) { // can only have no arguments or three arguments... 
      throw new ValueExprEvaluationException(
        "spectrum operations time function soo:spectrumOpsDateTime() requires " 
          + "zero arguments or three arguments, got " 
          + args.length + " arguments"); 
     } 

     // now run the generator and return the result... 

     IRI xsdDateTimeIRI = valueFactory.createIRI("http://www.w3.org/2001/XMLSchema#dateTime"); // long-form equivalent to xsd:dateTime 

     if (useSystemClock) { 
      String unscaledTimeString = millisTrailingZeroes(ZonedDateTime.now().format(DateTimeFormatter.ISO_OFFSET_DATE_TIME)); 
      return valueFactory.createLiteral(unscaledTimeString, xsdDateTimeIRI); 
     } else { 
      errString = null; 
      String scaledTimeString = millisTrailingZeroes(getScaledDateTime().format(DateTimeFormatter.ISO_OFFSET_DATE_TIME)); 
      if (scaledTimeString == null) { 
       if (errorMessages) System.err.println(thisMethodMessagePrefix + "ERROR - scaled time returned null"); 
       if (errorMessages) System.err.println(thisMethodMessagePrefix + "thowing exception..."); 
       throw new ValueExprEvaluationException("could not generate valid scaled time string" + ((errString == null) ? "" : "... " + errString)); 
      } 
      return valueFactory.createLiteral(scaledTimeString, xsdDateTimeIRI); 
     } 
    } 

    private static String errString = null; 

    /** 
    * Utility method to make all the millisecond fields of an <tt>ISO_OFFSET_DATE_TIME</tt> three digits by 
    * adding trailing zeroes as needed. Why? Because of trouble with various implementations interpreting 
    * 1 and 2 digit milliseconds differently. Should be standard decimal, but sometimes interpreted 
    * as number of milliseconds (e.g. .39T interpreted as 39 millieconds inststead of 390 milliseconds) 
    * @param <tt>ISO_OFFSET_DATE_TIME</tt> string to check for millisecond field length 
    * @return <tt>ISO_OFFSET_DATE_TIME</tt> strnig with trailing zeroes in milliseconds field 
    * as require to make the field three digits or <tt>null</tt> on error 
    */ 
    private static String millisTrailingZeroes(String isoDateTimeString) { 
     if (isoDateTimeString == null) { 
      errString = "DateTimeGenerator.millisTrailingZeroes: got null isoDateTimeString argument, returning null..."; 
      return null; 
     } 

     String[] ss_l1 = isoDateTimeString.split("\\."); // Example: 2017-08-18T13:01:05.39-05:00 --> 2017-08-18T13:01:05 AND 39-05:00 
     if (ss_l1.length != 2) { 
      errString = "DateTImeGenerator.millisTrailingZeros: first parsing split of isoDateTimeString=" + isoDateTimeString + " by '.' got unexpected number of parts=" + ss_l1.length; 
      return null; 
     } 

     String[] ss_l2 = ss_l1[1].split("-");    // 39-05:00 --> 39 AND 05:00 
     if (ss_l2.length != 2) { 
      errString = "DateTImeGenerator.millisTrailingZeros: second parsing split of " + ss_l1[1] + " by '-' got unexpected number of parts=" + ss_l2.length; 
      return null; 
     } 

     if (ss_l2[0].length() == 1) { 
      ss_l2[0] = ss_l2[0] + "00"; 
     } else if (ss_l2[0].length() == 2) 
      ss_l2[0] = ss_l2[0] + "0";      // 39 --> 390 

     return ss_l1[0] + "." + ss_l2[0] + "-" + ss_l2[1]; // 2017-08-18T13:01:05.390-05:00 
    } 

    /** 
    * Method to get the current scaled date time according to the state of this DateTimeGenerator. 
    * If <tt>useSystemClock</tt> is <tt>true</tt>, then time is not 
    * scaled and system time is returned instead of scaled time. 
    * @return scaled date time if <tt>useSystemClock</tt> is <tt>true</tt> or 
    * system date time if <tt>useSystemClock</tt> is <tt>false</tt> 
    */ 
    private ZonedDateTime getScaledDateTime() { 
     ZonedDateTime scaledDateTime = null; 

     if (useSystemClock) { 
      scaledDateTime = ZonedDateTime.now(); 
     } else { 
      if (startingRealDateTime == null) 
       startingRealDateTime = ZonedDateTime.now(); 
      long realMillisFromFirstCall = ChronoUnit.MILLIS.between(startingRealDateTime, ZonedDateTime.now()); 
      long scaledMillisFromFirstCall = (long) ((double) realMillisFromFirstCall * clockPace); 

      scaledDateTime = ChronoUnit.MILLIS.addTo(startingScaledDateTime, scaledMillisFromFirstCall); 
     } 

     return scaledDateTime; 
    } 


    @Override 
    public String getURI() { 
     return NAMESPACE + "spectrumOpsDateTime"; 
    } 

    /** 
    * Test main method 
    * @param args command line arguments (ignored) 
    */ 
    @SuppressWarnings("unused") 
    public static void main(String[] args) { 
     String thisMethodMessagePrefix = ""; 

     if (errorMessages || verboseMessages) { 
      String thisMethodName = ".main: "; 
      thisMethodMessagePrefix = thisClassName + thisMethodName; 
     } 

     DateTimeGenerator testGen = new DateTimeGenerator(); 

     if (verboseMessages) System.out.println(thisMethodMessagePrefix + "custom SPARQL method URI: " + testGen.getURI()); 
     if (verboseMessages) System.out.println(thisMethodMessagePrefix + "fully-qualified class name: " + thisClassFullName); 

     ValueFactory testVF = SimpleValueFactory.getInstance(); 
     Value testValues[] = new Value[0]; 

     while (true) { 

      if (verboseMessages) System.out.println(thisMethodMessagePrefix + "scaled: " + testGen.evaluate(testVF, testValues).stringValue() + 
        " current real: " + millisTrailingZeroes(ZonedDateTime.now().format(DateTimeFormatter.ISO_OFFSET_DATE_TIME))); 
      try { 
       Thread.sleep(1000); 
      } catch (InterruptedException e) { 
       // TODO Auto-generated catch block 
       e.printStackTrace(); 
      } 
     } 
    } 
} 

私の場合、jarファイルのエクスポートEclipseからのedは私のApacheのインストールの下で実行され、C:\Apache\apache-tomcat-8.5.15\webapps\rdf4j-server\WEB-INF\lib\ScaledDateTime.jarにあります。mofificationsを実行すると、このjarファイルを置き換えてApacheサーバーを再起動します。

2

custom SPARQL functionを実装し、実際のnow()関数の代わりにこれを使用して、これを達成できます。例えばmock_now()と呼んでください。あなたが実装するので、あなたはその動作を完全に制御することができます。

+1

はい、デバッグしていないときに実際の 'now()'関数の単純なラッパーにすることができます。ありがとう! –

+0

私はあなたの提案を実装しようとしています。私はこの手順に問題があります」適切なJARファイルを作成したら、RDF4Jプロジェクトの実行時クラスパスを追加する必要があります(RDF4Jサーバーでこれを使用することを目指している場合は、RDF4Jサーバーに追加してください)。その後、あなたは完了です:RDF4Jはあなたの新しいカスタム関数を自動的に拾うべきです。今からSPARQLクエリでそれを使うことができます。 "私はサーバーのケースを持っていて、私はApacheの下で走っています。 RDF4J Server Webアプリケーションのクラスパスはどこに設定されていますか?環境変数? –

+0

使用するサーブレットコンテナによって異なりますが、Apache Tomcatの場合は、 'tomcat/shared/lib'に追加するか、単に 'web-apps/rdf4j-server/WEB-INF/lib'に追加してください'限り、それはクラスローダーがそれをピックアップするどこかにインストールされています。 –