Saturday, January 26, 2013

Java Service Loader




JDK 1.1  მა შემოგვთავაზა მექანიზმი, რომლითაც ჩვენ შეგვიცლია JDBC driver დავამათოთ უბრალოდ  სწორი Class ის დალოადებით.   მაგალითად, შეგვიძლია JDBC-ODBC დრაივერი დავალოადოთ Class.forName ის  დახმარებით.   Class.forName("sun.jdbc.odbc.JdbcOdbcDriver")

Java 6 დან მოყოლებული ჩვენ შეგვიძლია უკვე java.util.ServiceLoader ის გამოყენება. ჩვენ ვიყენებთ  META-INF/services/ დირექტორიას, სადაც ვინახავთ იმპლემენტაციების სერვისებს - ფაილს ,  რომელის სახელი არის interface -ის მისამართი (package  +ClassName), მასში კი წერია  კონკრეტული იმპლემენტაციის მისამართი (package  +ClassName);

მაგალითად JDBC-ODBC  driver თან წვდომა თუ გვინდა, ამ შემთხვევაში ჩვენ აღარ გვჭირდება  Class.forName()-თი დალოადება (instance რომ ავიღოთ).  JRE/lib/resources.jar შეიცავს META-INF/services/java.sql.Driver ფაილს  (java.sql.Driver არის interface) , რომელშიც ერთი ხაზია  sun.jdbc.odbc.JdbcOdbcDriver.  ამ დროს ServiceLoader დაუვლის ყველა jar ფაილს, რომელიც  კლას path ში აქვს მითითებული, და  ყველა შესაძლო დრაივერებს იპოვის (თუ მოიძენა ასეთი).

მაგალითად, ჩვენ თუ გვინდა MS SQL-ის ბაზასთან წვდომა, ჩვენ მისი jar უნდა მივაბათ ჩვენს პროექტს კლას path ში. ან დერბიზე მაგალითად derby.jar და დრაივერი ავტომატურად იქნება წვდომადი ჩვენთვის.

ეს მექანიზმი ეხება ყველა სერვისს, და არა მხოლოდ JDBC-ს. მაგალითად თუ გვინდა   JRuby სკრიპტი  გამოვიძახოთ ჯავადან, ჯვენ მხოლოდ jar ფაილს მივაბამთ, რომელიც შეიცავს  META-INF/services/javax.ScriptEngineFactory ფაილს.  ეს უზრუნველყოფს შესაბამისი სკრიპტის  დალოადებას VM -ში.

მგალითად, გავაკეთოთ შემდეგი რამ. დავწეროთ ინტერფეისი Upload Service-სი. მის იმპლემეტაციებიც ბევრი შესაძლებელია გვქონდეს. მაგალითად აპლუადო google Drive-თი, ან Drop Box ით.   თუ მომხმარებელი ახალ იმპლემენტაციას დაამატებს, მას მხოლოდ JAR ფაილის  მიმატება მოუწევს კოდში (path). ანუ Plug In ების პონტში გვაქვს.



პირველრიგში ინტერფეისს ვაკეთებთ.

package ge.storage.remote
public interface FileStorage {
 . . . 
 void deleteFile(RemoteFile fileData) throws IOException; 
 void createFolder(RemoteDirectory directory) throws IOException; 
 . . . 
 boolean isProviderOf(String Name);

}
შემდეგ იმპლემენტაციებს, მაგალითად google სას (იმპლემენტაციის jar დაგვჭრდება მერე. რომ მივაბათ კლასფასში)
 
package ge.storage.google
public class GoogleStorage implements FileStorage {
  @override
  public boolean isProviderOf(String Name) {
  // გადმოვცემთ სტრინს და ვეუბნებით არის თუ არა ეს იმპლემენტაცია
  // მოთხოვნისა. თუ DropBox მოგვთხოვეს, Google -ს შესბამისად ხომ
  // არ დავუბრუნებთ
  }
  . . .
}


აქ ჩვენ ახლა გვინდა  META-INF/services/ge.storage.remote.FileStorage ფაილი, რომელიც შეიცავს ტექსტს ge.storage.google.GoogleStorage ს.


ახლა კი ვითრევთ ჩვენს სერვისებს. აქ უქვე  პროექტს დეფენენსი ინტერფეისზე გვინდა.
 
ServiceLoader<FileStorage > Services = ServiceLoader.load(FileStorage.class); 
    for (FileStorage service : Services) {
       if (service.isProviderOf(name)) {
         return service;
        }
 }

თუ მოთხოვნაა google-სი, და მისი jar მიბმულია. service.isProviderOf(name) დაგვიბრუნებს ჩვენს სერვისს. შედეგად კი, გვაქ იმ იმპლემენტაციასთან წვდომა  რომელიც ჩვენ გვინდოდა.

ესეიგი ჩვენი პროექტი უყურებს მხოლოდ ინტერფეისს და არა რომელიმე იმპლემენტაციას ინტერფეისიას. ხოლო თუ რომელი jarიქნება მიბმული ფასზე, მის მიხედვით აირჩევა შესაბამისი იმპლემენტაცია.


.net ში service loader-ს აქ შემდეგი სახე:

  1. Assembly asm = Assembly.
  2. LoadFrom(@"c:\myapp\plugins\myAssembly.dll");
  3. MyInterface IMy = null;
  4. foreach (Type t in asm.GetTypes()) {
  5. if (t.GetInterface("MyInterface") != null) {
  6. IMy = (MyInterface)Activator.CreateInstance(t);
  7. break;
  8. }
  9. }




No comments:

Post a Comment