About neohope

一直在努力,还没想过要放弃...

微软MVC实现REST风格编程

1、总体来说很简单,首先新建一个MVC框架的项目,模板选择WebAPI,这样就搞定80%了。

2、WebApiConfig.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Http;

namespace UrlToPngWebAPI
{
    public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );

            config.EnableSystemDiagnosticsTracing();
        }
    }
}

3、RouteConfig.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;

namespace UrlToPngWebAPI
{
    public class RouteConfig
    {
        public static void RegisterRoutes(RouteCollection routes)
        {
            routes.MapRoute(
                name: "Default",
                url: "{controller}/{action}/{id}",
                defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
            );
        }
    }
}

4、请求结构
Web2PNGRequest.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Newtonsoft.Json;

namespace UrlToPngWebAPI.Models
{
    public class Web2PNGRequest
    {
        [JsonProperty]
        public String WebURL { get; set; }
        [JsonProperty]
        public String HeaderPath { get; set; }
        [JsonProperty]
        public String FooterPath { get; set; }
    }
}

5、返回结构
Web2PNGResponse.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using Newtonsoft.Json;

namespace UrlToPngWebAPI.Models
{
    public class Web2PNGResponse
    {
        [JsonProperty]
        public int ErrorCode { get; set; }
        [JsonProperty]
        public String ErrorInfo { get; set; }
        [JsonProperty]
        public String PNGPath { get; set; }
    }
}

6、Controller
Url2PNGController.cs

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Web.Helpers;
using System.Web.Http;
using Newtonsoft.Json;
using UrlToPngCsTest;
using UrlToPngWebAPI.Models;
using UrlToPngWebAPI.Pulgins;

namespace UrlToPngWebAPI.Controllers
{
    public class Url2PNGController : ApiController
    {
        // 返回输入参数示例
        public HttpResponseMessage Get()
        {
            Web2PNGRequest req = new Web2PNGRequest();
            req.WebURL = "webURL";
            req.HeaderPath = "headerPath";
            req.FooterPath = "footerPath";

            String jsonString = JsonConvert.SerializeObject(req);
            HttpResponseMessage result = new HttpResponseMessage { Content = new StringContent(jsonString, Encoding.GetEncoding("UTF-8"), "application/json") };
            return result;
        }

        // GET
        public HttpResponseMessage Get(String WebURL, String HeaderPath, String FooterPath)
        {
            UrlToPng4Web.InitUrlTOPng4CS();

            Web2PNGRequest req = new Web2PNGRequest();
            req.WebURL = WebURL;
            req.HeaderPath = HeaderPath;
            req.FooterPath = FooterPath;
            Web2PNGResponse rsp = UrlToPng4Web.UrlToPNG(req);
            
            String jsonString = JsonConvert.SerializeObject(rsp);
            HttpResponseMessage result = new HttpResponseMessage { Content = new StringContent(jsonString, Encoding.GetEncoding("UTF-8"), "application/json") };
            return result;
        }

        // POST
        public HttpResponseMessage Post(Web2PNGRequest req)
        {
            UrlToPng4Web.InitUrlTOPng4CS();

            Web2PNGResponse rsp = UrlToPng4Web.UrlToPNG(req);

            String jsonString = JsonConvert.SerializeObject(rsp);
            HttpResponseMessage result = new HttpResponseMessage { Content = new StringContent(jsonString, Encoding.GetEncoding("UTF-8"), "application/json") };
            return result;
        }
    }
}

Jersey实现REST风格编程

首先是服务端:
1、增加服务类

package com.neohope.jessery.test;

import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;

/**
 * Created by Hansen
*/
@Path("neotest")
@Produces(MediaType.APPLICATION_JSON)
public class NeoTest {
    @GET
    @Path("/Add")
    public String Add(@QueryParam("a") int a,@QueryParam("b") int b) {
        return "{\"c\":" + (a+b) + "}";
    }

    @GET
    @Path("/SayHiTo")
    public String sayHiTo(@QueryParam("name") String name) {
        return "{\"msg\":\"hi " + name + "\"}";
    }
}

2、在web.xml里增加配置

    <servlet>
        <servlet-name>Jersey Web Application</servlet-name>
        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
        <init-param>
            <param-name>jersey.config.server.provider.packages</param-name>
            <param-value>com.neohope.jessery.test</param-value>
        </init-param>
    </servlet>

    <servlet-mapping>
        <servlet-name>Jersey Web Application</servlet-name>
        <url-pattern>/Rest/*</url-pattern>
    </servlet-mapping>

3、然后是客户端

package com.neohope.jessery.test;

import org.glassfish.jersey.client.ClientConfig;

import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.Response;

/**
 * Created by Hansen
 */
public class ClientTest {
    public static void main(String[] args) {
        ClientConfig clientConfig = new ClientConfig();
        Client client = ClientBuilder.newClient(clientConfig);

        WebTarget target = client.target("http://localhost:8080/Rest/neotest/" + "Add");
        Response response = target.queryParam("a", 1).queryParam("b", 2).request().get();
        System.out.println(response.getStatus());
        System.out.println(response.readEntity(String.class));
        response.close();

        WebTarget target1 = client.target("http://localhost:8080/Rest/neotest/" + "SayHiTo");
        Response response1 = target1.queryParam("name", "neohope").request().get();
        System.out.println(response1.getStatus());
        System.out.println(response1.readEntity(String.class));
        response1.close();
    }
}

WCF自托管服务

一般有两种方式实现:

1、通过代码设置直接实现

using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;
using System.ServiceModel.Description;
using System.Text;
using WcfTest;

namespace WcfHosting
{
    internal class Program
    {
        private static void Main(string[] args)
        {
            using (ServiceHost host = new ServiceHost(typeof (SoapService)))
            {
                host.AddServiceEndpoint(typeof(ISoapService), new WSHttpBinding(), "http://127.0.0.1:1234/neohope");

                if (host.Description.Behaviors.Find<ServiceMetadataBehavior>() == null)
                {
                    ServiceMetadataBehavior behavior = new ServiceMetadataBehavior();
                    behavior.HttpGetEnabled = true;
                    behavior.HttpGetUrl = new Uri("http://127.0.0.1:9999/neohope/metadata");
                    host.Description.Behaviors.Add(behavior);
                }

                host.Opened += delegate
                {
                    Console.WriteLine("service started, press enter to exit.");
                };
                host.Open();
                Console.Read();
            }
        }
    }
}

2、通过配置文件实现

using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;
using System.ServiceModel.Description;
using System.Text;
using WcfTest;

namespace WcfHosting
{
    internal class Program
    {
        private static void Main(string[] args)
        {
            using (ServiceHost host = new ServiceHost(typeof (SoapService)))
            {
                host.Opened += delegate
                {
                    Console.WriteLine("service started, press enter to exit.");
                };
                host.Open();
                Console.Read();
            }
        }
    }
}

app.config

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.serviceModel>
    <behaviors>
      <serviceBehaviors>
        <behavior name="metadataBehavior">
          <serviceMetadata httpGetEnabled="true" httpGetUrl="http://127.0.0.1:9999/neohope/metadata" />
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <services>
      <service behaviorConfiguration="metadataBehavior" name="WcfTest.SoapService">
        <endpoint address="http://127.0.0.9999/neohope" binding="wsHttpBinding"
                  contract="WcfTest.ISoapService" />
      </service>
    </services>
  </system.serviceModel>
</configuration>

WCF修改SOAP命名空间

有三个地方可以修改SOAP的命名空间:

1、ServiceContract

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;
using System.Web.Configuration;

namespace WcfTest
{
    // NOTE: You can use the "Rename" command on the "Refactor" menu to change the interface name "IService1" in both code and config file together.
    [ServiceContract(Name="SoapService",Namespace="http://wcftest.neohope.org")]
    public interface ISoapService
    {
        [OperationContract]
        [WebInvoke(Method = "GET", UriTemplate = "User/Get/{uid}", BodyStyle = WebMessageBodyStyle.Bare, ResponseFormat = WebMessageFormat.Json, RequestFormat = WebMessageFormat.Json)]
        NUser GetUser(String uid);

        [OperationContract]
        [WebInvoke(Method = "POST", UriTemplate = "/User", BodyStyle = WebMessageBodyStyle.Bare, ResponseFormat = WebMessageFormat.Json, RequestFormat = WebMessageFormat.Json)]
        string UpdateUser(NUser newuser);


        [OperationContract]
        [WebInvoke(Method = "PUT", UriTemplate = "/User", BodyStyle = WebMessageBodyStyle.Bare, ResponseFormat = WebMessageFormat.Json, RequestFormat = WebMessageFormat.Json)]
        string AddUser(NUser newuser);


        [OperationContract]
        [WebInvoke(Method = "DELETE", UriTemplate = "/User", BodyStyle = WebMessageBodyStyle.Bare, ResponseFormat = WebMessageFormat.Json, RequestFormat = WebMessageFormat.Json)]
        string DeleteUser(String uid);
    }

    [ServiceBehavior(Name = "SoapService", Namespace = "http://wcftest.neohope.org")]
    [DataContract]
    public class NUser
    {
        string uid = "";
        string uname = "";
        string usex = "";

        [DataMember]
        public String Uid
        {
            get { return uid; }
            set { uid = value; }
        }

        [DataMember]
        public string UName
        {
            get { return uname; }
            set { uname = value; }
        }

        [DataMember]
        public string USex
        {
            get { return usex; }
            set { usex = value; }
        }
    }
}

2、服务实现

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;

namespace WcfTest
{
    [ServiceBehavior(Name = "SoapService", Namespace = "http://wcftest.neohope.org")]
   public class SoapService : ISoapService
    {
        static List<NUser> users = new List<NUser>();

        static SoapService()
        {
            NUser aNUser = new NUser();
            aNUser.Uid = "001";
            aNUser.UName = "张三";
            aNUser.USex = "男";
            users.Add(aNUser);
        }

        public NUser GetUser(string uid)
        {
            foreach (NUser nuser in users)
            {
                if (nuser.Uid == uid)
                {
                    return nuser;
                }
            }

            return null;
        }

        public string UpdateUser(NUser newuser)
        {
            if (newuser == null)
            {
                return "nuewuser is null";
            }

            foreach (NUser nuser in users)
            {
                if (nuser.Uid == newuser.Uid)
                {
                    nuser.UName = newuser.UName;
                    newuser.USex = nuser.USex;
                    return "User updated";
                }
            }

            return "User not found";
        }

        public string AddUser(NUser newuser)
        {
            if (newuser == null)
            {
                return "nuewuser is null";
            }

            foreach (NUser nuser in users)
            {
                if (nuser.Uid == newuser.Uid)
                {
                    nuser.UName = newuser.UName;
                    newuser.USex = nuser.USex;
                    return "User updated";
                }
            }

            users.Add(newuser);

            return "User added";
        }

        public string DeleteUser(String uid)
        {
            bool bFound = false;
            for(int i=users.Count-1;i>=0;i--)
            {
                if (users[i].Uid == uid)
                {
                    bFound = true;
                    users.RemoveAt(i);
                }
            }

            if (!bFound)
            {
                return "User Not Found";
            }
            else
            {
                return "User Deleted";
            }
        }
    }
}

3、Web.config

<?xml version="1.0"?>
<configuration>
  
  <system.web>
    <compilation debug="true" targetFramework="4.0" />
  </system.web>
  
  <system.serviceModel>

    <services>
      <service name="WcfTest.SoapService">
        <endpoint binding="webHttpBinding" contract="WcfTest.ISoapService" bindingNamespace="http://wcftest.neohope.org"/>
      </service>
    </services>
 
    <behaviors>
      <endpointBehaviors>
      </endpointBehaviors>
      
      <serviceBehaviors>
        <behavior name="">
          <serviceMetadata httpGetEnabled="true" />
          <serviceDebug includeExceptionDetailInFaults="true" />
        </behavior>
      </serviceBehaviors>
    </behaviors>
    
    <serviceHostingEnvironment aspNetCompatibilityEnabled="false" multipleSiteBindingsEnabled="true" />
  </system.serviceModel>
  
  <system.webServer>
    <modules runAllManagedModulesForAllRequests="true"/>
    <directoryBrowse enabled="true"/>
  </system.webServer>
</configuration>

WCF实现REST风格编程

1、首先需要一个ServiceContract

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;

namespace WcfTest
{
    [ServiceContract]
    public interface IRestService
    {
        [OperationContract]
        [WebInvoke(Method = "GET", UriTemplate = "User/Get/{uid}", BodyStyle = WebMessageBodyStyle.Bare, ResponseFormat = WebMessageFormat.Json, RequestFormat = WebMessageFormat.Json)]
        NUser GetUser(String uid);


        [OperationContract]
        [WebInvoke(Method = "POST", UriTemplate = "/User", BodyStyle = WebMessageBodyStyle.Bare, ResponseFormat = WebMessageFormat.Json, RequestFormat = WebMessageFormat.Json)]
        string UpdateUser(NUser newuser);


        [OperationContract]
        [WebInvoke(Method = "PUT", UriTemplate = "/User", BodyStyle = WebMessageBodyStyle.Bare, ResponseFormat = WebMessageFormat.Json, RequestFormat = WebMessageFormat.Json)]
        string AddUser(NUser newuser);


        [OperationContract]
        [WebInvoke(Method = "DELETE", UriTemplate = "/User", BodyStyle = WebMessageBodyStyle.Bare, ResponseFormat = WebMessageFormat.Json, RequestFormat = WebMessageFormat.Json)]
        string DeleteUser(String uid);
    }

    [DataContract]
    public class NUser
    {
        string uid = "";
        string uname = "";
        string usex = "";

        [DataMember]
        public String Uid
        {
            get { return uid; }
            set { uid = value; }
        }

        [DataMember]
        public string UName
        {
            get { return uname; }
            set { uname = value; }
        }

        [DataMember]
        public string USex
        {
            get { return usex; }
            set { usex = value; }
        }
    }
}


2、然后需要实现服务

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Activation;
using System.ServiceModel.Web;
using System.Text;

namespace WcfTest
{
    public class RestService : IRestService
    {
        static List<NUser> users = new List<NUser>();

        static RestService()
        {
            NUser aNUser = new NUser();
            aNUser.Uid = "001";
            aNUser.UName = "张三";
            aNUser.USex = "男";
            users.Add(aNUser);
        }

        public NUser GetUser(string uid)
        {
            foreach (NUser nuser in users)
            {
                if (nuser.Uid == uid)
                {
                    return nuser;
                }
            }

            return null;
        }

        public string UpdateUser(NUser newuser)
        {
            if (newuser == null)
            {
                return "nuewuser is null";
            }

            foreach (NUser nuser in users)
            {
                if (nuser.Uid == newuser.Uid)
                {
                    nuser.UName = newuser.UName;
                    newuser.USex = nuser.USex;
                    return "User updated";
                }
            }

            return "User not found";
        }

        public string AddUser(NUser newuser)
        {
            if (newuser == null)
            {
                return "nuewuser is null";
            }

            foreach (NUser nuser in users)
            {
                if (nuser.Uid == newuser.Uid)
                {
                    nuser.UName = newuser.UName;
                    newuser.USex = nuser.USex;
                    return "User updated";
                }
            }

            users.Add(newuser);

            return "User added";
        }

        public string DeleteUser(String uid)
        {
            bool bFound = false;
            for(int i=users.Count-1;i>=0;i--)
            {
                if (users[i].Uid == uid)
                {
                    bFound = true;
                    users.RemoveAt(i);
                }
            }

            if (!bFound)
            {
                return "User Not Found";
            }
            else
            {
                return "User Deleted";
            }
        }
    }
}

3、修改服务的Markup

<%@ ServiceHost Language="C#" Debug="true" Service="WcfTest.RestService" CodeBehind="RestService.svc.cs" 
    Factory="System.ServiceModel.Activation.WebServiceHostFactory"%>

4、修改服务配置
添加serviceBehaviors、endpointBehaviors、service、endpoint

<?xml version="1.0"?>
<configuration>
  
  <system.web>
    <compilation debug="true" targetFramework="4.0" />
  </system.web>
  
  <system.serviceModel>
    <services>
      <service behaviorConfiguration="RestSvcBehavior" name="WcfTest.RestService">
        <endpoint binding="webHttpBinding" contract="WcfTest.IRestService" address="" behaviorConfiguration="RestEPBehavior"/>
      </service>
    </services>
 
    <behaviors>
      <endpointBehaviors>
        <behavior name="RestEPBehavior">
          <webHttp />
        </behavior>
      </endpointBehaviors>
      
      <serviceBehaviors>
        <behavior name="RestSvcBehavior">
          <serviceMetadata httpGetEnabled="true" />
          <serviceDebug includeExceptionDetailInFaults="true" />
        </behavior>
        <behavior name="">
          <serviceMetadata httpGetEnabled="true" />
          <serviceDebug includeExceptionDetailInFaults="true" />
        </behavior>
      </serviceBehaviors>
    </behaviors>
    
    <serviceHostingEnvironment aspNetCompatibilityEnabled="false" multipleSiteBindingsEnabled="true" />
  </system.serviceModel>
  
  <system.webServer>
    <modules runAllManagedModulesForAllRequests="true"/>
    <directoryBrowse enabled="true"/>
  </system.webServer>
</configuration>

5、访问服务

http://ip:port/RestService.svc/User/Get/001

JBoss EAP MessageBean的调用(Queue)

通讯流程图,其中EJB扮演了Client2的角色
jms-point-to-point-model.png

首先是Server端的开发及设置:
1、增加一个用户:

bin\add-user.bat

用户名密码随便,但要属于guest组

2、启动Server

standalone.bat -server-config=standalone-full.xml 

3、新建Queue

jboss-cli.bat --connect
jms-queue add --queue-address=neoQueue --entries=queue/neoQueue,java:jboss/exported/jms/queue/neoQueue

4、新建java项目
4.1、写一个QueueBean
MessageQueueBean.java

package com.neohope.ejb.test;

import javax.ejb.ActivationConfigProperty;
import javax.ejb.MessageDriven;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.TextMessage;

/**
 * Created by Hansen
 */
@MessageDriven(activationConfig = {
        @ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Queue"),
        @ActivationConfigProperty(propertyName = "destination", propertyValue = "queue/neoQueue"),
        @ActivationConfigProperty(propertyName = "acknowledgeMode", propertyValue = "Auto-acknowledge")
})
public class MessageQueueBean implements javax.jms.MessageListener {
    public MessageQueueBean() {
    }

    @Override
    public void onMessage(Message message) {
        try {
            System.out.println("MessageQueueBean.onMessage invoked");
            if(message instanceof TextMessage)
            {
                TextMessage textMessage = (TextMessage)message;
                System.out.println("Message: " + textMessage.getText());
            }
            else
            {
                System.out.println("Message is not a TextMessage");
            }
        }
        catch (JMSException e) {
            e.printStackTrace();
        }
    }
}

4.2、打jar包,或者自己压缩为TestEJBServer.jar

5、新建一个ear项目
5.1增加application.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<application xmlns="http://java.sun.com/xml/ns/javaee"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/application_6.xsd"
             version="6">
    <description>EAR Test</description>
    <display-name>EAR Test</display-name>
    <module>
        <ejb>TestEJBServer.jar</ejb>
    </module>
    <library-directory>lib</library-directory>
</application>

5.2打ear包,或自己压缩为TestEar.ear

5.3TestEar的结构为:

│  TestEJBServer.jar
│
├─lib
└─META-INF
        application.xml

6.通过EAP进行部署

到这里服务端已经完成了。

然后是客户端的设置:
1、通过Queue方式访问

package com.neohope.ejb.test;

import javax.jms.*;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import java.util.Hashtable;

/**
 * Created by Hansen
 */
public class TestQueueBean {
    public static void main(String[] args) throws NamingException, JMSException {

        final Hashtable jndiProperties = new Hashtable();
        jndiProperties.put(Context.INITIAL_CONTEXT_FACTORY, "org.jboss.naming.remote.client.InitialContextFactory");
        jndiProperties.put(Context.PROVIDER_URL, "remote://localhost:4447");
        final InitialContext ctx = new InitialContext(jndiProperties);

        QueueConnectionFactory factory = (QueueConnectionFactory) ctx.lookup("jms/RemoteConnectionFactory");
        QueueConnection connection = factory.createQueueConnection("user001", "user001#");
        QueueSession session = connection.createQueueSession(false, QueueSession.AUTO_ACKNOWLEDGE);

        Queue queue = (Queue) ctx.lookup("jms/queue/neoQueue");
        TextMessage msg = session.createTextMessage("Queue Test Messagee");
        QueueSender sender = session.createSender(queue);
        sender.send(msg);

        session.close();
        connection.close();
    }
}

2、通过MessageProducer方式访问

package com.neohope.ejb.test;

import javax.jms.*;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import java.util.Hashtable;

/**
 * Created by Hansen
 */
public class TestQueueBean {
    public static void main(String[] args) throws NamingException, JMSException {
        final Hashtable jndiProperties = new Hashtable();
        jndiProperties.put(Context.INITIAL_CONTEXT_FACTORY, "org.jboss.naming.remote.client.InitialContextFactory");
        jndiProperties.put(Context.PROVIDER_URL, "remote://localhost:4447");
        final InitialContext ctx = new InitialContext(jndiProperties);

        ConnectionFactory connectionFactory = (ConnectionFactory) ctx.lookup("jms/RemoteConnectionFactory");
        Destination destination = (Destination) ctx.lookup("jms/queue/neoQueue");
        Connection connection = connectionFactory.createConnection("user001", "user001#");
        Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
        MessageProducer producer = session.createProducer(destination);
        connection.start();

        TextMessage msg = session.createTextMessage("Queue Test Messagee");
        producer.send(msg);

        session.close();
        connection.close();
    }
}

JBoss EAP MessageBean的调用(Topic)

通讯流程图,其中EJB扮演了client2的角色
jms-publisher-subscriber-model.png

首先是Server端的开发及设置:
1、增加一个用户:

bin\add-user.bat

用户名密码随便,但要属于guest组

2、启动Server

standalone.bat -server-config=standalone-full.xml 

3、新建Topic

jboss-cli.bat --connect
jms-topic add --topic-address=neoTopic --entries=topic/neoTopic,java:jboss/exported/jms/topic/neoTopic

4、新建java项目
4.1、写一个TopicBean
MessageTopicBean .java

package com.neohope.ejb.test;

import javax.ejb.ActivationConfigProperty;
import javax.ejb.MessageDriven;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.TextMessage;

/**
 * Created by Hansen
 */
@MessageDriven(mappedName = "MessageTopicBean",activationConfig = {
        @ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Topic"),
        @ActivationConfigProperty(propertyName = "destination", propertyValue = "topic/neoTopic"),
        @ActivationConfigProperty(propertyName = "acknowledgeMode", propertyValue = "Auto-acknowledge")
})
public class MessageTopicBean implements javax.jms.MessageListener {
    public MessageTopicBean() {
    }

    @Override
    public void onMessage(Message message) {
        try {
            System.out.println("MessageTopicBean.onMessage invoked");
            if(message instanceof TextMessage)
            {
                TextMessage textMessage = (TextMessage)message;
                System.out.println("Message: " + textMessage.getText());
            }
            else
            {
                System.out.println("Message is not a TextMessage");
            }
        }
        catch (JMSException e) {
            e.printStackTrace();
        }
    }
}

4.2、打jar包,或者自己压缩为TestEJBServer.jar

5、新建一个ear项目
5.1增加application.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<application xmlns="http://java.sun.com/xml/ns/javaee"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/application_6.xsd"
             version="6">
    <description>EAR Test</description>
    <display-name>EAR Test</display-name>
    <module>
        <ejb>TestEJBServer.jar</ejb>
    </module>
    <library-directory>lib</library-directory>
</application>

5.2打ear包,或自己压缩为TestEar.ear

5.3TestEar的结构为:

│  TestEJBServer.jar
│
├─lib
└─META-INF
        application.xml

6.通过EAP进行部署

到这里服务端已经完成了。

然后是客户端的设置:
1、通过Topic方式访问

package com.neohope.ejb.test;

import javax.jms.*;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import java.util.Hashtable;

/**
 * Created by Hansen
 */
public class TestTopicBean {
    public static void main(String[] args) throws NamingException, JMSException {

        final Hashtable jndiProperties = new Hashtable();
        jndiProperties.put(Context.INITIAL_CONTEXT_FACTORY, "org.jboss.naming.remote.client.InitialContextFactory");
        jndiProperties.put(Context.PROVIDER_URL, "remote://localhost:4447");
        final InitialContext ctx = new InitialContext(jndiProperties);


        TopicConnectionFactory factory = (TopicConnectionFactory) ctx.lookup("jms/RemoteConnectionFactory");
        TopicConnection connection = factory.createTopicConnection("user001", "user001#");
        TopicSession session = connection.createTopicSession(false, TopicSession.AUTO_ACKNOWLEDGE);

        Topic topic = (Topic) ctx.lookup("jms/topic/neoTopic");
        TextMessage msg = session.createTextMessage("Topic Test Message");
        TopicPublisher publisher = session.createPublisher(topic);
        publisher.publish(msg);

        session.close();
        connection.close();
    }
}

2、通过MessageProducer方式访问

package com.neohope.ejb.test;

import javax.jms.*;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import java.util.Hashtable;

/**
 * Created by Hansen
 */
public class TestTopicBean {
    public static void main(String[] args) throws NamingException, JMSException {
        final Hashtable jndiProperties = new Hashtable();
        jndiProperties.put(Context.INITIAL_CONTEXT_FACTORY, "org.jboss.naming.remote.client.InitialContextFactory");
        jndiProperties.put(Context.PROVIDER_URL, "remote://localhost:4447");
        final InitialContext ctx = new InitialContext(jndiProperties);

        ConnectionFactory connectionFactory = (ConnectionFactory) ctx.lookup("jms/RemoteConnectionFactory");
        Destination destination = (Destination) ctx.lookup("jms/topic/neoTopic");
        Connection connection = connectionFactory.createConnection("user001", "user001#");
        Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
        MessageProducer producer = session.createProducer(destination);
        connection.start();

        TextMessage msg = session.createTextMessage("Topic Test Messagee");
        producer.send(msg);

        session.close();
        connection.close();
    }
}

JBoss EAP SessionBean的调用

首先是Server端的开发及设置:
1、增加一个用户:

bin\add-user.bat

用户名密码随便,但要属于guest组

2、启动Server

standalone.bat -server-config=standalone-full.xml 

3、新建java项目
3.1、写SessionBean的接口
ITest.java

package com.neohope.ejb.test;

import javax.ejb.Remote;

/**
 * Created by Hansen
 */

@Remote
public interface ITest{
    /**
     * 返回“Hi XXX"字符串,必须声明抛出RemoteException异常
     * @return 返回“Hi XXX"字符串
     */
    public String sayHiTo(String user);

    /**
     * 加法,必须声明抛出RemoteException异常
     * @param a
     * @parma b
     * @return a+b
     */
    public int add(int a, int b);
}

3.2、写一个无状态的SessionBean
SessionTestBean.java

package com.neohope.ejb.test;

import javax.ejb.Stateless;

/**
 * Created by Hansen
 */
@Stateless(name = "SessionTestEJB")
public class SessionTestBean implements ITest{
    public SessionTestBean() {
    }

    /**
     * 返回“Hi XXX"字符串,必须声明抛出RemoteException异常
     * @return 返回“Hi XXX"字符串
     */
    @Override
    public String sayHiTo(String user) {
        return "Hi " + user;
    }

    /**
     * 加法,必须声明抛出RemoteException异常
     * @param a
     * @parma b
     * @return a+b
     */
    @Override
    public int add(int a, int b) {
        return a+b;
    }
}

3.3、打jar包,或者自己压缩为TestEJBServer.jar

4、新建一个ear项目
4.1增加application.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<application xmlns="http://java.sun.com/xml/ns/javaee"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/application_6.xsd"
             version="6">
    <description>EAR Test</description>
    <display-name>EAR Test</display-name>
    <module>
        <ejb>TestEJBServer.jar</ejb>
    </module>
    <library-directory>lib</library-directory>
</application>

4.2打ear包,或自己压缩为TestEar.ear

4.3TestEar的结构为:

│  TestEJBServer.jar
│
├─lib
└─META-INF
        application.xml

5.通过EAP进行部署,部署后,会输出绑定结果

java:global/TestEar/TestEJBServer/SessionTestEJB!com.neohope.ejb.test.ITest
java:app/TestEJBServer/SessionTestEJB!com.neohope.ejb.test.ITest
java:module/SessionTestEJB!com.neohope.ejb.test.ITest
java:jboss/exported/TestEar/TestEJBServer/SessionTestEJB!com.neohope.ejb.test.ITest
java:global/TestEar/TestEJBServer/SessionTestEJB
java:app/TestEJBServer/SessionTestEJB
java:module/SessionTestEJB

到这里服务端已经完成了。

然后是客户端的设置:
1、在scr下新增配置文件jboss-ejb-client.properties

endpoint.name=client-endpoint
remote.connectionprovider.create.options.org.xnio.Options.SSL_ENABLED=false
remote.connections=default
remote.connection.default.host=localhost
remote.connection.default.port = 4447
remote.connection.default.connect.options.org.xnio.Options.SASL_POLICY_NOANONYMOUS=false
remote.connection.default.username=user001
remote.connection.default.password=user001#

2、通过ejb方式访问

package com.neohope.ejb.test;

import javax.jms.*;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import java.util.Hashtable;

/**
 * Created by Hansen
 */
public class TestSessionBean{
    public static void main(String[] args) throws NamingException, JMSException {
        final Hashtable jndiProperties = new Hashtable();
        jndiProperties.put(Context.URL_PKG_PREFIXES, "org.jboss.ejb.client.naming");

        final Context ctx = new InitialContext(jndiProperties);
        final String appName = "TestEar";
        final String moduleName = "TestEJBServer";
        final String distinctName = "";
        final String beanName = "SessionTestEJB";
        final String viewClassName = ITest.class.getName();
        final String namespace = "ejb:" + appName + "/" + moduleName + "/" + distinctName + "/" + beanName + "!" + viewClassName;
        System.out.println(namespace);
        ITest test =  (ITest)ctx.lookup(namespace);

        System.out.println(test.sayHiTo("neohope"));
        System.out.println(test.add(1,2));
    }
}

3、通过JMS方式访问

package com.neohope.ejb.test;

import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.JMSException;
import javax.jms.Session;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import java.util.Hashtable;

/**
 * Created by Hansen
 */
public class TestSessionBean{
    public static void main(String[] args) throws NamingException, JMSException {
        final Hashtable jndiProperties = new Hashtable();
        jndiProperties.put(Context.INITIAL_CONTEXT_FACTORY, "org.jboss.naming.remote.client.InitialContextFactory");
        jndiProperties.put(Context.PROVIDER_URL, "remote://localhost:4447");
        final InitialContext ctx = new InitialContext(jndiProperties);

        ConnectionFactory factory = (ConnectionFactory) ctx.lookup("jms/RemoteConnectionFactory");
        Connection connection = factory.createConnection("user001", "user001#");
        Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);

        ITest test = (ITest) ctx.lookup("TestEar/TestEJBServer/SessionTestEJB!com.neohope.ejb.test.ITest");

        session.close();
        connection.close();
        System.out.println(test.sayHiTo("neohope"));
        System.out.println(test.add(1,2));
    }
}

导致惨重代价的运维事故01

导致惨重代价的运维事故01

光大证券事件
2013年8月,光大证券在向客户演示时连接了正式数据库,导致股市震荡,被罚款5.2亿。

宁夏银行删库事件
2014年7月,宁夏银行在季末结算业务量较大的情况下,因备份系统异常导致备份存储磁盘读写处理严重延时,备份与主存储数据不一致。工程师在采取中断数据备份录像操作后,造成生产数据库损坏并宕机。造成38小时,700多定点医疗机构和定点零售药无法使用医保支付。

小插曲,2014年5月宁夏银行使用CDP软件进行了一场容灾演练,曾完成800公里的容灾切换。

携程删库事件
2015年5月,携程无法访问。官方反馈是由于运维工程师误操作,误删生产环境,而且重新部署后还是会被删除。经过十几小时努力,最终恢复成功。

小插曲,携程挂掉后,导流给了艺龙,结果艺龙也挂了。

Gitlab删库事件
2017年2月,Gitlab运维人员,在应对前一晚的DDOS攻击后,发现备库复制数据缓慢,并无法解决。最终决定删除备库,重新开始复制。但在十分疲倦的情况下,工程师误删了300G的生产数据,由于备份机制设置不合理,最终导致20多小时系统宕机,707位用户丢失数据,5,037项目丢失,受事故影响的用户基数不到1%。
我们可以看到的问题有:
1、审核和监控全部备份策略:虽然Gitlab号称有五重备份机制:常规备份24小时做一次、自动同步、LVM快照24小时做一次、Azure备份对数据库无效、S3备份。但没有一个可靠地运行或设置,而且备份失败也没有良好的预警机制。最终只能基于LVM的备份(最近6小时以前),还原了6 小时前的备份。
2、积极演练应对重大问题,保证备库是随时可用的,应急时也应该有序进行
3、数据中心之间数据传输要考虑好,本次数据传输也花费了较长时间
4、防止人肉运维,谨防开夜车,脚本工具化自动化。人总归会出错,而且总是在最不该发生的时候出错。
5、Gitlab本次事故发生后,公开透明的处理方式,值得大家借鉴和尊重。

AWS删服务器事件
2017年3月,AWS服务异常,经过4个多小时才恢复正常。
原因为:一名S3工程师根据预先编写的playbook执行一条命令时,输入命令时输错了一个字母,结果删除了一大批本不该删除的服务器。

verelox.com删库事件
2017年6月,荷兰云主机厂商verelox.com,一前任管理员,恶意报复公司,删除全部用户数据,并擦出了多数服务器上面的内容。

链家删库事件
2018年6月4日,链家网财务系统数据被删,这9TB数据,包括了该公司成立以来所有的财务数据。
经过紧急修复,该批数据最终得以恢复。
事件原因:由于管理制度漏洞百出,一名数据库运维人员轻易获得了不应有的高权限。该工程师与公司起了争执,修改MAC地址后,直接删库。

小插曲:
该工程师,只改了MAC地址,但并未变更主机名、IP,被系统日志记录。
修改MAC地址,也用了第三方软件,该软件日志几乎坐实了其攻击行为。
而且删除的数据,很容易就被恢复了,只花了18万。技术方面,及其不专业。
最终,该工程师拒不配合调查,也不认罪,最终被判刑7年。

广西移动扩容事件
2017年9月,华为工程师在进行广西移动扩容时,误将HSS设备的用户数据格式化,导致广西移动损失80万用户数据。

顺丰删库事件
2018年9月,顺丰一个高级工程误删线上库,然后跑路。导致部分服务无法使用并持续近10小时。

郑大附一数据库事件
2018年12月24日,郑大附一的一名工程师,由于操作不当导致HIS库被锁表,让该医院门诊业务停止2小时。
该工程师被判刑5年6个月。

微盟删库事件
2020年2月,微盟删库事件,导致系统6六天无法访问,市值蒸发28亿,预计赔偿金1.5亿,官方反馈是内部员工恶意行为导致。
数据最后在腾讯云的帮助下得以恢复。

小插曲:
小道消息,该工程师欠了网贷无力偿还,而且当天喝了不少酒。
该工程师被判刑6年月。

============================================================
注:本文主要是整理了系统运维导致的惨痛代价,没有记录下面几种情况(设计失败,黑客攻击,病毒爆发)

通过LDAP初步理解JNDI

LDAP与JNDI模型对比
jndi-ldap-model

1、LdapBinder
这个类的主要功能是,把消息放到一个预设的LDAP路径

package com.neohope.jndi.test;

import javax.naming.Binding;
import javax.naming.Context;
import javax.naming.NamingEnumeration;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
import java.util.Hashtable;

/**
 * Created by Hansen
 */
public class LdapBinder {

    public static void main(String[] args) {
        try {
            final Hashtable jndiProperties = new Hashtable();
            jndiProperties.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.fscontext.RefFSContextFactory");
            jndiProperties.put(Context.PROVIDER_URL, "file:///d:/Downloads/ldap");
            //jndiProperties.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
            //jndiProperties.put(Context.PROVIDER_URL, "ldap://localhost:389");
            //jndiProperties.put(Context.SECURITY_PRINCIPAL,"cn=Directory Manager");
            //jndiProperties.put(Context.SECURITY_CREDENTIALS,"password");

            DirContext ctx = new InitialDirContext(jndiProperties);
            NeoLdapMsgRef msgRef = new NeoLdapMsgRef("Ldap Text");
            ctx.bind("cn=anobject", msgRef);
            //ctx.unbind("cn=anobject");

            /*
            NamingEnumeration list = ctx.list("/");
            while (list.hasMore()) {
                NameClassPair nc = (NameClassPair) list.next();
                System.out.println(nc);
            }
            */

            NamingEnumeration list = ctx.listBindings("/");
            while (list.hasMore()) {
                Binding binding = (Binding)list.next();
                System.out.println(binding.getName() + " " +binding.getObject()
                );
            }

            ctx.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

2、LdapReader
这个类的主要功能是,从预设的LDAP路径读取消息

package com.neohope.jndi.test;

import javax.naming.Context;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
import java.util.Hashtable;

/**
 * Created by Hansen
 */
public class LdapReader {

    public static void main(String[] args) {
        try {
            final Hashtable jndiProperties = new Hashtable();
            jndiProperties.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.fscontext.RefFSContextFactory");
            jndiProperties.put(Context.PROVIDER_URL, "file:///d:/Downloads/ldap");

            //jndiProperties.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
            //jndiProperties.put(Context.PROVIDER_URL, "ldap://localhost:389");
            //jndiProperties.put(Context.SECURITY_PRINCIPAL,"cn=Directory Manager");
            //jndiProperties.put(Context.SECURITY_CREDENTIALS,"password");

            DirContext ctx = new InitialDirContext(jndiProperties);
            NeoLdapMsgRef msgRef = (NeoLdapMsgRef)ctx.lookup("cn=anobject");
            ctx.close();

            System.out.println(msgRef.message);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

3、LdapMonitor
这个类的主要功能是,监视LDAP路径下内容变动

package com.neohope.jndi.test;

import javax.naming.Context;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
import javax.naming.event.*;
import javax.naming.ldap.UnsolicitedNotificationEvent;
import javax.naming.ldap.UnsolicitedNotificationListener;
import java.util.Hashtable;

/**
 * Created by Hansen
 * 条件所限,没有进行测试
 */
public class LdapMonitor {

    public static void main(String[] args) {
        try {
            final Hashtable jndiProperties = new Hashtable();
            jndiProperties.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
            jndiProperties.put(Context.PROVIDER_URL, "ldap://localhost:389");
            jndiProperties.put(Context.SECURITY_PRINCIPAL,"cn=Manager");
            jndiProperties.put(Context.SECURITY_CREDENTIALS,"password");

            DirContext ctx = new InitialDirContext(jndiProperties);
            EventDirContext enentCtx=(EventDirContext)(ctx.lookup("/"));

            NamingListener unsolListener = new UnsolicitedNotificationListener() {
                public void notificationReceived(UnsolicitedNotificationEvent evt) {
                    System.out.println("received: " + evt + ",notification:" + evt.getNotification());
                }

                public void namingExceptionThrown(NamingExceptionEvent evt) {
                    System.out.println(">>> UnsolListener got an exception");
                    evt.getException().printStackTrace();
                }
            };

            NamingListener namespaceListener = new NamespaceChangeListener() {
                public void objectAdded(NamingEvent evt) {
                    System.out.println("objectAdded: " + evt.getOldBinding() + "\n=> " + evt.getNewBinding());
                    System.out.println("\tchangeInfo: " + evt.getChangeInfo());
                }

                public void objectRemoved(NamingEvent evt) {
                    System.out.println("objectRemoved: " + evt.getOldBinding() + "\n=> " + evt.getNewBinding());
                    System.out.println("\tchangeInfo: " + evt.getChangeInfo());
                }

                public void objectRenamed(NamingEvent evt) {
                    System.out.println("objectRenamed: " + evt.getOldBinding() + "\n=> " + evt.getNewBinding());
                    System.out.println("\tchangeInfo: " + evt.getChangeInfo());
                }

                public void namingExceptionThrown(NamingExceptionEvent evt) {
                    System.err.println(">>>NamespaceChangeListener Exception");
                    evt.getException().printStackTrace();
                }
            };

            NamingListener objectListener = new ObjectChangeListener() {
                public void objectChanged(NamingEvent evt) {
                    System.out.println("objectChanged: " + evt.getOldBinding() + "\n\t=> " + evt.getNewBinding());
                    System.out.println("\tchangeInfo: " + evt.getChangeInfo());
                }

                public void namingExceptionThrown(NamingExceptionEvent evt) {
                    System.err.println(">>>ObjectChangeListener Exception");
                    evt.getException().printStackTrace();
                }
            };

            enentCtx.addNamingListener("", EventContext.SUBTREE_SCOPE, unsolListener);
            enentCtx.addNamingListener("", EventContext.SUBTREE_SCOPE, namespaceListener);
            enentCtx.addNamingListener("", EventContext.SUBTREE_SCOPE, objectListener);

            System.in.read();

            //enentCtx.close();
            ctx.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

4、NeoLdapMsgRef

package com.neohope.jndi.test;

import javax.naming.NamingException;
import javax.naming.Reference;
import javax.naming.Referenceable;
import javax.naming.StringRefAddr;

/**
 * Created by Hansen
 */
public class NeoLdapMsgRef implements Referenceable {
    public String message = "";

    public NeoLdapMsgRef(String message)
    {
        this.message = message;
    }

    @Override
    public Reference getReference() throws NamingException {
        Reference ref = new Reference(this.getClass().getName(), NeoLdapMsgRefFactory.class.getName(), null);
        ref.add(new StringRefAddr("msg", message));
        return ref;
    }
}

5、NeoLdapMsgRefFactory

package com.neohope.jndi.test;

import javax.naming.*;
import javax.naming.spi.ObjectFactory;
import java.util.Hashtable;

/**
 * Created by Hansen
 */
public class NeoLdapMsgRefFactory implements ObjectFactory {
    @Override
    public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable<?, ?> environment) throws Exception {
        if (obj instanceof Reference) {
            Reference ref = (Reference) obj;
            String msg = (String) ref.get("msg").getContent();
            NeoLdapMsgRef msgRef = new NeoLdapMsgRef(msg);
            return msgRef;
        }
        else {
            return null;
        }
    }
}

LADP常用函数

LADP操作 解释 JNDI函数
Search Search directory for matching directory entries DirContext.search()
Compare Compare directory entry to a set of attributes DirContext.search()
Add Add a new directory entry DirContext.bind(), DirContext.createSubcontext()
Modify Modify a particular directory entry DirContext.modifyAttributes()
Delete Delete a particular directory entry Context.unbind(), Context.destroySubcontext()
Rename Rename or modify the DN Context.rename()
Bind Start a session with an LDAP server new InitialDirContext()
Unbind End a session with an LDAP server Context.close()
Abandon Abandon an operation previously sent to the server Context.close(), NamingEnumneration.close()
Extended Extended operations command LdapContext.extendedOperation()

LADP查询常用符号

o Organization
ou Organizational unit
cn Common name
sn Surname
givenname First name
uid Userid
dn Distinguished name
mail Email address

LADP查询常用操作符

符号 含义 示例 匹配示例
~ Approximate (sn~=Tyagi) Tyagi or variations in spelling
= Equality (sn=Tyagi) Surname of Tyagi only
> Greater than (sn=Tyagi) Any surname that alphabetically follows Tyagi
>= Greater than or equal to (sn>=Tyagi) Any surname that includes or alphabetically follows Tyagi
< Less than (sn Any surname that alphabetically precedes Tyagi
<= Less than or equal to (sn<=Tyagi) Any surname that includes or alphabetically precedes Tyagi
=* Presence (sn=*) All surnames (all entries with the sn attribute)
Substring (sn=Tya*), (sn=*yag*), (sn=Ty*g*) Any matching string, substring, or superstring that matches Tyagi
& And (&(sn=Tyagi) (cn=Sameer Tyagi)) Any entry that matches both surname of Tyagi and a common name of Sameer Tyagi
| Or (|(sn=Tyagi) (cn=Sameer Tyagi)) Any entry that matches either surname of Tyagi or a common name of Sameer Tyagi
! Not (!(sn=Tyagi)) Any entry other than that with a surname of Tyagi