2016年9月28日水曜日

Make a Snow Leopard 10.6.7 Installer Disc from a Mac Restore Disc

So there was this hint the other day at MacWorld's OS X Hints site:
10.6: Make a universal 10.6.7 Snow Leopard installer – Mac OS X Hints

It was a very useful hint for hacking together a retail Snow Leopard disc (10.6.0/10.6.3 ) and the newest system specific restore disc (10.6.7 for iMacs and MacBook Pros) to make a "universal" 10.6.7 disc that would work on new and old machines alike, that are qualified to run Snow Leopard. Useful not only for techs who need a one disc does it allbut others who might lack bandwidth for updates since this cuts down on some Software Update downloads (10.6.8 is only a smaller "delta" update this way.)

So, it was a good hint because it got to the heart of the matter, that OSInstall.mpkg was where the script logic determines if your machine is qualified for the restore. However using the original 10.6 disc and copying over all the old printer drivers is a little too kludgy for me, the less donethe better. I knew there had to be a more elegant way…

Voila – this bash script strives for automagic: it reads in your restore disc, prompts you to grab your disc and pop in a blank DVD, then modifies the install logic, and burns the disc you can use on all your Snow Leopard elligible Macs (P.S. Don't steal OS X). It hasn't been QC'd or QA'd beyond a few runs using one set of 10.6.7 restore disc, but I hope it comes in handy for some Mac tech out there somewhere who is in a pinch and needs to restore Snow Leopard – if only to get App Store on a machine and then up to 10.7 or 10.8!

makeUniversalSnowLeoDisc.command

#!/bin/bash
clear

destination=~/Desktop/Mac\ OS\ X\ Install\ DVD

#make destination folder
[ ! -d "$destination" ] && mkdir "$destination"

#make sure disc is in drive
while [ ! -d /Volumes/Mac\ OS\ X\ Install\ DVD ] ; do
echo Please insert Mac OS X Install DVD and press Enter
read
done

#get device node for block copy
device=$(diskutil info /Volumes/Mac\ OS\ X\ Install\ DVD/ | grep Node | cut -f2 -d:)

#create disk image from device (retains Finder window custom background)
echo "Creating Disc image at $destination, please wait..."
/usr/bin/hdiutil create -srcdevice $device -format UDRW "$destination"/Mac\ OS\ X\ Install\ DVD.dmg

#disk image using source folder (loses custom Finder background)
#/usr/bin/hdiutil create -srcfolder /Volumes/Mac\ OS\ X\ Install\ DVD/ -format UDRW "$destination"/Mac\ OS\ X\ Install\ DVD.dmg

#eject media
/usr/bin/drutil eject

#tell user to take out disc
echo -e $'\a'$'\a'$'\a'$'\n'"Please Remove the disc and press Enter"$'\n'
read

echo "Modifying Image"
#mount r/w image
hdiutil attach "$destination"/Mac\ OS\ X\ Install\ DVD.dmg

#expand OSINstall.mpkg
/usr/sbin/pkgutil --expand /Volumes/Mac\ OS\ X\ Install\ DVD/System/Installation/Packages/OSInstall.mpkg /Volumes/Mac\ OS\ X\ Install\ DVD/System/Installation/Packages/OSInstall.expanded

#modify Distribution script in place with no backup
/usr/bin/sed -i '' "s/modelProp\ \=\=\ hwbeSupportedMachines\[i\]/1/g" /Volumes/Mac\ OS\ X\ Install\ DVD/System/Installation/Packages/OSInstall.expanded/Distribution

#modify Distribution script in place with backup (for sissies)
#/usr/bin/sed -i '.original' "s/modelProp\ \=\=\ hwbeSupportedMachines\[i\]/1/g" /Volumes/Mac\ OS\ X\ Install\ DVD/System/Installation/Packages/OSInstall.expanded/Distribution

#remove original OSInstall.mpkg package
/bin/rm -rf /Volumes/Mac\ OS\ X\ Install\ DVD/System/Installation/Packages/OSInstall.mpkg

#flatten new package
/usr/sbin/pkgutil --flatten /Volumes/Mac\ OS\ X\ Install\ DVD/System/Installation/Packages/OSInstall.expanded /Volumes/Mac\ OS\ X\ Install\ DVD/System/Installation/Packages/OSInstall.mpkg

#remove expanded folder
/bin/rm -rf /Volumes/Mac\ OS\ X\ Install\ DVD/System/Installation/Packages/OSInstall.expanded

### image is now ready to be burned ###

#eject disk image
hdiutil eject /Volumes/Mac\ OS\ X\ Install\ DVD/

#burn disc image
/usr/bin/drutil burn "$destination"/Mac\ OS\ X\ Install\ DVD.dmg

echo -e $'\a'$'\a'$'\a'$'\n'"Disc Complete, Please Remove the disc"$'\n'

 

2016年9月27日火曜日

Springでのトランザクション管理

Springのトランザクション管理は大きく分けて以下の2つの管理方法が存在する。

1.プログラミングによるトランザクション管理

プログラミングによってトランザクションを手動管理する方法。以下のようなイメージでトランザクション制御コードをソースコード内に記述する。
transactionManager.begin();  transactionManager.commit();  transactionManager.rollback();  
ソースコード内にトランザクション制御コードも入りこむため、見通しが悪くなったり制御を間違えるとバグの温床にもなったりするので、極力使うべきではない。

2.宣言的トランザクション管理

「あるメソッドを呼び出したときにトランザクションをかける」と宣言する方法。以下のようなイメージでメソッド(やクラスに)に@Transactionalアノテーションを付与することで実現される。
以下の例では、updateFooメソッドを呼び出したときにトランザクションをかける、ということ。
※XMLベースでも宣言可能だがここではアノテーションベースを利用する
アノテーションベースのイメージ.
public class DefaultFooService implements FooService {            public Foo getFoo(String fooName) {                  // do something          }            // these settings have precedence for this method          @Transactional          public void updateFoo(Foo foo) {                  // do something          }  }  
トランザクション制御コードがソースコード内に入りこむことがないため、この管理方法が推奨されている。
詳細は後述するが、メソッドやクラスに@Transactionalアノテーションを付与することで管理され、トランザクションの開始、コミット、ロールバックは自動で行われる。
ただし、ロールバックの注意点として、非検査例外(RuntimeException及びそのサブクラス)が発生した場合はロールバックされるが、検査例外(Exception及びそのサブクラスでRuntimeExceptionのサブクラスじゃないもの)が発生した場合はロールバックされずコミットされる。
なお、Springの宣言的トランザクションはAOPを使って実現されている。
※AOPについてはSpringでAOPを参照。
本記事では「2.宣言的トランザクション管理」について明記する。

トランザクションマネージャ

Springではいくつかのトランザクションマネージャが用意されている。
これらは利用するDBアクセスAPIによって使い分ける。
DBアクセスAPIトランザクションマネージャ
JDBCorg.springframework.jdbc.datasource.DataSourceTransactionManager
JDOorg.springframework.orm.jdo.JdoTransactionManager
JTAorg.springframework.transaction.jta.JtaTransactionManager
Hibernateorg.springframework.orm.hibernate.HibernateTransactionManager

トランザクション制御

冒頭でも述べたが、以下のようにメソッドにTransactionalアノテーションを付与する方法とクラスにTransactionalアノテーションを付与する方法がある。

メソッドにTransactionalアノテーションを付与する

メソッドにTransactionalアノテーション付与することで、メソッドが呼ばれたタイミング(正確にはメソッド開始前)にトランザクションが開始され、対象のメソッドが正常終了した場合はコミット、例外で終了した場合はロールバックされるようになる(再掲になるがロールバックの注意点として、非検査例外(RuntimeException及びそのサブクラス)が発生した場合はロールバックされるが、検査例外(Exception及びそのサブクラスでRuntimeExceptionのサブクラスじゃないもの)が発生した場合はロールバックされずコミットされる)。いづれもアノテーションを付与しておくだけで自動でやってくれる。
public class DefaultFooService implements FooService {            public Foo getFoo(String fooName) {                  // do something          }            // these settings have precedence for this method          @Transactional          public void updateFoo(Foo foo) {                  // do something          }  }  

クラスにTransactionalアノテーションを付与する

以下のようにクラスにTransactionalアノテーションを付与することで、そのクラス内の全てのメソッドにトランザクション制御をかけることができる。
@Transactional(readOnly = true)  public class DefaultFooService implements FooService {        public Foo getFoo(String fooName) {          // do something      }        // these settings have precedence for this method      @Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)      public void updateFoo(Foo foo) {          // do something      }  }  
ただし、クラスに対して記述した設定はメソッドで記述された設定で上書きされることに注意する必要がある。上記の場合、updateFooメソッドは@Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)が適用される(クラスに付与されている@Transactional(readOnly = true)は効かない)。
なお、Transactionalアノテーションはpublicメソッドのみに適用可能。public以外のメソッドにも付与できてコンパイルも通るが、実行時エラーにもならないので注意が必要。
you should apply the @Transactional annotation only to methods with public visibility. If you do annotate protected, private or package-visible methods with the @Transactional annotation, no error is raised, but the annotated method does not exhibit the configured transactional settings. Consider the use of AspectJ (see below) if you need to annotate non-public methods.

Transactionalアノテーションのプロパティと設定例

Transactionalアノテーションに指定可能なプロパティ

プロパティ定義
valueString1つのアプリケーションで複数のトランザクションマネージャを利用する場合に、どのトランザクションマネージャを利用するかを指定する。
propagationEnum: Propagationどのような場合にトランザクションを開始するか等の属性を指定する。
isolationEnum: Isolationトランザクションの分離レベルを指定する。デフォルトはDEFAULTレベル。
readOnlyboolean読み取り専用かどうかを指定する。デフォルトはfalse。
timeoutint (in seconds granularity)そのトランザクションがタイムアウトする時間を指定する(秒指定)。デフォルトは利用しているトランザクション管理システムのタイムアウトを利用する。
rollbackForClass<? extends Throwable>[]検査例外が発生した際もロールバックしたい場合に指定する。
rollbackForClassNameString[]rollbackForと同じ、違いは例外クラス名また例外クラス名の配列を指定するところ。
noRollbackForClass<? extends Throwable>[]ロールバックしたくない場合に指定する。
noRollbackForClassNameString[]noRollbackForと同じ、違いは例外クラス名また例外クラス名の配列を指定するところ。

プロパティの設定例

value

// 利用したいトランザクションマネージャの名前を指定する  @Transactional("order")  

propagation

設定例.
//   常に新しいトランザクションを開始する  @Transactional(propagation=Propagation.REQUIRES_NEW)  
propagationのその他の設定値
プロパティ定義
MANDATORYトランザクションが開始されていなければ例外を発生させる。
NESTEDトランザクションが存在する場合はネストしたトランザクションを開始し、他のPROPAGATION_REQUIREDのように振る舞う。
NEVERトランザクションが開始されていれば例外を発生させる。
NOT_SUPPORTEDトランザクションが存在する場合は中断して、トランザクションをはらない(トランザクションを利用しない)
REQUIREDトランザクションが開始されていなければ新規に開始し、すでに開始されていればそのトランザクションをそのまま利用する。
REQUIRES_NEW常に新しいトランザクションを開始する。トランザクションが存在する場合は中断して新しいトランザクションを開始する。
SUPPORTSトランザクションが開始されていればそれを利用。開始されていなければトランザクションをはらない(トランザクションを利用しない)。

isolation

設定例.
// ダーティーリード・ファジーリードは発生しないが、ファントムリードは発生する可能性がある  @Transactional(isolation=Isolation.REPEATABLE_READ)  
isolationのその他の設定値
※下に行くほど分離レベルが高くなる
プロパティ定義
DEFAULT利用するデータストアのデフォルト分離レベルを利用する。
READ_UNCOMMITTEDダーティーリード・ファジーリード・ファントムリードが発生する可能性がある。
READ_COMMITTEDダーティーリードは発生しないが、ファジーリード・ファントムリードは発生する可能性がある。
REPEATABLE_READダーティーリード・ファジーリードは発生しないが、ファントムリードは発生する可能性がある。
SERIALIZABLEダーティーリード・ファジーリード・ファントムリードは発生する可能性がない。

readOnly

設定例.
// 読み取り専用トランザクション  @Transactional(readOnly = true)  

timeout

設定例.
// 10秒でタイムアウト  @Transactional(timeout = 10)  

rollbackFor

設定例.
//   全ての例外が発生した場合、ロールバックさせる  @Transactional(rollbackFor=Exception.class)  

rollbackForClassName

設定例.
//   全ての例外が発生した場合、ロールバックさせる  @Transactional(rollbackForClassName={"Exception"})  

noRollbackFor

設定例.
//   全ての例外が発生した場合、ロールバックさせないようにする  @Transactional(noRollbackFor=Exception.class)  

noRollbackForClassName

設定例.
//   全ての例外が発生した場合、ロールバックさせないようにする  @Transactional(noRollbackForClassName={"Exception"})  

参考