<commit_before>/*
http://www.apache.org/licenses/LICENSE-2.0.txt

Copyright 2015 Intel Corporation

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package pcm

import (
	"bufio"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/intelsdi-x/snap/control/plugin"
	"github.com/intelsdi-x/snap/control/plugin/cpolicy"
)

const (
	// Name of plugin
	name = "pcm"
	// Version of plugin
	version = 5
	// Type of plugin
	pluginType = plugin.CollectorPluginType
)

func Meta() *plugin.PluginMeta {
	return plugin.NewPluginMeta(name, version, pluginType, []string{plugin.SnapGOBContentType}, []string{plugin.SnapGOBContentType})
}

// PCM
type PCM struct {
	keys  []string
	data  map[string]interface{}
	mutex *sync.RWMutex
}

func (p *PCM) Keys() []string {
	return p.keys
}

func (p *PCM) Data() map[string]interface{} {
	return p.data
}

// CollectMetrics returns metrics from pcm
func (p *PCM) CollectMetrics(mts []plugin.PluginMetricType) ([]plugin.PluginMetricType, error) {
	metrics := make([]plugin.PluginMetricType, len(mts))
	p.mutex.RLock()
	defer p.mutex.RUnlock()
	hostname, _ := os.Hostname()
	for i, m := range mts {
		if v, ok := p.data[joinNamespace(m.Namespace())]; ok {
			metrics[i] = plugin.PluginMetricType{
				Namespace_: m.Namespace(),
				Data_:      v,
				Source_:    hostname,
				Timestamp_: time.Now(),
			}
		}
	}
	// fmt.Fprintf(os.Stderr, "collected >>> %+v\n", metrics)
	return metrics, nil
}

// GetMetricTypes returns the metric types exposed by pcm
func (p *PCM) GetMetricTypes(_ plugin.PluginConfigType) ([]plugin.PluginMetricType, error) {
	mts := make([]plugin.PluginMetricType, len(p.keys))
	p.mutex.RLock()
	defer p.mutex.RUnlock()
	for i, k := range p.keys {
		mts[i] = plugin.PluginMetricType{Namespace_: strings.Split(strings.TrimPrefix(k, "/"), "/")}
	}
	return mts, nil
}

//GetConfigPolicy
func (p *PCM) GetConfigPolicy() (*cpolicy.ConfigPolicy, error) {
	c := cpolicy.New()
	return c, nil
}

func NewPCMCollector() (*PCM, error) {
	pcm := &PCM{mutex: &sync.RWMutex{}, data: map[string]interface{}{}}
	var cmd *exec.Cmd
	if path := os.Getenv("SNAP_PCM_PATH"); path != "" {
		cmd = exec.Command(filepath.Join(path, "pcm.x"), "/csv", "-nc", "-r", "1")
	} else {
		c, err := exec.LookPath("pcm.x")
		if err != nil {
			fmt.Fprint(os.Stderr, "Unable to find PCM.  Ensure it's in your path or set SNAP_PCM_PATH.")
			panic(err)
		}
		cmd = exec.Command(c, "/csv", "-nc", "-r", "1")
	}

	cmdReader, err := cmd.StdoutPipe()
	if err != nil {
		fmt.Fprintln(os.Stderr, "Error creating StdoutPipe", err)
		return nil, err
	}
	// read the data from stdout
	scanner := bufio.NewScanner(cmdReader)
	go func() {
		first := true
		for scanner.Scan() {
			if first {
				first = false
				continue
			}
			if len(pcm.keys) == 0 {
				pcm.mutex.Lock()
				keys := strings.Split(strings.TrimSuffix(scanner.Text(), ";"), ";")
				//skip the date and time fields
				pcm.keys = make([]string, len(keys[2:]))
				for i, k := range keys[2:] {
					// removes all spaces from metric key
					pcm.keys[i] = fmt.Sprintf("/intel/pcm/%s", strings.Replace(k, " ", "", -1))
				}
				pcm.mutex.Unlock()
				continue
			}

			pcm.mutex.Lock()
			datal := strings.Split(strings.TrimSuffix(scanner.Text(), ";"), ";")
			for i, d := range datal[2:] {
				v, err := strconv.ParseFloat(strings.TrimSpace(d), 64)
				if err == nil {
					pcm.data[pcm.keys[i]] = v
				} else {
					fmt.Fprintln(os.Stderr, "Invalid metric value", err)
					pcm.data[pcm.keys[i]] = nil
				}
			}
			pcm.mutex.Unlock()
			// fmt.Fprintf(os.Stderr, "data >>> %+v\n", pcm.data)
			// fmt.Fprintf(os.Stdout, "data >>> %+v\n", pcm.data)
		}
	}()

	err = cmd.Start()
	if err != nil {
		fmt.Fprintln(os.Stderr, "Error starting pcm", err)
		return nil, err
	}

	// we need to wait until we have our metric types
	st := time.Now()
	for {
		pcm.mutex.RLock()
		c := len(pcm.keys)
		pcm.mutex.RUnlock()
		if c > 0 {
			break
		}
		if time.Since(st) > time.Second*2 {
			return nil, fmt.Errorf("Timed out waiting for metrics from pcm")
		}
	}

	// LEAVE the following block for debugging
	// err = cmd.Wait()
	// if err != nil {
	// 	fmt.Fprintln(os.Stderr, "Error waiting for pcm", err)
	// 	return nil, err
	// }

	return pcm, nil
}

func joinNamespace(ns []string) string {
	return "/" + strings.Join(ns, "/")
}
<commit_msg>Update version to reflect change in snap dependency<commit_after>/*
http://www.apache.org/licenses/LICENSE-2.0.txt

Copyright 2015 Intel Corporation

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package pcm

import (
	"bufio"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/intelsdi-x/snap/control/plugin"
	"github.com/intelsdi-x/snap/control/plugin/cpolicy"
)

const (
	// Name of plugin
	name = "pcm"
	// Version of plugin
	version = 6
	// Type of plugin
	pluginType = plugin.CollectorPluginType
)

func Meta() *plugin.PluginMeta {
	return plugin.NewPluginMeta(name, version, pluginType, []string{plugin.SnapGOBContentType}, []string{plugin.SnapGOBContentType})
}

// PCM
type PCM struct {
	keys  []string
	data  map[string]interface{}
	mutex *sync.RWMutex
}

func (p *PCM) Keys() []string {
	return p.keys
}

func (p *PCM) Data() map[string]interface{} {
	return p.data
}

// CollectMetrics returns metrics from pcm
func (p *PCM) CollectMetrics(mts []plugin.PluginMetricType) ([]plugin.PluginMetricType, error) {
	metrics := make([]plugin.PluginMetricType, len(mts))
	p.mutex.RLock()
	defer p.mutex.RUnlock()
	hostname, _ := os.Hostname()
	for i, m := range mts {
		if v, ok := p.data[joinNamespace(m.Namespace())]; ok {
			metrics[i] = plugin.PluginMetricType{
				Namespace_: m.Namespace(),
				Data_:      v,
				Source_:    hostname,
				Timestamp_: time.Now(),
			}
		}
	}
	// fmt.Fprintf(os.Stderr, "collected >>> %+v\n", metrics)
	return metrics, nil
}

// GetMetricTypes returns the metric types exposed by pcm
func (p *PCM) GetMetricTypes(_ plugin.PluginConfigType) ([]plugin.PluginMetricType, error) {
	mts := make([]plugin.PluginMetricType, len(p.keys))
	p.mutex.RLock()
	defer p.mutex.RUnlock()
	for i, k := range p.keys {
		mts[i] = plugin.PluginMetricType{Namespace_: strings.Split(strings.TrimPrefix(k, "/"), "/")}
	}
	return mts, nil
}

//GetConfigPolicy
func (p *PCM) GetConfigPolicy() (*cpolicy.ConfigPolicy, error) {
	c := cpolicy.New()
	return c, nil
}

func NewPCMCollector() (*PCM, error) {
	pcm := &PCM{mutex: &sync.RWMutex{}, data: map[string]interface{}{}}
	var cmd *exec.Cmd
	if path := os.Getenv("SNAP_PCM_PATH"); path != "" {
		cmd = exec.Command(filepath.Join(path, "pcm.x"), "/csv", "-nc", "-r", "1")
	} else {
		c, err := exec.LookPath("pcm.x")
		if err != nil {
			fmt.Fprint(os.Stderr, "Unable to find PCM.  Ensure it's in your path or set SNAP_PCM_PATH.")
			panic(err)
		}
		cmd = exec.Command(c, "/csv", "-nc", "-r", "1")
	}

	cmdReader, err := cmd.StdoutPipe()
	if err != nil {
		fmt.Fprintln(os.Stderr, "Error creating StdoutPipe", err)
		return nil, err
	}
	// read the data from stdout
	scanner := bufio.NewScanner(cmdReader)
	go func() {
		first := true
		for scanner.Scan() {
			if first {
				first = false
				continue
			}
			if len(pcm.keys) == 0 {
				pcm.mutex.Lock()
				keys := strings.Split(strings.TrimSuffix(scanner.Text(), ";"), ";")
				//skip the date and time fields
				pcm.keys = make([]string, len(keys[2:]))
				for i, k := range keys[2:] {
					// removes all spaces from metric key
					pcm.keys[i] = fmt.Sprintf("/intel/pcm/%s", strings.Replace(k, " ", "", -1))
				}
				pcm.mutex.Unlock()
				continue
			}

			pcm.mutex.Lock()
			datal := strings.Split(strings.TrimSuffix(scanner.Text(), ";"), ";")
			for i, d := range datal[2:] {
				v, err := strconv.ParseFloat(strings.TrimSpace(d), 64)
				if err == nil {
					pcm.data[pcm.keys[i]] = v
				} else {
					fmt.Fprintln(os.Stderr, "Invalid metric value", err)
					pcm.data[pcm.keys[i]] = nil
				}
			}
			pcm.mutex.Unlock()
			// fmt.Fprintf(os.Stderr, "data >>> %+v\n", pcm.data)
			// fmt.Fprintf(os.Stdout, "data >>> %+v\n", pcm.data)
		}
	}()

	err = cmd.Start()
	if err != nil {
		fmt.Fprintln(os.Stderr, "Error starting pcm", err)
		return nil, err
	}

	// we need to wait until we have our metric types
	st := time.Now()
	for {
		pcm.mutex.RLock()
		c := len(pcm.keys)
		pcm.mutex.RUnlock()
		if c > 0 {
			break
		}
		if time.Since(st) > time.Second*2 {
			return nil, fmt.Errorf("Timed out waiting for metrics from pcm")
		}
	}

	// LEAVE the following block for debugging
	// err = cmd.Wait()
	// if err != nil {
	// 	fmt.Fprintln(os.Stderr, "Error waiting for pcm", err)
	// 	return nil, err
	// }

	return pcm, nil
}

func joinNamespace(ns []string) string {
	return "/" + strings.Join(ns, "/")
}
<|endoftext|>


<commit_before>package server

import (
	"fmt"
	"net"
	"runtime"
	"strings"
	"sync"
	"time"

	"golang.org/x/net/websocket"
)

var (
	maxChatterRsp = 1000 // The max number of responses in the response channel.
)

// Chatter is a wrapper around a connection that represents one chat client on the server.
// It is a parasitic class in that it lives and dies within the context of the server and is trusted
// to use and modify server atttributes directly.
type Chatter struct {
	srvr      *Server            // The server this chatter is connected to.
	ws        *websocket.Conn    // The socket to the remote client.
	connected bool               // Indicates if the socket is active,
	nickname  string             // The friendly nickname to display in a conversation.
	start     time.Time          // The start time of the connection.
	lastReq   time.Time          // The last request time of the connection.
	lastRsp   time.Time          // The last response time to the connection.
	reqCount  uint64             // Total requests received.
	rspCount  uint64             // Total responses sent.
	rspq      chan *ChatResponse // A channel to receive information to send to the remote client.
	mu        sync.Mutex         // For locking access to chatter attributes.
	wg        sync.WaitGroup     // Synchronization of channel close.
}

// ChatterNew is a factory function that returns a new Chatter instance
func ChatterNew(s *Server, c *websocket.Conn) *Chatter {
	return &Chatter{
		srvr: s,
		ws:   c,
		rspq: make(chan *ChatResponse, maxChatterRsp),
	}
}

// Run starts the event loop that manages the sending and receiving of information to the client.
func (c *Chatter) Run() {
	c.start = time.Now()
	c.connected = true
	c.srvr.wg.Add(1) // We let the big boss also perform waits for chatters, so it can close down,
	c.wg.Add(1)      //   but we also have our own.
	go c.send()      // Spawn response handling to the client in the background.
	c.receive()      // Then wait on incoming requests.
}

// receive polls and handles any commands or information sent from the remote client.
func (c *Chatter) receive() {
	remoteAddr := fmt.Sprint(c.ws.Request().RemoteAddr)
	for {
		// Set optional idle timeout on the receive.
		if c.srvr.info.MaxIdle > 0 {
			c.ws.SetReadDeadline(time.Now().Add(time.Duration(c.srvr.info.MaxIdle) * time.Second))
		}
		var req ChatRequest
		if err := websocket.JSON.Receive(c.ws, &req); err != nil {
			e, ok := err.(net.Error)
			switch {
			case ok && e.Timeout():
				c.srvr.log.LogSession("disconnected", remoteAddr, "Client forced to disconnect due to inactivity.")
				c.shutDown()
			case err.Error() == "EOF":
				c.srvr.log.LogSession("disconnected", remoteAddr, "Client disconnected.")
				c.shutDown()
			case strings.Contains(err.Error(), "use of closed network connection"): // cntl-c safelty.
				c.shutDown()
			default:
				c.srvr.log.LogError(remoteAddr, fmt.Sprintf("Couldn't receive. Error: %s", err.Error()))
			}
			return
		}
		c.mu.Lock()
		c.lastReq = time.Now()
		c.reqCount++
		c.mu.Unlock()
		c.srvr.log.LogSession("received", remoteAddr, fmt.Sprintf("%s", &req))
		switch req.ReqType {
		case ChatReqTypeSetNickname:
			c.setNickname(&req)
		case ChatReqTypeGetNickname:
			c.getNickname()
		case ChatReqTypeListRooms:
			c.listRooms()
		default: // let room handle other requests or send error if no room name provided.
			req.Who = c
			c.sendRequestToRoom(&req)
		}
	}
}

// send is a go routine used to poll queued messages to send information to the client.
func (c *Chatter) send() {
	defer c.srvr.wg.Done()
	defer c.wg.Done()

	remoteAddr := fmt.Sprint(c.ws.Request().RemoteAddr)
	for {
		select {
		case <-c.srvr.done: // Server shutdown signal.
			c.ws.Close() // Break the receive() loop and force a chatter shutdown.
			return
		case rsp, ok := <-c.rspq:
			if !ok { // Assume ch closed is a shutdown notification from anybody.
				c.ws.Close() // Break the receive() looper and force a chatter shutdown.
				return
			}
			c.mu.Lock()
			c.lastRsp = time.Now()
			c.rspCount++
			c.mu.Unlock()
			c.srvr.log.LogSession("sent", remoteAddr, fmt.Sprintf("%s", rsp))
			if err := websocket.JSON.Send(c.ws, rsp); err != nil {
				switch {
				case err.Error() == "EOF":
					c.srvr.log.LogSession("disconnected", remoteAddr, "Client disconnected.")
					return
				default:
					c.srvr.log.LogError(remoteAddr, fmt.Sprintf("Couldn't send. Error: %s", err.Error()))
				}
			}
		}
		runtime.Gosched()
	}
}

// shutDown removes the chatter from any rooms, and shuts down sending/receiving.
func (c *Chatter) shutDown() {
	c.srvr.roomMngr.removeChatterAllRooms(c)
	c.mu.Lock()
	c.connected = false
	close(c.rspq) // signal stop to send()
	c.mu.Unlock()
	c.wg.Wait()
}

// setNickname sets the nickname for the chatter.
func (c *Chatter) setNickname(r *ChatRequest) {
	if r.Content == "" {
		c.sendResponse(ChatRspTypeErrNicknameMandatory, "Nickname cannot be blank.", nil)
		return
	}
	c.nickname = r.Content
	c.sendResponse(ChatRspTypeSetNickname, fmt.Sprintf(`Nickname set to "%s".`, c.nickname), nil)
}

// nickname returns the nickname for the chatter.
func (c *Chatter) getNickname() {
	c.sendResponse(ChatRspTypeGetNickname, c.nickname, nil)
}

// listRooms returns a list of chat rooms to the chatter.
func (c *Chatter) listRooms() {
	c.sendResponse(ChatRspTypeListRooms, "", c.srvr.roomMngr.list())
}

// ChatterStats is a simple structure for returning statistic information on the chatter.
type ChatterStats struct {
	Start    time.Time `json:"start"`    // The start time of the chatter.
	LastReq  time.Time `json:"lastReq"`  // The last request time from the chatter.
	LastRsp  time.Time `json:"lastRsp"`  // The last response time to the chatter.
	ReqCount uint64    `json:"reqcount"` // Total requests received.
	RspCount uint64    `json:"rspCount"` // Total responses sent.
}

// stats returns status information on the chatter.
func (c *Chatter) stats() *ChatterStats {
	c.mu.Lock()
	defer c.mu.Unlock()
	return &ChatterStats{
		Start:    c.start,
		LastReq:  c.lastReq,
		LastRsp:  c.lastRsp,
		ReqCount: c.reqCount,
		RspCount: c.rspCount,
	}
}

// sendRequestToRoom sends the request to a room or creates a mew room to receive the request.
func (c *Chatter) sendRequestToRoom(r *ChatRequest) {
	if r.RoomName == "" {
		c.sendResponse(ChatRspTypeErrRoomMandatory, "Room name is mandatory to access a room.", nil)
		return
	}
	m, err := c.srvr.roomMngr.findCreate(r.RoomName)
	if err != nil {
		c.sendResponse(ChatRspTypeErrMaxRoomsReached, err.Error(), nil)
		return
	}
	m.reqq <- r
}

// sendResponse sends a message to a chatter.
func (c *Chatter) sendResponse(rt int, msg string, l []string) {
	if c.rspq != nil {
		if l == nil {
			l = []string{}
		}
		rsp, err := ChatResponseNew("", rt, msg, l)
		if err == nil {
			c.rspq <- rsp
		}
	}
}
<commit_msg>race condition between go routines<commit_after>package server

import (
	"fmt"
	"net"
	"runtime"
	"strings"
	"sync"
	"time"

	"golang.org/x/net/websocket"
)

var (
	maxChatterRsp = 1000 // The max number of responses in the response channel.
)

// Chatter is a wrapper around a connection that represents one chat client on the server.
// It is a parasitic class in that it lives and dies within the context of the server and is trusted
// to use and modify server atttributes directly.
type Chatter struct {
	srvr      *Server            // The server this chatter is connected to.
	ws        *websocket.Conn    // The socket to the remote client.
	connected bool               // Indicates if the socket is active,
	nickname  string             // The friendly nickname to display in a conversation.
	start     time.Time          // The start time of the connection.
	lastReq   time.Time          // The last request time of the connection.
	lastRsp   time.Time          // The last response time to the connection.
	reqCount  uint64             // Total requests received.
	rspCount  uint64             // Total responses sent.
	rspq      chan *ChatResponse // A channel to receive information to send to the remote client.
	mu        sync.Mutex         // For locking access to chatter attributes.
	wg        sync.WaitGroup     // Synchronization of channel close.
}

// ChatterNew is a factory function that returns a new Chatter instance
func ChatterNew(s *Server, c *websocket.Conn) *Chatter {
	return &Chatter{
		srvr: s,
		ws:   c,
		rspq: make(chan *ChatResponse, maxChatterRsp),
	}
}

// Run starts the event loop that manages the sending and receiving of information to the client.
func (c *Chatter) Run() {
	c.start = time.Now()
	c.connected = true
	c.srvr.wg.Add(1) // We let the big boss also perform waits for chatters, so it can close down,
	c.wg.Add(1)      //   but we also have our own in send().
	go c.send()      // Spawn response handling to the client in the background.
	c.receive()      // Then wait on incoming requests.
}

// receive polls and handles any commands or information sent from the remote client.
func (c *Chatter) receive() {
	defer c.srvr.wg.Done()
	remoteAddr := fmt.Sprint(c.ws.Request().RemoteAddr)
	for {
		// Set optional idle timeout on the receive.
		if c.srvr.info.MaxIdle > 0 {
			c.ws.SetReadDeadline(time.Now().Add(time.Duration(c.srvr.info.MaxIdle) * time.Second))
		}
		var req ChatRequest
		if err := websocket.JSON.Receive(c.ws, &req); err != nil {
			e, ok := err.(net.Error)
			switch {
			case ok && e.Timeout():
				c.srvr.log.LogSession("disconnected", remoteAddr, "Client forced to disconnect due to inactivity.")
				c.shutDown()
			case err.Error() == "EOF":
				c.srvr.log.LogSession("disconnected", remoteAddr, "Client disconnected.")
				c.shutDown()
			case strings.Contains(err.Error(), "use of closed network connection"): // cntl-c safelty.
				c.shutDown()
			default:
				c.srvr.log.LogError(remoteAddr, fmt.Sprintf("Couldn't receive. Error: %s", err.Error()))
			}
			return
		}
		c.mu.Lock()
		c.lastReq = time.Now()
		c.reqCount++
		c.mu.Unlock()
		c.srvr.log.LogSession("received", remoteAddr, fmt.Sprintf("%s", &req))
		switch req.ReqType {
		case ChatReqTypeSetNickname:
			c.setNickname(&req)
		case ChatReqTypeGetNickname:
			c.getNickname()
		case ChatReqTypeListRooms:
			c.listRooms()
		default: // let room handle other requests or send error if no room name provided.
			req.Who = c
			c.sendRequestToRoom(&req)
		}
	}
}

// send is a go routine used to poll queued messages to send information to the client.
func (c *Chatter) send() {
	defer c.wg.Done()
	remoteAddr := fmt.Sprint(c.ws.Request().RemoteAddr)
	for {
		select {
		case <-c.srvr.done: // Server shutdown signal.
			c.ws.Close() // Break the receive() loop and force a chatter shutdown.
			return
		case rsp, ok := <-c.rspq:
			if !ok { // Assume ch closed is a shutdown notification from anybody.
				c.ws.Close() // Break the receive() looper and force a chatter shutdown.
				return
			}
			c.mu.Lock()
			c.lastRsp = time.Now()
			c.rspCount++
			c.mu.Unlock()
			c.srvr.log.LogSession("sent", remoteAddr, fmt.Sprintf("%s", rsp))
			if err := websocket.JSON.Send(c.ws, rsp); err != nil {
				switch {
				case err.Error() == "EOF":
					c.srvr.log.LogSession("disconnected", remoteAddr, "Client disconnected.")
					return
				default:
					c.srvr.log.LogError(remoteAddr, fmt.Sprintf("Couldn't send. Error: %s", err.Error()))
				}
			}
		}
		runtime.Gosched()
	}
}

// shutDown removes the chatter from any rooms, and shuts down sending/receiving.
func (c *Chatter) shutDown() {
	c.srvr.roomMngr.removeChatterAllRooms(c)
	c.mu.Lock()
	c.connected = false
	close(c.rspq) // signal stop to send()
	c.mu.Unlock()
	c.wg.Wait()
}

// setNickname sets the nickname for the chatter.
func (c *Chatter) setNickname(r *ChatRequest) {
	if r.Content == "" {
		c.sendResponse(ChatRspTypeErrNicknameMandatory, "Nickname cannot be blank.", nil)
		return
	}
	c.nickname = r.Content
	c.sendResponse(ChatRspTypeSetNickname, fmt.Sprintf(`Nickname set to "%s".`, c.nickname), nil)
}

// nickname returns the nickname for the chatter.
func (c *Chatter) getNickname() {
	c.sendResponse(ChatRspTypeGetNickname, c.nickname, nil)
}

// listRooms returns a list of chat rooms to the chatter.
func (c *Chatter) listRooms() {
	c.sendResponse(ChatRspTypeListRooms, "", c.srvr.roomMngr.list())
}

// ChatterStats is a simple structure for returning statistic information on the chatter.
type ChatterStats struct {
	Start    time.Time `json:"start"`    // The start time of the chatter.
	LastReq  time.Time `json:"lastReq"`  // The last request time from the chatter.
	LastRsp  time.Time `json:"lastRsp"`  // The last response time to the chatter.
	ReqCount uint64    `json:"reqcount"` // Total requests received.
	RspCount uint64    `json:"rspCount"` // Total responses sent.
}

// stats returns status information on the chatter.
func (c *Chatter) stats() *ChatterStats {
	c.mu.Lock()
	defer c.mu.Unlock()
	return &ChatterStats{
		Start:    c.start,
		LastReq:  c.lastReq,
		LastRsp:  c.lastRsp,
		ReqCount: c.reqCount,
		RspCount: c.rspCount,
	}
}

// sendRequestToRoom sends the request to a room or creates a mew room to receive the request.
func (c *Chatter) sendRequestToRoom(r *ChatRequest) {
	if r.RoomName == "" {
		c.sendResponse(ChatRspTypeErrRoomMandatory, "Room name is mandatory to access a room.", nil)
		return
	}
	m, err := c.srvr.roomMngr.findCreate(r.RoomName)
	if err != nil {
		c.sendResponse(ChatRspTypeErrMaxRoomsReached, err.Error(), nil)
		return
	}
	m.reqq <- r
}

// sendResponse sends a message to a chatter.
func (c *Chatter) sendResponse(rt int, msg string, l []string) {
	if c.rspq != nil {
		if l == nil {
			l = []string{}
		}
		rsp, err := ChatResponseNew("", rt, msg, l)
		if err == nil {
			c.rspq <- rsp
		}
	}
}
<|endoftext|>


<commit_before>// Package permute implements a generic method for in-place generation
// of all permutations for ordered collections.
package permute

// The algorithm used here is a non-recursive version of Heap's permutation method.
// See http://en.wikipedia.org/wiki/Heap's_algorithm
//
// Here's pseudocode from https://www.cs.princeton.edu/~rs/talks/perms.pdf
//
// generate(int N) {
//   int n;
//   for (n = 1; n <= N; n++) {
//     p[n] = n;
//     c[n] = 1;
//   }
//   doit();
//   for (n = 1; n <= N; ) {
//     if (c[n] < n) {
//       exch(N % 2 ? 1 : c, N)
//       c[n]++;
//       n = 1;
//       doit();
//     } else {
//       c[n++] = 1;
//     }
//   }
// }

// Interface is satisfied by types (usually ordered collections)
// which can have all permutations generated Permute.
type Interface interface {
	Len() int
	Swap(i, j int)
}

// Permute gives a Permuter to generate all permutations
// of the elements of v.
func Permute(v Interface) *Permuter {
	return &Permuter{
		iface:    v,
		started:  false,
		finished: false,
	}
}

// A Permuter holds state about an ongoing iteration of permutations.
type Permuter struct {
	iface    Interface
	started  bool
	finished bool
	n        int
	i        int
	p        []int
	c        []int
}

// Permute generates the next permutation of the contained collection in-place.
// If it returns false, the iteration is finished.
func (p *Permuter) Permute() bool {
	if p.finished {
		panic("Permute() called on finished Permuter")
	}
	if !p.started {
		p.started = true
		p.n = p.iface.Len()
		p.p = make([]int, p.n)
		p.c = make([]int, p.n)
		for i := 0; i < p.n; i++ {
			p.p[i] = i
			p.c[i] = 0
		}
		p.i = 0
		return true
	}
	for {
		if p.i >= p.n {
			p.finished = true
			return false
		}
		if c := p.c[p.i]; c < p.i {
			if p.i&1 == 0 {
				c = 0
			}
			p.iface.Swap(c, p.i)
			p.c[p.i]++
			p.i = 0
			return true
		} else {
			p.c[p.i] = 0
			p.i++
		}
	}
}

type intSlice []int

func (s intSlice) Len() int      { return len(s) }
func (s intSlice) Swap(i, j int) { s[i], s[j] = s[j], s[i] }

// Ints is a convenience function for generating permutations of []ints.
func Ints(s []int) *Permuter { return Permute(intSlice(s)) }

type stringSlice []string

func (s stringSlice) Len() int      { return len(s) }
func (s stringSlice) Swap(i, j int) { s[i], s[j] = s[j], s[i] }

// Strings is a convenience function for generating permutations of []strings.
func Strings(s []string) *Permuter { return Permute(stringSlice(s)) }
<commit_msg>Typo fix<commit_after>// Package permute implements a generic method for in-place generation
// of all permutations for ordered collections.
package permute

// The algorithm used here is a non-recursive version of Heap's permutation method.
// See http://en.wikipedia.org/wiki/Heap's_algorithm
//
// Here's pseudocode from https://www.cs.princeton.edu/~rs/talks/perms.pdf
//
// generate(int N) {
//   int n;
//   for (n = 1; n <= N; n++) {
//     p[n] = n;
//     c[n] = 1;
//   }
//   doit();
//   for (n = 1; n <= N; ) {
//     if (c[n] < n) {
//       exch(N % 2 ? 1 : c, N)
//       c[n]++;
//       n = 1;
//       doit();
//     } else {
//       c[n++] = 1;
//     }
//   }
// }

// Interface is satisfied by types (usually ordered collections)
// which can have all permutations generated by Permute.
type Interface interface {
	Len() int
	Swap(i, j int)
}

// Permute gives a Permuter to generate all permutations
// of the elements of v.
func Permute(v Interface) *Permuter {
	return &Permuter{
		iface:    v,
		started:  false,
		finished: false,
	}
}

// A Permuter holds state about an ongoing iteration of permutations.
type Permuter struct {
	iface    Interface
	started  bool
	finished bool
	n        int
	i        int
	p        []int
	c        []int
}

// Permute generates the next permutation of the contained collection in-place.
// If it returns false, the iteration is finished.
func (p *Permuter) Permute() bool {
	if p.finished {
		panic("Permute() called on finished Permuter")
	}
	if !p.started {
		p.started = true
		p.n = p.iface.Len()
		p.p = make([]int, p.n)
		p.c = make([]int, p.n)
		for i := 0; i < p.n; i++ {
			p.p[i] = i
			p.c[i] = 0
		}
		p.i = 0
		return true
	}
	for {
		if p.i >= p.n {
			p.finished = true
			return false
		}
		if c := p.c[p.i]; c < p.i {
			if p.i&1 == 0 {
				c = 0
			}
			p.iface.Swap(c, p.i)
			p.c[p.i]++
			p.i = 0
			return true
		} else {
			p.c[p.i] = 0
			p.i++
		}
	}
}

type intSlice []int

func (s intSlice) Len() int      { return len(s) }
func (s intSlice) Swap(i, j int) { s[i], s[j] = s[j], s[i] }

// Ints is a convenience function for generating permutations of []ints.
func Ints(s []int) *Permuter { return Permute(intSlice(s)) }

type stringSlice []string

func (s stringSlice) Len() int      { return len(s) }
func (s stringSlice) Swap(i, j int) { s[i], s[j] = s[j], s[i] }

// Strings is a convenience function for generating permutations of []strings.
func Strings(s []string) *Permuter { return Permute(stringSlice(s)) }
<|endoftext|>


<commit_before>package hub

import (
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"sync"
	"time"

	"github.com/asadovsky/gosh"
	"github.com/gorilla/websocket"

	"github.com/asadovsky/goatee/server/common"
	"github.com/asadovsky/goatee/server/crdt"
	"github.com/asadovsky/goatee/server/ot"
)

func ok(err error, v ...interface{}) {
	if err != nil {
		panic(fmt.Sprintf("%v: %s", err, fmt.Sprint(v...)))
	}
}

func assert(b bool, v ...interface{}) {
	if !b {
		panic(fmt.Sprint(v...))
	}
}

func jsonMarshal(v interface{}) []byte {
	buf, err := json.Marshal(v)
	ok(err)
	return buf
}

type hub struct {
	clients      map[chan<- []byte]bool // set of active clients
	subscribe    chan chan<- []byte
	unsubscribe  chan chan<- []byte
	broadcast    chan []byte
	mu           sync.Mutex // protects the fields below
	nextClientId int
	text         *ot.Text
	logoot       *crdt.Logoot
}

func newHub() *hub {
	return &hub{
		clients:     make(map[chan<- []byte]bool),
		subscribe:   make(chan chan<- []byte),
		unsubscribe: make(chan chan<- []byte),
		broadcast:   make(chan []byte),
		text:        ot.NewText(""),
		logoot:      crdt.NewLogoot(),
	}
}

func (h *hub) run() {
	for {
		select {
		case c := <-h.subscribe:
			h.clients[c] = true
		case c := <-h.unsubscribe:
			delete(h.clients, c)
		case msg := <-h.broadcast:
			for send := range h.clients {
				send <- msg
			}
		}
	}
}

type stream struct {
	h           *hub
	conn        *websocket.Conn
	send        chan []byte
	initialized bool
	isLogoot    bool
}

func (s *stream) processInitMsg(msg *common.Init) {
	s.h.mu.Lock()
	if s.initialized {
		panic("already initialized")
	}
	s.initialized = true
	s.isLogoot = msg.DataType == "crdt.Logoot"
	if !s.isLogoot {
		assert(msg.DataType == "ot.Text")
	}
	sn := &common.Snapshot{
		Type:     "Snapshot",
		ClientId: s.h.nextClientId,
	}
	if s.isLogoot {
		s.h.logoot.PopulateSnapshot(sn)
	} else {
		s.h.text.PopulateSnapshot(sn)
	}
	ok(s.conn.WriteJSON(sn))
	s.h.nextClientId++
	s.h.subscribe <- s.send
	s.h.mu.Unlock()
}

func (s *stream) processUpdateMsg(msg *common.Update) {
	s.h.mu.Lock()
	if !s.initialized {
		panic("not initialized")
	}
	ch := &common.Change{
		Type:     "Change",
		ClientId: msg.ClientId,
	}
	if s.isLogoot {
		ok(s.h.logoot.ApplyUpdate(msg, ch))
	} else {
		ok(s.h.text.ApplyUpdate(msg, ch))
	}
	s.h.mu.Unlock()
	s.h.broadcast <- jsonMarshal(ch)
}

func (h *hub) handleConn(w http.ResponseWriter, r *http.Request) {
	const bufSize = 1024
	conn, err := websocket.Upgrade(w, r, nil, bufSize, bufSize)
	ok(err)
	s := &stream{h: h, conn: conn, send: make(chan []byte)}
	eof, done := make(chan bool), make(chan bool)

	go func() {
		for {
			_, buf, err := conn.ReadMessage()
			if ce, ok := err.(*websocket.CloseError); ok && (ce.Code == websocket.CloseNormalClosure || ce.Code == websocket.CloseGoingAway) {
				eof <- true
				return
			}
			ok(err)
			// TODO: Avoid decoding multiple times.
			var mt common.MsgType
			ok(json.Unmarshal(buf, &mt))
			switch mt.Type {
			case "Init":
				var msg common.Init
				ok(json.Unmarshal(buf, &msg))
				s.processInitMsg(&msg)
			case "Update":
				var msg common.Update
				ok(json.Unmarshal(buf, &msg))
				s.processUpdateMsg(&msg)
			default:
				panic(fmt.Errorf("unknown message type: %s", mt.Type))
			}
		}
	}()

	go func() {
		for {
			select {
			case msg := <-s.send:
				ok(conn.WriteMessage(websocket.TextMessage, msg))
			case <-eof:
				done <- true
				return
			}
		}
	}()

	log.Printf("WAIT %v", s.send)
	<-done
	log.Printf("EXIT %v", s.send)

	h.mu.Lock()
	if s.initialized {
		h.unsubscribe <- s.send
	}
	h.mu.Unlock()
	close(s.send)
	close(eof)
	close(done)
	conn.Close()
}

func Serve(addr string) error {
	h := newHub()
	go h.run()
	http.HandleFunc("/", h.handleConn)
	go func() {
		time.Sleep(100 * time.Millisecond)
		gosh.SendReady()
	}()
	return http.ListenAndServe(addr, nil)
}
<commit_msg>close channels to signal eof/done<commit_after>package hub

import (
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"sync"
	"time"

	"github.com/asadovsky/gosh"
	"github.com/gorilla/websocket"

	"github.com/asadovsky/goatee/server/common"
	"github.com/asadovsky/goatee/server/crdt"
	"github.com/asadovsky/goatee/server/ot"
)

func ok(err error, v ...interface{}) {
	if err != nil {
		panic(fmt.Sprintf("%v: %s", err, fmt.Sprint(v...)))
	}
}

func assert(b bool, v ...interface{}) {
	if !b {
		panic(fmt.Sprint(v...))
	}
}

func jsonMarshal(v interface{}) []byte {
	buf, err := json.Marshal(v)
	ok(err)
	return buf
}

type hub struct {
	clients      map[chan<- []byte]bool // set of active clients
	subscribe    chan chan<- []byte
	unsubscribe  chan chan<- []byte
	broadcast    chan []byte
	mu           sync.Mutex // protects the fields below
	nextClientId int
	text         *ot.Text
	logoot       *crdt.Logoot
}

func newHub() *hub {
	return &hub{
		clients:     make(map[chan<- []byte]bool),
		subscribe:   make(chan chan<- []byte),
		unsubscribe: make(chan chan<- []byte),
		broadcast:   make(chan []byte),
		text:        ot.NewText(""),
		logoot:      crdt.NewLogoot(),
	}
}

func (h *hub) run() {
	for {
		select {
		case c := <-h.subscribe:
			h.clients[c] = true
		case c := <-h.unsubscribe:
			delete(h.clients, c)
		case msg := <-h.broadcast:
			for send := range h.clients {
				send <- msg
			}
		}
	}
}

type stream struct {
	h           *hub
	conn        *websocket.Conn
	send        chan []byte
	initialized bool
	isLogoot    bool
}

func (s *stream) processInitMsg(msg *common.Init) {
	s.h.mu.Lock()
	if s.initialized {
		panic("already initialized")
	}
	s.initialized = true
	s.isLogoot = msg.DataType == "crdt.Logoot"
	if !s.isLogoot {
		assert(msg.DataType == "ot.Text")
	}
	sn := &common.Snapshot{
		Type:     "Snapshot",
		ClientId: s.h.nextClientId,
	}
	if s.isLogoot {
		s.h.logoot.PopulateSnapshot(sn)
	} else {
		s.h.text.PopulateSnapshot(sn)
	}
	ok(s.conn.WriteJSON(sn))
	s.h.nextClientId++
	s.h.subscribe <- s.send
	s.h.mu.Unlock()
}

func (s *stream) processUpdateMsg(msg *common.Update) {
	s.h.mu.Lock()
	if !s.initialized {
		panic("not initialized")
	}
	ch := &common.Change{
		Type:     "Change",
		ClientId: msg.ClientId,
	}
	if s.isLogoot {
		ok(s.h.logoot.ApplyUpdate(msg, ch))
	} else {
		ok(s.h.text.ApplyUpdate(msg, ch))
	}
	s.h.mu.Unlock()
	s.h.broadcast <- jsonMarshal(ch)
}

func (h *hub) handleConn(w http.ResponseWriter, r *http.Request) {
	const bufSize = 1024
	conn, err := websocket.Upgrade(w, r, nil, bufSize, bufSize)
	ok(err)
	s := &stream{h: h, conn: conn, send: make(chan []byte)}
	eof, done := make(chan struct{}), make(chan struct{})

	go func() {
		for {
			_, buf, err := conn.ReadMessage()
			if ce, ok := err.(*websocket.CloseError); ok && (ce.Code == websocket.CloseNormalClosure || ce.Code == websocket.CloseGoingAway) {
				close(eof)
				return
			}
			ok(err)
			// TODO: Avoid decoding multiple times.
			var mt common.MsgType
			ok(json.Unmarshal(buf, &mt))
			switch mt.Type {
			case "Init":
				var msg common.Init
				ok(json.Unmarshal(buf, &msg))
				s.processInitMsg(&msg)
			case "Update":
				var msg common.Update
				ok(json.Unmarshal(buf, &msg))
				s.processUpdateMsg(&msg)
			default:
				panic(fmt.Errorf("unknown message type: %s", mt.Type))
			}
		}
	}()

	go func() {
		for {
			select {
			case msg := <-s.send:
				ok(conn.WriteMessage(websocket.TextMessage, msg))
			case <-eof:
				close(done)
				return
			}
		}
	}()

	log.Printf("WAIT %v", s.send)
	<-done
	log.Printf("EXIT %v", s.send)

	h.mu.Lock()
	if s.initialized {
		h.unsubscribe <- s.send
	}
	h.mu.Unlock()
	close(s.send)
	conn.Close()
}

func Serve(addr string) error {
	h := newHub()
	go h.run()
	http.HandleFunc("/", h.handleConn)
	go func() {
		time.Sleep(100 * time.Millisecond)
		gosh.SendReady()
	}()
	return http.ListenAndServe(addr, nil)
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"flag"
	"log"
	"net"
	"strconv"
	"strings"

	"gitlab.com/ahmdrz/simon/tree"
)

var simon struct {
	tree *tree.Tree
}

func init() {
	simon.tree = tree.NewTree()
}

func main() {
	port := flag.String("port", "8080", "Server port")
	proto := flag.String("protocol", "tcp", "Server net listen protocol")
	flag.Parse()

	ln, err := net.Listen(*proto, ":"+*port)
	if err != nil {
		log.Println(err)
		return
	}

	for {
		conn, err := ln.Accept()
		if err != nil {
			log.Println(err)
			continue
		}

		go handleRequest(conn)
	}
}

func handleRequest(conn net.Conn) {
	for {
		cmd, err := bufio.NewReader(conn).ReadString('\n')
		if err != nil {
			conn.Write([]byte("BAD REQUEST"))
			conn.Close()
			return
		}
		cmd = strings.TrimSpace(cmd)

		if strings.HasPrefix(cmd, "{") && strings.HasSuffix(cmd, "}") {
			cmd = cmd[1 : len(cmd)-1]
			if strings.HasPrefix(cmd, "set") {
				cmd = strings.Replace(cmd, "set", "", -1)
				cmd = strings.TrimSpace(cmd)
				parts := strings.Split(cmd, "=")
				if len(parts) == 2 {
					simon.tree.Set(parts[0], parts[1])
					conn.Write([]byte(parts[0] + " = " + parts[1] + "\n"))
				} else {
					conn.Write([]byte("INVALID\n"))
				}
			} else if strings.HasPrefix(cmd, "get") {
				cmd = strings.Replace(cmd, "get", "", -1)
				cmd = strings.TrimSpace(cmd)
				value := simon.tree.Get(cmd)
				conn.Write([]byte(cmd + " = " + value + "\n"))
			} else if strings.HasPrefix(cmd, "del") {
				cmd = strings.Replace(cmd, "del", "", -1)
				cmd = strings.TrimSpace(cmd)
				simon.tree.Del(cmd)
				conn.Write([]byte(cmd + " REMOVED\n"))
			} else if cmd == "clr" {
				n := simon.tree.Clr()
				conn.Write([]byte("MEMORY CLEARED (" + strconv.Itoa(n) + ")\n"))
			} else {
				conn.Write([]byte("INVALID\n"))
			}
		} else {
			conn.Write([]byte("INVALID\n"))
		}
	}
}
<commit_msg>Update simorgh.go<commit_after>package main

import (
	"bufio"
	"flag"
	"log"
	"net"
	"strconv"
	"strings"

	"github.com/ahmdrz/simorgh/tree"
)

var simon struct {
	tree *tree.Tree
}

func init() {
	simon.tree = tree.NewTree()
}

func main() {
	port := flag.String("port", "8080", "Server port")
	proto := flag.String("protocol", "tcp", "Server net listen protocol")
	flag.Parse()

	ln, err := net.Listen(*proto, ":"+*port)
	if err != nil {
		log.Println(err)
		return
	}

	for {
		conn, err := ln.Accept()
		if err != nil {
			log.Println(err)
			continue
		}

		go handleRequest(conn)
	}
}

func handleRequest(conn net.Conn) {
	for {
		cmd, err := bufio.NewReader(conn).ReadString('\n')
		if err != nil {
			conn.Write([]byte("BAD REQUEST"))
			conn.Close()
			return
		}
		cmd = strings.TrimSpace(cmd)

		if strings.HasPrefix(cmd, "{") && strings.HasSuffix(cmd, "}") {
			cmd = cmd[1 : len(cmd)-1]
			if strings.HasPrefix(cmd, "set") {
				cmd = strings.Replace(cmd, "set", "", -1)
				cmd = strings.TrimSpace(cmd)
				parts := strings.Split(cmd, "=")
				if len(parts) == 2 {
					simon.tree.Set(parts[0], parts[1])
					conn.Write([]byte(parts[0] + " = " + parts[1] + "\n"))
				} else {
					conn.Write([]byte("INVALID\n"))
				}
			} else if strings.HasPrefix(cmd, "get") {
				cmd = strings.Replace(cmd, "get", "", -1)
				cmd = strings.TrimSpace(cmd)
				value := simon.tree.Get(cmd)
				conn.Write([]byte(cmd + " = " + value + "\n"))
			} else if strings.HasPrefix(cmd, "del") {
				cmd = strings.Replace(cmd, "del", "", -1)
				cmd = strings.TrimSpace(cmd)
				simon.tree.Del(cmd)
				conn.Write([]byte(cmd + " REMOVED\n"))
			} else if cmd == "clr" {
				n := simon.tree.Clr()
				conn.Write([]byte("MEMORY CLEARED (" + strconv.Itoa(n) + ")\n"))
			} else {
				conn.Write([]byte("INVALID\n"))
			}
		} else {
			conn.Write([]byte("INVALID\n"))
		}
	}
}
<|endoftext|>


<commit_before>package network

import (
	"errors"
	"net"
)

type Network struct{}

const name = "network"

func (self *Network) Name() string {
	return name
}

func (self *Network) Collect() (result interface{}, err error) {
	result, err = getNetworkInfo()
	return
}

type Ipv6Address struct{}

func externalIpv6Address() (string, error) {
	ifaces, err := net.Interfaces()

	if err != nil {
		return "", err
	}

	for _, iface := range ifaces {
		if iface.Flags&net.FlagUp == 0 || iface.Flags&net.FlagLoopback != 0 {
			// interface down or loopback interface
			continue
		}
		addrs, err := iface.Addrs()
		if err != nil {
			return "", err
		}
		for _, addr := range addrs {
			var ip net.IP
			switch v := addr.(type) {
			case *net.IPNet:
				ip = v.IP
			case *net.IPAddr:
				ip = v.IP
			}
			if ip == nil || ip.IsLoopback() {
				continue
			}
			if ip.To4() != nil {
				// ipv4 address
				continue
			}
			return ip.String(), nil
		}
	}

	// We don't return an error if no IPv6 interface has been found. Indeed,
	// some orgs just don't have IPv6 enabled. If there's a network error, it
	// will pop out when getting the Mac address and/or the IPv4 address
	// (before this function's call; see network.go -> getNetworkInfo())
	return "", nil
}

type IpAddress struct{}

func externalIpAddress() (string, error) {
	ifaces, err := net.Interfaces()

	if err != nil {
		return "", err
	}

	for _, iface := range ifaces {
		if iface.Flags&net.FlagUp == 0 || iface.Flags&net.FlagLoopback != 0 {
			// interface down or loopback interface
			continue
		}
		addrs, err := iface.Addrs()
		if err != nil {
			return "", err
		}
		for _, addr := range addrs {
			var ip net.IP
			switch v := addr.(type) {
			case *net.IPNet:
				ip = v.IP
			case *net.IPAddr:
				ip = v.IP
			}
			if ip == nil || ip.IsLoopback() {
				continue
			}
			ip = ip.To4()
			if ip == nil {
				// not an ipv4 address
				continue
			}
			return ip.String(), nil
		}
	}
	return "", errors.New("not connected to the network")
}

type MacAddress struct{}

func macAddress() (string, error) {
	ifaces, err := net.Interfaces()

	if err != nil {
		return "", err
	}

	for _, iface := range ifaces {
		if iface.Flags&net.FlagUp == 0 || iface.Flags&net.FlagLoopback != 0 {
			// interface down or loopback interface
			continue
		}
		addrs, err := iface.Addrs()
		if err != nil {
			return "", err
		}
		for _, addr := range addrs {
			var ip net.IP
			switch v := addr.(type) {
			case *net.IPNet:
				ip = v.IP
			case *net.IPAddr:
				ip = v.IP
			}
			if ip == nil || ip.IsLoopback() || ip.To4() == nil {
				continue
			}
			return iface.HardwareAddr.String(), nil
		}
	}
	return "", errors.New("not connected to the network")
}
<commit_msg>collect network info from other nics<commit_after>package network

import (
	"errors"
	"net"
)

type Network struct{}

const name = "network"

func (self *Network) Name() string {
	return name
}

func (self *Network) Collect() (result interface{}, err error) {
	result, err = getNetworkInfo()
	interfaces := getMultiNetworkInfo()
	if interfaces != nil {
		result["interfaces"] = interfaces
	}
	return
}

func getMultiNetworkInfo() (multiNetworkInfo []map[string]interface{}) {
	ifaces, err := net.Interfaces()

	if err != nil {
		return nil
	}
	_iface := make(map[string]interface{})
	for _, iface := range ifaces {
		if iface.Flags&net.FlagUp == 0 || iface.Flags&net.FlagLoopback != 0 {
			// interface down or loopback interface
			continue
		}
		addrs, err := iface.Addrs()
		if err != nil {
			return nil
		}
		for _, addr := range addrs {
			ip, network, _ := net.ParseCIDR(addr.String())
			if ip == nil || ip.IsLoopback() {
				continue
			}
			if ip.To4() == nil {
				_iface["ipv6"] = ip.String()
				_iface["ipv6-network"] = network.String()
			} else {
				_iface["ipv4"] = ip.String()
				_iface["ipv4-network"] = network.String()
			}
			if len(iface.HardwareAddr.String()) > 0 {
				_iface["macaddress"] = iface.HardwareAddr.String()
			}
		}
		if len(_iface) > 0 {
			_iface["name"] = iface.Name
			multiNetworkInfo = append(multiNetworkInfo, _iface)
		}
	}
	if len(multiNetworkInfo) > 0 {
		return multiNetworkInfo
	}
	return nil
}

type Ipv6Address struct{}

func externalIpv6Address() (string, error) {
	ifaces, err := net.Interfaces()

	if err != nil {
		return "", err
	}

	for _, iface := range ifaces {
		if iface.Flags&net.FlagUp == 0 || iface.Flags&net.FlagLoopback != 0 {
			// interface down or loopback interface
			continue
		}
		addrs, err := iface.Addrs()
		if err != nil {
			return "", err
		}
		for _, addr := range addrs {
			var ip net.IP
			switch v := addr.(type) {
			case *net.IPNet:
				ip = v.IP
			case *net.IPAddr:
				ip = v.IP
			}
			if ip == nil || ip.IsLoopback() {
				continue
			}
			if ip.To4() != nil {
				// ipv4 address
				continue
			}
			return ip.String(), nil
		}
	}

	// We don't return an error if no IPv6 interface has been found. Indeed,
	// some orgs just don't have IPv6 enabled. If there's a network error, it
	// will pop out when getting the Mac address and/or the IPv4 address
	// (before this function's call; see network.go -> getNetworkInfo())
	return "", nil
}

type IpAddress struct{}

func externalIpAddress() (string, error) {
	ifaces, err := net.Interfaces()

	if err != nil {
		return "", err
	}

	for _, iface := range ifaces {
		if iface.Flags&net.FlagUp == 0 || iface.Flags&net.FlagLoopback != 0 {
			// interface down or loopback interface
			continue
		}
		addrs, err := iface.Addrs()
		if err != nil {
			return "", err
		}
		for _, addr := range addrs {
			var ip net.IP
			switch v := addr.(type) {
			case *net.IPNet:
				ip = v.IP
			case *net.IPAddr:
				ip = v.IP
			}
			if ip == nil || ip.IsLoopback() {
				continue
			}
			ip = ip.To4()
			if ip == nil {
				// not an ipv4 address
				continue
			}
			return ip.String(), nil
		}
	}
	return "", errors.New("not connected to the network")
}

type MacAddress struct{}

func macAddress() (string, error) {
	ifaces, err := net.Interfaces()

	if err != nil {
		return "", err
	}

	for _, iface := range ifaces {
		if iface.Flags&net.FlagUp == 0 || iface.Flags&net.FlagLoopback != 0 {
			// interface down or loopback interface
			continue
		}
		addrs, err := iface.Addrs()
		if err != nil {
			return "", err
		}
		for _, addr := range addrs {
			var ip net.IP
			switch v := addr.(type) {
			case *net.IPNet:
				ip = v.IP
			case *net.IPAddr:
				ip = v.IP
			}
			if ip == nil || ip.IsLoopback() || ip.To4() == nil {
				continue
			}
			return iface.HardwareAddr.String(), nil
		}
	}
	return "", errors.New("not connected to the network")
}
<|endoftext|>


<commit_before>/*
Copyright 2017 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package model

import (
	"errors"
	"fmt"
	"strings"

	"k8s.io/kops/pkg/apis/kops"
	"k8s.io/kops/pkg/systemd"
	"k8s.io/kops/upup/pkg/fi"
	"k8s.io/kops/upup/pkg/fi/nodeup/nodetasks"

	"github.com/golang/glog"
)

// HookBuilder configures the hooks
type HookBuilder struct {
	*NodeupModelContext
}

var _ fi.ModelBuilder = &HookBuilder{}

// Build is responsible for implementing the cluster hook
func (h *HookBuilder) Build(c *fi.ModelBuilderContext) error {
	// we keep a list of hooks name so we can allow local instanceGroup hooks override the cluster ones
	hookNames := make(map[string]bool, 0)
	for i, spec := range []*[]kops.HookSpec{&h.InstanceGroup.Spec.Hooks, &h.Cluster.Spec.Hooks} {
		for j, hook := range *spec {
			isInstanceGroup := i == 0
			// filter roles if required
			if len(hook.Roles) > 0 && !containsRole(h.InstanceGroup.Spec.Role, hook.Roles) {
				continue
			}

			// i dont want to effect those whom are already using the hooks, so i'm gonna try an keep the name for now
			// i.e. use the default naming convention - kops-hook-<index>, only those using the Name or hooks in IG should alter
			var name string
			switch hook.Name {
			case "":
				name = fmt.Sprintf("kops-hook-%d", j)
				if isInstanceGroup {
					name = fmt.Sprintf("%s-ig", name)
				}
			default:
				name = hook.Name
			}

			if _, found := hookNames[name]; found {
				glog.V(2).Infof("Skipping the hook: %v as we've already processed a similar service name", name)
				continue
			}
			hookNames[name] = true

			// are we disabling the service?
			if hook.Disabled {
				enabled := false
				managed := true
				c.AddTask(&nodetasks.Service{
					Name:        hook.Name,
					ManageState: &managed,
					Enabled:     &enabled,
					Running:     &enabled,
				})
				continue
			}

			service, err := h.buildSystemdService(name, &hook)
			if err != nil {
				return err
			}

			if service != nil {
				c.AddTask(service)
			}
		}
	}

	return nil
}

// buildSystemdService is responsible for generating the service
func (h *HookBuilder) buildSystemdService(name string, hook *kops.HookSpec) (*nodetasks.Service, error) {
	// perform some basic validation
	if hook.ExecContainer == nil && hook.Manifest == "" {
		glog.Warningf("hook: %s has neither a raw unit or exec image configured", name)
		return nil, nil
	}
	if hook.ExecContainer != nil {
		if err := isValidExecContainerAction(hook.ExecContainer); err != nil {
			glog.Warningf("invalid hook action, name: %s, error: %v", name, err)
			return nil, nil
		}
	}
	// build the base unit file
	unit := &systemd.Manifest{}
	unit.Set("Unit", "Description", "Kops Hook "+name)

	// add any service dependencies to the unit
	for _, x := range hook.Requires {
		unit.Set("Unit", "Requires", x)
	}
	for _, x := range hook.Before {
		unit.Set("Unit", "Before", x)
	}

	// are we a raw unit file or a docker exec?
	switch hook.ExecContainer {
	case nil:
		unit.SetSection("Service", hook.Manifest)
	default:
		if err := h.buildDockerService(unit, hook); err != nil {
			return nil, err
		}
	}

	service := &nodetasks.Service{
		Name:       name,
		Definition: s(unit.Render()),
	}

	service.InitDefaults()

	return service, nil
}

// buildDockerService is responsible for generating a docker exec unit file
func (h *HookBuilder) buildDockerService(unit *systemd.Manifest, hook *kops.HookSpec) error {
	dockerArgs := []string{
		"/usr/bin/docker", "run",
		"-v", "/:/rootfs/",
		"-v", "/var/run/dbus:/var/run/dbus",
		"-v", "/run/systemd:/run/systemd",
		"--net=host",
		"--privileged",
	}
	dockerArgs = append(dockerArgs, buildDockerEnvironmentVars(hook.ExecContainer.Environment)...)
	dockerArgs = append(dockerArgs, hook.ExecContainer.Image)
	dockerArgs = append(dockerArgs, hook.ExecContainer.Command...)

	dockerRunCommand := systemd.EscapeCommand(dockerArgs)
	dockerPullCommand := systemd.EscapeCommand([]string{"/usr/bin/docker", "pull", hook.ExecContainer.Image})

	unit.Set("Unit", "Requires", "docker.service")
	unit.Set("Service", "ExecStartPre", dockerPullCommand)
	unit.Set("Service", "ExecStart", dockerRunCommand)
	unit.Set("Service", "Type", "oneshot")
	unit.Set("Install", "WantedBy", "multi-user.target")

	return nil
}

// isValidExecContainerAction checks the validatity of the execContainer - personally i think this validation
// should be done high up the chain, but
func isValidExecContainerAction(action *kops.ExecContainerAction) error {
	action.Image = strings.TrimSpace(action.Image)
	if action.Image == "" {
		return errors.New("the image for the hook exec action not set")
	}

	return nil
}
<commit_msg>Added .service to hooks unit files<commit_after>/*
Copyright 2017 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package model

import (
	"errors"
	"fmt"
	"strings"

	"k8s.io/kops/pkg/apis/kops"
	"k8s.io/kops/pkg/systemd"
	"k8s.io/kops/upup/pkg/fi"
	"k8s.io/kops/upup/pkg/fi/nodeup/nodetasks"

	"github.com/golang/glog"
)

// HookBuilder configures the hooks
type HookBuilder struct {
	*NodeupModelContext
}

var _ fi.ModelBuilder = &HookBuilder{}

// Build is responsible for implementing the cluster hook
func (h *HookBuilder) Build(c *fi.ModelBuilderContext) error {
	// we keep a list of hooks name so we can allow local instanceGroup hooks override the cluster ones
	hookNames := make(map[string]bool, 0)
	for i, spec := range []*[]kops.HookSpec{&h.InstanceGroup.Spec.Hooks, &h.Cluster.Spec.Hooks} {
		for j, hook := range *spec {
			isInstanceGroup := i == 0
			// filter roles if required
			if len(hook.Roles) > 0 && !containsRole(h.InstanceGroup.Spec.Role, hook.Roles) {
				continue
			}

			// i dont want to effect those whom are already using the hooks, so i'm gonna try an keep the name for now
			// i.e. use the default naming convention - kops-hook-<index>, only those using the Name or hooks in IG should alter
			var name string
			switch hook.Name {
			case "":
				name = fmt.Sprintf("kops-hook-%d.service", j)
				if isInstanceGroup {
					name = fmt.Sprintf("%s-ig.service", name)
				}
			default:
				name = fmt.Sprintf("%s.service", hook.Name)
			}

			if _, found := hookNames[name]; found {
				glog.V(2).Infof("Skipping the hook: %v as we've already processed a similar service name", name)
				continue
			}
			hookNames[name] = true

			// are we disabling the service?
			if hook.Disabled {
				enabled := false
				managed := true
				c.AddTask(&nodetasks.Service{
					Name:        hook.Name,
					ManageState: &managed,
					Enabled:     &enabled,
					Running:     &enabled,
				})
				continue
			}

			service, err := h.buildSystemdService(name, &hook)
			if err != nil {
				return err
			}

			if service != nil {
				c.AddTask(service)
			}
		}
	}

	return nil
}

// buildSystemdService is responsible for generating the service
func (h *HookBuilder) buildSystemdService(name string, hook *kops.HookSpec) (*nodetasks.Service, error) {
	// perform some basic validation
	if hook.ExecContainer == nil && hook.Manifest == "" {
		glog.Warningf("hook: %s has neither a raw unit or exec image configured", name)
		return nil, nil
	}
	if hook.ExecContainer != nil {
		if err := isValidExecContainerAction(hook.ExecContainer); err != nil {
			glog.Warningf("invalid hook action, name: %s, error: %v", name, err)
			return nil, nil
		}
	}
	// build the base unit file
	unit := &systemd.Manifest{}
	unit.Set("Unit", "Description", "Kops Hook "+name)

	// add any service dependencies to the unit
	for _, x := range hook.Requires {
		unit.Set("Unit", "Requires", x)
	}
	for _, x := range hook.Before {
		unit.Set("Unit", "Before", x)
	}

	// are we a raw unit file or a docker exec?
	switch hook.ExecContainer {
	case nil:
		unit.SetSection("Service", hook.Manifest)
	default:
		if err := h.buildDockerService(unit, hook); err != nil {
			return nil, err
		}
	}

	service := &nodetasks.Service{
		Name:       name,
		Definition: s(unit.Render()),
	}

	service.InitDefaults()

	return service, nil
}

// buildDockerService is responsible for generating a docker exec unit file
func (h *HookBuilder) buildDockerService(unit *systemd.Manifest, hook *kops.HookSpec) error {
	dockerArgs := []string{
		"/usr/bin/docker", "run",
		"-v", "/:/rootfs/",
		"-v", "/var/run/dbus:/var/run/dbus",
		"-v", "/run/systemd:/run/systemd",
		"--net=host",
		"--privileged",
	}
	dockerArgs = append(dockerArgs, buildDockerEnvironmentVars(hook.ExecContainer.Environment)...)
	dockerArgs = append(dockerArgs, hook.ExecContainer.Image)
	dockerArgs = append(dockerArgs, hook.ExecContainer.Command...)

	dockerRunCommand := systemd.EscapeCommand(dockerArgs)
	dockerPullCommand := systemd.EscapeCommand([]string{"/usr/bin/docker", "pull", hook.ExecContainer.Image})

	unit.Set("Unit", "Requires", "docker.service")
	unit.Set("Service", "ExecStartPre", dockerPullCommand)
	unit.Set("Service", "ExecStart", dockerRunCommand)
	unit.Set("Service", "Type", "oneshot")
	unit.Set("Install", "WantedBy", "multi-user.target")

	return nil
}

// isValidExecContainerAction checks the validatity of the execContainer - personally i think this validation
// should be done high up the chain, but
func isValidExecContainerAction(action *kops.ExecContainerAction) error {
	action.Image = strings.TrimSpace(action.Image)
	if action.Image == "" {
		return errors.New("the image for the hook exec action not set")
	}

	return nil
}
<|endoftext|>


<commit_before>package ogdatv21

import (
	"encoding/csv"
	"fmt"
	"github.com/the42/ogdat"
	"io"
	"log"
	"os"
	"reflect"
)

type ISO6392Lang struct {
	Code, Identifier string
}

const iso639file = "ISO-639-2_utf-8.txt"
const schema_langauge = "ger"
const schema_characterset = "utf8"

var isolangfilemap map[string]*ISO6392Lang

func loadisolanguagefile(filename string) (isolangfilemap map[string]*ISO6392Lang) {
	reader, err := os.Open(iso639file)

	if err == nil {
		defer reader.Close()
		isolangfilemap = make(map[string]*ISO6392Lang)
		csvreader := csv.NewReader(reader)
		csvreader.Comma = '|'

		for record, err := csvreader.Read(); err != io.EOF; record, err = csvreader.Read() {
			isorecord := &ISO6392Lang{Code: record[0], Identifier: record[3]}
			isolangfilemap[record[0]] = isorecord
			if len(record[1]) > 0 {
				isorecord = &ISO6392Lang{Code: record[1], Identifier: record[3]}
				isolangfilemap[record[1]] = isorecord
			}
		}
		log.Printf("Info: Read %d ISO language records", len(isolangfilemap))
	} else {
		log.Printf("Warning: Can not read ISO language records")
	}
	return
}

func (md *MetaData) Check(checklinks bool) (message []ogdat.CheckMessage, err error) {
	const pflichtfeldfehlt = "Pflichtfeld nicht gesetzt"

	ogdset := ogdat.GetOGDSetForVersion(Version)
	if ogdset == nil {
		return nil, fmt.Errorf("Beschreibung für OGD Version %s ist nicht vorhanden, check kann nicht durchgeführt werden", Version)
	}

nextbeschreibung:
	for _, elm := range ogdset.Beschreibung {

		// check required fields for their presence. However, if the
		// cardinality on a required fiels is defined as 'N', it may be ok
		// that the field is not present, in which case we check
		// later explicitely and issue a warning
		if elm.IsRequired() && elm.Anzahl != "N" {
			ielements := reflect.TypeOf(md).Elem().NumField()
			for i := 0; i < ielements; i++ {
				f := reflect.TypeOf(md).Elem().Field(i)
				if ogdat.GetIDFromMetaDataStructField(f) == elm.ID && ogdat.MetaDataStructFieldIsNil(f) {
					message = append(message, ogdat.CheckMessage{Type: 3, OGDID: ogdat.GetIDFromMetaDataStructField(f), Text: pflichtfeldfehlt})
					break nextbeschreibung // required field is not present - nothing more to check
				}
			}
		}

		switch elm.OGD_Kurzname {
		case "metadata_identifier":
			if md.Extras.Metadata_Identifier.UUID == nil {
				message = append(message, ogdat.CheckMessage{
					Type:  3,
					OGDID: elm.ID,
					Text:  fmt.Sprintf("Feldwert vom Typ UUID erwartet, Wert ist aber keine UUID: '%s'", md.Extras.Metadata_Identifier.Raw)})
			}
		case "metadata_modified":
			if md.Extras.Metadata_Modified.Format != CustomTimeSpecifier2 {
				message = append(message, ogdat.CheckMessage{
					Type:  3,
					OGDID: elm.ID,
					Text:  fmt.Sprintf("Feldwert vom Typ ÖNORM ISO 8601 'YYYY-MM-DD' erwartet, Wert entspricht aber nicht diesem Typ: '%s'", md.Extras.Metadata_Modified.Raw)})
			}
		case "title":
			if ok, err := ogdat.CheckOGDTextStringForSaneCharacters(*md.Title); !ok {
				if cerr, ok := err.(*ogdat.CheckError); ok {
					message = append(message, ogdat.CheckMessage{
						Type:  cerr.Status,
						OGDID: elm.ID,
						Text:  fmt.Sprintf("Zeichenfolge enthält potentiell ungeeignete Zeichen ab Position %d: '%s'", cerr.Position, cerr)})
				}
			}
		case "description":
			if ok, err := ogdat.CheckOGDTextStringForSaneCharacters(*md.Description); !ok {
				if cerr, ok := err.(*ogdat.CheckError); ok {
					message = append(message, ogdat.CheckMessage{
						Type:  cerr.Status,
						OGDID: elm.ID,
						Text:  fmt.Sprintf("Zeichenfolge enthält potentiell ungeeignete Zeichen ab Position %d: '%s'", cerr.Position, cerr)})
				}
			}
		case "categorization":
			if cat := md.Extras.Categorization; cat == nil {
				message = append(message, ogdat.CheckMessage{
					Type:  2,
					OGDID: elm.ID,
					Text:  "Die Kategorisierung darf zwar mit Kardinalität 'N' optional auftreten, jedoch sollte zumindest eine Zuordnung getroffen werden"})

			} else {
				for _, element := range cat {
					if element.NumID == -1 {
						message = append(message, ogdat.CheckMessage{
							Type:  3,
							OGDID: elm.ID,
							Text:  fmt.Sprintf("Die Kategorie '%s' ist keine normierte OGD-Kategorie", element.ID)})
					}
				}
			}
		case "keywords":
			if keywords := md.Schlagworte; keywords == nil {
				message = append(message, ogdat.CheckMessage{
					Type:  2,
					OGDID: elm.ID,
					Text:  "Schlagworte dürfen zwar mit Kardinalität 'N' optional auftreten, die Angabe von Schlagworten wäre aber wünschenswert"})

			}
		case "maintainer":
			if ok, err := ogdat.CheckOGDTextStringForSaneCharacters(*md.Maintainer); !ok {
				if cerr, ok := err.(*ogdat.CheckError); ok {
					message = append(message, ogdat.CheckMessage{
						Type:  cerr.Status,
						OGDID: elm.ID,
						Text:  fmt.Sprintf("Zeichenfolge enthält potentiell ungeeignete Zeichen ab Position %d: '%s'", cerr.Position, cerr)})
				}
			}
		case "license":
			if ok, err := ogdat.CheckOGDTextStringForSaneCharacters(*md.License); !ok {
				if cerr, ok := err.(*ogdat.CheckError); ok {
					message = append(message, ogdat.CheckMessage{
						Type:  cerr.Status,
						OGDID: elm.ID,
						Text:  fmt.Sprintf("Zeichenfolge enthält potentiell ungeeignete Zeichen ab Position %d: '%s'", cerr.Position, cerr)})
				}
			}
		case "begin_datetime":
			if md.Extras.Begin_DateTime.Format != CustomTimeSpecifier1 {
				message = append(message, ogdat.CheckMessage{
					Type:  3,
					OGDID: elm.ID,
					Text:  fmt.Sprintf("Feldwert vom Typ ÖNORM ISO 8601 TM_Primitive 'YYYY-MM-DDThh:mm:ss' erwartet, Wert entspricht aber nicht diesem Typ: '%s'", md.Extras.Begin_DateTime.Raw)})
			}
			// ###################### OPTIONALE FELDER ######################
		case "schema_name":
			if schemaname := md.Extras.Schema_Name; schemaname != nil {
				if ok, err := ogdat.CheckOGDTextStringForSaneCharacters(*schemaname); !ok {
					if cerr, ok := err.(*ogdat.CheckError); ok {
						message = append(message, ogdat.CheckMessage{
							Type:  cerr.Status,
							OGDID: elm.ID,
							Text:  fmt.Sprintf("Zeichenfolge enthält potentiell ungeeignete Zeichen ab Position %d: '%s'", cerr.Position, cerr)})
					}
					const ogdschemaspec = "OGD Austria Metadata 2.0"
					if *schemaname != ogdschemaspec {
						message = append(message, ogdat.CheckMessage{
							Type:  1,
							OGDID: elm.ID,
							Text:  fmt.Sprintf("Schemabezeichnung als '%s' erwartet, der Wert ist aber '%s'", ogdschemaspec, *schemaname)})
					}
				}
			}
		case "schema_language":
			if lang := md.Extras.Schema_Language; lang != nil {
				const ogdschemalanguage = "ger"
				if *lang != ogdschemalanguage {
					message = append(message, ogdat.CheckMessage{
						Type:  3,
						OGDID: elm.ID,
						Text:  fmt.Sprintf("Schemasprache als '%s' erwartet, der Wert ist aber '%s'", ogdschemalanguage, *lang)})
				}
			}
		case "schema_characterset":
			if charset := md.Extras.Schema_Characterset; charset != nil {
				const ogdschemacharacterset = "utf8"
				if *charset != ogdschemacharacterset {
					message = append(message, ogdat.CheckMessage{
						Type:  3,
						OGDID: elm.ID,
						Text:  fmt.Sprintf("Characterset des Schemas als '%s' erwartet, der Wert ist aber '%s'", ogdschemacharacterset, *charset)})
				}
			}
		case "metadata_linkage":
			for _, element := range md.Extras.Metadata_Linkage {
				if element.URL == nil {
					message = append(message, ogdat.CheckMessage{
						Type:  3,
						OGDID: elm.ID,
						Text:  fmt.Sprintf("Gültigen Verweis (Link) erwartet, der Wert '%s' stellt keinen gültigen Link dar", element.Raw)})
				}
			}
		case "attribute_description":
			if desc := md.Extras.Attribute_Description; desc != nil {
				const ogddesclen = 20
				if i := len(*desc); i < ogddesclen {
					message = append(message, ogdat.CheckMessage{
						Type:  2,
						OGDID: elm.ID,
						Text:  fmt.Sprintf("Beschreibung enthält weniger als %d Zeichen", i)})

				}
				if ok, err := ogdat.CheckOGDTextStringForSaneCharacters(*desc); !ok {
					if cerr, ok := err.(*ogdat.CheckError); ok {
						message = append(message, ogdat.CheckMessage{
							Type:  cerr.Status,
							OGDID: elm.ID,
							Text:  fmt.Sprintf("Zeichenfolge enthält potentiell ungeeignete Zeichen ab Position %d: '%s'", cerr.Position, cerr)})
					}
				}
			}
		case "maintainer_link":
			if link := md.Extras.Maintainer_Link; link != nil {
				if link.URL == nil {
					message = append(message, ogdat.CheckMessage{
						Type:  3,
						OGDID: elm.ID,
						Text:  fmt.Sprintf("Gültigen Verweis (Link) erwartet, der Wert '%s' stellt keinen gültigen Link dar", link.Raw)})
				}
			}
		case "publisher":
			if publisher := md.Extras.Publisher; publisher != nil {
				if ok, err := ogdat.CheckOGDTextStringForSaneCharacters(*publisher); !ok {
					if cerr, ok := err.(*ogdat.CheckError); ok {
						message = append(message, ogdat.CheckMessage{
							Type:  cerr.Status,
							OGDID: elm.ID,
							Text:  fmt.Sprintf("Zeichenfolge enthält potentiell ungeeignete Zeichen ab Position %d: '%s'", cerr.Position, cerr)})
					}
				}
			}
		case "geographic_toponym":
			if toponym := md.Extras.Geographich_Toponym; toponym != nil {
				if ok, err := ogdat.CheckOGDTextStringForSaneCharacters(*toponym); !ok {
					if cerr, ok := err.(*ogdat.CheckError); ok {
						message = append(message, ogdat.CheckMessage{
							Type:  cerr.Status,
							OGDID: elm.ID,
							Text:  fmt.Sprintf("Zeichenfolge enthält potentiell ungeeignete Zeichen ab Position %d: '%s'", cerr.Position, cerr)})
					}
				}
			}
		case "geographic_bbox":
			if bbox := md.Extras.Geographic_BBox; bbox != nil {
				if ok, err := ogdat.CheckOGDBBox(*bbox); !ok {
					message = append(message, ogdat.CheckMessage{
						Type:  3,
						OGDID: elm.ID,
						Text:  fmt.Sprintf("Zeichenfolge enthält keinen gültigen WKT für die örtliche Begrenzung (Boundingbox): '%s'", err)})
				}
			}
		case "end_datetime":
			if endtime := md.Extras.End_DateTime; endtime != nil {
				if endtime.Format != CustomTimeSpecifier1 {
					message = append(message, ogdat.CheckMessage{
						Type:  3,
						OGDID: elm.ID,
						Text:  fmt.Sprintf("Feldwert vom Typ ÖNORM ISO 8601 TM_Primitive 'YYYY-MM-DDThh:mm:ss' erwartet, Wert entspricht aber nicht diesem Typ: '%s'", endtime.Raw)})
				}
			}
		case "update_frequency":
			if frequency := md.Extras.Update_Frequency; frequency != nil {

			}
		}
	}
	return

	/*
		 		case "resource_url":
			  if keywords := md.Resources.Url; keywords == nil {
			  message = append(message, ogdat.CheckMessage{
						Type:  2,
						OGDID: elm.ID,
						Text:  "Schlagworte dürfen zwar mit Karidnalität 'N' optional auftreten, die Angabe von Schlagworten wäre aber wünschenswert"})
	*/
}

func init() {
	isolangfilemap = loadisolanguagefile(iso639file)
}
<commit_msg>simplify code by getting rid of indention levels in if<commit_after>package ogdatv21

import (
	"encoding/csv"
	"fmt"
	"github.com/the42/ogdat"
	"io"
	"log"
	"os"
	"reflect"
)

type ISO6392Lang struct {
	Code, Identifier string
}

const iso639file = "ISO-639-2_utf-8.txt"
const schema_langauge = "ger"
const schema_characterset = "utf8"

var isolangfilemap map[string]*ISO6392Lang

func loadisolanguagefile(filename string) (isolangfilemap map[string]*ISO6392Lang) {
	reader, err := os.Open(iso639file)

	if err == nil {
		defer reader.Close()
		isolangfilemap = make(map[string]*ISO6392Lang)
		csvreader := csv.NewReader(reader)
		csvreader.Comma = '|'

		for record, err := csvreader.Read(); err != io.EOF; record, err = csvreader.Read() {
			isorecord := &ISO6392Lang{Code: record[0], Identifier: record[3]}
			isolangfilemap[record[0]] = isorecord
			if len(record[1]) > 0 {
				isorecord = &ISO6392Lang{Code: record[1], Identifier: record[3]}
				isolangfilemap[record[1]] = isorecord
			}
		}
		log.Printf("Info: Read %d ISO language records", len(isolangfilemap))
	} else {
		log.Printf("Warning: Can not read ISO language records")
	}
	return
}

func (md *MetaData) Check(checklinks bool) (message []ogdat.CheckMessage, err error) {
	const pflichtfeldfehlt = "Pflichtfeld nicht gesetzt"

	ogdset := ogdat.GetOGDSetForVersion(Version)
	if ogdset == nil {
		return nil, fmt.Errorf("Beschreibung für OGD Version %s ist nicht vorhanden, check kann nicht durchgeführt werden", Version)
	}

nextbeschreibung:
	for _, elm := range ogdset.Beschreibung {

		// check required fields for their presence. However, if the
		// cardinality on a required fiels is defined as 'N', it may be ok
		// that the field is not present, in which case we check
		// later explicitely and issue a warning
		if elm.IsRequired() && elm.Anzahl != "N" {
			ielements := reflect.TypeOf(md).Elem().NumField()
			for i := 0; i < ielements; i++ {
				f := reflect.TypeOf(md).Elem().Field(i)
				if ogdat.GetIDFromMetaDataStructField(f) == elm.ID && ogdat.MetaDataStructFieldIsNil(f) {
					message = append(message, ogdat.CheckMessage{Type: 3, OGDID: ogdat.GetIDFromMetaDataStructField(f), Text: pflichtfeldfehlt})
					break nextbeschreibung // required field is not present - nothing more to check
				}
			}
		}

		switch elm.OGD_Kurzname {
		case "metadata_identifier":
			if md.Extras.Metadata_Identifier.UUID == nil {
				message = append(message, ogdat.CheckMessage{
					Type:  3,
					OGDID: elm.ID,
					Text:  fmt.Sprintf("Feldwert vom Typ UUID erwartet, Wert ist aber keine UUID: '%s'", md.Extras.Metadata_Identifier.Raw)})
			}
		case "metadata_modified":
			if md.Extras.Metadata_Modified.Format != CustomTimeSpecifier2 {
				message = append(message, ogdat.CheckMessage{
					Type:  3,
					OGDID: elm.ID,
					Text:  fmt.Sprintf("Feldwert vom Typ ÖNORM ISO 8601 'YYYY-MM-DD' erwartet, Wert entspricht aber nicht diesem Typ: '%s'", md.Extras.Metadata_Modified.Raw)})
			}
		case "title":
			if ok, err := ogdat.CheckOGDTextStringForSaneCharacters(*md.Title); !ok {
				if cerr, ok := err.(*ogdat.CheckError); ok {
					message = append(message, ogdat.CheckMessage{
						Type:  cerr.Status,
						OGDID: elm.ID,
						Text:  fmt.Sprintf("Zeichenfolge enthält potentiell ungeeignete Zeichen ab Position %d: '%s'", cerr.Position, cerr)})
				}
			}
		case "description":
			if ok, err := ogdat.CheckOGDTextStringForSaneCharacters(*md.Description); !ok {
				if cerr, ok := err.(*ogdat.CheckError); ok {
					message = append(message, ogdat.CheckMessage{
						Type:  cerr.Status,
						OGDID: elm.ID,
						Text:  fmt.Sprintf("Zeichenfolge enthält potentiell ungeeignete Zeichen ab Position %d: '%s'", cerr.Position, cerr)})
				}
			}
		case "categorization":
			if cat := md.Extras.Categorization; cat == nil {
				message = append(message, ogdat.CheckMessage{
					Type:  2,
					OGDID: elm.ID,
					Text:  "Die Kategorisierung darf zwar mit Kardinalität 'N' optional auftreten, jedoch sollte zumindest eine Zuordnung getroffen werden"})

			} else {
				for _, element := range cat {
					if element.NumID == -1 {
						message = append(message, ogdat.CheckMessage{
							Type:  3,
							OGDID: elm.ID,
							Text:  fmt.Sprintf("Die Kategorie '%s' ist keine normierte OGD-Kategorie", element.ID)})
					}
				}
			}
		case "keywords":
			if keywords := md.Schlagworte; keywords == nil {
				message = append(message, ogdat.CheckMessage{
					Type:  2,
					OGDID: elm.ID,
					Text:  "Schlagworte dürfen zwar mit Kardinalität 'N' optional auftreten, die Angabe von Schlagworten wäre aber wünschenswert"})

			}
		case "maintainer":
			if ok, err := ogdat.CheckOGDTextStringForSaneCharacters(*md.Maintainer); !ok {
				if cerr, ok := err.(*ogdat.CheckError); ok {
					message = append(message, ogdat.CheckMessage{
						Type:  cerr.Status,
						OGDID: elm.ID,
						Text:  fmt.Sprintf("Zeichenfolge enthält potentiell ungeeignete Zeichen ab Position %d: '%s'", cerr.Position, cerr)})
				}
			}
		case "license":
			if ok, err := ogdat.CheckOGDTextStringForSaneCharacters(*md.License); !ok {
				if cerr, ok := err.(*ogdat.CheckError); ok {
					message = append(message, ogdat.CheckMessage{
						Type:  cerr.Status,
						OGDID: elm.ID,
						Text:  fmt.Sprintf("Zeichenfolge enthält potentiell ungeeignete Zeichen ab Position %d: '%s'", cerr.Position, cerr)})
				}
			}
		case "begin_datetime":
			if md.Extras.Begin_DateTime.Format != CustomTimeSpecifier1 {
				message = append(message, ogdat.CheckMessage{
					Type:  3,
					OGDID: elm.ID,
					Text:  fmt.Sprintf("Feldwert vom Typ ÖNORM ISO 8601 TM_Primitive 'YYYY-MM-DDThh:mm:ss' erwartet, Wert entspricht aber nicht diesem Typ: '%s'", md.Extras.Begin_DateTime.Raw)})
			}
			// ###################### OPTIONALE FELDER ######################
		case "schema_name":
			schemaname := md.Extras.Schema_Name
			if schemaname == nil {
				continue
			}
			if ok, err := ogdat.CheckOGDTextStringForSaneCharacters(*schemaname); !ok {
				if cerr, ok := err.(*ogdat.CheckError); ok {
					message = append(message, ogdat.CheckMessage{
						Type:  cerr.Status,
						OGDID: elm.ID,
						Text:  fmt.Sprintf("Zeichenfolge enthält potentiell ungeeignete Zeichen ab Position %d: '%s'", cerr.Position, cerr)})
				}
				const ogdschemaspec = "OGD Austria Metadata 2.0"
				if *schemaname != ogdschemaspec {
					message = append(message, ogdat.CheckMessage{
						Type:  1,
						OGDID: elm.ID,
						Text:  fmt.Sprintf("Schemabezeichnung als '%s' erwartet, der Wert ist aber '%s'", ogdschemaspec, *schemaname)})
				}
			}
		case "schema_language":
			lang := md.Extras.Schema_Language
			if lang == nil {
				continue
			}
			const ogdschemalanguage = "ger"
			if *lang != ogdschemalanguage {
				message = append(message, ogdat.CheckMessage{
					Type:  3,
					OGDID: elm.ID,
					Text:  fmt.Sprintf("Schemasprache als '%s' erwartet, der Wert ist aber '%s'", ogdschemalanguage, *lang)})
			}
		case "schema_characterset":
			charset := md.Extras.Schema_Characterset
			if charset == nil {
				continue
			}
			const ogdschemacharacterset = "utf8"
			if *charset != ogdschemacharacterset {
				message = append(message, ogdat.CheckMessage{
					Type:  3,
					OGDID: elm.ID,
					Text:  fmt.Sprintf("Characterset des Schemas als '%s' erwartet, der Wert ist aber '%s'", ogdschemacharacterset, *charset)})
			}
		case "metadata_linkage":
			for _, element := range md.Extras.Metadata_Linkage {
				if element.URL == nil {
					message = append(message, ogdat.CheckMessage{
						Type:  3,
						OGDID: elm.ID,
						Text:  fmt.Sprintf("Gültigen Verweis (Link) erwartet, der Wert '%s' stellt keinen gültigen Link dar", element.Raw)})
				}
			}
		case "attribute_description":
			desc := md.Extras.Attribute_Description
			if desc == nil {
				continue
			}
			const ogddesclen = 20
			if i := len(*desc); i < ogddesclen {
				message = append(message, ogdat.CheckMessage{
					Type:  2,
					OGDID: elm.ID,
					Text:  fmt.Sprintf("Beschreibung enthält weniger als %d Zeichen", i)})

			}
			if ok, err := ogdat.CheckOGDTextStringForSaneCharacters(*desc); !ok {
				if cerr, ok := err.(*ogdat.CheckError); ok {
					message = append(message, ogdat.CheckMessage{
						Type:  cerr.Status,
						OGDID: elm.ID,
						Text:  fmt.Sprintf("Zeichenfolge enthält potentiell ungeeignete Zeichen ab Position %d: '%s'", cerr.Position, cerr)})
				}
			}
		case "maintainer_link":
			link := md.Extras.Maintainer_Link
			if link == nil {
				continue
			}
			if link.URL == nil {
				message = append(message, ogdat.CheckMessage{
					Type:  3,
					OGDID: elm.ID,
					Text:  fmt.Sprintf("Gültigen Verweis (Link) erwartet, der Wert '%s' stellt keinen gültigen Link dar", link.Raw)})
			}
		case "publisher":
			publisher := md.Extras.Publisher
			if publisher == nil {
				continue
			}
			if ok, err := ogdat.CheckOGDTextStringForSaneCharacters(*publisher); !ok {
				if cerr, ok := err.(*ogdat.CheckError); ok {
					message = append(message, ogdat.CheckMessage{
						Type:  cerr.Status,
						OGDID: elm.ID,
						Text:  fmt.Sprintf("Zeichenfolge enthält potentiell ungeeignete Zeichen ab Position %d: '%s'", cerr.Position, cerr)})
				}
			}
		case "geographic_toponym":
			toponym := md.Extras.Geographich_Toponym
			if toponym == nil {
				continue
			}
			if ok, err := ogdat.CheckOGDTextStringForSaneCharacters(*toponym); !ok {
				if cerr, ok := err.(*ogdat.CheckError); ok {
					message = append(message, ogdat.CheckMessage{
						Type:  cerr.Status,
						OGDID: elm.ID,
						Text:  fmt.Sprintf("Zeichenfolge enthält potentiell ungeeignete Zeichen ab Position %d: '%s'", cerr.Position, cerr)})
				}
			}
		case "geographic_bbox":
			bbox := md.Extras.Geographic_BBox
			if bbox == nil {
				continue
			}
			if ok, err := ogdat.CheckOGDBBox(*bbox); !ok {
				message = append(message, ogdat.CheckMessage{
					Type:  3,
					OGDID: elm.ID,
					Text:  fmt.Sprintf("Zeichenfolge enthält keinen gültigen WKT für die örtliche Begrenzung (Boundingbox): '%s'", err)})
			}
		case "end_datetime":
			endtime := md.Extras.End_DateTime
			if endtime == nil {
				continue
			}
			if endtime.Format != CustomTimeSpecifier1 {
				message = append(message, ogdat.CheckMessage{
					Type:  3,
					OGDID: elm.ID,
					Text:  fmt.Sprintf("Feldwert vom Typ ÖNORM ISO 8601 TM_Primitive 'YYYY-MM-DDThh:mm:ss' erwartet, Wert entspricht aber nicht diesem Typ: '%s'", endtime.Raw)})
			}
		case "update_frequency":
			frequency := md.Extras.Update_Frequency
			if frequency == nil {
				continue
			}
		}
	}
	return

	/*
		 		case "resource_url":
			  if keywords := md.Resources.Url; keywords == nil {
			  message = append(message, ogdat.CheckMessage{
						Type:  2,
						OGDID: elm.ID,
						Text:  "Schlagworte dürfen zwar mit Karidnalität 'N' optional auftreten, die Angabe von Schlagworten wäre aber wünschenswert"})
	*/
}

func init() {
	isolangfilemap = loadisolanguagefile(iso639file)
}
<|endoftext|>


<commit_before>// Copyright (c) 2014 The SurgeMQ Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package service

import (
	"bufio"
	"fmt"
	"io"
	"sync/atomic"
//"github.com/surgemq/message"
	"encoding/binary"
	"math"
	"sync"
)

var (
	bufcnt int64
	DefaultBufferSize int64

	DeviceInBufferSize int64
	DeviceOutBufferSize int64

	MasterInBufferSize int64
	MasterOutBufferSize int64
)

const (
	smallReadBlockSize = 512
	defaultReadBlockSize = 8192
	defaultWriteBlockSize = 8192
)

/**
2016.03.03 修改
bingbuffer结构体
 */
type buffer struct {
	readIndex  int64        //读序号
	writeIndex int64        //写序号
	ringBuffer []*ByteArray //环形buffer指针数组
	bufferSize int64        //初始化环形buffer指针数组大小
	mask       int64        //掩码：bufferSize-1
	done       int64        //是否完成
	rcond      *sync.Cond
	wcond      *sync.Cond
}

type ByteArray struct {
	bArray []byte
}

func (this *buffer)ReadCommit(index int64) {
	this.rcond.L.Lock()
	defer this.rcond.L.Unlock()
	this.ringBuffer[index] = nil
	this.rcond.Broadcast()
	this.wcond.Broadcast()
}

func (this *buffer) Len() int {
	cpos := this.GetCurrentReadIndex()
	ppos := this.GetCurrentWriteIndex()
	return int(ppos - cpos)
}

/**
2016.03.03 添加
初始化ringbuffer
参数bufferSize：初始化环形buffer指针数组大小
 */
func newBuffer(size int64) (*buffer, error) {
	if size < 0 {
		return nil, bufio.ErrNegativeCount
	}
	if size == 0 {
		size = DefaultBufferSize
	}
	if !powerOfTwo64(size) {
		fmt.Printf("Size must be power of two. Try %d.", roundUpPowerOfTwo64(size))
		return nil, fmt.Errorf("Size must be power of two. Try %d.", roundUpPowerOfTwo64(size))
	}

	return &buffer{
		readIndex: int64(0), //读序号
		writeIndex: int64(0), //写序号
		ringBuffer: make([]*ByteArray, size), //环形buffer指针数组
		bufferSize: size, //初始化环形buffer指针数组大小
		mask:size - 1,
		rcond:         sync.NewCond(new(sync.Mutex)),
		wcond:         sync.NewCond(new(sync.Mutex)),
	}, nil
}

/**
2016.03.03 添加
获取当前读序号
 */
func (this *buffer)GetCurrentReadIndex() (int64) {
	return atomic.LoadInt64(&this.readIndex)
}
/**
2016.03.03 添加
获取当前写序号
 */
func (this *buffer)GetCurrentWriteIndex() (int64) {
	return atomic.LoadInt64(&this.writeIndex)
}

/**
2016.03.03 添加
读取ringbuffer指定的buffer指针，返回该指针并清空ringbuffer该位置存在的指针内容，以及将读序号加1
 */
func (this *buffer)ReadBuffer() ([]byte, int64, bool) {
	this.rcond.L.Lock()
	defer this.rcond.L.Unlock()

	for {
		readIndex := atomic.LoadInt64(&this.readIndex)
		writeIndex := atomic.LoadInt64(&this.writeIndex)
		switch  {
		case readIndex >= writeIndex:
			this.rcond.Wait()
		case writeIndex - readIndex > this.bufferSize:
			this.rcond.Wait()
		default:
			if readIndex == math.MaxInt64 {
				atomic.StoreInt64(&this.readIndex, int64(0))
			}else {
				atomic.AddInt64(&this.readIndex, int64(1))
			}

			index := readIndex & this.mask

			p_ := this.ringBuffer[index]
			//this.ringBuffer[index] = nil
			if p_ == nil {
				return nil, -1, false
			}
			p := p_.bArray

			if p == nil {
				return nil, -1, false
			}

			return p, index, true
		}
	}

}

/**
2016.03.03 添加
写入ringbuffer指针，以及将写序号加1
 */
func (this *buffer)WriteBuffer(in []byte) (bool) {
	this.wcond.L.Lock()
	defer this.wcond.L.Unlock()

	for {
		readIndex := atomic.LoadInt64(&this.readIndex)
		writeIndex := atomic.LoadInt64(&this.writeIndex)
		switch  {
		case writeIndex - readIndex < 0:
			this.wcond.Wait()
		default:
			index := writeIndex & this.mask
			if writeIndex == math.MaxInt64 {
				atomic.StoreInt64(&this.writeIndex, int64(0))
			}else {
				atomic.AddInt64(&this.writeIndex, int64(1))
			}
			if this.ringBuffer[index] == nil {
				this.ringBuffer[index] = &ByteArray{bArray:in}
				this.rcond.Broadcast()
				return true
			}else {
				this.rcond.Broadcast()
				return false
			}
		}
	}

}

/**
2016.03.03 修改
完成
 */
func (this *buffer) Close() error {
	atomic.StoreInt64(&this.done, 1)
	atomic.StoreInt64(&this.done, 1)

	this.wcond.L.Lock()
	this.rcond.Broadcast()
	this.wcond.L.Unlock()

	this.rcond.L.Lock()
	this.wcond.Broadcast()
	this.rcond.L.Unlock()

	return nil
}
/*

/**
2016.03.03 修改
向ringbuffer中写数据（从connection的中向ringbuffer中写）--生产者
*/
func (this *buffer) ReadFrom(r io.Reader) (int64, error) {
	defer this.Close()
	for {
		total := int64(0)
		if this.isDone() {
			return total, io.EOF
		}
		b := make([]byte, 5)
		n, err := r.Read(b[0:1])

		if n > 0 {
			total += int64(n)
			if err != nil {
				return total, err
			}
		}

		/**************************/
		cnt := 1

		// Let's read enough bytes to get the message header (msg type, remaining length)
		for {
			// If we have read 5 bytes and still not done, then there's a problem.
			if cnt > 4 {
				return 0, fmt.Errorf("sendrecv/peekMessageSize: 4th byte of remaining length has continuation bit set")
			}

			Log.Infoc(func() string {
				return fmt.Sprintf("sendrecv/peekMessageSize: %d=========", cnt)
			})
			// Peek cnt bytes from the input buffer.
			_, err := r.Read(b[cnt:cnt + 1])
			if err != nil {
				return 0, err
			}

			// If we got enough bytes, then check the last byte to see if the continuation
			// bit is set. If so, increment cnt and continue peeking
			if b[cnt] >= 0x80 {
				cnt++
			} else {
				break
			}
		}

		// Get the remaining length of the message
		remlen, m := binary.Uvarint(b[1:])
		Log.Infoc(func() string {
			return fmt.Sprintf("remlen: %d===%d", remlen,m)
		})
		// Total message length is remlen + 1 (msg type) + m (remlen bytes)

		remlen_ := int64(remlen)
		total = remlen_ + int64(1) + int64(m)
		Log.Infoc(func() string {
			return fmt.Sprintf("remlen===n===totle: %d===%d===%d", remlen,m,total)
		})
		//mtype := message.MessageType(b[0] >> 4)
		/****************/
		//var msg message.Message
		//
		//msg, err = mtype.New()
		//if err != nil {
		//	return 0, err
		//}
		b_ := make([]byte, remlen_)
		_, err = r.Read(b_[0:])
		if err != nil {
			return 0, err
		}

		b = append(b, b_...)
		Log.Infoc(func() string {
			return fmt.Sprintf("len(b):", len(b))
		})
		//n, err = msg.Decode(b)
		//if err != nil {
		//	return 0, err
		//}

		/*************************/

		if this.WriteBuffer(b) {
			return total, err
		}

		return total, nil
	}
}

/**
2016.03.03 修改
 */
func (this *buffer) WriteTo(w io.Writer) (int64, error) {
	defer this.Close()
	total := int64(0)
	for {
		if this.isDone() {
			return total, io.EOF
		}
		p, index, ok := this.ReadBuffer()
		defer this.ReadCommit(index)
		if !ok {
			return total, io.EOF
		}

		Log.Debugc(func() string {
			return fmt.Sprintf("defer this.ReadCommit(%s)", index)
		})
		Log.Debugc(func() string {
			return fmt.Sprintf("WriteTo函数》》读取*p：" + string(p))
		})

		Log.Debugc(func() string {
			return fmt.Sprintf(" WriteTo(w io.Writer)(7)")
		})
		//
		//Log.Errorc(func() string {
		//	return fmt.Sprintf("msg::" + msg.Name())
		//})
		//
		//p := make([]byte, msg.Len())
		//_, err := msg.Encode(p)
		//if err != nil {
		//	Log.Errorc(func() string {
		//		return fmt.Sprintf("msg.Encode(p)")
		//	})
		//	return total, io.EOF
		//}
		// There's some data, let's process it first
		if len(p) > 0 {
			n, err := w.Write(p)
			total += int64(n)
			Log.Debugc(func() string {
				return fmt.Sprintf("Wrote %d bytes, totaling %d bytes", n, total)
			})

			if err != nil {
				Log.Errorc(func() string {
					return fmt.Sprintf("w.Write(p) error")
				})
				return total, err
			}
		}

		return total, nil
	}
}

/**
2016.03.03 修改
*/
func (this *buffer) isDone() bool {
	if atomic.LoadInt64(&this.done) == 1 {
		return true
	}

	return false
}

func powerOfTwo64(n int64) bool {
	return n != 0 && (n & (n - 1)) == 0
}

func roundUpPowerOfTwo64(n int64) int64 {
	n--
	n |= n >> 1
	n |= n >> 2
	n |= n >> 4
	n |= n >> 8
	n |= n >> 16
	n |= n >> 32
	n++

	return n
}
<commit_msg>添加测试代码<commit_after>// Copyright (c) 2014 The SurgeMQ Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package service

import (
	"bufio"
	"fmt"
	"io"
	"sync/atomic"
//"github.com/surgemq/message"
	"encoding/binary"
	"math"
	"sync"
)

var (
	bufcnt int64
	DefaultBufferSize int64

	DeviceInBufferSize int64
	DeviceOutBufferSize int64

	MasterInBufferSize int64
	MasterOutBufferSize int64
)

const (
	smallReadBlockSize = 512
	defaultReadBlockSize = 8192
	defaultWriteBlockSize = 8192
)

/**
2016.03.03 修改
bingbuffer结构体
 */
type buffer struct {
	readIndex  int64        //读序号
	writeIndex int64        //写序号
	ringBuffer []*ByteArray //环形buffer指针数组
	bufferSize int64        //初始化环形buffer指针数组大小
	mask       int64        //掩码：bufferSize-1
	done       int64        //是否完成
	rcond      *sync.Cond
	wcond      *sync.Cond
}

type ByteArray struct {
	bArray []byte
}

func (this *buffer)ReadCommit(index int64) {
	this.rcond.L.Lock()
	defer this.rcond.L.Unlock()
	this.ringBuffer[index] = nil
	this.rcond.Broadcast()
	this.wcond.Broadcast()
}

func (this *buffer) Len() int {
	cpos := this.GetCurrentReadIndex()
	ppos := this.GetCurrentWriteIndex()
	return int(ppos - cpos)
}

/**
2016.03.03 添加
初始化ringbuffer
参数bufferSize：初始化环形buffer指针数组大小
 */
func newBuffer(size int64) (*buffer, error) {
	if size < 0 {
		return nil, bufio.ErrNegativeCount
	}
	if size == 0 {
		size = DefaultBufferSize
	}
	if !powerOfTwo64(size) {
		fmt.Printf("Size must be power of two. Try %d.", roundUpPowerOfTwo64(size))
		return nil, fmt.Errorf("Size must be power of two. Try %d.", roundUpPowerOfTwo64(size))
	}

	return &buffer{
		readIndex: int64(0), //读序号
		writeIndex: int64(0), //写序号
		ringBuffer: make([]*ByteArray, size), //环形buffer指针数组
		bufferSize: size, //初始化环形buffer指针数组大小
		mask:size - 1,
		rcond:         sync.NewCond(new(sync.Mutex)),
		wcond:         sync.NewCond(new(sync.Mutex)),
	}, nil
}

/**
2016.03.03 添加
获取当前读序号
 */
func (this *buffer)GetCurrentReadIndex() (int64) {
	return atomic.LoadInt64(&this.readIndex)
}
/**
2016.03.03 添加
获取当前写序号
 */
func (this *buffer)GetCurrentWriteIndex() (int64) {
	return atomic.LoadInt64(&this.writeIndex)
}

/**
2016.03.03 添加
读取ringbuffer指定的buffer指针，返回该指针并清空ringbuffer该位置存在的指针内容，以及将读序号加1
 */
func (this *buffer)ReadBuffer() ([]byte, int64, bool) {
	this.rcond.L.Lock()
	defer this.rcond.L.Unlock()

	for {
		readIndex := atomic.LoadInt64(&this.readIndex)
		writeIndex := atomic.LoadInt64(&this.writeIndex)
		switch  {
		case readIndex >= writeIndex:
			this.rcond.Wait()
		case writeIndex - readIndex > this.bufferSize:
			this.rcond.Wait()
		default:
			if readIndex == math.MaxInt64 {
				atomic.StoreInt64(&this.readIndex, int64(0))
			}else {
				atomic.AddInt64(&this.readIndex, int64(1))
			}

			index := readIndex & this.mask

			p_ := this.ringBuffer[index]
			//this.ringBuffer[index] = nil
			if p_ == nil {
				return nil, -1, false
			}
			p := p_.bArray

			if p == nil {
				return nil, -1, false
			}

			return p, index, true
		}
	}

}

/**
2016.03.03 添加
写入ringbuffer指针，以及将写序号加1
 */
func (this *buffer)WriteBuffer(in []byte) (bool) {
	this.wcond.L.Lock()
	defer this.wcond.L.Unlock()

	for {
		readIndex := atomic.LoadInt64(&this.readIndex)
		writeIndex := atomic.LoadInt64(&this.writeIndex)
		switch  {
		case writeIndex - readIndex < 0:
			this.wcond.Wait()
		default:
			index := writeIndex & this.mask
			if writeIndex == math.MaxInt64 {
				atomic.StoreInt64(&this.writeIndex, int64(0))
			}else {
				atomic.AddInt64(&this.writeIndex, int64(1))
			}
			if this.ringBuffer[index] == nil {
				this.ringBuffer[index] = &ByteArray{bArray:in}
				this.rcond.Broadcast()
				return true
			}else {
				this.rcond.Broadcast()
				return false
			}
		}
	}

}

/**
2016.03.03 修改
完成
 */
func (this *buffer) Close() error {
	atomic.StoreInt64(&this.done, 1)
	atomic.StoreInt64(&this.done, 1)

	this.wcond.L.Lock()
	this.rcond.Broadcast()
	this.wcond.L.Unlock()

	this.rcond.L.Lock()
	this.wcond.Broadcast()
	this.rcond.L.Unlock()

	return nil
}
/*

/**
2016.03.03 修改
向ringbuffer中写数据（从connection的中向ringbuffer中写）--生产者
*/
func (this *buffer) ReadFrom(r io.Reader) (int64, error) {
	defer this.Close()
	for {
		total := int64(0)
		if this.isDone() {
			return total, io.EOF
		}
		b := make([]byte,0, 5)
		n, err := r.Read(b[0:1])

		if n > 0 {
			total += int64(n)
			if err != nil {
				return total, err
			}
		}

		/**************************/
		cnt := 1

		// Let's read enough bytes to get the message header (msg type, remaining length)
		for {
			// If we have read 5 bytes and still not done, then there's a problem.
			if cnt > 4 {
				return 0, fmt.Errorf("sendrecv/peekMessageSize: 4th byte of remaining length has continuation bit set")
			}

			Log.Infoc(func() string {
				return fmt.Sprintf("sendrecv/peekMessageSize: %d=========", cnt)
			})
			// Peek cnt bytes from the input buffer.
			_, err := r.Read(b[cnt:cnt + 1])
			if err != nil {
				return 0, err
			}

			// If we got enough bytes, then check the last byte to see if the continuation
			// bit is set. If so, increment cnt and continue peeking
			if b[cnt] >= 0x80 {
				cnt++
			} else {
				break
			}
		}

		// Get the remaining length of the message
		remlen, m := binary.Uvarint(b[1:])
		Log.Infoc(func() string {
			return fmt.Sprintf("remlen: %d===%d", remlen,m)
		})
		// Total message length is remlen + 1 (msg type) + m (remlen bytes)

		remlen_ := int64(remlen)
		total = remlen_ + int64(1) + int64(m)
		Log.Infoc(func() string {
			return fmt.Sprintf("remlen===n===totle: %d===%d===%d", remlen,m,total)
		})
		//mtype := message.MessageType(b[0] >> 4)
		/****************/
		//var msg message.Message
		//
		//msg, err = mtype.New()
		//if err != nil {
		//	return 0, err
		//}
		b_ := make([]byte,0, remlen_)
		_, err = r.Read(b_[0:])
		if err != nil {
			return 0, err
		}

		b = append(b, b_...)
		Log.Infoc(func() string {
			return fmt.Sprintf("len(b):%d", len(b))
		})
		//n, err = msg.Decode(b)
		//if err != nil {
		//	return 0, err
		//}

		/*************************/

		if this.WriteBuffer(b) {
			return total, err
		}

		return total, nil
	}
}

/**
2016.03.03 修改
 */
func (this *buffer) WriteTo(w io.Writer) (int64, error) {
	defer this.Close()
	total := int64(0)
	for {
		if this.isDone() {
			return total, io.EOF
		}
		p, index, ok := this.ReadBuffer()
		defer this.ReadCommit(index)
		if !ok {
			return total, io.EOF
		}

		Log.Debugc(func() string {
			return fmt.Sprintf("defer this.ReadCommit(%s)", index)
		})
		Log.Debugc(func() string {
			return fmt.Sprintf("WriteTo函数》》读取*p：" + string(p))
		})

		Log.Debugc(func() string {
			return fmt.Sprintf(" WriteTo(w io.Writer)(7)")
		})
		//
		//Log.Errorc(func() string {
		//	return fmt.Sprintf("msg::" + msg.Name())
		//})
		//
		//p := make([]byte, msg.Len())
		//_, err := msg.Encode(p)
		//if err != nil {
		//	Log.Errorc(func() string {
		//		return fmt.Sprintf("msg.Encode(p)")
		//	})
		//	return total, io.EOF
		//}
		// There's some data, let's process it first
		if len(p) > 0 {
			n, err := w.Write(p)
			total += int64(n)
			Log.Debugc(func() string {
				return fmt.Sprintf("Wrote %d bytes, totaling %d bytes", n, total)
			})

			if err != nil {
				Log.Errorc(func() string {
					return fmt.Sprintf("w.Write(p) error")
				})
				return total, err
			}
		}

		return total, nil
	}
}

/**
2016.03.03 修改
*/
func (this *buffer) isDone() bool {
	if atomic.LoadInt64(&this.done) == 1 {
		return true
	}

	return false
}

func powerOfTwo64(n int64) bool {
	return n != 0 && (n & (n - 1)) == 0
}

func roundUpPowerOfTwo64(n int64) int64 {
	n--
	n |= n >> 1
	n |= n >> 2
	n |= n >> 4
	n |= n >> 8
	n |= n >> 16
	n |= n >> 32
	n++

	return n
}
<|endoftext|>