logo

サイト内検索
ココログ最強検索 by 暴想

最近のトラックバック

無料ブログはココログ

« 2011年5月 | トップページ | 2011年7月 »

2011年6月

javaScriptからWebApiで公開Apexにアクセス

前回自組織のApexを動かしただけですが、

別環境からWebApiで公開Apexを呼ぶには、

その環境にログインすればよいです。

↓それもこんな形で簡単です。
sforce.connection.login("ユーザ名","パスワード");

これでログインすれば、
↓ログイン先の公開Apexを呼び出せますね。
var result = sforce.apex.execute("HelloWorld","sayHello",{a:'いぬずら'});

以上です。

Salesforceのconnection.jsとapex.js

SalesForceが提供しているconnection.jsやapex.jsを使って
DB読んだりApex呼んだりしてみるついでに、この辺のライブラリを調べてみました。

↓こんな感じのVisualForceページを書きます。

<apex:page >
    <!-- sessionIDを取得する -->
    <script type="text/javascript">
        var __sfdcSessionId = '{!GETSESSIONID()}';
    </script>
   
    <!-- Salesforceのjsライブラリをインクルードする -->
    <script src="../../soap/ajax/22.0/connection.js" type="text/javascript"></script>
    <script src="../../soap/ajax/22.0/apex.js" type="text/javascript"></script>
   
    <!-- ついで調査 googleからjquery1.5系最新を頂く -->
    <script type="text/javascript" src="http://www.google.com/jsapi"></script>
    <script type="text/javascript">google.load("jquery", "1.5");</script>
   
   
    <script type="text/javascript">   
       
       function callSayhello(){
            var result = sforce.apex.execute("HelloWorld","sayHello",{a:'いぬずら'});
            alert(result);
       }
       function queryAccount(){
            var state = {
          output : document.getElementById("output"),
                startTime : new Date().getTime()
           };
           var callback = {
                onSuccess: setResults,
                onFailure: queryError,
                source: state
            };
            sforce.connection.query(
                  "Select Name From Account",callback);
        }
       
        function setResults(result,source){
            var output = "";
            var records = result.getArray('records');
            
            for (var i = 0; i < records.length; i++) {
                 var account = records[i];
                 output += account.Name + "<br>";
            }
            source.output.innerHTML = output;
            //$('#output').html(output);
        }
       
        function queryError(error, source){
             alert(error);
        }
   
    </script>
   
    <input type="button" value="callSayHello" onclick="callSayhello();" />
    <input type="button" value="queryAccount" onclick="queryAccount();" />
   
    <div id="output"></div>
</apex:page>




ここではcallSayhello()が前回JavaからアクセスしたApexクラスのメソッドに
アクセスしています。
↓結果

Ws000000

別にむずかしいことはないと思いますが、
queryAccount()の中で、変数stateにJSONを設定しています。
そんで、これを変数callbackのJSONに入れ込んどきます。

こうすることでcallBackされるsetResultsのsourceのoutputプロパティに関数
document.getElementById("output")が入ってるので、
source.output.innerHTMLで<div id="output"></div>にアクセスできます。

↓queryAccount()の結果
Ws000002

ついでと言っちゃなんですが、
JQueryのライブラリも普通にVisualForceに読み込めます。
まぁ当然ですね。

source.output.innerHTMLの下の行は、
同じことをJQueryを使ってやってます。
こっち使えば、stateの設定など必要ないですね!

さすがJQuery!

ではまた。

 

Force.com ApexでUnicode \uxxxx を文字に変換

Force.comのApexのコードからFacebookのGraph APIにアクセスしてJSONのデータを入手したところ、日本語の文字がすべて、\uxxxx の形になって返ってくる。

たとえば、私の作ったFacebook Page 「東京多摩地区・自転車ガイド」の情報を見ると、こんな感じ。

{
   "id": "136781559729514",
   "name": "\u6771\u4eac\u591a\u6469\u5730\u533a\u30fb\u81ea\u8ee2\u8eca\u30ac\u30a4\u30c9",
   "picture": "http://profile.ak.fbcdn.net/hprofile-ak-snc4/203479_136781559729514_3670300_s.jpg",
   "link": "http://www.facebook.com/pages/\u0025E6\u00259D\u0025B1\u0025E4\u0025BA\u0025AC\u0025E5\u0025A4\u00259A\u0025E6\u002591\u0025A9\u0025E5\u00259C\u0025B0\u0025E5\u00258C\u0025BA\u0025E8\u002587\u0025AA\u0025E8\u0025BB\u0025A2\u0025E8\u0025BB\u00258A\u0025E3\u002582\u0025AC\u0025E3\u002582\u0025A4\u0025E3\u002583\u002589/136781559729514",
   "likes": 12,
   "category": "Community",
   "description": "\u30b9\u30dd\u30fc\u30c4\u81ea\u8ee2\u8eca\u611b\u597d\u5bb6\u306e\u305f\u3081\u306e\u591a\u6469\u5730\u533a\u304b\u3089\u81ea\u8d70\u3057\u3066\u884c\u3051\u308b\u7bc4\u56f2\u306e\u304a\u52e7\u3081\u30b9\u30dd\u30c3\u30c8\u3092\u7d39\u4ecb\u3057\u307e\u3059\u3002",
   "can_post": true
}

これは正しいのか?と調べたところ、JSONのルールでは、Unicodeは上記形式で表現するのが正道だということでした。

さて、この形式を普通の文字にApex上で変換するメソッドが無いか調べたが、見当たらない。かろうじて、StringのfromCharArrayというメソッドでIntegerの配列から文字列を作れる事はわかった。
結局、Javaの例やあちこちのサンプルコードを参考にして以下のようなサービスメソッドを作りました。
メソッド decUtf です。
[APEXコード]
     //\uxxxx系をdeescapeする
    public static String decUtf(String s) {
        if (s == null) return null;
        Pattern p = Pattern.compile('\\\\u[0-9a-f]{4}');
        Matcher m = p.matcher(s);
        String sb = '';
        Integer st = 0;
        while (m.find()) {
            String chars = m.group().substring(2, 6);
            if(m.start() -st>0){
                sb = sb + s.substring(st,m.start());
                system.debug(sb);
            }
            st = m.end();
            sb = sb + String.fromCharArray(hexToInt(chars));
        }
        if(s.length()-st>0){
            sb = sb + s.substring(st,s.length());
        }
        return sb;
    }

    private static Map<String,Integer> hexMap = new Map<String,Integer>();
    static {
        hexMap.put('0',0);
        hexMap.put('1',1);
        hexMap.put('2',2);
        hexMap.put('3',3);
        hexMap.put('4',4);
        hexMap.put('5',5);
        hexMap.put('6',6);
        hexMap.put('7',7);
        hexMap.put('8',8);
        hexMap.put('9',9);
        hexMap.put('A',10);
        hexMap.put('B',11);
        hexMap.put('C',12);
        hexMap.put('D',13);
        hexMap.put('E',14);
        hexMap.put('F',15);
        hexMap.put('a',10);
        hexMap.put('b',11);
        hexMap.put('c',12);
        hexMap.put('d',13);
        hexMap.put('e',14);
        hexMap.put('f',15);
    }

    public static List<Integer> hexToInt(String hex) {
        List<Integer> retVal = new List<Integer>();
        for(Integer i=0;i<hex.length();i+=4) {
            retVal.add(hexMap.get(hex.substring(i,i+1)) * 16 * 16 * 16 +hexMap.get(hex.substring(i+1,i+2)) * 16 * 16
             +hexMap.get(hex.substring(i+2,i+3)) * 16 + hexMap.get(hex.substring(i+3,i+4)));
        }
        return retVal;
    }

引数の文字列の中から、\uxxxxの形式の部分を正規表現で抜き出して、文字に変換します。

いかかでしょうか?

Force.com研修メルマガQ&A集:ファイルのアップロード

○ファイルアップロード機能

 標準オブジェクトDocumentとして、アップロードされたファイルを登録します。
 (今回はCSVファイルをアップロードして検証しました)

 以下サンプルコード
 Visualforceタグの「inputFile」を使うとアップロードボタンができます。
  <apex:inputFile value="{!document.body}" filename="{!document.name}"/>
 Apexクラス
  ・登録
  Document d = (Document) controller.getRecord();
  insert d;
  ・ファイル内容出力
  String t = d.body.toString();
  ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.INFO, t));

なお、弊社提供サービス
salesforce.com社のForce.com環境を使用した
不動産業向け顧客管理、営業支援システム 「顧きゃく録」では、
複数のレコードをリスト化してPDFファイルで保存、印刷を行っています。

これからセールスフォース上にアプリケーション構築をお考えの方は
「顧きゃく録」開発時のノウハウをそのまま内容に反映したForce.com研修
是非、ご受講ください!!

Force.com研修受講者様へのメルマガを少しだけ公開します

ケーピーエスが行っているForce.com研修の受講者様に向けて

ほぼ月一回のペースで、
Force.comに関するマメ知識をお届けするメルマガを発行しています。

その内容の中から、厳選したコンテンツを
本ブログにて公開いたします! 近日スタートですので、お楽しみに!!

Force.com Sitesから呼び出されたコントローラーで時刻がGMTになる

Force.comのsites機能(最近はSiteforceという体系にまとめられるようですが)を使用すると、salesforceのクラウド環境からインターネットへホームページを公開できます。

構築するページは、Force.comの一般と同様、Visualforceページと、Apexのコントローラーで開発することができます。

実際にやってみたところ、なぜか、公開したページからアクセスした場合、コントローラー内で時刻を扱うと、GMT扱いになってしまう事態が発生しました。

salesforceでは、ユーザーごとにタイムゾーンを設定することができます。しかし、sites機能で使用されるユーザーは通常の「設定→ユーザーの管理→ユーザー」と辿った先の一覧には出てきません。

色々、調べてようやくわかったのは、以下の手順です。

[設定]→[開発]→[サイト]→[サイトの表示ラベル]→[公開アクセス設定]→
[このプロファイルに属するユーザの参照]

ここで、「サイトゲストユーザxxx」というユーザーにたどり着くので、このユーザーを編集して、タイムゾーンを(GMT+09:00)日本標準時(Asia/Tokyo)に設定して解決。

- * - * - *- * - * - * - *- * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * -
     salesforce force.comを学ぶ!
     Force.comの実践的開発研修です。  Force.com研修のケーピーエス
- * - * - * - * - * - * - * - *- * - * - * - * - * - *- * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - *

Apex soqlでリッチテキストエリアのフィールドがNo such columnと言われる

select xxx__c from yyy__c

というSOQLを発行すると、なぜか、

System.QueryException: No such column 'xxx__c' on entity 'yyy__c'. If you are attempting to use a custom field, be sure to append the '__c' after the custom field name. Please reference your WSDL or the describe call for the appropriate names.

というエラーになってしまう。
yyy__cというオブジェクトにxxx__cというカラムは無いと言ってる。

しかし、同じSOQLをシステムログから流してやると問題なく動きます。ちなみに、xxx__cというカラムの型はリッチテキストエリアです。

と、いう問題でハマリかけましたが、原因が判明しました。
上記エラーを起こしているクラスのAPI バージョンの問題でした。

実装したクラスは、随分と昔に作成したもので、API バージョンが16でした。

リッチテキストエリアは、Spring ‘10 から出てきたものなので、API バージョン18以上のクラスでないと、無いものとみなされてしまうようです。

とりあえず、API バージョンを21に上げて、解決。

ちなみに、クラスのAPI バージョン18の設定は、Apex クラスの編集画面のVersion Settingsタブから設定できます。

- * - * - *- * - * - * - *- * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * -
     salesforce force.comを学ぶ!
     Force.comの実践的開発研修です。  Force.com研修のケーピーエス
- * - * - * - * - * - * - * - *- * - * - * - * - * - *- * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - *

Apex Transientキーワード

ケーピーエスのForce.com研修でApexのTransientキーワードの件を説明する部分があったのですが、少々内容が専門的すぎるので、次回から割愛するつもりでいます。
そこで、とりあえず、こちらのブログの方にポイントを書いておくことにします。
まずは、マニュアルで説明されている例。

[コントローラークラス]
public class ExampleController {     DateTime t1;     transient DateTime t2;     public String getT1() {         if (t1 == null) t1 = System.now();         return '' + t1;     }     public String getT2() {         if (t2 == null) t2 = System.now();         return '' + t2;     } }

上記例では、コントローラークラスにt1,t2の二つのインスタンス変数が設定されています。
t2の方にのみ transient キーワードが設定されています。
このコントローラーを、以下のVisualforceページでアクセスします。

[Visualforceページ]

<apex:page controller="ExampleController">
  T1: {!t1} <br/>
  T2: {!t2} <br/>
  <apex:form>
    <apex:commandLink value="refresh"/>
  </apex:form>
</apex:page>

このページをリフレッシュすると、 transient キーワードを設定したt2のみが更新されるはずです。

Force.comでは、呼ばれるコントローラが同一の場合、コントローラのインスタンス変数はリクエストをまたがって保管されます。これは、Force.comのフレームワークが、コントローラのインスタンスをリクエスト終了時にシリアラズ化して、ブラウザー側に保管し、それを次回のリクエスト時に再構築してくれるからです。

一方、 transient キーワードは、このシリアライズ化を行わない宣言になります。よって、上記の変数t2は、毎回リクエスト開始時点ではnullなので、表示が更新されるわけです。

さて、では transient キーワードは、どんな時に使うものでしょうか?

①パフォーマンス上の観点

コントローラーの変数として、量の大きいオブジェクト(エントリーの多いリストとかマップとか)を持っていると、リクエスト終了時に、Force.comのフレームワークはこれらをすべてシリアライズ化してブラウザーに送信します。

あえて、リクエスト間で保管する必要の無い変数に関しては、transient キーワードを設定することで、このオーバーヘッドを無くすことができます。

②シリアライズできないオブジェクトをインスタンス変数に持つ場合

Apex環境では、シリアライズ不可のタイプのオブジェクトがあります。

メタデータ系のタイプ等がこれに該当します。

例えば、コントローラーのインスタンス変数に以下のように定義した場合。

public class ExampleController {

private Schema.Describesobjectresult dsc = Task.sobjectType.getDescribe();

        :

以下のようなエラーになります。

System.SerializationException: Not Serializable: Schema.DescribeSObjectResult

これは、Schema.Describesobjectresultというタイプはシリアライズ不可のタイプである事を示しています。

この場合変数の定義に transient 加える事で、エラーを回避できます。

public class ExampleController {

private transient Schema.Describesobjectresult dsc = Task.sobjectType.getDescribe();

    :

- * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * -
     ネットのパワーを不動産業へ生かす!
     不動産業向け顧客管理・営業支援システム  顧きゃく録!
- * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - *

« 2011年5月 | トップページ | 2011年7月 »