JSMPPというHELL

どうやら、すべてのプログラマーの生活の中で、彼がSMSメッセージを送信する方法を学ぶことが必要になるときが来ます。 昨日、そんな瞬間が来ました。 この必要性は、広告メールやその他のスパムとはまったく関係がないことをすぐに言わなければなりません。 SMS-kiは、機器の監視中に検出されたイベントへの応答の一部として、純粋に平和的な目的で送信する必要がありました。



このようなニュースレターを作成することの重要性を過大評価することは困難です。 確かに、事故の通知をEメールの連絡先に送信することにより、私たちは即座の対応を期待することはできません。 受信者がいつメールを読むかはわかりません。 SMSははるかに高速に配信されます。



当社は、 SMPPサービスの独自の実装を長く成功裏に使用してきました。Javaで既製のSMPPクライアントを使用するという考えは、理にかなっているように思えました。 「java smpp client」という言葉をグーグル検索バーに勇気を持って駆り立てて、すぐに必要なライブラリを見つけました。 次に何が起こったのかについて、今日の投稿に語ります。



私はmavenを使用してプロジェクトをビルドするため、まず、サイトからライブラリの最新バージョンをダウンロードし、次のコマンドでローカルリポジトリにアップロードしました。



mvn install:install-file -Dfile=jsmpp-2.1.0.jar -DgroupId=org.jsmpp -DartifactId=smpp -Dversion=2.1.0 -Dpackaging=jar
      
      





次に、pomファイルを作成しました。



pom.xml
 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.acme.ae.tests.smpp</groupId> <artifactId>SMPPTest</artifactId> <version>1.0.0</version> <packaging>jar</packaging> <name>smppTest-${project.version}</name> <url>http://maven.apache.org</url> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <resource_dir>${project.basedir}/src/main/resources</resource_dir> </properties> <build> <finalName>${project.name}-${project.version}</finalName> <resources> <resource> <directory>${resource_dir}</directory> </resource> </resources> </build> <dependencies> <dependency> <groupId>org.jsmpp</groupId> <artifactId>smpp</artifactId> <version>2.1.0</version> </dependency> </dependencies> </project>
      
      







テストアプリケーションの準備は非常に標準的でした。



Test.java
 package com.acme.ae.tests.smpp; public class Test { private void start() throws IOException { } private void stop() throws IOException { } private void test() { } public static void main(String[] args) { Test t = new Test(); try { try { t.start(); t.test(); } finally { t.stop(); } } catch (Exception e) { System.out.println(e.toString()); } } }
      
      







その後、開発者から親切に提供された例の研究が行われました。 翻訳仕様は 、インスピレーションの第2のソースとして使用されました。



サーバーに接続するために、上記の例に完全に従って、次のコードが使用されました。



サーバー接続
  ... private SMPPSession session = null; private void start() throws IOException { session = new SMPPSession(); session.connectAndBind(SMPP_IP, SMPP_PORT, new BindParameter( BindType.BIND_TX, SMPP_LOGIN, SMPP_PASS, "cp", TypeOfNumber.UNKNOWN, NumberingPlanIndicator.UNKNOWN, null)); } private void stop() throws IOException { if (session != null) { session.unbindAndClose(); } } ...
      
      







パラメータの一部の目的は、コンテキストから非常に明確です。 残りのパラメーターは、サンプルコードから変更されていません。 メッセージを送信するだけなので、BindType.BIND_TXを使用します。



例から取られたメッセージ転送コード(またはエンコードタスク)は、コンパイルを拒否しました。



 ... new GeneralDataCoding(Alphabet.ALPHA_DEFAULT, MessageClass.CLASS1, false) ...
      
      





ダウンロードしたライブラリ(利用可能な最新バージョン2.1.0)のソースとGitHubのソースを比較した後、開発者は、何らかの理由でデザイナーの署名を変更したことがわかりました。



 - public GeneralDataCoding(boolean compressed, boolean containMessageClass, - MessageClass messageClass, Alphabet alphabet) { + public GeneralDataCoding(Alphabet alphabet, MessageClass messageClass, + boolean compressed) throws IllegalArgumentException { ... }
      
      





古いバージョンを使用したため、コードを調整する必要がありました(コード内の送信者と受信者のアドレスが変更されました)。



ショートメッセージ転送
  ... private static TimeFormatter timeFormatter = new AbsoluteTimeFormatter(); private void test() throws PDUException, ResponseTimeoutException, InvalidResponseException, NegativeResponseException, IOException { String messageId = session.submitShortMessage( "CMT", TypeOfNumber.ALPHANUMERIC, NumberingPlanIndicator.UNKNOWN, "ACME", TypeOfNumber.INTERNATIONAL, NumberingPlanIndicator.ISDN, "7XXXXXXXXXX", new ESMClass(), (byte)0, (byte)1, timeFormatter.format(new Date()), null, new RegisteredDelivery(SMSCDeliveryReceipt.DEFAULT), (byte)0, new GeneralDataCoding( false, false, MessageClass.CLASS1, Alphabet.ALPHA_DEFAULT), (byte)0, "jSMPP simplify SMPP on Java platform".getBytes()); System.out.println("Message submitted, message_id is " + messageId); } ...
      
      







実行のためのコードの実行は失敗しました。 SMPPSessionを作成しようとすると、例外がスローされました。



 Exception in thread "main" java.lang.NoClassDefFoundError: org/slf4j/LoggerFactory at org.jsmpp.session.AbstractSession.<clinit>(AbstractSession.java:51) at com.amfitel.m2000.ae.tests.smpp.Test.start(Test.java:56) at com.amfitel.m2000.ae.tests.smpp.Test.main(Test.java:179) Caused by: java.lang.ClassNotFoundException: org.slf4j.LoggerFactory at java.net.URLClassLoader$1.run(URLClassLoader.java:202) at java.security.AccessController.doPrivileged(Native Method) at java.net.URLClassLoader.findClass(URLClassLoader.java:190) at java.lang.ClassLoader.loadClass(ClassLoader.java:307) at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301) at java.lang.ClassLoader.loadClass(ClassLoader.java:248) ... 3 more
      
      





実際、 GettingStartedにはSLF4Jの使用についての平均的な言及がありました 。 pom.xmlに依存関係を追加する必要がありました。



  ... <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.6.1</version> </dependency> ...
      
      





バージョン番号(GettingStartedで述べた1.4.3ではなく、1.6.1)は、GitHubのpomファイルから取得されました。 これで、不快なエラーがログに書き込まれ始めましたが、少なくともサーバーとの接続は確立されました。



 SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder". SLF4J: Defaulting to no-operation (NOP) logger implementation SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
      
      





発言
この問題は、 GitHubのソースからも取得した別の依存関係を追加することで解決しました。



  ... <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.6.1</version> </dependency> ...
      
      





追加すると、log4j.propertiesで指定された設定に従って、通常のログが形成され始めました。 ヒントを提供してくれたGoogolplexに感謝します。



メッセージの送信も成功しましたが、文字ではなく「正方形」が電話に届きました。 WireShark助けになりました:



画像



SMPPサーバー開発者は、ラテン語での通常のメッセージ送信では、データコーディングは0を送信する必要があると主張しました。 ソースコードの短い瞑想の後、問題は解決されました。



  ... new GeneralDataCoding( false, false, - MessageClass.CLASS1, + MessageClass.CLASS0, Alphabet.ALPHA_DEFAULT) ...
      
      





今では、キリル文字を使用して長いメッセージとメッセージを送信する方法を学ぶことが残っています。 長いメッセージを送信するために、開発者からの例で提案されているアプローチを使用して、MESSAGE_PAYLOADで賢くはなりませんでした。



キリル文字を送信するには、エンコードを変更し、メッセージをUCS-2に変換する必要がありました。 実用的な観点から、後者はUTF-16エンコーディングのバイトシーケンスとしてテキスト表現をもたらしました(UCS-2とUTF-16の違いについてはこちらをご覧ください )。 その結果、メッセージ送信コードは次のようになりました。



長いキリル文字メッセージの送信
 GeneralDataCoding coding = new GeneralDataCoding( false, false, MessageClass.CLASS0, Alphabet.ALPHA_UCS2); final int totalSegments = 3; Random random = new Random(); OptionalParameter sarMsgRefNum = OptionalParameters.newSarMsgRefNum((short)random.nextInt()); OptionalParameter sarTotalSegments = OptionalParameters.newSarTotalSegments(totalSegments); for (int i = 0; i < totalSegments; i++) { final int seqNum = i + 1; String message = " " + seqNum + " of " + totalSegments + " "; OptionalParameter sarSegmentSeqnum = OptionalParameters.newSarSegmentSeqnum(seqNum); String messageId = session.submitShortMessage( "CMT", TypeOfNumber.ALPHANUMERIC, NumberingPlanIndicator.UNKNOWN, "ACME", TypeOfNumber.INTERNATIONAL, NumberingPlanIndicator.ISDN, "7XXXXXXXXXX", new ESMClass(), SMPP_PROTOCOL_ID, // (byte)0 SMPP_PRIORITY_FLAG, // (byte)1 null, null, new RegisteredDelivery(SMSCDeliveryReceipt.DEFAULT), SMPP_REP_IF_P_FLAG, // (byte)0 coding, (byte)0, message.getBytes(Charset.forName("UTF-16")), sarMsgRefNum, sarSegmentSeqnum, sarTotalSegments); System.out.println("Message submitted, message_id is " + messageId); }
      
      







この記事を締めくくりますが、この記事はJSMPPを批判するために書かれたものではないことを強調したいと思います。 SMPP仕様はそれ自体が複雑であり、ライブラリの開発者は可能な限りすべてを使用して、その使用プロセスをできるだけ簡単にしました。 このようなプロジェクトの小さな欠陥は避けられません。



この投稿は、誰かをscる目的ではなく、他の人が彼自身が通った同じ熊手で歩きやすくするという願望に基づいて書かれました。 私の不平をあまり真剣に受け止めないでください。




All Articles