Archive for the ‘ IT ’ Category


互联网中的视频(HTML5) — (2)

音频编码器

除非你坚持观看1927年之前的电影,不然你一定会希望在你的视频中含有音频。和视频编码器一样,音频解码器也是一种算法来对音频流进行编码。同样和视频编码器一样,音频编码器也分为有损和无损两种类型。同样的原因,无损的音频是在是太大而很难再网络上使用,我们会更加关注有损音频。

事实上,我们关注的将要比这更窄,因为有损音频编码器有几种种类。音频在很多不需要视频的地方也在使用(电话,比如),于是存在着一整套关于如何对演讲进行编码优化的音频解码器。你不会使用这些编码器到音乐CD,不然结果就像是听到一个四岁的小孩在从话筒里唱歌一样。但是你会在Asterisk PBX中使用它,因为在这种情况下带宽是极其珍贵的,而这些编码器能够将人的演讲压缩的一般编码器的几分之一。不过,因为缺少浏览器和第三方的支持,演讲优化的音频编码器从来没有在网络上得到普及。所以我们将更加关注普通目的的有损编码器。

如果我再前面所提到的,当播放一段视频的时候,电脑同时在做几件事情:

1. 解释容器的格式

2. 解码视频流

3. 解码音频流并且传递到播放器中

音频编码器解决了上述的第三个问题 - 将音频流进行解码变成数据格式从而使得播放器可以播出。就像视频解码器一样,也有很多的技巧能够减小音频流中的数据存储量。也因为我们在讨论有损音频编码器,信息在录制-编码-解码-收听的循环中在不断丢失。不同的音频编码器会丢掉不同的东西,但是他们的共同目的是一样的,就是使得你的耳朵无法辨析出有东西在丢失。

有一个概念在视频中没有的是频道。我们把声音传送到喇叭中,那你有多少个喇叭呢?如果你坐在电脑前,极大的可能你只有两个,一个在左边,一个在右边。我的桌子上有三个,左边,右边,还有一个在地板上。被称作为环绕声系统的可以有六个或者更多的喇叭,被安排好而防止在房间里。每一个喇叭专门的播放一个频道,当你坐在六个喇叭中间的时候,你会被六个不同的喇叭轨道所环绕,你的大脑进行综合和感觉你就好像身临其境。有用么?至少这个几十亿美元的产业认为是。

大多数的普通音频解码器可以处理两个频道的声音。在录制的过程中,声音被分开到左音道和右音道中;在编码过程中,两个音道都被存放到同一个音频流中;然后在解码时,两个音道分别被送到合适的喇叭。有一些音频解码器可以处理多余两个的轨道,同时将他们传送到准确的喇叭中。

音频解码器有很多很多种,但是在网络中,真正只有三种是需要了解的:Mp3, AAC和Vorbis。

MPEG-1 Audio Layer 3

MPEG-1 Audio Layer 3  通常被称为MP3, 它被编码为不同的率,64kbps, 128kbps, 192kbps,还有的分布在32到320之间。更高的编码率代表着更大的文件和更好的音频效果,不过编码率和音频质量并不是线性关系:128kbps的声音比64kbps的要好绝不止两倍,但是256kbps没有128kbps的两倍好。此外,MP3允许多率编码,也就是说可以在音频的一部分使用一种编码率,而在另一部分使用另外一种编码率。比如,当两段对话之间的空白时,便可以使用很低的编码率,而在多种声音开始时使用更高的编码率。

MP3规范并没有完全意义上定义如何进行MP3的编码(不过定义了如何解码),不同的编码器使用不同的听觉心理学模型得到了很多不同的结果,但是他们都可以被同一个播放器所解码。开源的LAME项目是一个最好的免费编码器。

MP3格式在1991年标准化,同时也是受专利所保护的,这也解释了为什么Linux不同直接播放MP3的原因。绝大多数的移动音乐播放器都支持单独的MP3文件,同时MP3音频流也可以被内嵌在任意的视频容器中。Adobe Flash既可以播放单独的MP3文件,也可以将MP3内嵌到MP4视频容器之中。

ADVANCED AUDIO CODING

AAC于1997年标准化,它在被Apple选中成为其iTunes商店的默认格式后得到了极大的发展。最初,所有的从iTunes商店购买的AAC文件都是被Apple的DRM所加密的,被称之为FairPlay。现在iTunes商店的很多歌曲已经没有了这层保护,被Apple称为iTunes Plus。AAC格式是受专利保护的。

AAC是被设计用来提供一个在同样解码率下比MP3音质更好的格式,并且能够在任意的编码率下编码音频。(MP3只能限制在几种编码率下,并且上限是320kbps)。AAC可以最多编码48个频道的声音,尽管在实践中不会有人真的这样去做。AAC格式另一个和MP3不同的地方在它也定义了多Profiles,类似于H。264。低复杂度的profile被设计用来在实时的系统中低CPU功耗的播放,而高Profile则被用来提供更好的音质通过更慢的编码和解码来换取。

所有当前的Apple产品,包括iPods, AppleTV, 和QuickTime支持一定profiles的AAC格式在单独的音频文件或者被内嵌在MP4视频容器中。Adobe Flash支持MP4中的各种Profiles的AAC,开源的播放器mplayer和VLC播放器也是。对于编码而言,FAAC库是一个开源的选择。

VORBIS

Vorbis也经常被叫做”Ogg Vorbis”, 尽管从技术角度来说是不正确的。(”Ogg”只是一个容器格式,而Vorbis音频流还可以被内嵌在其他的容器之中)。Vorbis不被任何专利所限制,因此它被绝大多数的Linux系统和移动设备直接支持。Mozilla Firefox 3.5 支持Vorbis音频文件在Ogg容器中,或者Ogg视频中包含着Vorbis音频轨道。Android移动手机可以播放单独的Vorbis音频文件。Vorbis音频流通常被内嵌在Ogg或者WebM容器中,但是他也可以被内嵌在MP4或者MKV中。Vorbis支持任意数目的音频轨道。

互联网中的视频(HTML5) — (1)

自从上个月Google IO以来,对于HTML5的讨论是越来越多,而其中十分重要的一个部分就是视频Video。作为内嵌进入HTML的新的标记,Video将在HTML5中发挥其核心的作用,将互联网的表达能力继续提升。

不过在实际中,有很多人对于视频的了解还很浅显,对于WebM等这些新的名词到底代表的什么意思,以及和以前的MP4,AVI等有什么关系是完全的一头雾水。在这里找到了一篇很好的文章,直接翻译过来,希望对大家都有所帮助。至少我在读完这篇文章之后,对于Video的确是有了更为清晰的认识。

文章的网址在http://diveintohtml5.org/video.html,是一本新书的其中一个章节。

每一个在过去四年中访问过YouTube的人都应该知道你可以将视频内嵌在网页中。但是在HTML5之前,是没有一个标准的方式来完成这件事情的。实际上,所有你在网络上所观看的视频,都是通过的第三方插件 - 或许是QuickTime,或许是RealPlayer,或许是Flash(YouTube使用的是Flash)。这些插件能够很好的和你的浏览器结合,以至于很有可能你都意识不到你在使用它们,直到你试图观看一个视频但是你的平台不支持这个插件的时候。

HTML5定义了一个标准,使用<video>符号来在网页中内嵌视屏。目前对<video>的支持还在慢慢发展,当然这只是对其还无法使用的一个礼貌的说辞。至少,它现在还不能在所有的地方使用。但是不要灰心,还是有很多的替代,退路和选择存在。

<VIDEO> 的支持

IE9    IE8    IE7    FX3.5    FX3.0    SAF4    SAF3    CHROME     OPERA
yes                              yes                          yes        yes           yes              yes

不过支持<video>仅仅只是这个故事中的很小一部分。在我们开始讨论HTML5视频之前,你必须对视频本身有一定的了解。

视频容器

你可以把视频文件看做是AVI文件或者是MP4文件。在现实之中,AVI和MP4仅仅只是视频容器的格式。就像一个ZIP压缩文件可以存储任意的文件在其中一样,视频容器格式只是定义了如何在容器中存储东西,而不是什么类型的数据可以进行存储。(当然这个可能稍微要复杂一些,因为不是所有的视频流都和所有的视频容器格式所匹配的,不过暂时不要担心这些)

一个视频文件通常会包括几个轨道 - 一个视频轨道(没有音频),加上一个或者多个的音频轨道(没有视频)。 轨道通常是互相关联在一起的。一个音频轨道之中存储着标记来帮助和视频同步。单一的轨道可以包含元数据,比如视频的标题,视频的封面,集数(对于电视剧而言),还有很多很多。

视频的容器格式有很多很多,其中最为重要的一些包括:

  • MPEG4,通常是.mp4或者.m4v的扩展名。MPEG4容器时基于苹果公司老的QuickTime(.mov)容器的。在苹果公司网站上的电影片头,仍然使用的是QuickTime容器格式,但是从iTunes上下载的电影已经是MPEG4容器模式了。
  • Flash,通常以.flv结尾。Flash视频是,不意外的,被Adobe Flash所使用的。在Flash9.0.60.184之前(也就是Flash Player 9 Update 3),这是Flash唯一所支持的格式。现在的新版本Flash同时也支持MPEG4容器。
  • Ogg,通常是以.ogv作为其扩展名。Ogg是一个开放标准,开源并且没有任何专业牵连的标准。FireFox3.5, Chrome 4, 还有Opera 10.5都直接支持Ogg容器,Ogg视频(被称作Theora)和Ogg音频(被称作Vorbis),而不需要任何插件。在PC中,Ogg能够被所有的主流Linux直接使用,同时你可以在Mac和Windows上通过安装QuickTime Components或者DirectShow filters来使用。它同时可以通过VLC在各个平台上播放。
  • WebM是一个新的容器格式,从技术的角度来说,它和Matroska非常相似。WebM在Google I/O 2010中被发布,它是通过VP8视频编码器和Vorbis音频编码器来实现的(等会会有更多提到)。WebM将会被下个版本的Chromium, Google Chrome, Mozilla Firefox, 和Opera内置直接支持而不需要任何插件。Adobe也声称将在下个版本的Flash中支持WebM视频。
  • Audio Video Interleave, 通常以.avi结尾。AVI容器格式是微软在早期的时候当播放视频还被认为是十分惊喜的时候发明的。它并没有官方支持很多最新的容器格式,它也不官方支持各种元数据,它甚至不支持很多现在的视频和音频编码器。很长一段时间以来,各个公司通过各种方法来扩展AVI使其能够和各种格式相匹配,目前它仍然是很多著名的编码器的默认格式比如MEncoder.

视频编码器

当你谈到观看视频的时候,你通常所说的应该是一个视频流和音频流的组合。但是你并没有两种不同的文件,你只有一个视频文件。可能是AVI,也可能是MP4。这些仅仅只是容器的格式,就像ZIP压缩文件可以存放各种文件一样。视频容器格式定义了如何在单一文件中储存视频和音频流。

当你观看视频的时候,你的视频播放器在同时做以下几件事:

1. 通过对视频容器格式的解析,来判断哪种视频和音频轨道,和他们是如何存储在这个容器之中的,从而可以将这些数据找出并进行解码。
2. 对视频流进行解码并将一帧一帧的图像播放在屏幕上。
3. 对音频流进行解码并将声音送到喇叭中。

一个视频编码器指的是一个视频流进行编码的算法,它规定了如何完成上述的第二步。(词语Codec是一个合成词,他是编码coder和解码decoder的合成)。你的视频播放器通过视频编码器将视频流进行解码,然后播放一些列的图像或者帧在屏幕上。当今大多数的视频编码器使用了各种技巧来减少大量的播放帧之间的信息。比如,仅仅只存储两帧图像之间的差异,而不是完完全全存储两帧单独的图片。大多数的视频在两帧之间的变化都很小,这样就能够使用高压缩来产生更小的文件大小。

有损耗和无损耗两种类型的编码器。无损耗的视频太大而不适于在网络上使用,所以我们将更多的目光放在损耗的编码器上。一个有损的编码器说明信息在编码的时候会不可挽回的丢失。比如从一个磁带中拷贝时,你将会丢失原有的信息降低音频的质量,并且在每一次的编码中都会发生。除了音频中的zizi的声音之外,多次的视频编码会使得图像看上去更块状,特别是在有很多运动的场景之中。(事实上,这也可能发生在直接从原视频中进行编码,当你使用一个很差的编码器,或者传错了参数的时候发生)。从另一面来说,有损的视频解码器仍够提供很好的压缩器,同时很多提供了一些技巧使得块状的视屏仍够不那么容易被人的眼睛所发觉。

视频编码器的种类有很多很多,三个最为著名的分别是H.264, Theora和VP8.

H.264

H.264也通常被视为MPEG-4 part 10或者MPEG-4 AVC或者MPEG-4 Advanced Video Coding. H.264是被MPEG公司研发,在2003年标准化的。它的目标是提供向低带宽,低CPU设备(手机)或者高带宽,高CPU设备(现代PC)以及所有两者之间的所有设备的一个单一的编码器。为了完成这个目的,H.264标准被划分问profiles,用来定义一些列的可选的属性来再文件大小和复杂程度之间进行选择。高Profiles使用了更多的附加属性,提供了更好的视频效果,花费更多的时间进行编码,并且需要更多的CPU进行实时解码。

为了给大家一个粗略的profiles范围的概念,Apple公司的iPhone使用Baseline profile,AppltTV使用Baseline和Main profile,PC中的Adobe Flash使用Baseline, Main和High profile. YouTube使用H.264来编码高分辨率的视频,通过Adobe Flash来播放。YouTube同时也提供H.264编码的视频给移动设备包括苹果的iPhone和Google的Android。同时H.264也是蓝光Blue-Ray格式所指定的编码格式,蓝光一般使用High profile.

大多数的非PC设备在播放H.264视频的时候(包括iPhone和蓝光),实际上使用的是一个专门的芯片来进行解码,因为它们的CPU还没有强大到能够在实时对视频进行解码。大多数的PC的显卡也支持硬件H.264解码。还有很多H.264解码器的竞争者,包括开源的x264 library。H.264标准是受专利保护的,通过MPEG LA建团所代理。H.264视频可以集成在绝大多数的视频容器格式之中,包括MP4和MKV.

THEORA

THEORA是从VP3解码器发展而来,而如今被Xiph.org Foundation所支持。Theora不像其他的VP3专利,是完全免费没有任何专利保护的编码方式。尽管这个标准从2004年以来就没有变化,Theora项目仅仅只在2008年11月和2009年9月发布了1.0和1.1版本。

Theora视频可以被集成到任意的视频容器格式之中,尽管大多数时候是在Ogg容器中。所有的主流Linux都内置支持Theora,并且Mozilla Firefox 3.5也内置支持。内置支持,我指的是不需要任何插件就可以进行播放。同时Theora视频也可以在Windows和Mac OS X中通过安装Xiph.org的开源解码器软件来进行播放。

VP8

VP8是另外一个On2公司的视频编码器,它们同时也最初研发了VP3(后来的Theora)。从技术上讲,VP8的视频质量类似于Baseline profile的H.264,同时有着很大的提升的潜力。

在2010年,Google收购了On2公司,并且将视频编码器标准和编码解码的样例进行了开源。同时,Google还通过宣布其完全免费打开了所有On2在VP8上的专利。(这也是最好的办法,因为你无法解除任何专利)。

在Ubuntu上使用80端口

在Stackoverflow上问的问题,直接贴个地址,方便自己查找:
http://stackoverflow.com/questions/2444591/opening-port-80-with-java-application-on-ubuntu

简而言之,用iptables进行redirect,命令长的样子:
iptables -t nat -A PREROUTING -i eth0 -p tcp –dport http -j REDIRECT –to-ports 8080

一句话Spring

Spring3中,很多方法都会抛出org.springframework.dao.DataAccessException异常,而这个异常被加入了org.springframework.transaction.jar中。于是大部分的开发都需要添加这个jar包的依赖。
这个问题已经有issue提出,估计可能在下一个版本中会有改进。

一句话mac操作

在mac里添加环境变量,可以更改系统的/etc/profile,或者更改用户的.bash-profile.推荐后者。
在Terminal下执行echo命令:(以添加Derby_home为例来添加derby的环境变量,其中xxx为路径)
echo ‘export DERBY_HOME=xxx$DERBY_HOME’ >> ~/.bash_profile
或者直接用vi更改也可以

在GAE/J中使用memcache

memcache其实也不算是一个很新的概念了,在各种大型网站中是运用的非常的多的。著名的Twitter的白鲸事件,也和memcache有着巨大的关系。想了解其背景概念的和八卦的,不妨Google一下。简而言之,memcache就是将那些经常需要查询的数据或者经常需要调用的外部文件,存在缓存之中,来减少数据库查询或者远程网络调用的浪费。这个概念在数据库的缓存中已经十分的悠久了,但是由于涉及到太多极低层次的代码编写,一般的程序员是不会接触到这的。现在有了memcache的帮助,能够很好的将这一思想移植到应用层面来,对大型项目的开发有着很大的帮助。

Google App Engine很早就有了memcache的支持,算是其天生的一个优势,在这里我简单的介绍一下如何在GAE/J中使用memcache。对Python感兴趣的同学就十分抱歉了。

首先我们假象一个例子,在GAE/J中有一个学校的学生系统,在一个有着4-5万人的大学里,如果该系统包括了所有的诸如选课,学习,注册,论坛等等各种信息,对于数据库的查询需求是非常的大的。不过,这种网虫又往往局限在部分学生之中,就比如学校邮箱,喜欢经常去check的和很少去check的都会存在。于是怎么能让那些经常使用的数据能够得到更好的利用呢?我们假定数据库中存有一个表,是Student表专门存放学生信息,学生无论是登录,还是做什么也好,都需要查询其Student信息。于是,我们来看memcache如何实现对Student的缓存。

首先我们生成一个JDO的POJO类,来存放学生信息,在这里简单开来:
[java]@PersistenceCapable(identityType = IdentityType.APPLICATION)

@Inheritance(customStrategy = “complete-table”)

public class Student implements Serializable{

@PrimaryKey

@Persistent

private String uuid;

@Persistent

private String name;

@Persistent

private String email;

@Persistent

private String address;

public Student(){

this.uuid = UUID.randomUUID().toString();

//setter&getter

}[/java]
接着,我们需要创立一个Cache类,来负责缓存层的操作:

[java]

public class QueryCache {

private static final Logger log = Logger.getLogger(QueryCache.class

.getName());

private static QueryCache instance;

private Cache cache;

private QueryCache(){

try{

CacheFactory cacheFactory = CacheManager.getInstance().getCacheFactory();

cache = cacheFactory.createCache(Collections.emptyMap());

}catch(CacheException e){

log.severe(”Error in creating the cache”);

}

}

public static synchronized QueryCache getInstance(){

if(instance==null){

instance = new QueryCache();

}

return instance;

}

public void putInCache(String address, String student){

cache.put(address, student);

}

public String findInCache(String address){

if(cache.containsKey(address)){

return (String)cache.get(address);

}else{

return null;

}

}

}

[/java]

在这个类中,其单例模式下的构造函数生成了一个新的Cache,它的内部结构是一个Map。同时我们定义了两个方法,一个是将学生信息放进cache,一个是从cache中取出学生信息。

最后,我们创建一个Servlet,来进行学生信息的查询:

public class QueryServlet extends HttpServlet{
private static final Logger log = Logger.getLogger(QueryServlet.class.getName());
@Override
protected void doGet(HttpServletRequest req, HttpServletResposne resp) throws ServletException, IOException{
log.info(”Now start……”);
QueryCache cache = QueryCache.getInstance();
String studentC = cache.findInCache(”Address7694″);
if(studentC!=null){
resp.getWriter().write(”Found the item in cache!”);
}else{
resp.getWriter().write(”No hit in cache!”);
PersistenceManager pm = PMF.get().getPersistenceManager();
Query query = pm.newQuery(Student.class);
query.setFilter(”address==’Address7694′”);
List students = List query.execute();
if(students.iterator().hasNext()){
log.info(”Found one:”+student.toString());
resp.getWriter().write(”Found one:”+student.toString());
cache.putInCache(”Address7694″, student.toString());
}else{
log.info(”None found!”);
resp.getWriter().write(”None Found!”);
}
}
}

这样一个很简单的例子,显示出了整个memcache在GAE/J中的运用方法。当然这只是最最基础的,还有许多的东西需要思考,比如该在什么样的地方使用memcache,memcache的有效时间是多少等等。

GWT学习研究

1. GWT与Restlet的结合

目前进行的项目中,使用App Engine作为底层以及数据库的平台,Restlet是作为HTTP调用的库,然后GWT来前台界面的实现。基本上整个架构是严格按照Google的思路来进行的。最近把重心从底层慢慢走到了前台,于是对GWT有了些研究。把一些经验和总结列举出来。

首先,可以看到Restlet在最新的2.0m6和Snapshot之中,有五种版本,每一种版本的JAR的结构和路径都不一样。我们的项目在后台是使用的J2EE的版本,而在前台需要使用的是for GWT版本,于是这里就产生了第一个问题,在同一个项目中均需要使用这两个版本的冲突。在两个JAR包下都有org.restlet.jar存在。于是一个很简单的方法,就是改变其中一个JAR包的名字,来避免冲突。这里一定要注意的是,之后在引用JAR包时,一定要看清楚你的类是属于前台还是后台的,如果是前台的,一般包名是以com.google.gwt.core.client开头的。

2. – GWT的项目结构

在一个创建好的GWT项目中,一般会有以下三个重要的部分,第一个是项目名.gwt.xml,这个文件是整个项目的核心文件,里面包括了模块的引用,入口点的定义以及其他的一些配置方面的设置。接下来两部分分别存在两个包中,client包和server包。其中,client包就是为了做前台界面而存在的包,而server包可以认为是后台的,当然你可以使用你已经做好的而不是强制性非要放在里面。client包下的项目名同名java文件,即是在配置文件中定义的入口点,这个文件定义了界面UI的结构,以及各种事件的触发,就等于是一个以java而写的界面。GWT在编译过程中,是将其转化为javascript而给浏览器运行的。

现在有这样一个问题存在,如何实现页面的跳转。我们知道对于AJAX实现的Web应用,习惯于只用单页面来实现不同的内容,也就是利用同样的定位符如div等,来不断改变其中的内容。那么在GWT中,有没有比较好的办法做到这一点呢。我采用的方法,是使用一个单例模式下的固定div来实现。我们可以将不同的页面均视作为Content的不同实现,然后只需要替换页面下的Content即可。具体办法为:

1)创建一个Content的抽象类继承Composite类,这个类将作为所有通用的内容类,在主页面中替换使用。

public abstract class Content extends Composite {

}

2)在项目名java文件中,利用单例模式定一个该入口点的实例,同时创建一个Content实例,这个实例用来承载不同的实现内容类。同时加上一个setContent(Content content)方法,用来设定当前的Content。

public class XXX implements EntryPoint {

private static final XXX INSTANCE = new XXX();

private Content content;

private XXX(){}

public static XXX getInstance(){

return INSTANCE;

}

@Override

public void onModuleLoad() {

XXX.getInstance().setContent(new LoginContent());

}

public void setContent(Content content){

this.content = content;

RootPanel.get(”content”).clear();

RootPanel.get(”content”).add(content);

}

}

3)对于每一个不同的实现页面,分别创建一个类继承Content类,在这个类的构造函数中定义其界面和事件的触发函数。

public class LoginContent extends Content {

private Label usrLabel = new Label(”Username”);

……

public LoginContent(){

usrPanel.add(usrLabel);

usrPanel.add(usrTextBox);

……

initWidget(verPanel);

}

4)在今后的每次需要更换页面的时候,只需要获得该项目下的Content实例,进行替换即可。

3. – GWT下的UiBinder的实现

用上面的方法来实现界面存在的一个问题是,经常需要在类文件中写大量的界面元素的定义,如同Swing一样。一来对于Java程序员不太方便,二来对于界面程序员更为苦恼。于是在最新的GWT的版本中,Google推出了UiBinder,这是一个采用xml来作为界面管理器的实现方法,将界面的元素与功能实现了脱离,一方面简化了代码,另一方面也给前台和后台的程序员提供了便利。

在uiBinder下,我们如果需要创建一个新的界面,不再直接使用类来实现,而是创建一个UiBinder对象,在Eclipse下,new一个UiBinder会生成两个文件,一个是扩展了Composite的类,另一个是同名的.ui.xml文件。在.ui.xml中,xmlns:g=”urn:import:com.google.gwt.user.client.ui”的空间下定义的是所有的Widget,于是可以就像使用Java一样,列出这些需要的Widget了。在<ui:style>中,还可以定义所需要使用的样式,当然也可以单独在CSS中定义。以下是一个简单的例子:

<!DOCTYPE ui:UiBinder SYSTEM “http://dl.google.com/gwt/DTD/xhtml.ent”>

<ui:UiBinder xmlns:ui=”urn:ui:com.google.gwt.uibinder”

xmlns:g=”urn:import:com.google.gwt.user.client.ui”

xmlns:c=”urn:import:com.featheast.client”>

<ui:style>

.important {

font-weight: bold;

}

.bolder {

font-weight: bold;

}

</ui:style>

<g:HTMLPanel>

Hello,

<g:Button styleName=”{style.important}” ui:field=”button” />

<g:Label styleName=”{style.bolder}” text=”This is my label in bold!”></g:Label>

<g:VerticalPanel ui:field=”myPanelContent” spacing=”5″></g:VerticalPanel>

</g:HTMLPanel>

</ui:UiBinder>

对于每一个在.ui.xml中的元素,我们都有一个在类文件中的对应,比如这里,我们有一个Button,一个Label,这些可以操作的值在类中分别有

@UiField

Button button;

@UiField

Label label;

的对应。这样的结果就是我们能更好的操作这些元素。在该类的构造函数中,可以定义这些元素的事件函数和其他的操作。这样将界面和执行分隔开,效果的确比较好,思路也更加清晰。以下是一个对应的示例:

public class MyUIBinder extends Composite {

private static MyUIBinderUiBinder uiBinder = GWT.create(MyUIBinderUiBinder.class);

interface MyUIBinderUiBinder extends UiBinder<Widget, MyUIBinder> {}

@UiField

Button button;

@UiField

VerticalPanel myPanelContent;

public MyUIBinder(String firstName) {

initWidget(uiBinder.createAndBindUi(this));

button.setText(firstName);

HTML html1 = new HTML();

html1.setHTML(”<a href=’http://www.google.com’>Click me!</a>”);

myPanelContent.add(html1);

HTML html2 = new HTML();

html2.setHTML(”This is my sample <b>content</b>!”);

myPanelContent.add(html2);

}

@UiHandler(”button”)

void onClick(ClickEvent e) {

RootPanel.get().add(new FavouriteColorWidget());

}

}

运用同样的单例模式,我们可以更改同一个界面的内容,在此就不重复了。

几个GAE/J中的Transaction的概念

Transaction是在计算机很多领域里都需要的,最基础的知识相信大家都懂,那就是一个或一组操作,要么都成功,一旦有任何一个失败则所有的都失败,要回滚到原始位置。这个概念在GAE/J中一样。在datastore中,所有的写操作都被认为是atomic的,任何尝试着创建,更新或者删除实体的操作要么成功,要么就失败。一个操作有可能因为很多原因而失败,比如当许多用户同时尝试去修改这个值,或者该操作超过了其允许的配额值,当然也有可能是datastore的内部错误造成。在任何情况下的出错,都会使得所有操作就像没有发生一样而回滚。

实体组在GAE/J中是一个十分重要的概念。最开始的时候,我也是什么都想着往关系数据库上套,结果发现一来出现很多外键上的问题,二来出现很多包含关系的问题。个人认为,对于datastore而言,实体组是必须十分熟悉的内容。实体组之间的关系是以父子来称呼的,比如一个实体是国家,另一个实体是省份,那么显然一个国家会有很多省份而一个省份属于一个国家,于是我们可以将国家视作一个父实体,则通过每一个子实体都可以得到父实体也就是这个国家。当实体之间有了父子关系的时候,我们可以将他们视为在一个实体组中。对于一个没有父实体的实体而言,它就是一个根实体。一个实体的父实体是在该实体创建时就设定好了而不可以更改的。 处在同一个根实体下的所有实体都可以看作属于同一个实体组,被存在同一个datastore节点上。

那么实体组和Transaction的关系在哪呢?一个单一的Transaction只能对一个实体组进行操作,或者将另一个实体加入到该实体组中去。无论是查询,更新还是删除实体,如果一旦涉及到多个实体组,就会报错。同样的道理,在一个单一的Transaction中,不能涉及到多个实体有多个根节点的,因为一个根节点就等于代表了一个实体组。

如果在任何的tx.begin()和tx.commit()之间,有多个进程同时调用同样的实体组,那么JDO就会抛出JDODataStoreException或者JDOException异常,这是由java.util.ConcurrentModificationException造成的。解决的办法可以通过建立一个循环,多次进行重试来保证每个进程都能够成功。如果在一个进程之间,更新多个实体组,则会报JDOFatalUserException的异常。

Datastore的隔离级别在Transaction之外是最接近于READ_COMMITTED,在Transaction之内是SERIALIZABLE,特别指出是一种Snapshot Isolation。

我们在更新一个实体的部分值时需要使用Transaction,原因是我们在修改的同时可能也会有其他的进程在修改。另外一个需要使用的地方实在更新或者创建Key的时候,道理与上者类似,以避免多进程的同时操作。最后一个需要使用的地方是多个读操作发生的时候,为了保持数据的一致性而使用Transaction。

一个Transaction应该很快的进行,来减少发生冲突的机会从而减少重试的次数。尽可能的在Transaction之外把数据准备好,然后在Transaction内部进行执行需要保持数据一致性的操作。应该在Transaction之内来准备Key,和使用Key来获得实体。将datanucleus.appengine.autoCreateDatastoreTxns设置为false,则所有的Transaction都被禁用了。该文件的位置位于war/WEB-INF/classes/META-INF。

如果你的代码是以前根据关系数据库编写的,里面的Transaction极有可能是基于全局的,于是在GAE/J下必然会报错。一个好的解决办法是,首先禁用掉GAE/J中的所有的Transaction,然后一步步的操作来解决这个Transaction的问题。

推荐Amarok

一款看上去很不错的听歌软件,目前稳定版只能在Linux下使用,有非官方的WIN和MAC版本,不过因为后者上面相关播放器太多,所以忽略。

界面很漂亮,播放效果也不错,特别是自动链接的一些东西很不错。

下载为:sudo apt-get install amarok。当然也可以使用Ubuntu的下载工具下载。

问题:如果你在安装之后,无法播放歌曲,尝试安装如下文件;
sudo apt-get install phonon-backend-xine libxine1-ffmpeg。问题应该可以迎刃而解,我使用的是Ubuntu Karmic
,amarok的版本是2.2.0

终于可以在Ubuntu上使用正宗的Chrome了

说GOOGLE这么大的声势,却对UNIX市场始终进度不够,虽然发布了Chrome发布了这么久,UNIX的BETA版现在才出现。不过至少有了官方货,不需要在去使用盗版的Chromium了。在这还是要好好赞赏一下Chromium,除了颜色之外,功能个方面做的都是相当到位阿!

好了,装好了,但是我没发现有多大的区别。等使用两天再来汇报.

Older Entries
  • English Version

    • Cannot read Chinese? Please take a look at my English site, hope you can find more you need there!
  • 感谢支持

  • twitter

    facebook

    linkedin

    • You are currently browsing the archives for the IT category.

  • Categories