|
چارچوبی
برای
محاسبات
شبكهای
ساخت
برنامه
محاسبات
شبكهای را
با كمك
ابزارهای open-source فرا
بگيريد
نويسنده:
Anthony Karre
مترجم: شهناز پيروزفر
چكيده
چارچوبهاي
محاسباتي
شبكهاي در
مقياس بزرگ
را ميتوان
براي ايجاد
زيرساختارهاي
شبكهاي
محاسباتي
بكار برد،
اما پيچيدگي
اين
چارچوبها ميتواند
مانعي براي
طراحان نرمافزار
در آزمايش
محاسبات
شبكهاي سطح
ورودي باشد.
اين
مقاله با كمك ClassLoader
سفارشي
جاوا،
كانتينر
سرولت Tomcat، Axis SOAP (Simple object Access
protocol)،
چارچوب سادهاي
را براي
پشتيباني از
حداقل
نيازمندهاي
شبكهاي
(گرير) نظير
استقلال
ماشين،
توزيع
محاسبه فعاليت
انتزاعي شده
و اجرا، و
زيرساختار
امن و اندازهپذيري،
تشريح ميكند.
محاسبات
اينترنتي
اصطلاحي است
كه براي توصيف
كاربرد چند
كامپيوتر
شبكهبندي
شده براي
اجراي
برنامههاي
محاسباتي
انحصاري
بكار ميرود.
پروژه The search for
Extraterrestrial Intelligence SETI
مشهورترين
مثال اين
مورد است كه
در آن پروژه،
نيم ميليون
كاربر خانگي
و سازماني از
توان پردازشي
براي تحليل
دادههاي
تلسكوپ
راديويي
براي SETI استفاده
ميكردند.
پروژه
SETI نمونهاي
از قلمرو محاسبات
اينترنتي
است كه
برنامه
انحصاري و انسجام
عمودي دارد.
به عبارتي
استفاده
عمومي از
منابع
محاسباتي
وجود ندارد و
جريان دادهاي
بين منبع
محاسبات و
سرور دادهاي
از APIهاي
اختصاصي
استفاده ميكند.
هنگامي كه
ارتقا يك
برنامه لازم
است، بايد در
فواصلي
اجراهاي جديد
download شوند،
و در واقع هيچ
لايه
مديريتي
براي قرار دادن
ديناميكي
فعاليتهاي
محاسباتي
جديد وجود
ندارد.
تبديل:
اينترنت به
محاسبات
شبكهاي
با
اين تبديل كه
حاصل موفقيت
پروژه SETI است،
محققان
تصميم گرفتهاند
براي بهرهبرداري
از حجم عظيم
منابع
محاسبات
متصل به
اينترنت،
اما به گونهاي
امن، قابل
مديريت و
قابل توسعه،
كارهايي را
انجام دهند.
در چند سال
گذشته،
چارچوبهاي
متعددي با
تاكيد بر
مديريت
منابع
اشتراكي،
توسعه
يافتند. اين
چارچوبها
گونههاي
زير را فراهم
ميآورند:
-
استقلال
ماشين در حين
استفاده از
جاوا و سرويسهاي
وب
-
امنيت در
پيادهسازيهاي
سرويس وب
نظير SOAP با تركيب
پيش ساخته HTTP و
كنترل
اعتبار
كاربر
-
تجرد
فعاليت، كه
قابليت
سيستم
مديريت براي
سپردن وظايف
اختياري به
منابع مبتني
بر شبكه است.
-
مديريت
منابعي به جز
پردازنده
نظير فضاي
ذخيرهسازي
فايل
-
مديريت
پوياي همه
موارد فوق با
ارزيابي
بيدرنگ وضعيت
شبكه
بعضي
از چارچوبها
در اين زمينه
موفق عمل
كردند به
طوري كه طراحان
اينگونه
چارچوبها،
آنها را در
قالب تولكيت
عرضه نمودند. Globus
Toolkit 3.0
نمونهاي از
اين تولكيتهاست.
اين تولكيت
توسعه OGSA (Open Grid Services
Architecture)
را تحت تاثير
قرار ميدهد.
محققان سان
ميكروسيستمز
نيز چارچوب
محاسبات
ديگر مبتني
بر API ارتباطي jxta
peer-to-peer
توسعه دادهاند.
اين
چارچوبها با
وجود
توانمندي كه
دارند نيازمند
دانش بسار
بالايي براي
بهرهبرداري
موفقيتآميزشان
هستند. اين امر
مانعي براي
افراد يا
گروههايي
است كه قصد
دارند از اين
طريق تجربه
بيندوزند.
اين چارچوبها
براي برنامهنويسان
جديد همانند
استفاده از struts براي
اولين
برنامه وب
است.

شكل 1.
توالي ساده
اجراي گريد
حداقل
چارچوب
محاسبات
شبكهاي
من
يك چارچوب
كوچكي را در
اين مقاله
نشان ميدهم
كه به سادگي
محيط
محاسباتي
شبكهاي را
ميسازد. اين
چارچوب را ميتوان
به سرعت با
كمك TomCat، Axis توسعه
داد. چارچوب
مذكور با
وجود سادگي،
حداقل
نيازمنديهاي
شبكه را
برآورده ميسازد:

شكل 2.
خروجي فرمان IS
در يونيكس
- استقلال
ماشين از
طريق به كارگيري
جاوا، Tomcat و Axis حاصل ميشود.
-
زيرساختار
امن و اندازهپذير
كه با
بكارگيري
سرويسهاي
وب مبتني بر SOAP براي
ارتباطات
كلاينت سرور
حاصل ميشود.
-
اجراي
فعاليت
انتزاعي كه
با بكارگيري ClassLoader به دست
ميآيد.
كلاينت با
كمك ClassLoader ميتواند
كلاسهاي thread
اختياري
جاوا را
شناسايي و
اجرا كند.
شكل
1 نماي سادهاي
از معماري
چارچوب و
توالي اجرا
را به تصوير
ميكشد.
ساخت
يك برنامه
نمونه
من
در اين مقاله
بر ساخت و
آزمايش
چارچوب مذكور
متمركز ميشوم
و مثالهايي
را به ترتيب
زير ارائه ميدهم:
1.
ساخت سرويس Server-side
SOAP با
كمك TomCat و Axis
2.
برقراري stubs
ارتباطي
براي
پشتيباني از
كاربرد client-side سرويس SOAP.
3.
ساخت ClassLoader
سفارشي Client-side
4.
ساخت برنامه
اصلي كلاينت
5.
ساخت فعاليت
محاسباتي
براي تمرين
كردن ClassLoader

شكل 3. مشاهده WSDL از
اينترنت
اكسپلورر
ساخت
سرويس SOAP
سرويس
SOAP كه در
اين مقاله ميسازم،
رابطه
تنگاتنگي با
لايه مديريت
دارد. سرويس SOAP به
برنامه
محاسباتي
شبكهاي
امكان ميدهد
تا كلاسهاي
مورد نياز
خود را از
سرور SOAP بگيرد. در
حاليكه مثال
من يك فايل Jar خاص را
ارائه ميدهد،
نسخه واقعي
اين سرويس به
چند فايل jar (كه هر
يك حاوي
فعاليت
محاسباتي
متفاوتي
هستند)
دسترسي
خواهد داشت و
منطقي براي
كنترل JAR دارد.

شكل 4. نتايج WSDL2Java
اولين
گام در فراهم
آوردن سرويس SOAP،
تنظيم
زيرساختار SOAP است.
من TomCot را به
عنوان سرور
كانتينر
سرولت /HTTP برگزيدم،
زيرا يك
پروژه open-source
قابل
اعتماد و
كاربرد آن
آسان است. من Axis را به
عنوان سرويسدهنده
SOAP
برگزيدم،
زيرا از Service
installer با
كاربرد آن drap and
drop
پشتيباني ميكند
و همراه با
ابزاري است
كه SOAP client-side stubs را از
فايلهاي (web services Description Language) WSDL ميسازد.
پس
از download و نصب Tomcat 4.0.6 و Axis 1.0، كلاس Grid
Connection
سرويس SOAP را نوشتم.
اين سرويس
فايل Jar شناخته
شدهاي را
استخراج، آن
را در آرايه
بايت
بارگذاري نموده
و آرايه بايت
را به
فراخوان باز
ميگرداند،
كد زير فايل
كلي Grid connection Java است:
//// GridConnection.java
//
import java.util.*;
import java.io.* ;
public class GridConnection {
public byte[] getJarBytes () {
byte[] jarBytes = null ;
try {
FileInputStream
fi = new FileInputStream("/Users/tkarre/MySquare/build/MySquare.jar");
jarBytes = new
byte[fi.available()];
fi.read(jarBytes);
fi.close() ;
}
catch(Exception e) {}
return jarBytes ;
}
}
نصب
سرويس SOAP
طرح
نصب (Axis Java web Service) JWS، نصب
سرويس جديد
ما را آسان ميسازد.
شما ميتوانيد
يك سرويس
ساده (مانند
كلاس GridConnection فوق)
را با Axis بنويسيد،
پسوند java. فايل را به JWS تغيير
دهيد و آن را
به دايكتوري Axis Webapp، درگ و drop كنيد. Axis،
.jws جديد را
شناسايي و و
تفسير نموده
و آن را به
صورت نقطه
انتهايي SOAP، در
دسترس قرار
ميدهد. شكل 2
اين فايل و
فايلهاي
ساخته شده
كلاس JWS را نشان ميدهد.
در خروجي
فرمان IS يونيكس،
فايل GridConnection .JWS را
بيابيد.
ساخت
Stubهاي
ارتباطي Client-side
به
محض اينكه
سرويس جديد SOAP را نصب
نمودم، بايد
كد Client-side را براي
استفاده از
سرويس توليد
ميكردم. من
از ابزار خط
فرمان Axis WSDL2Java براي
توليد
فايلهاي
جاوا
استفاده
نمودم. اين فايلها،
stubهاي
لازم براي
درخواست
سرويس SOAP را فراهم
ميآورند.
اين
ابزار
فرايند كدنويسي
دستي
كلاسهاي
مورد نياز
براي پيادهسازي
client-side واسط SOAP را حذف
ميكند.
ابزار WSDL2Java به فايل WSDL نياز
دارد، لذا از
گونه ديگر Axis براي
سرويسمان
استفاده ميكنم.
Axis ميتواند
WSDL قابل
رويت را با
استفاده از
مرورگر وب
براي هر گونه
سرويسي كه به
طور محلي نصب
شده است،
توليد كند. من
در كلاس GridConnection از URL.
http://pcworldi.ipowermysql.com:8080/axis/GridConnection.JWS?wsdl
استفاده
نمودم.
شكل
3، WSDL را كه Axis براي
مشاهده در
اينترنت
اكسپلورر
توليد ميكند،
نشان ميدهد.
شما ميتوانيد
پس از ذخيره
فايل WSDL (بدين
منظور ميتواند
از فرمان View Source
استفاده و آن
را ذخيره
نماييد)،
ساخت stub را ادامه
دهيد. من WSDL را به
صورت Grid Connection.wsdl ذخيره
و سپس فرمان
زير را انجام
دادم.
Java org. apache. axis. wsdl. WSDL2Java - 0 -pcom. perficient.
grid. client Grid Connection.wsdl
پارامتر
–p به WSDL2Java ميگويد
كه ساختار
پكيج را براي com.perficient.grid.client بسازد.
من براي ساخت
برنامه
واقعي
كلاينت، از
اين پكيج
استفاده
نمودم. شكل 4
اجراي اين
فرمان را
نشان ميدهد.
(توجه نماييد
كه مسير كلاس
را به گونهاي
تنظيم نماييد
كه شامل JARهاي
متعدد نصب
شده با Axis باشد.) WSDL2Java در شكل
4، ساختار
دايركتوري
پكيج و چهار
فايل جاوا
ساخته است.
محتواي چهار
فايل جاوا
كه با ابزار WSDL2Java ساخته
شده، در ذيل
آمده است:
/**
* GridConnection.java
*
* This file was auto-generated from WSDL
* by the Apache Axis WSDL2Java emitter.
*/
package com.perficient.grid.client;
public interface GridConnection extends java.rmi.Remote {
public byte[] getJarBytes() throws java.rmi.RemoteException;
}
/**
* GridConnectionService.java
*
* This file was auto-generated from WSDL
* by the Apache Axis WSDL2Java emitter.
*/
package com.perficient.grid.client;
public interface GridConnectionService extends javax.xml.rpc.Service {
public java.lang.String getGridConnectionAddress();
public com.perficient.grid.client.GridConnection
getGridConnection() throws javax.xml.rpc.ServiceException;
public com.perficient.grid.client.GridConnection
getGridConnection(java.net.URL
portAddress) throws javax.xml.rpc.ServiceException;
}
/**
* GridConnectionServiceLocator.java
*
* This file was auto-generated from WSDL
* by the Apache Axis WSDL2Java emitter.
*/
package com.perficient.grid.client;
public class GridConnectionServiceLocator extends org.apache.axis.client.Service implements com.perficient.grid.client.GridConnectionService {
// Use to get a proxy class for GridConnection.
private final java.lang.String
GridConnection_address =
"http://pcworldi.ipowermysql.com:8080/axis/GridConnection.jws";
public java.lang.String getGridConnectionAddress() {
return GridConnection_address;
}
// The WSDD service name defaults to the port name.
private java.lang.String GridConnectionWSDDServiceName = "GridConnection";
public java.lang.String getGridConnectionWSDDServiceName() {
return GridConnectionWSDDServiceName;
}
public void setGridConnectionWSDDServiceName(java.lang.String name) {
GridConnectionWSDDServiceName
= name;
}
public com.perficient.grid.client.GridConnection
getGridConnection() throws javax.xml.rpc.ServiceException
{
java.net.URL endpoint;
try {
endpoint = new java.net.URL(GridConnection_address);
}
catch (java.net.MalformedURLException
e) {
return null; // unlikely as
URL was validated in WSDL2Java
}
return getGridConnection(endpoint);
}
public com.perficient.grid.client.GridConnection
getGridConnection(java.net.URL
portAddress) throws javax.xml.rpc.ServiceException
{
try {
com.perficient.grid.client.GridConnectionSoapBindingStub
_stub = new com.perficient.grid.client.GridConnectionSoapBindingStub(portAddress, this);
_stub.setPortName(getGridConnectionWSDDServiceName());
return _stub;
}
catch (org.apache.axis.AxisFault
e) {
return null;
}
}
/**
* For the given interface, get the stub implementation.
* If this service has no port for the given interface,
* then ServiceException is thrown.
*/
public java.rmi.Remote getPort(Class serviceEndpointInterface)
throws javax.xml.rpc.ServiceException {
try {
if (com.perficient.grid.client.GridConnection.class.isAssignableFrom(serviceEndpointInterface)) {
com.perficient.grid.client.GridConnectionSoapBindingStub
_stub = new com.perficient.grid.client.GridConnectionSoapBindingStub(new
java.net.URL(GridConnection_address),
this);
_stub.setPortName(getGridConnectionWSDDServiceName());
return _stub;
}
}
catch (java.lang.Throwable
t) {
throw new javax.xml.rpc.ServiceException(t);
}
throw new javax.xml.rpc.ServiceException("There
is no stub implementation for the interface: " + (serviceEndpointInterface == null ? "null" : serviceEndpointInterface.getName()));
}
/**
* For the given interface, get the stub implementation.
* If this service has no port for the given interface,
* then ServiceException is thrown.
*/
public java.rmi.Remote getPort(javax.xml.namespace.QName
portName, Class serviceEndpointInterface)
throws javax.xml.rpc.ServiceException {
java.rmi.Remote _stub = getPort(serviceEndpointInterface);
((org.apache.axis.client.Stub)
_stub).setPortName(portName);
return _stub;
}
public javax.xml.namespace.QName getServiceName() {
return new javax.xml.namespace.QName("http://pcworldi.ipowermysql.com:8080/axis/GridConnection.jws",
"GridConnectionService");
}
private java.util.HashSet ports = null;
public java.util.Iterator getPorts() {
if (ports == null) {
ports = new java.util.HashSet();
ports.add(new javax.xml.namespace.QName("GridConnection"));
}
return ports.iterator();
}
}
/**
* GridConnectionSoapBindingStub.java
*
* This file was auto-generated from WSDL
* by the Apache Axis WSDL2Java emitter.
*/
package com.perficient.grid.client;
public class GridConnectionSoapBindingStub extends org.apache.axis.client.Stub implements com.perficient.grid.client.GridConnection {
private java.util.Vector cachedSerClasses = new java.util.Vector();
private java.util.Vector cachedSerQNames = new java.util.Vector();
private java.util.Vector cachedSerFactories = new java.util.Vector();
private java.util.Vector cachedDeserFactories = new java.util.Vector();
public GridConnectionSoapBindingStub()
throws org.apache.axis.AxisFault {
this(null);
}
public GridConnectionSoapBindingStub(java.net.URL endpointURL, javax.xml.rpc.Service service) throws org.apache.axis.AxisFault
{
this(service);
super.cachedEndpoint = endpointURL;
}
public GridConnectionSoapBindingStub(javax.xml.rpc.Service service) throws org.apache.axis.AxisFault
{
if (service == null) {
super.service =
new org.apache.axis.client.Service();
} else {
super.service =
service;
}
}
private org.apache.axis.client.Call createCall() throws java.rmi.RemoteException
{
try {
org.apache.axis.client.Call
_call =
(org.apache.axis.client.Call)
super.service.createCall();
if (super.maintainSessionSet)
{
_call.setMaintainSession(super.maintainSession);
}
if (super.cachedUsername
!= null) {
_call.setUsername(super.cachedUsername);
}
if (super.cachedPassword
!= null) {
_call.setPassword(super.cachedPassword);
}
if (super.cachedEndpoint
!= null) {
_call.setTargetEndpointAddress(super.cachedEndpoint);
}
if (super.cachedTimeout
!= null) {
_call.setTimeout(super.cachedTimeout);
}
if (super.cachedPortName
!= null) {
_call.setPortName(super.cachedPortName);
}
java.util.Enumeration
keys = super.cachedProperties.keys();
while (keys.hasMoreElements())
{
java.lang.String
key = (java.lang.String) keys.nextElement();
if(_call.isPropertySupported(key))
_call.setProperty(key, super.cachedProperties.get(key));
else
_call.setScopedProperty(key, super.cachedProperties.get(key));
}
return _call;
}
catch (java.lang.Throwable
t) {
throw new org.apache.axis.AxisFault("Failure
trying to get the Call object", t);
}
}
public byte[] getJarBytes() throws java.rmi.RemoteException {
if (super.cachedEndpoint ==
null) {
throw new org.apache.axis.NoEndPointException();
}
org.apache.axis.client.Call
_call = createCall();
_call.setReturnType(new javax.xml.namespace.QName("http://www.w3.org/2001/XMLSchema",
"base64Binary"), byte[].class);
_call.setUseSOAPAction(true);
_call.setSOAPActionURI("");
_call.setOperationStyle("rpc");
_call.setOperationName(new javax.xml.namespace.QName("http://pcworldi.ipowermysql.com:8080/axis/GridConnection.jws",
"getJarBytes"));
java.lang.Object _resp = _call.invoke(new java.lang.Object[] {});
if (_resp instanceof java.rmi.RemoteException)
{
throw (java.rmi.RemoteException)_resp;
}
else {
try {
return (byte[]) _resp;
} catch (java.lang.Exception
_exception) {
return (byte[]) org.apache.axis.utils.JavaUtils.convert(_resp, byte[].class);
}
}
}
}
اين
كدها بايد با
برنامه
كلاينت لحاظ
شوند آنها
كلاسهاي
لازم براي
اتصال به
انتهاي SOAP را
فراهم ميآورند
و ClassLoader
سفارشي از
آنها
استفاده
خواهد نمود.
ساخت
ClassLoader
سفارشي
ClassLoader
سفارشي
مهمترين جز
برنامه
كلاينت گريد
است - كه فايل jar را از
طريق سرويس SOAP
بازيابي
نموده و سپس
كلاسهاي
لحاظ شده در JAR را
براي اجرا در
دسترس قرار
ميدهد. من با
ساختار ClassLoader
مستحكمي كه
در ابتدا
توسط كنمككراري
در Creat a Custom
Java 1.2- style ClassLoader منتشر
گرديد، آغاز
نمودم. سپس ClassLoader را با
كد ويژهاي
براي برنامه
كامل كردم. كد
اصلي Grid Connection ClassLoader.Java در ذيل
آمده است.
//
// GridConnectionClassLoader.java
//
// Created by Anthony Karre on Wed Dec 11 2002.
// Copyright (c) 2002 Perficient.
All rights reserved, except for Ken McCrary stuff.
//
// Structure of this class was
kick-started by published Ken McCrary examples:
// Here is his copyright notice:
//
/* Copyright (c) 1999 Ken McCrary, All Rights Reserved.
*
* Permission to use, copy, modify, and distribute this software
* and its documentation for NON-COMMERCIAL purposes and without
* fee is hereby granted provided that this copyright notice
* appears in all copies.
*
* KEN MCCRARY MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE
* SUITABILITY OF THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING
* BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. KEN MCCRARY
* SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT
* OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.
*/
package com.perficient.grid.client ;
import java.util.Enumeration;
import java.util.NoSuchElementException;
import java.util.zip.* ;
import java.util.jar.Manifest;
import java.util.jar.JarInputStream ;
import java.util.jar.JarFile ;
import java.util.jar.JarEntry ;
import java.util.jar.Attributes;
import java.util.jar.Attributes.Name;
import java.util.Hashtable ;
import java.io.* ;
import java.net.URL ;
public class GridConnectionClassLoader extends ClassLoader {
// Provide normal constructor.
GridConnectionClassLoader() {
super();
init();
}
// Provide delegation constructor.
GridConnectionClassLoader(ClassLoader parent) {
super(parent);
init();
}
در
اينجا اولين
متد آمده است: .init() اين متد با
استفاده از SOAP
stubs،
سعي دارد به
انتهاي SOAP متصل
شود. بدين
ترتيب آرايه
بابت محتوي
فايل Jar را
استخراج ميكند.
اين متد دو
فعاليت ديگر
را نيز انجام
ميدهد. مشخص
نمودن
اندازه هر
منبع JAR به بايت
و شناسايي
كلاس اصلي كه
بايد اجرا
شود.
در
اين طراحي
بايد كلاس
اصلي آبجكت thread جاوا
باشد. و از
طريق صفت Task-Thread (مشابه
با مفهوم صفت Main-Class كه در
كلاسهاي
اصلي ايستا
به كار ميرود)
شناسايي شود.
//******************************************
// Initialize the ClassLoader by using
Web
// services (SOAP)
to fetch our JAR.
// This jar file will contain the classes that
// compose our
grid task.
// *****************************************
private void init() {
byte[] localjarbytes = null
;
File tempfile = null ;
FileInputStream tempfis = null ;
JarInputStream jis = null ;
try {
// Make a SOAP service to start the
process of fetching the JAR.
com.perficient.grid.client.GridConnectionService
service =
new com.perficient.grid.client.GridConnectionServiceLocator()
;
// Now use the service to get a stub to
the SOAP service itself.
com.perficient.grid.client.GridConnection
gc = service.getGridConnection()
;
// Make the actual call and fetch the
bytes.
localjarbytes
= gc.getJarBytes() ;
// If localjarbytes
is null, then the grid connection failed. One possibility
// is
that a jar file could not be found or loaded by the grid server.
// Print
a message and exit.
if (localjarbytes == null) {
System.out.println("Class
Loader: The grid connection returned no bytes. No Jar file is
currently available.") ;
return ;
}
// At this point we can store
our bytes into a temp file. This temp file
// will
be a repository for the JAR, allowing the classloader
to fetch
// classes as necessary.
System.out.println("Class Loader: the grid connection returned " +
localjarbytes.length + " bytes") ;
// Save the size of the JAR for later
use.
this.jarsize = localjarbytes.length ;
try {
// Create a temp
jar file for these bytes. Having an actual file will
// allow
us to retrieve various resources more easily.
tempfile = File.createTempFile("grid",
".jar") ;
// Make sure our
file is deleted when the VM exits.
tempfile.deleteOnExit() ;
// Get an output stream and
dump our bytes to the file.
FileOutputStream
tempfileos = new FileOutputStream(tempfile) ;
tempfileos.write(localjarbytes) ;
tempfileos.close()
;
}
catch (IOException
e) {
System.out.println("Class
Loader: Problem with temp Jar file: " + e.getMessage())
;
}
}
catch (Exception e) {
System.out.println("Class
Loader: a grid connection failure occurred:\n" + e.toString()
+ "\n" + e.getMessage()) ;
}
try {
// Save the filename for later reference.
jarfilename
= new String(tempfile.getCanonicalPath()) ;
// Try to get a manifest, if one
exists. This will
// let us know what the main task thread
class is.
// The main task thread is the primary
work unit
// that we will be executing in our grid.
tempfis = new FileInputStream(tempfile) ;
jis = new JarInputStream(tempfis) ;
manifest = jis.getManifest()
;
Attributes attr
= manifest.getMainAttributes();
if (attr !=
null) {
taskthread
= attr.getValue("Task-Thread") ;
}
// Now scan all of the entries in the
file and
// get the size of each
resource/class. This is the
// only way to get the true size when the
JAR is compressed.
ZipFile zipfile = new ZipFile(tempfile);
enum = zipfile.entries() ;
while (enum.hasMoreElements())
{
ZipEntry
ze = (ZipEntry) enum.nextElement() ;
htSizes.put(ze.getName(), new Integer((int)ze.getSize())) ;
}
} // Try.
catch (Exception e) {
System.out.println("Class
Loader: " + e.toString() + " : " + e.getMessage()) ;
}
finally {
if ( tempfis !=
null ) {
try {
tempfis.close();
}
catch (Exception e){}
}
if ( jis !=
null ) {
try {
jis.close();
}
catch (Exception e){}
}
} // Finally.
}
متد findclass()
همانگونه كه
در ذيل آمده
است، توسط ClassLoader براي
رديابي و
بارگذاري
كلاس خاصي
بكار ميرود.
من در اين
پيادهسازي
كلاس مورد
نظر را در
فايل Jar جستجو ميكنم.
در صورتي كه
ان را نيابم،
از ClassNot Found
Exception
استاندارد
استفاده ميكنم.
/**
* This is the method where
the task of classloading
* is delegated to our custom loader.
*
* @param name the name of
the class
* @return the resulting Class object
* @exception ClassNotFoundException if
the class could not be found
*/
protected Class findClass(String name)
throws ClassNotFoundException {
FileInputStream fis = null ;
ZipInputStream zis = null ;
try {
String path = name.replace('.',
'/') + ".class" ;
fis = new FileInputStream(jarfilename) ;
zis = new ZipInputStream(fis) ;
ZipEntry ze = null ;
boolean classfound = false ;
while ((ze = zis.getNextEntry()) != null) {
if (path.equals(ze.getName())) {
classfound = true ;
// Get the size
of the classfile so we know how many bytes
// to
read. If the size appears to be -1, then the
// jar file is
compressed, and we need to consult our
// hashtable to get the uncompressed size.
int size = (int)
ze.getSize() ;
if (size == -1) {
size
= ((Integer) htSizes.get(path)).intValue()
;
}
// Now that we
know the actual size of the class,
// fetch the
data.
byte[] classBytes = new byte[size]
;
int rb = 0 ;
int chunk = 0 ;
while ((size - rb) > 0) {
chunk
= zis.read(classBytes, rb, size - rb) ;
if
(chunk == -1) {break ;}
rb += chunk ;
}
// Now that we
have our class data, define the class.
System.out.println ("Class Loader: Found class
" + name) ;
definePackage(name);
return defineClass(name, classBytes,
0, classBytes.length);
}
}
if (!classfound)
{
// We apparently failed to
locate the class within the jar file,
// so indicate the problem
with an exception.
throw
new ClassNotFoundException(name) ;
}
}
catch (Exception e) {
// We could not find the class, so
indicate the problem with an exception.
throw new ClassNotFoundException(name);
}
finally {
if ( fis !=
null ) {
try {
fis.close();
}
catch (Exception e){}
}
if ( zis !=
null ) {
try {
zis.close();
}
catch (Exception e){}
}
}
return null ;
}
متدهاي
find
Resource()، find Resurces() همانگونه
كه در ذيل
آمدهاند،
براي يافتن
منابعي كه
احتمالا در JAR
هستند، به
كار ميروند.
از آنجاييكه
همه منابع در JARهاي
محصور شده،
بستهبندي
شدهاند نه
در فايل
سيستم، در
دسترس URL قرار
دارند، لذا
اين متدها
همواره
سودمند نيستند.
/**
* Identify where to load a
resource from.
* Normally the resources will be extracted
* from a JAR and
reside in a filesystem
* somewhere. Since we are loading our classes
* directly from a
jar file, they won't really
* be available
via URL.
*
* We'll always return a null to indicate
* that the resource can't be found, i.e.,
* unavailable.
* You could also dump all of the resources
* into a
local/temp filesystem and "find" them
* there.
*
* @param name the resource
name
* @return URL for resource or null if not
* found
*/
protected URL findResource(String name) {
return null;
}
/**
* Used for identifying resources from multiple URLS.
* We will return a null for
the same reasons as above.
*
* @param name the resource
name
* @return Enumeration of one URL
*/
protected Enumeration findResources(final
String name) throws IOException {
return null ;
}
متد
defindpackage()، متد
استاندارد
نهايي را
براي ClassLoader ما
فراهم ميكند
و از مثال كن
مك كراري،
بدون تغيير
اقتباس شده
است:
/**
* Minimal package definition
*
*/
private void definePackage(String className) {
// Extract the package name from the class name.
String pkgName = className;
int index = className.lastIndexOf('.');
if (index != -1) {
pkgName
= className.substring(0, index);
}
// Preconditions -- need a manifest and the package
// is not previously defined.
if ( null == manifest ||
getPackage(pkgName) != null) {
return;
}
String specTitle,
specVersion,
specVendor,
implTitle,
implVersion,
implVendor;
// Look up the versioning information.
// This should really look for a named attribute.
Attributes attr = manifest.getMainAttributes();
if (attr != null) {
specTitle
= attr.getValue(Name.SPECIFICATION_TITLE);
specVersion = attr.getValue(Name.SPECIFICATION_VERSION);
specVendor =
attr.getValue(Name.SPECIFICATION_VENDOR);
implTitle
= attr.getValue(Name.IMPLEMENTATION_TITLE);
implVersion = attr.getValue(Name.IMPLEMENTATION_VERSION);
implVendor =
attr.getValue(Name.IMPLEMENTATION_VENDOR);
definePackage(pkgName,
specTitle,
specVersion,
specVendor,
implTitle,
implVersion,
implVendor,
null);
// No sealing for simplicity.
}
}
سه
متدي كه در
ذيل آمدهاند،
ClassLoaderمان
را كامل ميكنند.
متد get Task Thread Name() نام thread
فعاليتي كه
مايلم اجرا
شود (مبتني بر
اطلاعات manifest) باز
ميگرداند.
متد isloaded مشخص
ميكند كه
آيا ClassLoader با
موفقيت
واكنشي و در
فايل Jar بارگذاري
شده است يا
خير. كلاس main
از اين متد
براي تعيين
در دسترس
بودن thread فعاليت
اصلي كه
بايد اجرا
شود،
استفاده ميكند.
متد getResourceEntries()،
شماره اسامي
منبع را در JAR بر ميگرداند
و براي
خطايابي
بكار ميرود.
// The getTaskThreadName is a getter for the taskthread
field.
public String getTaskThreadName()
{
return this.taskthread ;
}
// If a JAR has been loaded, then we will have
// a nonzero jarsize.
public boolean
isLoaded() {
return (this.jarsize >
0) ;
}
// getResourceEntries
returns an enumeration of the keys
// in the hashtable. The
keys are the names of the JAR
// entries we picked up
earlier. This is useful for
// printing the list of JAR
resources.
public Enumeration getResourceEntries()
{
return htSizes.keys() ;
}
private Manifest manifest = null ;
private String taskthread = null ;
private String jarfilename = null ;
private int jarsize
= 0 ;
private Enumeration enum = null ;
private Hashtable htSizes
= new Hashtable() ;
ساخت
برنامه اصلي
كلاينت
اين
برنامه
مسوول آغاز
نمودن فوري Classloader است،
سپس از آن
براي
بارگذاري thread
فعاليت اصلي
استفاده ميكند.
من در اين
مثال thread را به
يكباره
بارگذاري و
اجرا ميكنم.
توليد
برنامه تلاش
براي آغاز
مجدد thread به محض
اينكه اجرا
ميشود يا
حتي آبجكت ClassLoader را
خراب ميكند
و بارگذاري thread جديد
است. (احتمالا
با واكنشي JAR جديد)
كد برنامه
اصلي در ذيل
آمده است.
//
// GridClientMain.java
//
package com.perficient.grid.client ;
import java.util.*;
import java.io.* ;
public class GridClientMain {
public static void main (String args[])
{
// Start by creating our custom classloader.
The classloader, during
// initialization, will use Web services to attempt
to download a JAR
// containing the grid task we will execute.
try {
GridConnectionClassLoader
gcloader = new GridConnectionClassLoader
() ;
// Check to see if any bytes
of data were received. If the classloader
is not loaded,
// then
the classloader was unable to initialize properly.
if (!gcloader.isLoaded()) {
System.out.println("GridClientMain: No bytes were loaded by the classloader, so no grid task is available to be
executed.") ;
}
// A JAR has been loaded by
the classloader, but
// the JAR manifest may not
have a registered Task-Thread attribute. This means
// that
we have no way of knowing which class we are supposed to run.
// If
our classloader does not have a Task-Thread name,
print a message,
// then print the list of
resources found in the JAR for reference.
else if (gcloader.getTaskThreadName() == null) {
System.out.println("GridClientMain: The Jar manifest did not have a
Task-Thread attribute.\nPlease identify the
Task-Thread and re-jar the data. Here are the known Jar resources:\n") ;
Enumeration enum = gcloader.getResourceEntries()
;
while (enum.hasMoreElements())
{
System.out.println((String) enum.nextElement())
;
}
}
else {
// We have a Task-Thread, so
let's try to load it. Again, note that we assume
// that the Task-Thread class
is a Thread class. This is a design decision for building
// the grid.
System.out.println("GridClientMain: Attempting to load " + gcloader.getTaskThreadName()) ;
Thread task = (Thread) gcloader.loadClass(gcloader.getTaskThreadName()).newInstance();
System.out.println("GridClientMain: Attempting to start Thread...") ;
task.start();
}
}
catch (ClassNotFoundException
e) {
System.out.println("GridClientMain: Could not find class:\n" + e.toString() + "\n" + e.getMessage())
;
}
catch (Exception e) {
System.out.println("GridClientMain: exception:\n" + e.toString()
+ "\n" + e.getMessage()) ;
}
} // Main.
}
ساخت
thread
محاسباتي
آزمايشي
من
براي آزمايش
چارچوب گرير
به thread محاسباتي
آزمايشي
نياز دارم.
البته لازم
نيست اين thread كاري
انجام دهد،
اما بايد به
گونهاي
ساخته شود كه
بتواند Classloader را
آزمايش كند. Thread،
آزمايشي را
به گونهاي
طراحي نمودم
كه از چند
كلاس
استفاده كند
و حداقل يك
كلاس داخلي
داشته باشد.
وقتي برنامه اصلي
را اجرا ميكنم،
بايد ClassLoader
سفارشي در
صورت لزوم
درخواست شود.
كد thread آزمايشي در
ذيل آمده است.
//
// TestThread.java
//
package com.perficient.tasks ;
import java.util.*;
import com.perficient.tasks.TestThreadHelper ;
public class TestThread extends Thread {
public TestThread () {
super() ;
}
public void run() {
System.out.println("TestThread: You are now inside the TestThread
thread.");
System.out.println("TestThread: Accessing another class in this jar...")
;
// Demonstrate the ability to access another class in
the
// same jar file.
try {
TestThreadHelper
tth = new TestThreadHelper()
;
System.out.println("TestThread: tth.getDemoString()
returned " + tth.getDemoString()) ;
}
catch (Exception e) {
System.out.println("TestThread: Failed to create TestThreadHelper.")
;
System.out.println(e.toString() + " : " + e.getMessage())
;
}
System.out.println("TestThread: Exiting TestThread
thread.") ;
}
}
//
// TestThreadHelper.java
// TestThread
//
// Created by Anthony Karre on Sun Jan
05 2003.
// Copyright (c) 2003 Perficient.
All rights reserved.
//
package com.perficient.tasks ;
public class TestThreadHelper {
public String getDemoString () {
// demonstrate correct inner class load
TTHInnerClass myinner = new TTHInnerClass()
;
return myinner.getInnerClassString()
;
}
class TTHInnerClass {
public String getInnerClassString
() {
return "innerclassworked"
;
}
}
}
به
دليل اينكه
از Apple Mac OS X Project Builder IDE براي ساخت
اين برنامه
استفاده
نمودم، JAR
آزمايشي
حاوي، فايل
متني ديگري
به نام mainfest.txt است.
شما بايد با
اين ابزار به
وضوح فايل
مبدايي حاوي
اطلاعات mainfest كه مثلا Project
Builder قبلا
آن را فراهم
نكرده، تهيه
كنيد. البته
در صورتي كه
بخواهيد دادههاي
اضافي ديگري
را در mainfest ادغام
كنيد. اين
فايل يك خطي
به صورت ذيل
است:
Task-Thread: com.perficient.tasks.TestThread

شكل 5. خروجي
كنسول جاوا
آزمايش
چارچوب
محاسباتی
شبكهای
من
اين چارچوب
را با روشن
كردن سرور Tomcat/Axis
SOAP بر
روي مكينتاش
آزمايش
نمودم سپس
برنامه اصلي
را از Apple Mac OS X project Builder IDE اجرا
كردم. شرايط
متعدد بروز
خطا را نظير عدم
وجود JARها،
عدم وجود
صفات Task-Thread و مشكلات. SOAP كه در
اثر خطاهاي
ارتباطي
حاصل ميشوند
را شبيه سازي
كردم. Classloader در همه
موارد
همانگونه كه
طراحي شده
بود، رفتار
كرد. شكل 5
اجراي عادي برنامه
كلاينت گرير
را نشان ميدهد.
به نحوه
درخواست ClassLoader براي
پشتيباني از
آغاز نمودن
كلاس در thread توجه
كنيد.
آغاز
تجربه با
محاسبات
شبكهای
در
حاليكه تولكيتهاي
محاسبات
شبكهاي
موجود و
اثربخش
هستند،
پيادهسازي
آنها
نيازمند
تجربه است.
شايد منحني
يادگيري
براي توسعهدهندگاني
كه ميخواهند
به سرعت
معماري
محاسباتي
شبكهاي را
آزمايش
كنند، مانعي
به شمار رود
چارچوب محاسبات
شبكهاي كه
در اين مقاله
تشريح شد،
ابزار سادهاي
براي تجربه
اصول
اينگونه
محاسبات است.
اين ابزار از
مولفههاي open-source
متداول كه
براي اكثر
برنامهنويسان
جاوا
آشناست،
استفاده ميكند،
در حاليكه
اصول اين
محاسبات
يعني: استقلال
ماشين،
اجراي
فعاليت
انتزاعي و
زير ساختار
امن و اندازهپذير
را حفظ مينمايد.
t
منابع:
·
Learn about the Search for Extraterrestrial
Intelligence (SETI@home) project:
http://setiathome.ssl.berkeley.edu/
·
Global Grid Forum:
http://www.ggf.org/
·
Open Grid Services Architecture
Working Group (OGSA WG):
http://www.ggf.org/ogsa-wg/
·
The Globus
Project:
http://www.globus.org
·
Sun Microsystems Research Paper:
"Framework for Peer-to-Peer Distributed Computing in a Heterogeneous,
Decentralized Environment," Jerome Verbeke, Neelakanth Nadgir, Greg Ruetsch, Ilya Sharapov:
http://wwws.sun.com/software/jxta/mdejxta-paper.pdf
·
Project Jxta:
http://wwws.sun.com/software/jxta
·
Apache Tomcat homepage:
http://jakarta.apache.org/tomcat/
·
SOAP W3C (World Wide Web Consortium)
specification:
http://www.w3.org/TR/SOAP/
·
The Apache Axis SOAP implementation:
http://ws.apache.org/axis/
·
"Create a Custom Java 1.2-Style ClassLoader," Ken McCrary (JavaWorld,
March 2000):
http://www.javaworld.com/javaworld/jw-03-2000/jw-03-classload.html
·
The Apache Struts Web application
framework:
http://jakarta.apache.org/struts/
·
Apple Mac OS X Project Builder IDE:
http://developer.apple.com/tools/projectbuilder/index.html
·
Learn more about Macintosh OS X
development:
http://developer.apple.com/macosx/
·
More JavaWorld
stories on Axis:
o
"Axis-Orizing Objects for SOAP," Mitch Gitman (April 2003)
o
"Axis:
The Next Generation of Apache SOAP," Tarak
Modi (January 2002)
·
For SOAP basics, read "Clean Up
Your Wire Protocol with SOAP," Tarak Modi (JavaWorld)
o
"Part
1: An introduction to SOAP basics" (March 2001)
o
"Part
2: Use Apache SOAP to create SOAP-based applications" (April 2001)
o
"Part
3: Create SOAP services in Apache SOAP with JavaScript" (June 2001)
o
"Part
4: Dynamic proxies make Apache SOAP client development easy" (July
2001)
·
For an overview of grid computing,
read "IBM's Grid Conversion," Robert McMillan (JavaWorld,
September 2002):
http://www.javaworld.com/javaworld/jw-09-2002/jw-0906-grid.html
·
Browse the Enterprise Java
section of JavaWorld's Topical Index:
http://www.javaworld.com/channel_content/jw-enterprise-index.shtml
·
Browse the Java and Web
Services section of JavaWorld's
Topical Index:
http://www.javaworld.com/channel_content/jw-webserv-index.shtml
|