PariScamper的java天空

  BlogJava :: 首页 :: 联系 :: 聚合  :: 管理
  14 Posts :: 0 Stories :: 7 Comments :: 0 Trackbacks

2007年11月19日 #

以下文章在我配置vsftpd时有帮助,特转载:

关于“vsftpd 部分本地用户不能登录,部分可以”的问题,我重新做了一些实验,我把这个问题结合实验的结果再重新描述一下,请各位高人,帮忙看看可能的原因。 谢谢了!

系统中原来就有的本地帐号都不能登录,我的/etc/vsftpd/vsftpd.conf文件的配置如下:
local_enable=YES
write_enable=YES
chroot_local_user=YES
pam_service_name=vsftpd
/etc/pam.d/vsftpd存在且正常。

登录时错误信息都是一样的:
500 OOPS: cannot change directory:/home/xxxx
Login failed.
421 Service not available, remote server has closed connection

他们的home目录都是/home/xxxx。/home和/home/xxxx的权限都是755。
以上这些帐号都不能ftp登录,这些都是平常经常使用的,可以用shell登录的。

我新创建了一个usr1帐号,
# useradd -G test -d /tmp/usr1 usr1
能ftp登录,他的home为/tmp/usr1,在/分区上。而/home我是mount到/dev/hda9上的。
#mount
/dev/hdb1 on / type ext3 (rw)
/dev/hda9 on /home type ext2 (rw)

所以,我猜想:是否是由于/home分区的原因,而造成“主目录在/home分区的帐号”都不能登录呢?

为了验证以上设想,我试着再创建了一个帐号,
useradd -G test -d /home/usr3 usr3
/home, /home/usr3 的权限都是755。

usr3 ftp登录失败。
500 OOPS: cannot change directory:/home/usr3
Login failed.
421 Service not available, remote server has closed connection

至此,我觉得可以确定是由于/home分区的原因,而造成“主目录在/home分区的帐号”都不能登录。
参考文章:
-----------------------------------------------------------------------------------------
I finished my second upgrade to Fedora Core 4. Not everything is ironed out yet with the build of course. But one thing is for sure a lot has happened to the RedHat I knew before.

I must say of all the changes, for me the nicest addition is the new SELinux extensions. For deep background on the reasons for and theory of SELinux read, The Inevitability of Failure: The Flawed Assumption of Security in Modern Computing Environments

The more I work with SELinux the more I realize I need to know about it, and how exactly it does all its stuff. It certainly changes things relating to users, directories and access. As I am starting to learn it, I'm sure I'm doing things the hard-way. :)

The major difference, so far for me, in Red Hat's SELinux is the way ftp is handled. vsftpd is still the server which is great. However, it seems to be designed to run as a daemon rather than invoked via xinet.d. If you grab a working copy of the xinet.d file for vsftpd you can invoke it via xinet.d wrapper. I did my first server upgrade in this manner. The current one I am trying as a daemon. I certainly think I will miss some of the features that the xinet.d wrapper brings, and may yet return to it.

Of all the issues I saw most notable is if you want to enable chroot directory's outside of the normal /home/xxx vsftpd. These will fail with a

    500 OOPS: cannot change directory: /mnt/xxxxx

I was able to use ftp if I logged in with an account with a directory in /home, but once I set a user account to have a home drive outside of /home (in this case on a mounted secondary disk) vsftpd barfs the above.


I found information at the NSA that indicates you can disable SELinux protection of the ftp daemon.

    setsebool -P ftpd_disable_trans 1

This seems a bit drastic. It certainly works for now though.

I think ultimately the issue resides with policies, but as SELinux policies are new to me, it will take time before it all gets sorted out. As I spend time with the new SELinux extensions in Fedora Core 4 I will keep you updated on my thoughts and configuration lessons.


---------------------------------------------------------------------------------------

解决办法:
--------------------------------------------------------------------------------------
 # setsebool ftpd_disable_trans 1
  # service vsftpd restart
我用的是FC4,按照你上一帖子里的方法试了,马上就解决了。所以,可以确定原因就在SELinux。
------------------------------------------------------------------------------------

posted @ 2008-02-27 22:45 PariScamper 阅读(436) | 评论 (0)编辑 收藏

1          什么是 Clone ,容易实现吗?

简单地说, Clone 就是对于给定的一个对象实例 o ,得到另一个对象实例 o’ o o’

型相同( o.getClass() == o’.getClass() ),内容相同(对于 o/o’ 中的字段 f ,如果 f 是基本数据类型,则 o.f == o’.f ;如果 f 是对象引用,则 o.f == o’.f o.f 指向的对象与 o’.f 指向的对象的内容相同)。通常称 o’ o 的克隆或副本。

       直观上看,似乎很容易为一个类加上 clone 方法:

class A {

       private Type1 field1;

    private Type2 field2;

     …..

    public Object clone() {

              A a = new A();

        a.field1 = a.getField1();

        a.field2 = a.getField2();

        ……

        return a;

    }

}

 

然而,稍加推敲,就会发现这样的实现方法有两个问题:

1.         要想某个类有 clone 功能,必须单独为其实现 clone() 函数,函数实现代码与该类定义密切相关。

2.         即使基类 A 已有 clone() 函数,其子类 ExtendA 若要具备 clone 功能,则必须 override 其基类 A clone() 函数。否则,对类型为 ExtendA 的对象 ea clone() 方法的调用,会执行于类 A 中定义的 clone() 方法而返回一个类型为 A 的对象,它显然不是 ea 的克隆。

2          Java clone 的支持

万类之初的 Object 类有 clone() 方法:

protected native Object clone() throws CloneNotSupportedException;

该方法是 protected 的,显然是留待被子类 override 的。该方法又是 native 的,必然做了

与具体平台相关的底层工作。

事实上,类 Object clone() 方法首先会检查 this.getClass() 是否实现了 Cloneable 接口。

Cloneable 只是一个标志接口而已,用来标志该类是否有克隆功能。

public interface Cloneable {

}

       如果 this.getClass() 没有实现 Cloneable 接口, clone() 就会抛 CloneNotSupportedException 返回。否则就会创建一个类型为 this.getClass() 的对象 other ,并将 this field 的值赋值给 other 的对应 field ,然后返回 other

       如此一来,我们要定义一个具有 Clone 功能的类就相当方便:

1.         在类的声明中加入“ implements Cloneable ”,标志该类有克隆功能;

2.         Override Object clone() 方法,在该方法中调用 super.clone()

class CloneableClass implements Cloneable {

       ……

public Object clone() {

       try {

              return super.clone(); // 直接让 Object.clone() 为我们代劳一切

    } catch (CloneNotSupportedException e) {

              throw new InternalError();

       }

}

}

 

3          Shallow Clone Deep Clone

3.1     Shallow Deep 从何而来

一个具有克隆功能的类,如果有可变( Mutable )类类型的字段 field ,如何为其克隆(副

本)对象 o’ 中的 field 赋值?

       方法一、如 Object clone() 方法所实现:设原始对象为 o ,其克隆对象是 o’ ,执行 o’.field = o.field 。这样, o’.field o.field 指向同一个可变对象 m o o’ 可能会相互影响(一个对象的状态可能会随着另一个对象的状态的改变而改变)。这样的 Clone 称为 Shallow Clone 。这也是 Object clone() 方法的实现方式。

       方法二、将 o.field 指向的可变对象 m 克隆,得到 m’ ,将 m’ 的引用赋值给 o’.field 。这样 o’ o 内容相同,且相互之间无影响(一个对象状态的改变不会影响另一个对象的状态)。这样的 Clone 称为 Deep Clone

       Java Collection 类库中具体数据结构类( ArrayList/LinkedList HashSet/TreeSet HashMap/TreeMap 等)都具有克隆功能,且都是 Shallow Clone ,这样设计是合理的,因为它们不知道存放其中的每个数据对象是否也有克隆功能。 System.arrayCopy() 的实现采用的也是 Shallow Clone

       Deep Clone 对于实现不可变( Immutable )类很有帮助。设一个类包含可变类 M 类型的 field ,如何将其设计为不可变类呢?先为 M 实现 Deep Clone 功能,然后这样设计类 ImmutableClass

class ImmutableClass {

       MutableClass m;

ImmutableClass(MutableClass m) {

       this.m = m.clone(); // 将传入的 m clone 赋值给内部 m

}

public MutableClass getM() {

    return this.m.clone(); // 将内部 m clone 返回给外部

}

}

 

      

3.2     如何实现 Deep Clone

检查类有无可变类类型的字段。如果无,返回 super.clone() 即可;

如果有,确保包含的可变类本身都实现了 Deep Clone

Object o = super.clone(); // 先执行浅克隆,确保类型正确和基本类型及非可变类类型字段内容正确

对于每一个可变类类型的字段 field

       o.field = this.getField().clone();

返回 o

posted @ 2007-12-12 14:49 PariScamper 阅读(745) | 评论 (0)编辑 收藏

什么要用日志(Log?
这个……就不必说了吧。

为什么不用System.out.println()?
功能太弱;不易于控制。如果暂时不想输出了怎么办?如果想输出到文件怎么办?如果想部分输出怎么办?……

为什么同时使用commons-loggingLog4j?为什么不仅使用其中之一?
Commons-loggin的目的是为“所有的Java日志实现”提供一个统一的接口,它自身的日志功能平常弱(只有一个简单的SimpleLog?),所以一般不会单独使用它。

Log4j的功能非常全面强大,是目前的首选。我发现几乎所有的Java开源项目都会用到Log4j,但我同时发现,所有用到Log4j的项目一般也同时会用到commons-loggin。我想,大家都不希望自己的项目与Log4j绑定的太紧密吧。另外一个我能想到的“同时使用commons-loggingLog4j”的原因是,简化使用和配置。

       强调一点,“同时使用commons-loggingLog4j”,与“单独使用Log4j”相比,并不会带来更大的学习、配置和维护成本,反而更加简化了我们的工作。我想这也是为什么“所有用到Log4j的项目一般也同时会用到commons-loggin”的原因之一吧。

Commons-logging能帮我们做什么?
l         提供一个统一的日志接口,简单了操作,同时避免项目与某个日志实现系统紧密a耦合
l         很贴心的帮我们自动选择适当的日志实现系统(这一点非常好!)
l         它甚至不需要配置

这里看一下它怎么“‘很贴心的’帮我们‘自动选择’‘适当的’日志实现系统”:
1)        首先在classpath下寻找自己的配置文件commons-logging.properties,如果找到,则使用其中定义的Log实现类;
2)        如果找不到commons-logging.properties文件,则在查找是否已定义系统环境变量org.apache.commons.logging.Log,找到则使用其定义的Log实现类;
3)        否则,查看classpath中是否有Log4j的包,如果发现,则自动使用Log4j作为日志实现类;
4)        否则,使用JDK自身的日志实现类(JDK1.4以后才有日志实现类);
5)        否则,使用commons-logging自己提供的一个简单的日志实现类SimpleLog
(以上顺序不保证完全准确,请参考官方文档)

可见,commons-logging总是能找到一个日志实现类,并且尽可能找到一个“最合适”的日志实现类。我说它“很贴心”实际上是因为:1、可以不需要配置文件;2、自动判断有没有Log4j包,有则自动使用之;3、最悲观的情况下也总能保证提供一个日志实现(SimpleLog)。 
       可以看到,commons-logging对编程者和Log4j都非常友好。

       为了简化配置commons-logging,一般不使用commons-logging的配置文件,也不设置与commons-logging相关的系统环境变量,而只需将Log4jJar包放置到classpash中就可以了。这样就很简单地完成了commons-loggingLog4j的融合。如果不想用Log4j了怎么办?只需将classpath中的Log4jJar包删除即可。

就这么简单!

代码应该怎么写?

我们在需要输出日志信息的“每一人”类中做如下的三个工作:
1、导入所有需的commongs-logging类:
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

如果愿意简化的话,还可以两行合为一行:
import org.apache.commons.logging.*;  

2、在自己的类中定义一个org.apache.commons.logging.Log类的私有静态类成员:
private static Log log = LogFactory.getLog(YouClassName.class);

注意这里定义的是static成员,以避免产生多个实例。

LogFactory.getLog()方法的参数使用的是当前类的class,这是目前被普通认为的最好的方式。为什么不写作LogFactory.getLog(this.getClass())?因为static类成员访问不到this指针!

3、使用org.apache.commons.logging.Log类的成员方法输出日志信息:
log.debug("111");
log.info("222");
log.warn("333");
log.error("444");
log.fatal("555");

这里的log,就是上面第二步中定义的类成员变量,其类型是org.apache.commons.logging.Log,通过该类的成员方法,我们就可以将不同性质的日志信息输出到目的地(目的地是哪里?视配置可定,可能是stdout,也可能是文件,还可能是发送到邮件,甚至发送短信到手机……详见下文对log4j.properties的介绍):
l         debug()   输出“调试”级别的日志信息;
l         info()      输出“信息”级别的日志信息;
l         warn()    输出“警告”级别的日志信息;
l         error()     输出“错误”级别的日志信息;
l         fatal()      输出“致命错误”级别的日志信息;

根据不同的性质,日志信息通常被分成不同的级别,从低到高依次是:“调试(DEBUG)”“信息(INFO)”“警告(WARN)”“错误(ERROR)”“致命错误(FATAL)”。为什么要把日志信息分成不同的级别呢?这实际上是方便我们更好的控制它。比如,通过Log4j的配置文件,我们可以设置“输出‘调试’及以上级别的日志信息”(即“调试”“信息”“警告”“错误”“致命错误”),这对项目开发人员可能是有用的;我们还可以设置“输出“警告”及以上级别的日志信息”(即“警告”“错误”“致命错误”),这对项目最终用户可能是有用的。 
       仅从字面上理解,也可以大致得出结论:最常用的应该是debug()info();而warn()error()fatal()仅在相应事件发生后才使用。  

从上面三个步骤可以看出,使用commons-logging的日志接口非常的简单,不需要记忆太多东西:仅仅用到了两个类Log, LogFactory,并且两个类的方法都非常少(后者只用到一个方法,前者经常用到的也只是上面第三步中列出的几个),同时参数又非常简单。

上面所介绍的方法是目前被普通应用的,可以说是被标准化了的方法,几乎所有的人都是这么用。如果不信,或想确认一下,就去下载几个知名的Java开源项目源代码看一下吧。  

下面给出一个完整的Java类的代码: 
package liigo.testlog; 
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; 

public class TestLog
    private static Log log = LogFactory.getLog(TestLog.class); 
    public void test()    
        log.debug("111"); 
        log.info("222"); 
        log.warn("333"); 
        log.error("444"); 
        log.fatal("555"); 
     

    public static void main(String[] args)    
        TestLog testLog = new TestLog(); 
        testLog.test(); 
    }
 

只要保证commons-loggingjar包在classpath中,上述代码肯定可以很顺利的编译通过。那它的执行结果是怎么样的呢?恐怕会有很大的不同,请继续往下看。  

Log4j在哪里呢?它发挥作用了吗?

应该注意到,我们上面给出的源代码,完全没有涉及到Log4j——这正是我们所希望的,这也正是commons-logging所要达到的目标之一。

可是,怎么才能让Log4j发挥它的作用呢?答案很简单,只需满足“classpath中有Log4jjar包”。前面已经说过了,commons-logging会自动发现并应用Log4j。所以只要它存在,它就发挥作用。(它不存在呢?自然就不发挥作用,commons-logging会另行选择其它的日志实现类。)

注意:配置文件log4j.propertiesLog4j来说是必须的。如果classpath中没有该配置文件,或者配置不对,将会引发运行时异常。

       这样,要正确地应用Log4j输出日志信息,log4j.properties的作用就很重要了。好在该文件有通用的模板,复制一份(稍加修改)就可以使用。几乎每一个Java项目目录内都会有一个log4j.properties文件,可下载几个Java开源项目源代码查看。本文最后也附一个模板性质的log4j.properties文件,直接复制过去就可以用,或者根据自己的需要稍加修改。后文将会log4j.properties文件适当作一些介绍。

关于Log4j比较全面的配置

LOG4J的配置之简单使它遍及于越来越多的应用中了:Log4J配置文件实现了输出到控制台、文件、

回滚文件、发送日志邮件、输出到数据库日志表、自定义标签等全套功能。择其一二使用就够用了

log4j.rootLogger=DEBUG,CONSOLE,A1,im
log4j.addivity.org.apache=true 
 

# 应用于控制台
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.Threshold=DEBUG
log4j.appender.CONSOLE.Target=System.out
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=[framework] %d - %c -%-4r [%t] %-5p %c %x - %m%n
#log4j.appender.CONSOLE.layout.ConversionPattern=[start]%d{DATE}[DATE]%n%p[PRIORITY]%n%x[NDC]%n%t[THREAD] n%c[CATEGORY]%n%m[MESSAGE]%n%n

#应用于文件
log4j.appender.FILE=org.apache.log4j.FileAppender
log4j.appender.FILE.File=file.log
log4j.appender.FILE.Append=false
log4j.appender.FILE.layout=org.apache.log4j.PatternLayout
log4j.appender.FILE.layout.ConversionPattern=[framework] %d - %c -%-4r [%t] %-5p %c %x - %m%n
# Use this layout for LogFactor 5 analysis


# 应用于文件回滚
log4j.appender.ROLLING_FILE=org.apache.log4j.RollingFileAppender
log4j.appender.ROLLING_FILE.Threshold=ERROR
log4j.appender.ROLLING_FILE.File=rolling.log
log4j.appender.ROLLING_FILE.Append=true
log4j.appender.ROLLING_FILE.MaxFileSize=10KB
log4j.appender.ROLLING_FILE.MaxBackupIndex=1
log4j.appender.ROLLING_FILE.layout=org.apache.log4j.PatternLayout
log4j.appender.ROLLING_FILE.layout.ConversionPattern=[framework] %d - %c -%-4r [%t] %-5p %c %x - %m%n

#应用于socket
log4j.appender.SOCKET=org.apache.log4j.RollingFileAppender
log4j.appender.SOCKET.RemoteHost=localhost
log4j.appender.SOCKET.Port=5001
log4j.appender.SOCKET.LocationInfo=true
# Set up for Log Facter 5
log4j.appender.SOCKET.layout=org.apache.log4j.PatternLayout
log4j.appender.SOCET.layout.ConversionPattern=[start]%d{DATE}[DATE]%n%p[PRIORITY]%n%x[NDC]%n%t[THREAD]%n%c[CATEGORY]%n%m[MESSAGE]%n%n


# Log Factor 5 Appender
log4j.appender.LF5_APPENDER=org.apache.log4j.lf5.LF5Appender
log4j.appender.LF5_APPENDER.MaxNumberOfRecords=2000

# 发送日志给邮件
log4j.appender.MAIL=org.apache.log4j.net.SMTPAppender
log4j.appender.MAIL.Threshold=FATAL
log4j.appender.MAIL.BufferSize=10
log4j.appender.MAIL.From=web@www.wuset.com
log4j.appender.MAIL.SMTPHost=www.wusetu.com
log4j.appender.MAIL.Subject=Log4J Message
log4j.appender.MAIL.To=web@www.wusetu.com
log4j.appender.MAIL.layout=org.apache.log4j.PatternLayout
log4j.appender.MAIL.layout.ConversionPattern=[framework] %d - %c -%-4r [%t] %-5p %c %x - %m%n


# 用于数据库
log4j.appender.DATABASE=org.apache.log4j.jdbc.JDBCAppender
log4j.appender.DATABASE.URL=jdbc:mysql://localhost:3306/test
log4j.appender.DATABASE.driver=com.mysql.jdbc.Driver
log4j.appender.DATABASE.user=root
log4j.appender.DATABASE.password=
log4j.appender.DATABASE.sql=INSERT INTO LOG4J (Message) VALUES ('[framework] %d - %c -%-4r [%t] %-5p %c %x - %m%n')
log4j.appender.DATABASE.layout=org.apache.log4j.PatternLayout
log4j.appender.DATABASE.layout.ConversionPattern=[framework] %d - %c -%-4r [%t] %-5p %c %x - %m%n


log4j.appender.A1=org.apache.log4j.DailyRollingFileAppender
log4j.appender.A1.File=SampleMessages.log4j
log4j.appender.A1.DatePattern=yyyyMMdd-HH'.log4j'
log4j.appender.A1.layout=org.apache.log4j.xml.XMLLayout

#自定义Appender
log4j.appender.im = net.cybercorlin.util.logger.appender.IMAppender

log4j.appender.im.host = mail.cybercorlin.net
log4j.appender.im.username = username
log4j.appender.im.password = password
log4j.appender.im.recipient = corlin@cybercorlin.net

log4j.appender.im.layout=org.apache.log4j.PatternLayout
log4j.appender.im.layout.ConversionPattern =[framework] %d - %c -%-4r [%t] %-5p %c %x - %m%n



Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1545873

posted @ 2007-12-12 14:48 PariScamper 阅读(2040) | 评论 (2)编辑 收藏

就servlet规范本身,数据可以放在3个地方:request、session、servletContext.

request:
好处:用完就仍,不会导致资源占用的无限增长。
弊处:每次要用都从数据库中抓,多做操作,自然会对性能有一些影响。

session:
好处:不用每次都去数据库抓,少做操作。
弊处:每个客户都有一个session,只能自己使用,不同session可能保存大量重复数据;
可能耗费大量服务器内存;
另外session构建在cookie和url重写的基础上,所以用session实现会话跟踪,会用掉一点点服务器带宽和客户端保持联络,
当然session越多,耗费的带宽越多,理论上也会对性能造成影响。
集群的session同步会是个问题。

servletContext:
好处:不用每次都去数据库抓,少做操作。
存储的数据所有客户都可以用。
可减少重复在内存中存储数据造成的开销。
弊处:很多时候相同的数据可能不多(相当于cache的命中率很低)。


其实以上3中方法都有利有弊,各自的好处在某种条件下,也都会转变为弊处。所以不妨综合使用,相当于一个“第三方用法”(只讲一下思路,否则太过繁琐,涉及到的相关技术点请参考有关技术资料):

request不说了,重点说说session和servletContext:

--session的可控应用
session的最大问题是资源回收,两类回收方法:
主动回收:浏览器被关闭,而为提交触发清理动作的请求时,该方法失效,而且很常见。
超时回收:设置session的setMaxInactiveInterval属性或在web.xml中配置超时时间,然后交给jvm的垃圾处理器处理。
不过不要报太大希望,jvm的垃圾收集器并不灵光。
可以用另一种替代方法缓解该问题,比如限制session的数量,可以用HttpSessionListener实现,这样可以缓解session带来的吃内存问题,当然这种做法每次都需要判断session数量,当session达到限定数量时还必须用其他方法处理了,这些细节繁琐,而且要谨慎处理。

--servletContext
如果说session是一个“局部缓存”,那servletContext就是一个“全局缓存”了,不妨把它当作cache(这里不讲究用词的严谨性,仅为了更好说明问题)。cache的大小是当前应用可使用的最大内存。cache的最大问题是提高命中率,命中率高,内存占用少,效率高,命中率低,则内存占用多而且效率低。这种应用的技术实现比“session的可空应用”要简单,适用于相同数据多的地方,这个要事先有所判断,如果用不好则有弊无利。

如果仅使用servlet规范给出的3种机制,任何一种都达不到好处兼收的效果,所以要发挥3种方法的好处、摒弃弊处,必须综合运用,做一些技术框架的构建工作,而且有些地方还比较繁琐(还好框架是可重用的)。
 

有时候寻求或实现“平衡”(或者说尽取其利而摒其害),要付出很大代价,根据不同的情况,这些代价或是值得,或是不值得。也可以“两害相权取其轻”,或许是最便捷的方法。

posted @ 2007-11-19 13:18 PariScamper 阅读(769) | 评论 (0)编辑 收藏