Java 调用 .net Webservice的一些问题和注意事项

March 28th, 2009 Arale No comments

最后在研究GWT,需要用到.net 的webservice,网上搜了点代码,基本上问题多多.
Java要调用.net的webservice是需要一个axis的包的. 需要下载,然后引用.
引用的时候,需要注意一些小问题.

Java 端

public String ServiceLogon(String user, String pwd) throws ServiceException  
{  
    String url="http://localhost/WebServices/Service.asmx";  
    Service service = new Service();  
    Call call = (Call)service.createCall();  
    //We have to figure our the namespace. The java doesn't know where is the namespace  
    call.setOperationName(new QName("http://localhost/WebServices/Service.asmx","Logon"));  
    call.addParameter(new QName("http://localhost/WebServices/Service.asmx","username"),XMLType.XSD_STRING, javax.xml.rpc.ParameterMode.IN);  
    call.addParameter(new QName("http://localhost/WebServices/Service.asmx","password"),XMLType.XSD_STRING, javax.xml.rpc.ParameterMode.IN);  
    String res = "";  
    try {  
       call.setTargetEndpointAddress(new URL("http://localhost/WebServices/Service.asmx"));  
       res = (String)call.invoke(new Object[]{user,pwd});   
    } catch (MalformedURLException e) {  
       // TODO Auto-generated catch block  
       e.printStackTrace();  
    } catch (RemoteException e) {  
       // TODO Auto-generated catch block  
       e.printStackTrace();  
    }  
    return res;  
}

需要注意的一个小问题是, 在引用的时候,要指出.net webservice的namespace.不同的代码namespace不一样的.

还有添加参数的时候, 一定要new 出一个QName来才可以.

call.addParameter(new QName("http://localhost/WebServices/Service.asmx","username"),XMLType.XSD_STRING, javax.xml.rpc.ParameterMode.IN);

不new的话, 接收到的参数会是空的.

.net 端.

using System;  
using System.Web;  
using System.Web.Services;  
using System.Web.Services.Protocols;  
[WebService(Namespace = "http://localhost/WebServices/Service.asmx")] //namespace  
[SoapDocumentService(RoutingStyle=SoapServiceRoutingStyle.RequestElement)] //very import must figure out  
public class Service : System.Web.Services.WebService  
{  
    public Service () {  
        //Uncomment the following line if using designed components   
        //InitializeComponent();   
    }  
    [WebMethod]  
    public string HelloWorld() {  
        return "Hello World";  
    }  
    [WebMethod]  
    public string Logon(string username, string password)  
    {  
        if (username == "steven_wang" && password == "123123")  
            return "Welcome to Asp.net WebService";  
        else  
            return "username:" +username +", Password:" + password;  
    }  
 
}

本代码在vs2005 framework2.0, windowXP, IIS5.0
JDK1.6, Eclipse3.3, axis1.4
情况下调试通过.

Read more...

iTextSharp存于MemoryStream的信息无法做为邮件附件的解决方法

January 8th, 2009 Arale No comments

用iTextSharp的时候,出了点小问题. 不可否则, 这的确是一个很方便,而且很完美的组件, 只是国内网上一些解决方案是抄来抄去的, 来搜索去, 都一样,相当恶心. 当使用iTextSharp的时候, 如果需要将生成的pdf内容写入Stream,需要注意一点的是,Stream会被iTextSharp对象关闭的. 否则在后面对stream操作的时候, 会报出一个不能链接已经关闭的Stream的错误 “Can not access with closed stream.”
PdfWriter.GetInstance(document, stream).CloseStream = false;
还有一个问题是当iTextSharp写入MemoryStream的时候,如果将该Stream做为附件, 则需要将stream的指针移动到开始的地方, 否则得到的附件可能会是一个为0的错误的文件.
MemoryStream attachment = this.GeneratePDF();
attachment.Seek(0, SeekOrigin.Begin);
这样之后,就可以使用System.Net.Mail中的Attachment来添加附件的时候就可以直接将该Stream做为附件添加.

如果使用的是StreamWriter的话, 生成之前还需要Flush()一下.
做为邮件附件发送出去之后, 如果仍然需要使用该STREAM的时候,需要再次将stream重置. 因为添加附件的时候, stream的指针又指向了stream尾部.

Read more...

.net 扫盲文章,浅拷贝和深度拷贝.

December 4th, 2008 Arale No comments

深拷贝的精确描述是这样的:对对象的所有成员进行深拷贝。
浅表拷贝则是:
对对象的所有成员进行拷贝,如果该成员是引用类型的,则只拷贝引用。

深拷贝和浅表拷贝的区别在于对象拥有引用类型成员时。
深拷贝要求对引用类型的成员也进行深拷贝。浅表拷贝则只是简单的复制引用。

假设一个对象A,它包含了B,则深拷贝会先将B进行深拷贝产生B1,再创建A1,把B1作为A1的成员。
浅表拷贝因为只拷贝引用,对象B不会被复制一份。

实现克隆操作,在.NET中可以使用Object类的MemberwiseClone()方法来实现对象的浅表拷贝或通过序列化的方式来实现深拷贝。

Read more...

Decorator Pattern

November 11th, 2008 Arale No comments

博客员中这位高手的例子.记录日志.

在软件系统中,我们会使用继承来扩展对象的功能,随着功能的扩展,子类的增多,会导致更多的子类的膨胀. 为了解决这个问题,便产生了装饰模式(Decorator Pattern)

动态的给一个对象添加一些额外的职责,就增加功能来说,Decorator模式相比生成子类更为灵活.[GOF 设计模式]

我们来使用这位高手的例子,数据库的日志的记录.我们已经有写好了的Log类及DatabaseLog类, 来实现数据日志的记录工作.

public abstract class Log
{
    {
        public abstract void Write(string log);
    }
}
 
public class DatabaseLog:Log
{
    public override void Write(string log)
    {
        //throw new NotImplementedException();
        //记录日志
    }
}

不要有太多的问题,上面的类是已经实现的,用来记录日志的. 假如万恶的资本家又有了新的需求怎么办? Steven请你给这个再加一个功能,让他可以记录错误的级别吧.

题外话,一些我对装饰模式的理解:

固然,我们可以在类中实现,再加上一个方法.比如说public override ovid SetError(),但是问题是,如果我们加上了这个方法. 在new出来实例引用的时候,就要instant.Write(),instant.SetError(). 我对这个倒是可以接受,只是高手中提到这个破坏了类的单一性原则. 在面向对象这一块,我掌握的不是很好,不甚理解.回去要找本书好好看看, 可能是这个破坏了类的单一性???

我们用装饰模式来实现. 要装饰,就先做一个装饰的类.装饰嘛,就是在原来的基础上再加点什么.

public abstract class LogWrapper:Log
{
    private Log _log;
    public LogWrapper(Log log)
    {
        _log = log;
    }
    public override void Write(string log)
    {
        //throw new NotImplementedException();
        _log.Write(log);
    }
}

这个类继承于Log,他重载了Log中的Write方法.

为什么要这么用呢? 我们的扩展功能SetError是要继承于LogWrapper的,LogWrapper相当一个包装盒,他即具有Log类的一切特质,又为其派生的类提供了一个接口.

public class LogErrorWrapper:LogWrapper
{
    public LogErrorWrapper(Log _log)
       : base(_log)
    { }
    public override void Write(string log)
    {
        //Run some extenal features
        //SetError()
        base.Write(log);
    }
}

LogErrorWrapper即是继承自LogWrapper. 然后LogErrorWrapper又重载了其父类的Write方法,在实现了一系列的其它工作之后”SetError()”,回过来执行其父类的Write()方法,所需要的只是提供一个他父类所需要的对象而已.

前台的调用办法

Log log = new DatabaseLog();
LogWrapper lew1 = new LogErrorWrapper(log);
lew1.Write("Log Message");

可见,Log中的Write方法在LogWrapper中又被包装了一次.

Confusion: 类的单一性是什么? 类除了具有单一性之外,还应该具备一些其它什么样的特征呢? 面向对象这一课应该好好补一下了.

Read more...

工厂模式与抽象工厂模式

November 7th, 2008 Arale No comments

先举一个问题.来说明一下. 假如我要发RMB给中国员工,中国员工工资是1000元, 美国员工的工资是5000$,但是需要转换成为RMB就是6.8, 想用抽象工厂模式来实现发工资这个功能.
1. 确定工资Bouns 我们要确定一个方法,来计算工资的.这个Class我们叫做Bouns里面有一个方法Calculate()是计算工资.

namespace AbstractFactory
{
    public abstract class Bouns
    {
         public abstract double Calculate();
    }
}

2. 然后我们声明再来建立两个员工,一个是中国员工,另一个是美国员工.

namespace AbstractFactory
{
    public class ChineseBouns:Bouns
    {
        public override double Calculate()
        {
            return 1000;
        }
     }
}
//这个是美国的员工工资
namespace AbstractFactory
{
   public class AmericaBouns:Bouns
   {
      public override double Calculate()
      {
            return 5000*6.8;
         }
      }
}

3. 在普通工厂模式中,如果我们需要调用的话.只需要建立一个工厂,或者说是一个中间类.来实现这个功能.这就是工厂,一个用于处理业务的方法.

public class CommonFactory
{
    public Bouns CreateBouns()
    {
        return new ChineseBouns();
    }
}
//前端代码可以直接
CommonFactory factory = new CommonFactory();
double salary = Factory.CreateBouns().Calculate();

但是问题在于,如果你要给美国员工开工资,你就要修改你的CommonFactory

4. 在抽像工厂模式中,这个问题被解决了.
首先建立两个工厂,分别用于处理中国员工工资和美国员工工资的.

namespace AbstractFactory
{
    public class AmericanFactory:AbstractFactory
    {
        public override Bouns CreateBonus()
        {
            return new AmericaBouns();
        }
    }
}
//针对中国员工的计算方法,用来计算中国员工的工资.
namespace AbstractFactory
{
     public class ChineseFactory:AbstractFactory
     {
         public override Bouns CreateBonus()
         {
            return new ChineseBouns();
         }
     }     
}

接下来,我们要利用这个工厂.于是我们建立一个抽像的工厂,所谓抽象是相对于中国工厂和美国工厂的抽象,他对于调用而言他只要工厂,不分中国还是美国.

namespace AbstractFactory
{
    public abstract class AbstractFactory
    {
         public static AbstractFactory GetInstance(string countyName)
        {
            AbstractFactory instance;
             if (countyName.Equals("China"))
                 instance = new ChineseFactory();
             else if (countyName.Equals("American"))
                 instance = new AmericanFactory();
            else
                 instance = null;
          return instance;
        }
       public abstract Bouns CreateBonus();
    }
}

前端使用的时候

AbstractFactory factory = AbstractFactory.GetInstance("American");
double money = factory.CreateBonus().Calculate();

只要将参数传递进去,就可以根据参数来判断需要开的工资类型.
当然这个参数可以写到配置文件里面,在AbstractFactory的GetInstance事件里面可以通过读取配置文件来实现不同员工工资的读取.

当然例子可以举的不太恰当,
只是处于一个简单理解的角度出发的,如果你把工资换成别的,
比如说不同国家人的有不同的工资计算方法,很复杂. 或者是用于其它一些其它业务.

参考文章

Read more...

图片的按比例缩小.

October 22nd, 2008 Arale No comments
public static byte[] ResizeImageFile(byte[] imageFile, int targetSizeW, int targetSizeH)
{
   System.Drawing.Image original = System.Drawing.Image.FromStream(new MemoryStream(imageFile));
   int targetH, targetW;
   targetW = targetSizeW;
   targetH = (int)(original.Height * ((float)targetSizeW / (float)original.Width));
   if (targetH > targetSizeH)
   {
      targetH = targetSizeH;
      targetW = (int)(original.Width * ((float)targetSizeH / (float)original.Height));
   }
   if (targetSizeW < (int)original.Width || targetSizeH < (int)original.Height)
   {
      System.Drawing.Image imgPhoto = System.Drawing.Image.FromStream(new MemoryStream(imageFile));
      // Create a new blank canvas.  The resized image will be drawn on this canvas.
      Bitmap bmPhoto = new Bitmap(targetW, targetH, PixelFormat.Format24bppRgb);
      bmPhoto.SetResolution(72, 72);
      Graphics grPhoto = Graphics.FromImage(bmPhoto);
      grPhoto.SmoothingMode = SmoothingMode.AntiAlias;
      grPhoto.InterpolationMode = InterpolationMode.HighQualityBicubic;
      grPhoto.PixelOffsetMode = PixelOffsetMode.HighQuality;
      grPhoto.DrawImage(imgPhoto, new Rectangle(0, 0, targetW, targetH), 0, 0, original.Width, original.Height, GraphicsUnit.Pixel);
      // Save out to memory and then to a file.  We dispose of all objects to make sure the files don't stay locked.
      MemoryStream mm = new MemoryStream();
      bmPhoto.Save(mm, System.Drawing.Imaging.ImageFormat.Jpeg);
      original.Dispose();
      imgPhoto.Dispose();
      bmPhoto.Dispose();
      grPhoto.Dispose();
      return mm.GetBuffer();
   }
   else
   {
      return imageFile;
   }
}
Read more...

向Excel中快速写入数据

October 21st, 2008 Arale No comments

一个单元格一个单元格的写入Excel虽然不错,但是速度太慢,要重复的操作Excel对象. 在网上找到了个办法,是写入数组,然后由数组直写入Excel中,速度要快许多.

1.生成Object数组的方法

public object[,] GetObjectArray(System.Data.DataTable dt)
{
    object[,] objData = new object[dt.Rows.Count, dt.Columns.Count];
    for (int i = 0; i < dt.Rows.Count; i++)
    {
        //  string col ="A" + (i+2).ToString();
        for (int j = 0; j < dt.Columns.Count; j++)
        {
           objData[i, j] = dt.Rows[i][j].ToString();
        }
   }
   return objData;
}

2. 数据操作的代码

Worksheet sheetUnknown;
sheetUnknown.Name = "NYBB";
Range rgeData = this.GetHeaderRange(dt, sheetUnknown);
object[] objHeader = 
{
    "CompanyClaimID","FullCOmpanyClaimID","ValueDate","ClaimantName","MEVStatus",
    "ClosedDate","Reserve","Deductible","TotalPaid","ExpensePaid","TotalIncurred",
    "Description","StatusID","Effective","NamedInsured","LossTypeID","CoverageID",
    "DateOfLoss","DateReported","LocationAddress","LocationCity","LocationState",
    "LocationZip","CauseID","Recovery"
};
rgeData.Value2 = objHeader;
//因为我的Excel表中有表头,所以Cell的起始行从2开始,到总行数+1... 表头的生成方法如下.
sheetUnknown.get_Range(sheetUnknown.Cells[2, 1], sheetUnknown.Cells[dt.Rows.Count + 1, dt.Columns.Count]).Value2 = GetObjectArray(dt);

3.生成表头的代码

public Range GetHeaderRange(System.Data.DataTable dt,Worksheet worksheet)
{
   Range rgeData = worksheet.get_Range(worksheet.Cells[1, 1], worksheet.Cells[1, dt.Columns.Count]);
   rgeData.Font.Bold = true;
   rgeData.Font.Size = 9;
   rgeData.Interior.ColorIndex = 15;
   rgeData.HorizontalAlignment = XlHAlign.xlHAlignCenter;
   rgeData.EntireColumn.AutoFit();
   return rgeData;
}

其中dt是DataTable对象,是由select查询结果返回的. Worksheet是Office的Excel对象,我使用的是Office2003, 但是强烈使用Office2000,兼容性可能会好一些,Office中在引用了Excel.dll之后,可以用 Excel.对象名来操作对象.

Read more...

Response.Redirect 打开新窗口

October 21st, 2008 Arale No comments

重新写一下Redirct的方法

 public static class ResponseHelper {
     public static void Redirect(string url, string target, string windowFeatures) {
         HttpContext context = HttpContext.Current;
         if ((String.IsNullOrEmpty(target) ||
             target.Equals("_self", StringComparison.OrdinalIgnoreCase)) &&
             String.IsNullOrEmpty(windowFeatures)) {
             context.Response.Redirect(url);
         }
         else {
            Page page = (Page)context.Handler;
            if (page == null) {
                throw new InvalidOperationException(
				"Cannot redirect to new window outside Page context.");
             }
             url = page.ResolveClientUrl(url);
             string script;
             if (!String.IsNullOrEmpty(windowFeatures)) {
                 script = @"window.open(""{0}"", ""{1}"", ""{2}"");";
             }
             else {
                script = @"window.open(""{0}"", ""{1}"");";
             }
             script = String.Format(script, url, target, windowFeatures);
             ScriptManager.RegisterStartupScript(page,
                 typeof(Page),"Redirect",script,true);
 
         }
 
     }
 
 }

//Use
ResponseHelper.Redirect(“popup.aspx”, “_blank”, “menubar=0,width=100,height=100″);

Read more...

关于RuleSet Engine的用法

October 17th, 2008 Arale No comments

最近在维护一个项目,遇到阿三程序员写的一段关于WWF的代码.其中使用了RuleSet, RuleValidation 和RuleExecution 三个方法,这三个方法把我弄的很郁闷.首先,我能看明白这几个方法, 就是将生成的Rules通过DeserializeRuleSet这个方法来反序列化,变成RuleSet对象, 然后用RuleValidation对象来声明需要操作对象(objClass),然后让RuleSet对象根据事先已经写好了的对象直接执行就可以了…. 所谈的代码,我去掉了try catch和if判断. 如下.

private RuleSet DeserializeRuleSet(string ruleSetXmlDefinition)
{
    RuleSet result = null;
    System.Workflow.Activities.Rules.Rule rule = new System.Workflow.Activities.Rules.Rule();
    if(!String.IsNullOrEmpty(ruleSetXmlDefinition))
    {
       XmlReader reader = XmlReader.Create(new StringReader(ruleSetXmlDefinition));
       //reader = XmlTextReader.Create(@"D:\user.rules");
       result = serializer.Deserialize(reader) as RuleSet;
    }
    return result;
 }
public void ExecuteRuleSet(object objClass,string xmlString)
{
   RuleValidation ruleValidation = new RuleValidation(objClass.GetType(),null);
   RuleExecution ruleExecution = new RuleExecution(ruleValidation,objClass);
   RuleSet ruleSet = DeserializeRuleSet(xmlString);
   ruleSet.Execute(ruleExecution);
}

由于不明白RuleSet是如何创建的,便咨询阿三,结果是”我们自己写了一个工具来生成规则,你只要用就行了”.这话听的我很不爽,顺便BS一下.

于是我决定来研究这个东西,看了他源数据库里面存储的Rules的格式来看,应该是由RuleSet Editor生成的. 但是令人郁闷的是,我创建了WWF项目,然后将Policy生成的rules文件(是RuleSet Editor),读出出来,做为ExecuteRuleSet的参数来,结果反序列化的时候,失败.原因很明显,是我创建rules的方法不对.于是我在网上游荡了近两个周,在若干次失败之后,终于找到了生成RuleSet的方法. 经过无数的夜晚的沉思,互联网的搜索,终于找到了方法.

为了生成这个RuleSet规则,我特地的创建了一个windows的项目. “阿三” 的生成RuleSet引擎的工具大概也就是这么个东西,无非是他可以将Rules再序列化出来,结果是添加到数据库里面.这里不是我要做的,我要找到是他怎么生成这个RuleSet.

private void Form1_Load(object sender, EventArgs e)
{
    Type entityType = typeof(Users);
    RuleSet ruleSet = new RuleSet();
    RuleSetDialog dialog = new RuleSetDialog(entityType, null, ruleSet);
    DialogResult result = dialog.ShowDialog(this);
    if (result == DialogResult.OK)
    {
       SerializeRuleSet(dialog.RuleSet);
    }
}
private void SerializeRuleSet(RuleSet ruleSet)
{
    StringBuilder ruleSetXml = new StringBuilder();
    using (XmlWriter xmlWriter = XmlWriter.Create(ruleSetXml))
    {
        new WorkflowMarkupSerializer().Serialize(xmlWriter, ruleSet);
    }
    XmlDocument xmlDoc = new XmlDocument();
    xmlDoc.LoadXml(ruleSetXml.ToString());
    xmlDoc.Save("user.rules");
}

来简单解释一下Users, 就是要进行验证的对象,这里我创建了一个Users的类,里面有几个成员Age,Name. 这个其实并不重要,我这里主要讲怎么样使用这个RuleSet引擎, 在使用RuleSetDialog的时候,会弹出一个和你在使用图形化的编辑Policy或者是WorkFlow的时候弹出的对话框是一样的.需要你来添 加规则.添加结束之后.会返回,返回的时候,取出dialog的RuleSet方法,并将这个方法序列化. 保存为user.rules,当然,保存成什么格式.保存到哪里并不重要.重要的是,这个规则我们已经生成了.

最后回到开头讲的,将我自己定义的Users类和刚刚生成的Ruels对象代入到ExecuteRuleSet(). 成功通过验证….
至于如何添加规则,就不属于这里讨论的范围了,网上WWF教程就有很多了.
我参考的文章: http://melgrubb.spaces.live.com/Blog/cns!A44BB98A805C8996!182.entry

我的环境是VisualStudio 2008 Perfessional Framework3.5 SP1 以上代码所用到的命名空间.

using System.Workflow.Activities.Rules;
using System.Workflow.ComponentModel.Compiler;
using System.Workflow.ComponentModel.Serialization;
using System.Workflow.Activities.Rules.Design;
using System.XML;

P.S.
该文章写自2008年10月17日,那个时候用的国内主机,并且还没有挂掉. 现补上.

Read more...

Bridge Pattern(桥模式)

October 12th, 2008 Arale No comments

首先要了解一下桥模式, 桥的作用是什么? 连接,桥起的是连接的作用.引用别人的一个例子,写日志. 我想往数据库中写日志,一个是MySQL数据库,另一个是MSSQL数据库.就设计模式桥模式来实现.
首先要做一个桥. 我们做一个抽象类来叫做ImpLog, 有一个抽象的Execute()方法,这个就是桥.然后声明两个类,一个是NImpLog用于往MSSQL中写入数据的,另一个是JImpLog用于往 MySQL中写入数据的. 这两个类就是需要用桥连接起来的类.

namespace BridgePattern
{
    public abstract class ImpLog
    {
         public abstract void Execute(string log);
    }
}
 
namespace BridgePattern
{
     public class JImpLog:ImpLog
     {
        public override void Execute(string log)
        {
          //throw new NotImplementedException();
		  //写入MSSQL
      }
     }
}
 
namespace BridgePattern
{
    public class NImpLog:ImpLog
    {
         public override void Execute(string log)
         {
             //throw new NotImplementedException();
             //写入MySQL
         }
     }
}

由于NImpLog和JImpLog都是继承自ImpLog,因此,根据继承的原理,ImpLog可以接受该被继承对象创建出来的实体.(太别扭了,就是 NImplog和JImplog new出来的对象可以赋值于ImpLog) 桥建好了,我们要使用这坐桥了,怎么样来使用呢?
好吧,为了使用这座桥,我们创建一个叫做Log的类.

namespace BridgePattern
{
    public class Log
    {
	   protected ImpLog implementor; //this is brige....
       public ImpLog Implementor
       {
            set { implementor = value; }
       }
       public virtual void Write(string log)
       {
            implementor.Execute(log);
       }
    }
}

聪明的你一定发现了,Log类里面使用了我们的桥. 是这样的. 并且细心的你也发现了这里我们声明了一个虚方法来执行Execute(),这个虚方法在其子类里面会被重载, 他的子方法我们写好了,就是DatabaseLog类.

namespace BridgePattern
{
     public class DatabaseLog:Log
     {
         public override void Write(string log)
         {
             implementor.Execute(log);
         }
     }
}

看到这里,我们的桥和如何使用已经做好了,要怎么用呢?

     Log dblog = new DatabaseLog();
     dblog.Implementor = new NImpLog();
     dblog.Write("sss");
     dblog.Implementor = new JImpLog();
     dblog.Write("ssss");

看上面的代码,不用我多少什么了吧.

想看专业一点的可以直接去看原文,原文虽然也很浅显,但是也得自己花一点时间消化一下才能理解.
引用地址:
参考的文章

Read more...